From 0cb93bd62f2e4820a8e981c8a30a11c22f748a39 Mon Sep 17 00:00:00 2001 From: Mike Panetta Date: Sat, 23 Dec 2023 22:29:32 -0500 Subject: [PATCH 01/10] Add some more fields and VDO's. --- usb-pd/src/message.rs | 14 ++-- usb-pd/src/pdo.rs | 183 +++++++++++++++++++++++++++++++++++++++++- usb-pd/src/sink.rs | 10 ++- 3 files changed, 193 insertions(+), 14 deletions(-) diff --git a/usb-pd/src/message.rs b/usb-pd/src/message.rs index 0b5ed21..e94090e 100644 --- a/usb-pd/src/message.rs +++ b/usb-pd/src/message.rs @@ -19,7 +19,7 @@ pub enum Message { Reject, Ready, SourceCapabilities(Vec), - VendorDefined(VDMHeader), // TODO: Incomplete + VendorDefined((VDMHeader, Vec)), // TODO: Incomplete SoftReset, Unknown, } @@ -57,9 +57,9 @@ impl Message { ), MessageType::Data(DataMessageType::VendorDefined) => { // Keep for now... - // let len = payload.len(); - // let num_obj = header.num_objects(); - //debug!("VENDOR: {:?}, {:?}, {:?}", len, num_obj, payload); + let len = payload.len(); + let num_obj = header.num_objects(); + trace!("VENDOR: {:?}, {:?}, {:x}", len, num_obj, payload); let header = { let raw = VDMHeaderRaw(LittleEndian::read_u32(&payload[..4])); @@ -71,7 +71,9 @@ impl Message { } }; - trace!("VDM RX: {:?}", header); + let data = payload[4..].chunks_exact(4).take(7).map(|buf| LittleEndian::read_u32(buf)).collect::>(); + + trace!("VDM RX: {:?} {:?}", header, data); // trace!("HEADER: VDM:: TYPE: {:?}, VERS: {:?}", header.vdm_type(), // header.vdm_version()); trace!("HEADER: CMD:: TYPE: {:?}, CMD: // {:?}", header.command_type(), header.command()); @@ -83,7 +85,7 @@ impl Message { // .map(|i| i[0]) // .collect::>(); - Message::VendorDefined(header) + Message::VendorDefined((header, data)) } _ => { warn!("unknown message type"); diff --git a/usb-pd/src/pdo.rs b/usb-pd/src/pdo.rs index fd4710b..bda0437 100644 --- a/usb-pd/src/pdo.rs +++ b/usb-pd/src/pdo.rs @@ -190,6 +190,7 @@ pub enum VDMCommand { DisplayPortStatus, DisplayPortConfig, } + impl From for u8 { fn from(value: VDMCommand) -> Self { match value { @@ -252,6 +253,13 @@ pub enum VDMHeader { Unstructured(VDMHeaderUnstructured), } +#[derive(Clone, Copy, Format)] +pub enum VendorDataObject { + IDHeader(VDMIdentityHeader), + CertStat(CertStatVDO), + Product(ProductVDO), +} + bitfield! { #[derive(Clone, Copy, PartialEq, Eq, Format)] pub struct VDMHeaderRaw(pub u32): Debug, FromRaw, IntoRaw { @@ -276,7 +284,8 @@ bitfield! { /// VDM Type (Unstructured/Structured) pub vdm_type: bool [VDMType] @ 15, /// Structured VDM Version - pub vdm_version: u8 @ 13..=14, + pub vdm_version_major: u8 @ 13..=14, + pub vdm_version_minor: u8 @ 11..=12, /// Object Position pub object_position: u8 @ 8..=10, /// Command Type @@ -292,6 +301,56 @@ impl VDMHeaderStructured { } } +#[derive(Clone, Copy, Format)] +pub enum VDMVersionMajor { + Version10, + Version2x, +} + +impl From for u8 { + fn from(value: VDMVersionMajor) -> Self { + match value { + VDMVersionMajor::Version10 => 0b00, + VDMVersionMajor::Version2x => 0b01, + } + } +} + +impl From for VDMVersionMajor { + fn from(value: u8) -> Self { + match value { + 0b00 => VDMVersionMajor::Version10, + 0b01 => VDMVersionMajor::Version2x, + _ => panic!("Cannot convert {:} to VDMVersionMajor", value), // Illegal values shall panic. + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum VDMVersionMinor { + Version20, + Version21, +} + +impl From for u8 { + fn from(value: VDMVersionMinor) -> Self { + match value { + VDMVersionMinor::Version20 => 0b00, + VDMVersionMinor::Version21 => 0b01, + } + } +} + +impl From for VDMVersionMinor { + fn from(value: u8) -> Self { + match value { + 0b00 => VDMVersionMinor::Version20, + 0b01 => VDMVersionMinor::Version21, + _ => panic!("Cannot convert {:} to VDMVersionMinor", value), // Illegal values shall panic. + } + } +} + bitfield! { #[derive(Clone, Copy, PartialEq, Eq, Format)] pub struct VDMHeaderUnstructured(pub u32): Debug, FromRaw, IntoRaw { @@ -318,13 +377,13 @@ bitfield! { /// Device data capable pub device_data: bool @ 30, /// Product type UFP - pub product_type_ufp: u8 @ 27..=29, + pub product_type_ufp: u8 [SOPProductTypeUFP] @ 27..=29, /// Modal Operation Supported pub modal_supported: bool @ 26, /// Product type DFP - pub product_type_dfp: u8 @ 23..=25, + pub product_type_dfp: u8 [SOPProductTypeDFP] @ 23..=25, /// Connector type - pub connector_type: u8 @ 21..=22, + pub connector_type: u8 [ConnectorType] @ 21..=22, /// VID pub vid: u16 @ 0..=15, } @@ -336,6 +395,122 @@ impl VDMIdentityHeader { } } +#[derive(Clone, Copy, Format)] +pub enum SOPProductTypeUFP { + NotUFP, + PDUSBHub, + PDUSBPeripheral, + PSD, +} + +impl From for u8 { + fn from(value: SOPProductTypeUFP) -> Self { + match value { + SOPProductTypeUFP::NotUFP => 0b000, + SOPProductTypeUFP::PDUSBHub => 0b001, + SOPProductTypeUFP::PDUSBPeripheral => 0b010, + SOPProductTypeUFP::PSD => 0b011, + } + } +} + +impl From for SOPProductTypeUFP { + fn from(value: u8) -> Self { + match value { + 0b000 => SOPProductTypeUFP::NotUFP, + 0b001 => SOPProductTypeUFP::PDUSBHub, + 0b010 => SOPProductTypeUFP::PDUSBPeripheral, + 0b011 => SOPProductTypeUFP::PSD, + + _ => panic!("Cannot convert {:} to SOPProductTypeUFP", value), // Illegal values shall panic. + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum SOPProductTypeDFP { + NotDFP, + PDUSBHub, + PDUSBHost, + PowerBrick, +} + +impl From for u8 { + fn from(value: SOPProductTypeDFP) -> Self { + match value { + SOPProductTypeDFP::NotDFP => 0b000, + SOPProductTypeDFP::PDUSBHub => 0b001, + SOPProductTypeDFP::PDUSBHost => 0b010, + SOPProductTypeDFP::PowerBrick => 0b011, + } + } +} + +impl From for SOPProductTypeDFP { + fn from(value: u8) -> Self { + match value { + 0b000 => SOPProductTypeDFP::NotDFP, + 0b001 => SOPProductTypeDFP::PDUSBHub, + 0b010 => SOPProductTypeDFP::PDUSBHost, + 0b011 => SOPProductTypeDFP::PowerBrick, + + _ => panic!("Cannot convert {:} to SOPProductTypeDFP", value), // Illegal values shall panic. + } + } +} + +pub enum ConnectorType { + USBTypeCReceptacle, + USBTypeCPlug, +} + +impl From for u8 { + fn from(value: ConnectorType) -> Self { + match value { + ConnectorType::USBTypeCReceptacle => 0b10, + ConnectorType::USBTypeCPlug => 0b11, + } + } +} + +impl From for ConnectorType { + fn from(value: u8) -> Self { + match value { + 0b10 => ConnectorType::USBTypeCReceptacle, + 0b11 => ConnectorType::USBTypeCPlug, + _ => panic!("Cannot convert {:} to ConnectorType", value), // Illegal values shall panic. + } + } +} +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format)] + pub struct CertStatVDO(pub u32): FromRaw, IntoRaw { + /// XID + pub xid: u32 @ 0..=31, + } +} + +impl CertStatVDO { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format)] + pub struct ProductVDO(pub u32): FromRaw, IntoRaw { + /// USB Product ID + pub pid: u16 @ 16..=31, + pub bcd_device: u16 @ 0..=15, + } +} + +impl ProductVDO { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + bitfield! { #[derive(Clone, Copy, PartialEq, Eq, Format)] pub struct DisplayPortCapabilities(pub u32): Debug, FromRaw, IntoRaw { diff --git a/usb-pd/src/sink.rs b/usb-pd/src/sink.rs index 1227e85..175e81b 100644 --- a/usb-pd/src/sink.rs +++ b/usb-pd/src/sink.rs @@ -2,7 +2,7 @@ use { crate::{ header::{DataMessageType, Header, SpecificationRevision}, message::Message, - pdo::{FixedVariableRequestDataObject, PowerDataObject}, + pdo::{FixedVariableRequestDataObject, PowerDataObject, VDMHeader}, PowerRole, }, core::future::Future, @@ -38,6 +38,8 @@ pub enum Event { PowerRejected, /// Requested power is now ready PowerReady, + /// VDM received + VDMReceived((VDMHeader, Vec)), } /// Requests made to sink @@ -158,8 +160,8 @@ impl Sink { Some(Event::PowerReady) } Message::SourceCapabilities(caps) => Some(Event::SourceCapabilitiesChanged(caps)), - Message::VendorDefined(payload) => { - match payload { + Message::VendorDefined((hdr, data)) => { + match hdr { crate::pdo::VDMHeader::Structured(hdr) => { warn!( "UNHANDLED: Structured VDM! CMD_TYPE: {:?}, CMD: @@ -177,7 +179,7 @@ impl Sink { ); } } - None + Some(Event::VDMReceived((hdr, data))) } Message::SoftReset => { warn!("UNHANDLED: Soft RESET request."); From eee56204f81d04e7c9547c88982b30e3878f2b22 Mon Sep 17 00:00:00 2001 From: Mike Panetta Date: Sun, 24 Dec 2023 01:05:51 -0500 Subject: [PATCH 02/10] Add some more fields and VDO's. --- usb-pd/src/pdo.rs | 193 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 185 insertions(+), 8 deletions(-) diff --git a/usb-pd/src/pdo.rs b/usb-pd/src/pdo.rs index bda0437..94cf72f 100644 --- a/usb-pd/src/pdo.rs +++ b/usb-pd/src/pdo.rs @@ -253,11 +253,65 @@ pub enum VDMHeader { Unstructured(VDMHeaderUnstructured), } -#[derive(Clone, Copy, Format)] +impl VDMHeader { + pub fn to_bytes(&self, buf: &mut [u8]) { + match self { + VDMHeader::Structured(header) => header.to_bytes(buf), + VDMHeader::Unstructured(header) => header.to_bytes(buf), + } + } +} + +impl From for u32 { + fn from(value: VDMHeader) -> Self { + match value { + VDMHeader::Structured(header) => header.into(), + VDMHeader::Unstructured(header) => header.into(), + } + } +} + +impl From for VDMHeader { + fn from(value: u32) -> Self { + let header = VDMHeaderRaw(value); + match header.vdm_type() { + VDMType::Structured => VDMHeader::Structured(VDMHeaderStructured(value)), + VDMType::Unstructured => VDMHeader::Unstructured(VDMHeaderUnstructured(value)), + } + } +} + +#[derive(Clone, Copy, Format, Debug)] pub enum VendorDataObject { + VDMHeader(VDMHeader), IDHeader(VDMIdentityHeader), CertStat(CertStatVDO), Product(ProductVDO), + UFPType(UFPTypeVDO), +} + +impl VendorDataObject { + pub fn to_bytes(&self, buf: &mut [u8]) { + match self { + VendorDataObject::VDMHeader(header) => header.to_bytes(buf), + VendorDataObject::IDHeader(header) => header.to_bytes(buf), + VendorDataObject::CertStat(header) => header.to_bytes(buf), + VendorDataObject::Product(header) => header.to_bytes(buf), + VendorDataObject::UFPType(header) => header.to_bytes(buf), + } + } +} + +impl From for u32 { + fn from(value: VendorDataObject) -> Self { + match value { + VendorDataObject::VDMHeader(header) => header.into(), + VendorDataObject::IDHeader(header) => header.into(), + VendorDataObject::CertStat(header) => header.into(), + VendorDataObject::Product(header) => header.into(), + VendorDataObject::UFPType(header) => header.into(), + } + } } bitfield! { @@ -321,7 +375,8 @@ impl From for VDMVersionMajor { match value { 0b00 => VDMVersionMajor::Version10, 0b01 => VDMVersionMajor::Version2x, - _ => panic!("Cannot convert {:} to VDMVersionMajor", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to VDMVersionMajor", value), /* Illegal values shall + * panic. */ } } } @@ -346,7 +401,8 @@ impl From for VDMVersionMinor { match value { 0b00 => VDMVersionMinor::Version20, 0b01 => VDMVersionMinor::Version21, - _ => panic!("Cannot convert {:} to VDMVersionMinor", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to VDMVersionMinor", value), /* Illegal values shall + * panic. */ } } } @@ -422,7 +478,8 @@ impl From for SOPProductTypeUFP { 0b010 => SOPProductTypeUFP::PDUSBPeripheral, 0b011 => SOPProductTypeUFP::PSD, - _ => panic!("Cannot convert {:} to SOPProductTypeUFP", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to SOPProductTypeUFP", value), /* Illegal values + * shall panic. */ } } } @@ -454,7 +511,8 @@ impl From for SOPProductTypeDFP { 0b010 => SOPProductTypeDFP::PDUSBHost, 0b011 => SOPProductTypeDFP::PowerBrick, - _ => panic!("Cannot convert {:} to SOPProductTypeDFP", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to SOPProductTypeDFP", value), /* Illegal values + * shall panic. */ } } } @@ -478,12 +536,13 @@ impl From for ConnectorType { match value { 0b10 => ConnectorType::USBTypeCReceptacle, 0b11 => ConnectorType::USBTypeCPlug, - _ => panic!("Cannot convert {:} to ConnectorType", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to ConnectorType", value), /* Illegal values shall + * panic. */ } } } bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format)] + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] pub struct CertStatVDO(pub u32): FromRaw, IntoRaw { /// XID pub xid: u32 @ 0..=31, @@ -497,7 +556,7 @@ impl CertStatVDO { } bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format)] + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] pub struct ProductVDO(pub u32): FromRaw, IntoRaw { /// USB Product ID pub pid: u16 @ 16..=31, @@ -511,6 +570,124 @@ impl ProductVDO { } } +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] + pub struct UFPTypeVDO(pub u32): FromRaw, IntoRaw { + /// USB Product ID + pub version: u8 @ 29..=31, + pub device_capability: u8 @ 24..=27, + pub vconn_power: u8 @ 8..=10, + pub vconn_required: bool @ 7, + pub vbus_required: bool @ 6, + pub alternate_modes: u8 @ 3..=5, + pub usb_highest_speed: u8 @ 0..=2, + } +} + +impl UFPTypeVDO { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +#[derive(Clone, Copy, Format)] +pub enum USBHighestSpeed { + USB20Only, + USB32Gen1, + USB32Gen2, + USB40Gen3, + USB40Gen4, +} + +impl From for u8 { + fn from(value: USBHighestSpeed) -> Self { + match value { + USBHighestSpeed::USB20Only => 0b000, + USBHighestSpeed::USB32Gen1 => 0b001, + USBHighestSpeed::USB32Gen2 => 0b010, + USBHighestSpeed::USB40Gen3 => 0b011, + USBHighestSpeed::USB40Gen4 => 0b100, + } + } +} + +impl From for USBHighestSpeed { + fn from(value: u8) -> Self { + match value { + 0b000 => USBHighestSpeed::USB20Only, + 0b001 => USBHighestSpeed::USB32Gen1, + 0b010 => USBHighestSpeed::USB32Gen2, + 0b011 => USBHighestSpeed::USB40Gen3, + 0b100 => USBHighestSpeed::USB40Gen4, + _ => panic!("Cannot convert {:} to USBHighestSpeed", value), /* Illegal values shall + * panic. */ + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum VconnPower { + P1W, + P1_5W, + P2W, + P3W, + P4W, + P5W, + P6W, +} + +impl From for u8 { + fn from(value: VconnPower) -> Self { + match value { + VconnPower::P1W => 0b000, + VconnPower::P1_5W => 0b001, + VconnPower::P2W => 0b010, + VconnPower::P3W => 0b011, + VconnPower::P4W => 0b100, + VconnPower::P5W => 0b101, + VconnPower::P6W => 0b110, + } + } +} + +impl From for VconnPower { + fn from(value: u8) -> Self { + match value { + 0b000 => VconnPower::P1W, + 0b001 => VconnPower::P1_5W, + 0b010 => VconnPower::P2W, + 0b011 => VconnPower::P3W, + 0b100 => VconnPower::P4W, + 0b101 => VconnPower::P5W, + 0b110 => VconnPower::P6W, + _ => panic!("Cannot convert {:} to VconnPower", value), // Illegal values shall panic. + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum UFPVDOVersion { + Version1_3, +} + +impl From for u8 { + fn from(value: UFPVDOVersion) -> Self { + match value { + UFPVDOVersion::Version1_3 => 0b011, + } + } +} + +impl From for UFPVDOVersion { + fn from(value: u8) -> Self { + match value { + 0b011 => UFPVDOVersion::Version1_3, + _ => panic!("Cannot convert {:} to UFPVDOVersion", value), /* Illegal values shall + * panic. */ + } + } +} + bitfield! { #[derive(Clone, Copy, PartialEq, Eq, Format)] pub struct DisplayPortCapabilities(pub u32): Debug, FromRaw, IntoRaw { From 443227f14dc9eb145d7e2f86befa15b5b9245d6b Mon Sep 17 00:00:00 2001 From: Mike Panetta Date: Wed, 27 Dec 2023 19:05:30 -0500 Subject: [PATCH 03/10] Working VDM discover identity response. --- fusb302b/src/lib.rs | 2 +- usb-pd/src/sink.rs | 107 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/fusb302b/src/lib.rs b/fusb302b/src/lib.rs index 86a6e9d..a9bb9e5 100644 --- a/fusb302b/src/lib.rs +++ b/fusb302b/src/lib.rs @@ -345,7 +345,7 @@ impl Fusb302b { let message = Message::parse(Header(header), &payload[..]); - trace!("{:?}, {:x}:{:x}", message, header, payload); + debug!("{:?}, {:x}:{:x}", message, header, payload); if self.message.replace(message).is_some() { panic!("pending message already set"); diff --git a/usb-pd/src/sink.rs b/usb-pd/src/sink.rs index 175e81b..a98dc40 100644 --- a/usb-pd/src/sink.rs +++ b/usb-pd/src/sink.rs @@ -2,8 +2,11 @@ use { crate::{ header::{DataMessageType, Header, SpecificationRevision}, message::Message, - pdo::{FixedVariableRequestDataObject, PowerDataObject, VDMHeader}, - PowerRole, + pdo::{ + self, CertStatVDO, FixedVariableRequestDataObject, PowerDataObject, ProductVDO, + UFPTypeVDO, VDMHeader, VDMIdentityHeader, + }, + DataRole, PowerRole, }, core::future::Future, defmt::{warn, Format}, @@ -50,6 +53,15 @@ pub enum Request { index: usize, current: u16, }, + REQDiscoverIdentity, + ACKDiscoverIdentity { + identity: VDMIdentityHeader, + cert_stat: CertStatVDO, + product: ProductVDO, + product_type_ufp: UFPTypeVDO, + // Does not exist yet... product_type_dfp: Option, + }, + REQDiscoverSVIDS, } /// Driver state @@ -127,6 +139,97 @@ impl Sink { pub async fn request(&mut self, request: Request) { match request { Request::RequestPower { index, current } => self.request_power(current, index).await, + Request::ACKDiscoverIdentity { + identity, + cert_stat, + product, + product_type_ufp, + //product_type_dfp, + } => { + debug!("ACKDiscoverIdentity"); + // The size of this array will actually change depending on data... + // TODO: Fix this! + let mut payload = [0; 5 * 4]; + let header = Header(0) + .with_message_type_raw(DataMessageType::VendorDefined as u8) + .with_num_objects(5) // 5 VDOs, vdm header, id header, cert, product, UFP product type + .with_port_data_role(DataRole::Ufp) + .with_port_power_role(PowerRole::Sink) + .with_spec_revision(SpecificationRevision::from(self.spec_rev)); + + let vdm_header_vdo = pdo::VDMHeader::Structured( + pdo::VDMHeaderStructured(0) + .with_command(pdo::VDMCommand::DiscoverIdentity) + .with_command_type(pdo::VDMCommandType::ResponderACK) + .with_object_position(0) // 0 Must be used for descover identity + .with_standard_or_vid(0xff00) // PD SID must be used with descover identity + .with_vdm_type(pdo::VDMType::Structured) + .with_vdm_version_major(pdo::VDMVersionMajor::Version2x.into()) + .with_vdm_version_minor(pdo::VDMVersionMinor::Version20.into()), + ); + vdm_header_vdo.to_bytes(&mut payload[0..4]); + identity.to_bytes(&mut payload[4..8]); + cert_stat.to_bytes(&mut payload[8..12]); + product.to_bytes(&mut payload[12..16]); + product_type_ufp.to_bytes(&mut payload[16..20]); + // if let Some(product_type_dfp) = product_type_dfp { + // // 20..24 are padding bytes + // product_type_dfp.to_bytes(&mut payload[24..32]); + // } + debug!("Sending VDM {:x}", payload); + self.driver.send_message(header, &payload); + debug!("Sent VDM"); + } + Request::REQDiscoverSVIDS => { + debug!("REQDiscoverSVIDS"); + let mut payload = [0; 4]; + let header = Header(0) + .with_message_type_raw(DataMessageType::VendorDefined as u8) + .with_num_objects(1) // 1 VDO, vdm header + .with_port_data_role(DataRole::Ufp) + .with_port_power_role(PowerRole::Sink) + .with_spec_revision(SpecificationRevision::from(self.spec_rev)); + + let vdm_header_vdo = pdo::VDMHeader::Structured( + pdo::VDMHeaderStructured(0) + .with_command(pdo::VDMCommand::DiscoverSVIDS) + .with_command_type(pdo::VDMCommandType::InitiatorREQ) + .with_object_position(0) // 0 Must be used for discover SVIDS + .with_standard_or_vid(0xff00) // PD SID must be used with discover SVIDS + .with_vdm_type(pdo::VDMType::Structured) + .with_vdm_version_major(pdo::VDMVersionMajor::Version10.into()) + .with_vdm_version_minor(pdo::VDMVersionMinor::Version20.into()), + ); + vdm_header_vdo.to_bytes(&mut payload[0..4]); + debug!("Sending VDM {:x}", payload); + self.driver.send_message(header, &payload); + debug!("Sent VDM"); + } + Request::REQDiscoverIdentity => { + debug!("REQDiscoverIdentity"); + let mut payload = [0; 4]; + let header = Header(0) + .with_message_type_raw(DataMessageType::VendorDefined as u8) + .with_num_objects(1) // 1 VDO, vdm header + .with_port_data_role(DataRole::Ufp) + .with_port_power_role(PowerRole::Sink) + .with_spec_revision(SpecificationRevision::from(self.spec_rev)); + + let vdm_header_vdo = pdo::VDMHeader::Structured( + pdo::VDMHeaderStructured(0) + .with_command(pdo::VDMCommand::DiscoverIdentity) + .with_command_type(pdo::VDMCommandType::InitiatorREQ) + .with_object_position(0) // 0 Must be used for descover identity + .with_standard_or_vid(0xff00) // PD SID must be used with descover identity + .with_vdm_type(pdo::VDMType::Structured) + .with_vdm_version_major(pdo::VDMVersionMajor::Version10.into()) + .with_vdm_version_minor(pdo::VDMVersionMinor::Version20.into()), + ); + vdm_header_vdo.to_bytes(&mut payload[0..4]); + debug!("Sending VDM {:x}", payload); + self.driver.send_message(header, &payload); + debug!("Sent VDM"); + } } } From f779981d2cf0ea86821be6590bf398ec3c16cf10 Mon Sep 17 00:00:00 2001 From: Ferdia McKeogh Date: Mon, 6 May 2024 15:01:18 +0100 Subject: [PATCH 04/10] Fix errors and warnings --- fusb302b/src/lib.rs | 2 +- pd-interceptor/src/main.rs | 1 + usb-pd/src/message.rs | 6 +++++- usb-pd/src/pdo.rs | 5 +++-- usb-pd/src/sink.rs | 8 ++++---- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/fusb302b/src/lib.rs b/fusb302b/src/lib.rs index a9bb9e5..3afaf93 100644 --- a/fusb302b/src/lib.rs +++ b/fusb302b/src/lib.rs @@ -8,7 +8,7 @@ use { }, timeout::Timeout, }, - defmt::{debug, trace, warn}, + defmt::{debug, warn}, embassy_time::{Duration, Instant}, embedded_hal_async::i2c::I2c, usb_pd::{ diff --git a/pd-interceptor/src/main.rs b/pd-interceptor/src/main.rs index 0a9445d..f32f3d2 100644 --- a/pd-interceptor/src/main.rs +++ b/pd-interceptor/src/main.rs @@ -132,6 +132,7 @@ fn handle_event(event: Event) -> Option { Event::ProtocolChanged => info!("protocol changed"), Event::PowerAccepted => info!("power accepted"), Event::PowerRejected => info!("power rejected"), + _ => todo!(), } None diff --git a/usb-pd/src/message.rs b/usb-pd/src/message.rs index e94090e..631469f 100644 --- a/usb-pd/src/message.rs +++ b/usb-pd/src/message.rs @@ -71,7 +71,11 @@ impl Message { } }; - let data = payload[4..].chunks_exact(4).take(7).map(|buf| LittleEndian::read_u32(buf)).collect::>(); + let data = payload[4..] + .chunks_exact(4) + .take(7) + .map(|buf| LittleEndian::read_u32(buf)) + .collect::>(); trace!("VDM RX: {:?} {:?}", header, data); // trace!("HEADER: VDM:: TYPE: {:?}, VERS: {:?}", header.vdm_type(), diff --git a/usb-pd/src/pdo.rs b/usb-pd/src/pdo.rs index 94cf72f..7d1f812 100644 --- a/usb-pd/src/pdo.rs +++ b/usb-pd/src/pdo.rs @@ -451,7 +451,7 @@ impl VDMIdentityHeader { } } -#[derive(Clone, Copy, Format)] +#[derive(Clone, Copy, Format, Debug)] pub enum SOPProductTypeUFP { NotUFP, PDUSBHub, @@ -484,7 +484,7 @@ impl From for SOPProductTypeUFP { } } -#[derive(Clone, Copy, Format)] +#[derive(Clone, Copy, Format, Debug)] pub enum SOPProductTypeDFP { NotDFP, PDUSBHub, @@ -517,6 +517,7 @@ impl From for SOPProductTypeDFP { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConnectorType { USBTypeCReceptacle, USBTypeCPlug, diff --git a/usb-pd/src/sink.rs b/usb-pd/src/sink.rs index a98dc40..2e2e78c 100644 --- a/usb-pd/src/sink.rs +++ b/usb-pd/src/sink.rs @@ -9,7 +9,7 @@ use { DataRole, PowerRole, }, core::future::Future, - defmt::{warn, Format}, + defmt::{debug, warn, Format}, embassy_time::Instant, heapless::Vec, }; @@ -177,7 +177,7 @@ impl Sink { // product_type_dfp.to_bytes(&mut payload[24..32]); // } debug!("Sending VDM {:x}", payload); - self.driver.send_message(header, &payload); + self.driver.send_message(header, &payload).await; debug!("Sent VDM"); } Request::REQDiscoverSVIDS => { @@ -202,7 +202,7 @@ impl Sink { ); vdm_header_vdo.to_bytes(&mut payload[0..4]); debug!("Sending VDM {:x}", payload); - self.driver.send_message(header, &payload); + self.driver.send_message(header, &payload).await; debug!("Sent VDM"); } Request::REQDiscoverIdentity => { @@ -227,7 +227,7 @@ impl Sink { ); vdm_header_vdo.to_bytes(&mut payload[0..4]); debug!("Sending VDM {:x}", payload); - self.driver.send_message(header, &payload); + self.driver.send_message(header, &payload).await; debug!("Sent VDM"); } } From 7d24cf0e762f03de8fa317e02a01bd362f2fdd0d Mon Sep 17 00:00:00 2001 From: Mike Panetta Date: Thu, 28 Dec 2023 14:11:15 -0500 Subject: [PATCH 05/10] Move VDO's to vdo.rs --- usb-pd/src/lib.rs | 1 + usb-pd/src/message.rs | 4 +- usb-pd/src/pdo.rs | 566 ------------------------------------------ usb-pd/src/sink.rs | 47 ++-- usb-pd/src/vdo.rs | 563 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 590 insertions(+), 591 deletions(-) create mode 100644 usb-pd/src/vdo.rs diff --git a/usb-pd/src/lib.rs b/usb-pd/src/lib.rs index d348c32..7657f58 100644 --- a/usb-pd/src/lib.rs +++ b/usb-pd/src/lib.rs @@ -3,6 +3,7 @@ pub mod header; pub mod message; pub mod pdo; +pub mod vdo; pub mod sink; pub mod source; pub mod token; diff --git a/usb-pd/src/message.rs b/usb-pd/src/message.rs index 631469f..ca7eaf4 100644 --- a/usb-pd/src/message.rs +++ b/usb-pd/src/message.rs @@ -4,9 +4,9 @@ use { pdo::{ AugmentedPowerDataObject, AugmentedPowerDataObjectRaw, Battery, EPRAdjustableVoltageSupply, FixedSupply, PowerDataObject, PowerDataObjectRaw, - SPRProgrammablePowerSupply, VDMHeader, VDMHeaderRaw, VDMHeaderStructured, - VDMHeaderUnstructured, VDMType, VariableSupply, + SPRProgrammablePowerSupply, VariableSupply, }, + vdo::{VDMHeader, VDMHeaderRaw, VDMHeaderStructured, VDMHeaderUnstructured, VDMType}, }, byteorder::{ByteOrder, LittleEndian}, defmt::{trace, warn, Format}, diff --git a/usb-pd/src/pdo.rs b/usb-pd/src/pdo.rs index 7d1f812..1dd9aa8 100644 --- a/usb-pd/src/pdo.rs +++ b/usb-pd/src/pdo.rs @@ -146,569 +146,3 @@ impl FixedVariableRequestDataObject { LittleEndian::write_u32(buf, self.0); } } - -#[derive(Clone, Copy, Debug, Format)] -pub enum VDMCommandType { - InitiatorREQ, - ResponderACK, - ResponderNAK, - ResponderBSY, -} - -impl From for u8 { - fn from(value: VDMCommandType) -> Self { - match value { - VDMCommandType::InitiatorREQ => 0, - VDMCommandType::ResponderACK => 1, - VDMCommandType::ResponderNAK => 2, - VDMCommandType::ResponderBSY => 3, - } - } -} - -impl From for VDMCommandType { - fn from(value: u8) -> Self { - match value { - 0 => VDMCommandType::InitiatorREQ, - 1 => VDMCommandType::ResponderACK, - 2 => VDMCommandType::ResponderNAK, - 3 => VDMCommandType::ResponderBSY, - _ => panic!("Cannot convert {:} to VDMCommandType", value), /* Illegal values shall - * panic. */ - } - } -} - -#[derive(Clone, Copy, Debug, Format)] -pub enum VDMCommand { - DiscoverIdentity, - DiscoverSVIDS, - DiscoverModes, - EnterMode, - ExitMode, - Attention, - DisplayPortStatus, - DisplayPortConfig, -} - -impl From for u8 { - fn from(value: VDMCommand) -> Self { - match value { - VDMCommand::DiscoverIdentity => 0x1, - VDMCommand::DiscoverSVIDS => 0x2, - VDMCommand::DiscoverModes => 0x3, - VDMCommand::EnterMode => 0x4, - VDMCommand::ExitMode => 0x5, - VDMCommand::Attention => 0x6, - VDMCommand::DisplayPortStatus => 0x10, - VDMCommand::DisplayPortConfig => 0x11, - } - } -} - -impl From for VDMCommand { - fn from(value: u8) -> Self { - match value { - 0x01 => VDMCommand::DiscoverIdentity, - 0x02 => VDMCommand::DiscoverSVIDS, - 0x03 => VDMCommand::DiscoverModes, - 0x04 => VDMCommand::EnterMode, - 0x05 => VDMCommand::ExitMode, - 0x06 => VDMCommand::Attention, - 0x10 => VDMCommand::DisplayPortStatus, - 0x11 => VDMCommand::DisplayPortConfig, - // TODO: Find document that explains what 0x12-0x1f are (DP_SID??) - _ => panic!("Cannot convert {:} to VDMCommand", value), // Illegal values shall panic. - } - } -} - -#[derive(Clone, Copy, Debug, Format)] -pub enum VDMType { - Unstructured, - Structured, -} - -impl From for bool { - fn from(value: VDMType) -> Self { - match value { - VDMType::Unstructured => false, - VDMType::Structured => true, - } - } -} - -impl From for VDMType { - fn from(value: bool) -> Self { - match value { - true => VDMType::Structured, - false => VDMType::Unstructured, - } - } -} - -#[derive(Clone, Copy, Debug, Format)] -pub enum VDMHeader { - Structured(VDMHeaderStructured), - Unstructured(VDMHeaderUnstructured), -} - -impl VDMHeader { - pub fn to_bytes(&self, buf: &mut [u8]) { - match self { - VDMHeader::Structured(header) => header.to_bytes(buf), - VDMHeader::Unstructured(header) => header.to_bytes(buf), - } - } -} - -impl From for u32 { - fn from(value: VDMHeader) -> Self { - match value { - VDMHeader::Structured(header) => header.into(), - VDMHeader::Unstructured(header) => header.into(), - } - } -} - -impl From for VDMHeader { - fn from(value: u32) -> Self { - let header = VDMHeaderRaw(value); - match header.vdm_type() { - VDMType::Structured => VDMHeader::Structured(VDMHeaderStructured(value)), - VDMType::Unstructured => VDMHeader::Unstructured(VDMHeaderUnstructured(value)), - } - } -} - -#[derive(Clone, Copy, Format, Debug)] -pub enum VendorDataObject { - VDMHeader(VDMHeader), - IDHeader(VDMIdentityHeader), - CertStat(CertStatVDO), - Product(ProductVDO), - UFPType(UFPTypeVDO), -} - -impl VendorDataObject { - pub fn to_bytes(&self, buf: &mut [u8]) { - match self { - VendorDataObject::VDMHeader(header) => header.to_bytes(buf), - VendorDataObject::IDHeader(header) => header.to_bytes(buf), - VendorDataObject::CertStat(header) => header.to_bytes(buf), - VendorDataObject::Product(header) => header.to_bytes(buf), - VendorDataObject::UFPType(header) => header.to_bytes(buf), - } - } -} - -impl From for u32 { - fn from(value: VendorDataObject) -> Self { - match value { - VendorDataObject::VDMHeader(header) => header.into(), - VendorDataObject::IDHeader(header) => header.into(), - VendorDataObject::CertStat(header) => header.into(), - VendorDataObject::Product(header) => header.into(), - VendorDataObject::UFPType(header) => header.into(), - } - } -} - -bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format)] - pub struct VDMHeaderRaw(pub u32): Debug, FromRaw, IntoRaw { - /// VDM Standard or Vendor ID - pub standard_or_vid: u16 @ 16..=31, - /// VDM Type (Unstructured/Structured) - pub vdm_type: bool [VDMType] @ 15, - } -} - -impl VDMHeaderRaw { - pub fn to_bytes(&self, buf: &mut [u8]) { - LittleEndian::write_u32(buf, self.0); - } -} - -bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format)] - pub struct VDMHeaderStructured(pub u32): Debug, FromRaw, IntoRaw { - /// VDM Standard or Vendor ID - pub standard_or_vid: u16 @ 16..=31, - /// VDM Type (Unstructured/Structured) - pub vdm_type: bool [VDMType] @ 15, - /// Structured VDM Version - pub vdm_version_major: u8 @ 13..=14, - pub vdm_version_minor: u8 @ 11..=12, - /// Object Position - pub object_position: u8 @ 8..=10, - /// Command Type - pub command_type: u8 [VDMCommandType] @ 6..=7, - /// Command - pub command: u8 [VDMCommand] @ 0..=4, - } -} - -impl VDMHeaderStructured { - pub fn to_bytes(&self, buf: &mut [u8]) { - LittleEndian::write_u32(buf, self.0); - } -} - -#[derive(Clone, Copy, Format)] -pub enum VDMVersionMajor { - Version10, - Version2x, -} - -impl From for u8 { - fn from(value: VDMVersionMajor) -> Self { - match value { - VDMVersionMajor::Version10 => 0b00, - VDMVersionMajor::Version2x => 0b01, - } - } -} - -impl From for VDMVersionMajor { - fn from(value: u8) -> Self { - match value { - 0b00 => VDMVersionMajor::Version10, - 0b01 => VDMVersionMajor::Version2x, - _ => panic!("Cannot convert {:} to VDMVersionMajor", value), /* Illegal values shall - * panic. */ - } - } -} - -#[derive(Clone, Copy, Format)] -pub enum VDMVersionMinor { - Version20, - Version21, -} - -impl From for u8 { - fn from(value: VDMVersionMinor) -> Self { - match value { - VDMVersionMinor::Version20 => 0b00, - VDMVersionMinor::Version21 => 0b01, - } - } -} - -impl From for VDMVersionMinor { - fn from(value: u8) -> Self { - match value { - 0b00 => VDMVersionMinor::Version20, - 0b01 => VDMVersionMinor::Version21, - _ => panic!("Cannot convert {:} to VDMVersionMinor", value), /* Illegal values shall - * panic. */ - } - } -} - -bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format)] - pub struct VDMHeaderUnstructured(pub u32): Debug, FromRaw, IntoRaw { - /// VDM Standard or Vendor ID - pub standard_or_vid: u16 @ 16..=31, - /// VDM Type (Unstructured/Structured) - pub vdm_type: bool [VDMType] @ 15, - /// Message defined - pub data: u16 @ 0..=14 - } -} - -impl VDMHeaderUnstructured { - pub fn to_bytes(&self, buf: &mut [u8]) { - LittleEndian::write_u32(buf, self.0); - } -} - -bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format)] - pub struct VDMIdentityHeader(pub u32): Debug, FromRaw, IntoRaw { - /// Host data capable - pub host_data: bool @ 31, - /// Device data capable - pub device_data: bool @ 30, - /// Product type UFP - pub product_type_ufp: u8 [SOPProductTypeUFP] @ 27..=29, - /// Modal Operation Supported - pub modal_supported: bool @ 26, - /// Product type DFP - pub product_type_dfp: u8 [SOPProductTypeDFP] @ 23..=25, - /// Connector type - pub connector_type: u8 [ConnectorType] @ 21..=22, - /// VID - pub vid: u16 @ 0..=15, - } -} - -impl VDMIdentityHeader { - pub fn to_bytes(&self, buf: &mut [u8]) { - LittleEndian::write_u32(buf, self.0); - } -} - -#[derive(Clone, Copy, Format, Debug)] -pub enum SOPProductTypeUFP { - NotUFP, - PDUSBHub, - PDUSBPeripheral, - PSD, -} - -impl From for u8 { - fn from(value: SOPProductTypeUFP) -> Self { - match value { - SOPProductTypeUFP::NotUFP => 0b000, - SOPProductTypeUFP::PDUSBHub => 0b001, - SOPProductTypeUFP::PDUSBPeripheral => 0b010, - SOPProductTypeUFP::PSD => 0b011, - } - } -} - -impl From for SOPProductTypeUFP { - fn from(value: u8) -> Self { - match value { - 0b000 => SOPProductTypeUFP::NotUFP, - 0b001 => SOPProductTypeUFP::PDUSBHub, - 0b010 => SOPProductTypeUFP::PDUSBPeripheral, - 0b011 => SOPProductTypeUFP::PSD, - - _ => panic!("Cannot convert {:} to SOPProductTypeUFP", value), /* Illegal values - * shall panic. */ - } - } -} - -#[derive(Clone, Copy, Format, Debug)] -pub enum SOPProductTypeDFP { - NotDFP, - PDUSBHub, - PDUSBHost, - PowerBrick, -} - -impl From for u8 { - fn from(value: SOPProductTypeDFP) -> Self { - match value { - SOPProductTypeDFP::NotDFP => 0b000, - SOPProductTypeDFP::PDUSBHub => 0b001, - SOPProductTypeDFP::PDUSBHost => 0b010, - SOPProductTypeDFP::PowerBrick => 0b011, - } - } -} - -impl From for SOPProductTypeDFP { - fn from(value: u8) -> Self { - match value { - 0b000 => SOPProductTypeDFP::NotDFP, - 0b001 => SOPProductTypeDFP::PDUSBHub, - 0b010 => SOPProductTypeDFP::PDUSBHost, - 0b011 => SOPProductTypeDFP::PowerBrick, - - _ => panic!("Cannot convert {:} to SOPProductTypeDFP", value), /* Illegal values - * shall panic. */ - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ConnectorType { - USBTypeCReceptacle, - USBTypeCPlug, -} - -impl From for u8 { - fn from(value: ConnectorType) -> Self { - match value { - ConnectorType::USBTypeCReceptacle => 0b10, - ConnectorType::USBTypeCPlug => 0b11, - } - } -} - -impl From for ConnectorType { - fn from(value: u8) -> Self { - match value { - 0b10 => ConnectorType::USBTypeCReceptacle, - 0b11 => ConnectorType::USBTypeCPlug, - _ => panic!("Cannot convert {:} to ConnectorType", value), /* Illegal values shall - * panic. */ - } - } -} -bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] - pub struct CertStatVDO(pub u32): FromRaw, IntoRaw { - /// XID - pub xid: u32 @ 0..=31, - } -} - -impl CertStatVDO { - pub fn to_bytes(&self, buf: &mut [u8]) { - LittleEndian::write_u32(buf, self.0); - } -} - -bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] - pub struct ProductVDO(pub u32): FromRaw, IntoRaw { - /// USB Product ID - pub pid: u16 @ 16..=31, - pub bcd_device: u16 @ 0..=15, - } -} - -impl ProductVDO { - pub fn to_bytes(&self, buf: &mut [u8]) { - LittleEndian::write_u32(buf, self.0); - } -} - -bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] - pub struct UFPTypeVDO(pub u32): FromRaw, IntoRaw { - /// USB Product ID - pub version: u8 @ 29..=31, - pub device_capability: u8 @ 24..=27, - pub vconn_power: u8 @ 8..=10, - pub vconn_required: bool @ 7, - pub vbus_required: bool @ 6, - pub alternate_modes: u8 @ 3..=5, - pub usb_highest_speed: u8 @ 0..=2, - } -} - -impl UFPTypeVDO { - pub fn to_bytes(&self, buf: &mut [u8]) { - LittleEndian::write_u32(buf, self.0); - } -} - -#[derive(Clone, Copy, Format)] -pub enum USBHighestSpeed { - USB20Only, - USB32Gen1, - USB32Gen2, - USB40Gen3, - USB40Gen4, -} - -impl From for u8 { - fn from(value: USBHighestSpeed) -> Self { - match value { - USBHighestSpeed::USB20Only => 0b000, - USBHighestSpeed::USB32Gen1 => 0b001, - USBHighestSpeed::USB32Gen2 => 0b010, - USBHighestSpeed::USB40Gen3 => 0b011, - USBHighestSpeed::USB40Gen4 => 0b100, - } - } -} - -impl From for USBHighestSpeed { - fn from(value: u8) -> Self { - match value { - 0b000 => USBHighestSpeed::USB20Only, - 0b001 => USBHighestSpeed::USB32Gen1, - 0b010 => USBHighestSpeed::USB32Gen2, - 0b011 => USBHighestSpeed::USB40Gen3, - 0b100 => USBHighestSpeed::USB40Gen4, - _ => panic!("Cannot convert {:} to USBHighestSpeed", value), /* Illegal values shall - * panic. */ - } - } -} - -#[derive(Clone, Copy, Format)] -pub enum VconnPower { - P1W, - P1_5W, - P2W, - P3W, - P4W, - P5W, - P6W, -} - -impl From for u8 { - fn from(value: VconnPower) -> Self { - match value { - VconnPower::P1W => 0b000, - VconnPower::P1_5W => 0b001, - VconnPower::P2W => 0b010, - VconnPower::P3W => 0b011, - VconnPower::P4W => 0b100, - VconnPower::P5W => 0b101, - VconnPower::P6W => 0b110, - } - } -} - -impl From for VconnPower { - fn from(value: u8) -> Self { - match value { - 0b000 => VconnPower::P1W, - 0b001 => VconnPower::P1_5W, - 0b010 => VconnPower::P2W, - 0b011 => VconnPower::P3W, - 0b100 => VconnPower::P4W, - 0b101 => VconnPower::P5W, - 0b110 => VconnPower::P6W, - _ => panic!("Cannot convert {:} to VconnPower", value), // Illegal values shall panic. - } - } -} - -#[derive(Clone, Copy, Format)] -pub enum UFPVDOVersion { - Version1_3, -} - -impl From for u8 { - fn from(value: UFPVDOVersion) -> Self { - match value { - UFPVDOVersion::Version1_3 => 0b011, - } - } -} - -impl From for UFPVDOVersion { - fn from(value: u8) -> Self { - match value { - 0b011 => UFPVDOVersion::Version1_3, - _ => panic!("Cannot convert {:} to UFPVDOVersion", value), /* Illegal values shall - * panic. */ - } - } -} - -bitfield! { - #[derive(Clone, Copy, PartialEq, Eq, Format)] - pub struct DisplayPortCapabilities(pub u32): Debug, FromRaw, IntoRaw { - /// UFP_D Pin Assignments Supported - pub ufp_d_pin_assignments: u8 @ 16..=23, - /// DFP_D Pin Assignments Supported - pub dfp_d_pin_assignments: u8 @ 8..=15, - /// USB r2.0 Signalling Not Used - pub usb20_signalling_not_used: bool @ 7, - /// Receptacle Indication - pub receptacle_indication: bool @ 6, - /// Signalling for Transport of DisplayPort Protocol - pub signaling_rate: u8 @ 2..=5, - /// Port Capability - pub capability: u8 @ 0..=1, - } -} - -impl DisplayPortCapabilities { - pub fn to_bytes(&self, buf: &mut [u8]) { - LittleEndian::write_u32(buf, self.0); - } -} diff --git a/usb-pd/src/sink.rs b/usb-pd/src/sink.rs index 2e2e78c..8b63b71 100644 --- a/usb-pd/src/sink.rs +++ b/usb-pd/src/sink.rs @@ -6,6 +6,7 @@ use { self, CertStatVDO, FixedVariableRequestDataObject, PowerDataObject, ProductVDO, UFPTypeVDO, VDMHeader, VDMIdentityHeader, }, + vdo::{self, CertStatVDO, ProductVDO, UFPTypeVDO, VDMHeader, VDMIdentityHeader}, DataRole, PowerRole, }, core::future::Future, @@ -157,15 +158,15 @@ impl Sink { .with_port_power_role(PowerRole::Sink) .with_spec_revision(SpecificationRevision::from(self.spec_rev)); - let vdm_header_vdo = pdo::VDMHeader::Structured( - pdo::VDMHeaderStructured(0) - .with_command(pdo::VDMCommand::DiscoverIdentity) - .with_command_type(pdo::VDMCommandType::ResponderACK) + let vdm_header_vdo = vdo::VDMHeader::Structured( + vdo::VDMHeaderStructured(0) + .with_command(vdo::VDMCommand::DiscoverIdentity) + .with_command_type(vdo::VDMCommandType::ResponderACK) .with_object_position(0) // 0 Must be used for descover identity .with_standard_or_vid(0xff00) // PD SID must be used with descover identity - .with_vdm_type(pdo::VDMType::Structured) - .with_vdm_version_major(pdo::VDMVersionMajor::Version2x.into()) - .with_vdm_version_minor(pdo::VDMVersionMinor::Version20.into()), + .with_vdm_type(vdo::VDMType::Structured) + .with_vdm_version_major(vdo::VDMVersionMajor::Version2x.into()) + .with_vdm_version_minor(vdo::VDMVersionMinor::Version20.into()), ); vdm_header_vdo.to_bytes(&mut payload[0..4]); identity.to_bytes(&mut payload[4..8]); @@ -190,15 +191,15 @@ impl Sink { .with_port_power_role(PowerRole::Sink) .with_spec_revision(SpecificationRevision::from(self.spec_rev)); - let vdm_header_vdo = pdo::VDMHeader::Structured( - pdo::VDMHeaderStructured(0) - .with_command(pdo::VDMCommand::DiscoverSVIDS) - .with_command_type(pdo::VDMCommandType::InitiatorREQ) + let vdm_header_vdo = vdo::VDMHeader::Structured( + vdo::VDMHeaderStructured(0) + .with_command(vdo::VDMCommand::DiscoverSVIDS) + .with_command_type(vdo::VDMCommandType::InitiatorREQ) .with_object_position(0) // 0 Must be used for discover SVIDS .with_standard_or_vid(0xff00) // PD SID must be used with discover SVIDS - .with_vdm_type(pdo::VDMType::Structured) - .with_vdm_version_major(pdo::VDMVersionMajor::Version10.into()) - .with_vdm_version_minor(pdo::VDMVersionMinor::Version20.into()), + .with_vdm_type(vdo::VDMType::Structured) + .with_vdm_version_major(vdo::VDMVersionMajor::Version10.into()) + .with_vdm_version_minor(vdo::VDMVersionMinor::Version20.into()), ); vdm_header_vdo.to_bytes(&mut payload[0..4]); debug!("Sending VDM {:x}", payload); @@ -215,15 +216,15 @@ impl Sink { .with_port_power_role(PowerRole::Sink) .with_spec_revision(SpecificationRevision::from(self.spec_rev)); - let vdm_header_vdo = pdo::VDMHeader::Structured( - pdo::VDMHeaderStructured(0) - .with_command(pdo::VDMCommand::DiscoverIdentity) - .with_command_type(pdo::VDMCommandType::InitiatorREQ) + let vdm_header_vdo = vdo::VDMHeader::Structured( + vdo::VDMHeaderStructured(0) + .with_command(vdo::VDMCommand::DiscoverIdentity) + .with_command_type(vdo::VDMCommandType::InitiatorREQ) .with_object_position(0) // 0 Must be used for descover identity .with_standard_or_vid(0xff00) // PD SID must be used with descover identity - .with_vdm_type(pdo::VDMType::Structured) - .with_vdm_version_major(pdo::VDMVersionMajor::Version10.into()) - .with_vdm_version_minor(pdo::VDMVersionMinor::Version20.into()), + .with_vdm_type(vdo::VDMType::Structured) + .with_vdm_version_major(vdo::VDMVersionMajor::Version10.into()) + .with_vdm_version_minor(vdo::VDMVersionMinor::Version20.into()), ); vdm_header_vdo.to_bytes(&mut payload[0..4]); debug!("Sending VDM {:x}", payload); @@ -265,7 +266,7 @@ impl Sink { Message::SourceCapabilities(caps) => Some(Event::SourceCapabilitiesChanged(caps)), Message::VendorDefined((hdr, data)) => { match hdr { - crate::pdo::VDMHeader::Structured(hdr) => { + crate::vdo::VDMHeader::Structured(hdr) => { warn!( "UNHANDLED: Structured VDM! CMD_TYPE: {:?}, CMD: {:?}", @@ -273,7 +274,7 @@ impl Sink { hdr.command() ); } - crate::pdo::VDMHeader::Unstructured(hdr) => { + crate::vdo::VDMHeader::Unstructured(hdr) => { warn!( "UNHANDLED: Unstructured VDM! SVID: {:x}, DATA: {:x}", diff --git a/usb-pd/src/vdo.rs b/usb-pd/src/vdo.rs new file mode 100644 index 0000000..1a948ab --- /dev/null +++ b/usb-pd/src/vdo.rs @@ -0,0 +1,563 @@ +use { + byteorder::{ByteOrder, LittleEndian}, + defmt::Format, + proc_bitfield::bitfield, +}; + +#[derive(Clone, Copy, Format, Debug)] +pub enum VendorDataObject { + VDMHeader(VDMHeader), + IDHeader(VDMIdentityHeader), + CertStat(CertStatVDO), + Product(ProductVDO), + UFPType(UFPTypeVDO), +} + +impl VendorDataObject { + pub fn to_bytes(&self, buf: &mut [u8]) { + match self { + VendorDataObject::VDMHeader(header) => header.to_bytes(buf), + VendorDataObject::IDHeader(header) => header.to_bytes(buf), + VendorDataObject::CertStat(header) => header.to_bytes(buf), + VendorDataObject::Product(header) => header.to_bytes(buf), + VendorDataObject::UFPType(header) => header.to_bytes(buf), + } + } +} + +impl From for u32 { + fn from(value: VendorDataObject) -> Self { + match value { + VendorDataObject::VDMHeader(header) => header.into(), + VendorDataObject::IDHeader(header) => header.into(), + VendorDataObject::CertStat(header) => header.into(), + VendorDataObject::Product(header) => header.into(), + VendorDataObject::UFPType(header) => header.into(), + } + } +} + +#[derive(Clone, Copy, Format, Debug)] +pub enum VDMCommandType { + InitiatorREQ, + ResponderACK, + ResponderNAK, + ResponderBSY, +} + +impl From for u8 { + fn from(value: VDMCommandType) -> Self { + match value { + VDMCommandType::InitiatorREQ => 0, + VDMCommandType::ResponderACK => 1, + VDMCommandType::ResponderNAK => 2, + VDMCommandType::ResponderBSY => 3, + } + } +} + +impl From for VDMCommandType { + fn from(value: u8) -> Self { + match value { + 0 => VDMCommandType::InitiatorREQ, + 1 => VDMCommandType::ResponderACK, + 2 => VDMCommandType::ResponderNAK, + 3 => VDMCommandType::ResponderBSY, + _ => panic!("Cannot convert {:} to VDMCommandType", value), /* Illegal values shall + * panic. */ + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum VDMCommand { + DiscoverIdentity, + DiscoverSVIDS, + DiscoverModes, + EnterMode, + ExitMode, + Attention, + DisplayPortStatus, + DisplayPortConfig, +} + +impl From for u8 { + fn from(value: VDMCommand) -> Self { + match value { + VDMCommand::DiscoverIdentity => 0x1, + VDMCommand::DiscoverSVIDS => 0x2, + VDMCommand::DiscoverModes => 0x3, + VDMCommand::EnterMode => 0x4, + VDMCommand::ExitMode => 0x5, + VDMCommand::Attention => 0x6, + VDMCommand::DisplayPortStatus => 0x10, + VDMCommand::DisplayPortConfig => 0x11, + } + } +} + +impl From for VDMCommand { + fn from(value: u8) -> Self { + match value { + 0x01 => VDMCommand::DiscoverIdentity, + 0x02 => VDMCommand::DiscoverSVIDS, + 0x03 => VDMCommand::DiscoverModes, + 0x04 => VDMCommand::EnterMode, + 0x05 => VDMCommand::ExitMode, + 0x06 => VDMCommand::Attention, + 0x10 => VDMCommand::DisplayPortStatus, + 0x11 => VDMCommand::DisplayPortConfig, + // TODO: Find document that explains what 0x12-0x1f are (DP_SID??) + _ => panic!("Cannot convert {:} to VDMCommand", value), // Illegal values shall panic. + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum VDMType { + Unstructured, + Structured, +} + +impl From for bool { + fn from(value: VDMType) -> Self { + match value { + VDMType::Unstructured => false, + VDMType::Structured => true, + } + } +} + +impl From for VDMType { + fn from(value: bool) -> Self { + match value { + true => VDMType::Structured, + false => VDMType::Unstructured, + } + } +} + +#[derive(Clone, Copy, Format, Debug)] +pub enum VDMHeader { + Structured(VDMHeaderStructured), + Unstructured(VDMHeaderUnstructured), +} + +impl VDMHeader { + pub fn to_bytes(&self, buf: &mut [u8]) { + match self { + VDMHeader::Structured(header) => header.to_bytes(buf), + VDMHeader::Unstructured(header) => header.to_bytes(buf), + } + } +} + +impl From for u32 { + fn from(value: VDMHeader) -> Self { + match value { + VDMHeader::Structured(header) => header.into(), + VDMHeader::Unstructured(header) => header.into(), + } + } +} + +impl From for VDMHeader { + fn from(value: u32) -> Self { + let header = VDMHeaderRaw(value); + match header.vdm_type() { + VDMType::Structured => VDMHeader::Structured(VDMHeaderStructured(value)), + VDMType::Unstructured => VDMHeader::Unstructured(VDMHeaderUnstructured(value)), + } + } +} + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format)] + pub struct VDMHeaderRaw(pub u32): FromRaw, IntoRaw { + /// VDM Standard or Vendor ID + pub standard_or_vid: u16 @ 16..=31, + /// VDM Type (Unstructured/Structured) + pub vdm_type: bool [VDMType] @ 15, + } +} + +impl VDMHeaderRaw { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] + pub struct VDMHeaderStructured(pub u32): FromRaw, IntoRaw { + /// VDM Standard or Vendor ID + pub standard_or_vid: u16 @ 16..=31, + /// VDM Type (Unstructured/Structured) + pub vdm_type: bool [VDMType] @ 15, + /// Structured VDM Version + pub vdm_version_major: u8 @ 13..=14, + pub vdm_version_minor: u8 @ 11..=12, + /// Object Position + pub object_position: u8 @ 8..=10, + /// Command Type + pub command_type: u8 [VDMCommandType] @ 6..=7, + /// Command + pub command: u8 [VDMCommand] @ 0..=4, + } +} + +impl VDMHeaderStructured { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +#[derive(Clone, Copy, Format)] +pub enum VDMVersionMajor { + Version10, + Version2x, +} + +impl From for u8 { + fn from(value: VDMVersionMajor) -> Self { + match value { + VDMVersionMajor::Version10 => 0b00, + VDMVersionMajor::Version2x => 0b01, + } + } +} + +impl From for VDMVersionMajor { + fn from(value: u8) -> Self { + match value { + 0b00 => VDMVersionMajor::Version10, + 0b01 => VDMVersionMajor::Version2x, + _ => panic!("Cannot convert {:} to VDMVersionMajor", value), // Illegal values shall panic. + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum VDMVersionMinor { + Version20, + Version21, +} + +impl From for u8 { + fn from(value: VDMVersionMinor) -> Self { + match value { + VDMVersionMinor::Version20 => 0b00, + VDMVersionMinor::Version21 => 0b01, + } + } +} + +impl From for VDMVersionMinor { + fn from(value: u8) -> Self { + match value { + 0b00 => VDMVersionMinor::Version20, + 0b01 => VDMVersionMinor::Version21, + _ => panic!("Cannot convert {:} to VDMVersionMinor", value), // Illegal values shall panic. + } + } +} + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] + pub struct VDMHeaderUnstructured(pub u32): FromRaw, IntoRaw { + /// VDM Standard or Vendor ID + pub standard_or_vid: u16 @ 16..=31, + /// VDM Type (Unstructured/Structured) + pub vdm_type: bool [VDMType] @ 15, + /// Message defined + pub data: u16 @ 0..=14 + } +} + +impl VDMHeaderUnstructured { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] + pub struct VDMIdentityHeader(pub u32): FromRaw, IntoRaw { + /// Host data capable + pub host_data: bool @ 31, + /// Device data capable + pub device_data: bool @ 30, + /// Product type UFP + pub product_type_ufp: u8 [SOPProductTypeUFP] @ 27..=29, + /// Modal Operation Supported + pub modal_supported: bool @ 26, + /// Product type DFP + pub product_type_dfp: u8 [SOPProductTypeDFP] @ 23..=25, + /// Connector type + pub connector_type: u8 [ConnectorType] @ 21..=22, + /// VID + pub vid: u16 @ 0..=15, + } +} + +impl VDMIdentityHeader { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +#[derive(Clone, Copy, Format)] +pub enum SOPProductTypeUFP { + NotUFP, + PDUSBHub, + PDUSBPeripheral, + PSD, +} + +impl From for u8 { + fn from(value: SOPProductTypeUFP) -> Self { + match value { + SOPProductTypeUFP::NotUFP => 0b000, + SOPProductTypeUFP::PDUSBHub => 0b001, + SOPProductTypeUFP::PDUSBPeripheral => 0b010, + SOPProductTypeUFP::PSD => 0b011, + } + } +} + +impl From for SOPProductTypeUFP { + fn from(value: u8) -> Self { + match value { + 0b000 => SOPProductTypeUFP::NotUFP, + 0b001 => SOPProductTypeUFP::PDUSBHub, + 0b010 => SOPProductTypeUFP::PDUSBPeripheral, + 0b011 => SOPProductTypeUFP::PSD, + + _ => panic!("Cannot convert {:} to SOPProductTypeUFP", value), // Illegal values shall panic. + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum SOPProductTypeDFP { + NotDFP, + PDUSBHub, + PDUSBHost, + PowerBrick, +} + +impl From for u8 { + fn from(value: SOPProductTypeDFP) -> Self { + match value { + SOPProductTypeDFP::NotDFP => 0b000, + SOPProductTypeDFP::PDUSBHub => 0b001, + SOPProductTypeDFP::PDUSBHost => 0b010, + SOPProductTypeDFP::PowerBrick => 0b011, + } + } +} + +impl From for SOPProductTypeDFP { + fn from(value: u8) -> Self { + match value { + 0b000 => SOPProductTypeDFP::NotDFP, + 0b001 => SOPProductTypeDFP::PDUSBHub, + 0b010 => SOPProductTypeDFP::PDUSBHost, + 0b011 => SOPProductTypeDFP::PowerBrick, + + _ => panic!("Cannot convert {:} to SOPProductTypeDFP", value), // Illegal values shall panic. + } + } +} + +pub enum ConnectorType { + USBTypeCReceptacle, + USBTypeCPlug, +} + +impl From for u8 { + fn from(value: ConnectorType) -> Self { + match value { + ConnectorType::USBTypeCReceptacle => 0b10, + ConnectorType::USBTypeCPlug => 0b11, + } + } +} + +impl From for ConnectorType { + fn from(value: u8) -> Self { + match value { + 0b10 => ConnectorType::USBTypeCReceptacle, + 0b11 => ConnectorType::USBTypeCPlug, + _ => panic!("Cannot convert {:} to ConnectorType", value), // Illegal values shall panic. + } + } +} +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] + pub struct CertStatVDO(pub u32): FromRaw, IntoRaw { + /// XID + pub xid: u32 @ 0..=31, + } +} + +impl CertStatVDO { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] + pub struct ProductVDO(pub u32): FromRaw, IntoRaw { + /// USB Product ID + pub pid: u16 @ 16..=31, + pub bcd_device: u16 @ 0..=15, + } +} + +impl ProductVDO { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format, Debug)] + pub struct UFPTypeVDO(pub u32): FromRaw, IntoRaw { + /// USB Product ID + pub version: u8 @ 29..=31, + pub device_capability: u8 @ 24..=27, + pub vconn_power: u8 @ 8..=10, + pub vconn_required: bool @ 7, + pub vbus_required: bool @ 6, + pub alternate_modes: u8 @ 3..=5, + pub usb_highest_speed: u8 @ 0..=2, + } +} + +impl UFPTypeVDO { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +#[derive(Clone, Copy, Format)] +pub enum USBHighestSpeed { + USB20Only, + USB32Gen1, + USB32Gen2, + USB40Gen3, + USB40Gen4, +} + +impl From for u8 { + fn from(value: USBHighestSpeed) -> Self { + match value { + USBHighestSpeed::USB20Only => 0b000, + USBHighestSpeed::USB32Gen1 => 0b001, + USBHighestSpeed::USB32Gen2 => 0b010, + USBHighestSpeed::USB40Gen3 => 0b011, + USBHighestSpeed::USB40Gen4 => 0b100, + } + } +} + +impl From for USBHighestSpeed { + fn from(value: u8) -> Self { + match value { + 0b000 => USBHighestSpeed::USB20Only, + 0b001 => USBHighestSpeed::USB32Gen1, + 0b010 => USBHighestSpeed::USB32Gen2, + 0b011 => USBHighestSpeed::USB40Gen3, + 0b100 => USBHighestSpeed::USB40Gen4, + _ => panic!("Cannot convert {:} to USBHighestSpeed", value), // Illegal values shall panic. + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum VconnPower { + P1W, + P1_5W, + P2W, + P3W, + P4W, + P5W, + P6W, +} + +impl From for u8 { + fn from(value: VconnPower) -> Self { + match value { + VconnPower::P1W => 0b000, + VconnPower::P1_5W => 0b001, + VconnPower::P2W => 0b010, + VconnPower::P3W => 0b011, + VconnPower::P4W => 0b100, + VconnPower::P5W => 0b101, + VconnPower::P6W => 0b110, + } + } +} + +impl From for VconnPower { + fn from(value: u8) -> Self { + match value { + 0b000 => VconnPower::P1W, + 0b001 => VconnPower::P1_5W, + 0b010 => VconnPower::P2W, + 0b011 => VconnPower::P3W, + 0b100 => VconnPower::P4W, + 0b101 => VconnPower::P5W, + 0b110 => VconnPower::P6W, + _ => panic!("Cannot convert {:} to VconnPower", value), // Illegal values shall panic. + } + } +} + +#[derive(Clone, Copy, Format)] +pub enum UFPVDOVersion { + Version1_3, +} + +impl From for u8 { + fn from(value: UFPVDOVersion) -> Self { + match value { + UFPVDOVersion::Version1_3 => 0b011, + } + } +} + +impl From for UFPVDOVersion { + fn from(value: u8) -> Self { + match value { + 0b011 => UFPVDOVersion::Version1_3, + _ => panic!("Cannot convert {:} to UFPVDOVersion", value), // Illegal values shall panic. + } + } +} + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format)] + pub struct DisplayPortCapabilities(pub u32): FromRaw, IntoRaw { + /// UFP_D Pin Assignments Supported + pub ufp_d_pin_assignments: u8 @ 16..=23, + /// DFP_D Pin Assignments Supported + pub dfp_d_pin_assignments: u8 @ 8..=15, + /// USB r2.0 Signalling Not Used + pub usb20_signalling_not_used: bool @ 7, + /// Receptacle Indication + pub receptacle_indication: bool @ 6, + /// Signalling for Transport of DisplayPort Protocol + pub signaling_rate: u8 @ 2..=5, + /// Port Capability + pub capability: u8 @ 0..=1, + } +} + +impl DisplayPortCapabilities { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} From 17dac32b2b75b1850015d65fd2fc972323e02bb4 Mon Sep 17 00:00:00 2001 From: Ferdia McKeogh Date: Mon, 6 May 2024 15:16:33 +0100 Subject: [PATCH 06/10] Fix imports --- usb-pd/src/lib.rs | 2 +- usb-pd/src/sink.rs | 54 +++++++++++++++++++++++----------------------- usb-pd/src/vdo.rs | 21 ++++++++++++------ 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/usb-pd/src/lib.rs b/usb-pd/src/lib.rs index 7657f58..d2ceb45 100644 --- a/usb-pd/src/lib.rs +++ b/usb-pd/src/lib.rs @@ -3,10 +3,10 @@ pub mod header; pub mod message; pub mod pdo; -pub mod vdo; pub mod sink; pub mod source; pub mod token; +pub mod vdo; #[derive(Clone, Copy, PartialEq, Eq)] pub enum CcPin { diff --git a/usb-pd/src/sink.rs b/usb-pd/src/sink.rs index 8b63b71..0ee8563 100644 --- a/usb-pd/src/sink.rs +++ b/usb-pd/src/sink.rs @@ -2,11 +2,11 @@ use { crate::{ header::{DataMessageType, Header, SpecificationRevision}, message::Message, - pdo::{ - self, CertStatVDO, FixedVariableRequestDataObject, PowerDataObject, ProductVDO, - UFPTypeVDO, VDMHeader, VDMIdentityHeader, + pdo::{FixedVariableRequestDataObject, PowerDataObject}, + vdo::{ + CertStatVDO, ProductVDO, UFPTypeVDO, VDMCommand, VDMCommandType, VDMHeader, + VDMHeaderStructured, VDMIdentityHeader, VDMType, VDMVersionMajor, VDMVersionMinor, }, - vdo::{self, CertStatVDO, ProductVDO, UFPTypeVDO, VDMHeader, VDMIdentityHeader}, DataRole, PowerRole, }, core::future::Future, @@ -158,15 +158,15 @@ impl Sink { .with_port_power_role(PowerRole::Sink) .with_spec_revision(SpecificationRevision::from(self.spec_rev)); - let vdm_header_vdo = vdo::VDMHeader::Structured( - vdo::VDMHeaderStructured(0) - .with_command(vdo::VDMCommand::DiscoverIdentity) - .with_command_type(vdo::VDMCommandType::ResponderACK) + let vdm_header_vdo = VDMHeader::Structured( + VDMHeaderStructured(0) + .with_command(VDMCommand::DiscoverIdentity) + .with_command_type(VDMCommandType::ResponderACK) .with_object_position(0) // 0 Must be used for descover identity .with_standard_or_vid(0xff00) // PD SID must be used with descover identity - .with_vdm_type(vdo::VDMType::Structured) - .with_vdm_version_major(vdo::VDMVersionMajor::Version2x.into()) - .with_vdm_version_minor(vdo::VDMVersionMinor::Version20.into()), + .with_vdm_type(VDMType::Structured) + .with_vdm_version_major(VDMVersionMajor::Version2x.into()) + .with_vdm_version_minor(VDMVersionMinor::Version20.into()), ); vdm_header_vdo.to_bytes(&mut payload[0..4]); identity.to_bytes(&mut payload[4..8]); @@ -191,15 +191,15 @@ impl Sink { .with_port_power_role(PowerRole::Sink) .with_spec_revision(SpecificationRevision::from(self.spec_rev)); - let vdm_header_vdo = vdo::VDMHeader::Structured( - vdo::VDMHeaderStructured(0) - .with_command(vdo::VDMCommand::DiscoverSVIDS) - .with_command_type(vdo::VDMCommandType::InitiatorREQ) + let vdm_header_vdo = VDMHeader::Structured( + VDMHeaderStructured(0) + .with_command(VDMCommand::DiscoverSVIDS) + .with_command_type(VDMCommandType::InitiatorREQ) .with_object_position(0) // 0 Must be used for discover SVIDS .with_standard_or_vid(0xff00) // PD SID must be used with discover SVIDS - .with_vdm_type(vdo::VDMType::Structured) - .with_vdm_version_major(vdo::VDMVersionMajor::Version10.into()) - .with_vdm_version_minor(vdo::VDMVersionMinor::Version20.into()), + .with_vdm_type(VDMType::Structured) + .with_vdm_version_major(VDMVersionMajor::Version10.into()) + .with_vdm_version_minor(VDMVersionMinor::Version20.into()), ); vdm_header_vdo.to_bytes(&mut payload[0..4]); debug!("Sending VDM {:x}", payload); @@ -216,15 +216,15 @@ impl Sink { .with_port_power_role(PowerRole::Sink) .with_spec_revision(SpecificationRevision::from(self.spec_rev)); - let vdm_header_vdo = vdo::VDMHeader::Structured( - vdo::VDMHeaderStructured(0) - .with_command(vdo::VDMCommand::DiscoverIdentity) - .with_command_type(vdo::VDMCommandType::InitiatorREQ) + let vdm_header_vdo = VDMHeader::Structured( + VDMHeaderStructured(0) + .with_command(VDMCommand::DiscoverIdentity) + .with_command_type(VDMCommandType::InitiatorREQ) .with_object_position(0) // 0 Must be used for descover identity .with_standard_or_vid(0xff00) // PD SID must be used with descover identity - .with_vdm_type(vdo::VDMType::Structured) - .with_vdm_version_major(vdo::VDMVersionMajor::Version10.into()) - .with_vdm_version_minor(vdo::VDMVersionMinor::Version20.into()), + .with_vdm_type(VDMType::Structured) + .with_vdm_version_major(VDMVersionMajor::Version10.into()) + .with_vdm_version_minor(VDMVersionMinor::Version20.into()), ); vdm_header_vdo.to_bytes(&mut payload[0..4]); debug!("Sending VDM {:x}", payload); @@ -266,7 +266,7 @@ impl Sink { Message::SourceCapabilities(caps) => Some(Event::SourceCapabilitiesChanged(caps)), Message::VendorDefined((hdr, data)) => { match hdr { - crate::vdo::VDMHeader::Structured(hdr) => { + VDMHeader::Structured(hdr) => { warn!( "UNHANDLED: Structured VDM! CMD_TYPE: {:?}, CMD: {:?}", @@ -274,7 +274,7 @@ impl Sink { hdr.command() ); } - crate::vdo::VDMHeader::Unstructured(hdr) => { + VDMHeader::Unstructured(hdr) => { warn!( "UNHANDLED: Unstructured VDM! SVID: {:x}, DATA: {:x}", diff --git a/usb-pd/src/vdo.rs b/usb-pd/src/vdo.rs index 1a948ab..2d3eb65 100644 --- a/usb-pd/src/vdo.rs +++ b/usb-pd/src/vdo.rs @@ -232,7 +232,8 @@ impl From for VDMVersionMajor { match value { 0b00 => VDMVersionMajor::Version10, 0b01 => VDMVersionMajor::Version2x, - _ => panic!("Cannot convert {:} to VDMVersionMajor", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to VDMVersionMajor", value), /* Illegal values shall + * panic. */ } } } @@ -257,7 +258,8 @@ impl From for VDMVersionMinor { match value { 0b00 => VDMVersionMinor::Version20, 0b01 => VDMVersionMinor::Version21, - _ => panic!("Cannot convert {:} to VDMVersionMinor", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to VDMVersionMinor", value), /* Illegal values shall + * panic. */ } } } @@ -333,7 +335,8 @@ impl From for SOPProductTypeUFP { 0b010 => SOPProductTypeUFP::PDUSBPeripheral, 0b011 => SOPProductTypeUFP::PSD, - _ => panic!("Cannot convert {:} to SOPProductTypeUFP", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to SOPProductTypeUFP", value), /* Illegal values + * shall panic. */ } } } @@ -365,7 +368,8 @@ impl From for SOPProductTypeDFP { 0b010 => SOPProductTypeDFP::PDUSBHost, 0b011 => SOPProductTypeDFP::PowerBrick, - _ => panic!("Cannot convert {:} to SOPProductTypeDFP", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to SOPProductTypeDFP", value), /* Illegal values + * shall panic. */ } } } @@ -389,7 +393,8 @@ impl From for ConnectorType { match value { 0b10 => ConnectorType::USBTypeCReceptacle, 0b11 => ConnectorType::USBTypeCPlug, - _ => panic!("Cannot convert {:} to ConnectorType", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to ConnectorType", value), /* Illegal values shall + * panic. */ } } } @@ -471,7 +476,8 @@ impl From for USBHighestSpeed { 0b010 => USBHighestSpeed::USB32Gen2, 0b011 => USBHighestSpeed::USB40Gen3, 0b100 => USBHighestSpeed::USB40Gen4, - _ => panic!("Cannot convert {:} to USBHighestSpeed", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to USBHighestSpeed", value), /* Illegal values shall + * panic. */ } } } @@ -533,7 +539,8 @@ impl From for UFPVDOVersion { fn from(value: u8) -> Self { match value { 0b011 => UFPVDOVersion::Version1_3, - _ => panic!("Cannot convert {:} to UFPVDOVersion", value), // Illegal values shall panic. + _ => panic!("Cannot convert {:} to UFPVDOVersion", value), /* Illegal values shall + * panic. */ } } } From 0cb5821e9ee77354f9e2c67049f1792c3c3cebe8 Mon Sep 17 00:00:00 2001 From: Mike Panetta Date: Fri, 29 Dec 2023 17:25:37 -0500 Subject: [PATCH 07/10] Move vdo.rs, pdo.rs to messages/, move message.rs messages/mod.rs --- fusb302b/src/lib.rs | 9 ++++++--- usb-pd/src/lib.rs | 4 +--- usb-pd/src/{message.rs => messages/mod.rs} | 19 ++++++++++--------- usb-pd/src/{ => messages}/pdo.rs | 0 usb-pd/src/{ => messages}/vdo.rs | 6 ++++++ usb-pd/src/sink.rs | 14 ++++++++------ 6 files changed, 31 insertions(+), 21 deletions(-) rename usb-pd/src/{message.rs => messages/mod.rs} (89%) rename usb-pd/src/{ => messages}/pdo.rs (100%) rename usb-pd/src/{ => messages}/vdo.rs (99%) diff --git a/fusb302b/src/lib.rs b/fusb302b/src/lib.rs index 3afaf93..0699552 100644 --- a/fusb302b/src/lib.rs +++ b/fusb302b/src/lib.rs @@ -3,8 +3,8 @@ use { crate::{ registers::{ - Control1, Control3, Mask1, MaskA, MaskB, Power, Register, Registers, Reset, Slice, - Switches0, Switches1, + Control0, Control1, Control3, Mask1, MaskA, MaskB, Power, Register, Registers, Reset, + Slice, Switches0, Switches1, }, timeout::Timeout, }, @@ -13,7 +13,7 @@ use { embedded_hal_async::i2c::I2c, usb_pd::{ header::{ControlMessageType, Header, MessageType}, - message::Message, + messages::Message, sink::{Driver as SinkDriver, DriverState}, token::Token, CcPin, @@ -126,6 +126,9 @@ impl SinkDriver for Fusb302b { .set_mask_b(MaskB::default().with_m_gcrcsent(true)) .await; + self.registers + .set_control0(Control0::default().with_int_mask(false).with_host_cur(01)) + .await; self.state = State::Measuring { cc_pin: CcPin::CC1 }; self.message = None; self.did_change_protocol = false; diff --git a/usb-pd/src/lib.rs b/usb-pd/src/lib.rs index d2ceb45..1a4c02f 100644 --- a/usb-pd/src/lib.rs +++ b/usb-pd/src/lib.rs @@ -1,12 +1,10 @@ #![no_std] pub mod header; -pub mod message; -pub mod pdo; +pub mod messages; pub mod sink; pub mod source; pub mod token; -pub mod vdo; #[derive(Clone, Copy, PartialEq, Eq)] pub enum CcPin { diff --git a/usb-pd/src/message.rs b/usb-pd/src/messages/mod.rs similarity index 89% rename from usb-pd/src/message.rs rename to usb-pd/src/messages/mod.rs index ca7eaf4..028cf21 100644 --- a/usb-pd/src/message.rs +++ b/usb-pd/src/messages/mod.rs @@ -1,16 +1,17 @@ +pub mod pdo; +pub mod vdo; + use { - crate::{ - header::{ControlMessageType, DataMessageType, Header, MessageType}, - pdo::{ - AugmentedPowerDataObject, AugmentedPowerDataObjectRaw, Battery, - EPRAdjustableVoltageSupply, FixedSupply, PowerDataObject, PowerDataObjectRaw, - SPRProgrammablePowerSupply, VariableSupply, - }, - vdo::{VDMHeader, VDMHeaderRaw, VDMHeaderStructured, VDMHeaderUnstructured, VDMType}, - }, + crate::header::{ControlMessageType, DataMessageType, Header, MessageType}, byteorder::{ByteOrder, LittleEndian}, defmt::{trace, warn, Format}, heapless::Vec, + pdo::{ + AugmentedPowerDataObject, AugmentedPowerDataObjectRaw, Battery, EPRAdjustableVoltageSupply, + FixedSupply, PowerDataObject, PowerDataObjectRaw, SPRProgrammablePowerSupply, + VariableSupply, + }, + vdo::{VDMHeader, VDMHeaderRaw, VDMHeaderStructured, VDMHeaderUnstructured, VDMType}, }; #[derive(Debug, Clone, Format)] diff --git a/usb-pd/src/pdo.rs b/usb-pd/src/messages/pdo.rs similarity index 100% rename from usb-pd/src/pdo.rs rename to usb-pd/src/messages/pdo.rs diff --git a/usb-pd/src/vdo.rs b/usb-pd/src/messages/vdo.rs similarity index 99% rename from usb-pd/src/vdo.rs rename to usb-pd/src/messages/vdo.rs index 2d3eb65..ed30608 100644 --- a/usb-pd/src/vdo.rs +++ b/usb-pd/src/messages/vdo.rs @@ -212,6 +212,12 @@ impl VDMHeaderStructured { } } +impl Default for VDMHeaderStructured { + fn default() -> Self { + VDMHeaderStructured(0).with_vdm_type(VDMType::Structured) + } +} + #[derive(Clone, Copy, Format)] pub enum VDMVersionMajor { Version10, diff --git a/usb-pd/src/sink.rs b/usb-pd/src/sink.rs index 0ee8563..b5994c2 100644 --- a/usb-pd/src/sink.rs +++ b/usb-pd/src/sink.rs @@ -1,11 +1,13 @@ use { crate::{ header::{DataMessageType, Header, SpecificationRevision}, - message::Message, - pdo::{FixedVariableRequestDataObject, PowerDataObject}, - vdo::{ - CertStatVDO, ProductVDO, UFPTypeVDO, VDMCommand, VDMCommandType, VDMHeader, - VDMHeaderStructured, VDMIdentityHeader, VDMType, VDMVersionMajor, VDMVersionMinor, + messages::{ + pdo::{FixedVariableRequestDataObject, PowerDataObject}, + vdo::{ + CertStatVDO, ProductVDO, UFPTypeVDO, VDMCommand, VDMCommandType, VDMHeader, + VDMHeaderStructured, VDMIdentityHeader, VDMType, VDMVersionMajor, VDMVersionMinor, + }, + Message, }, DataRole, PowerRole, }, @@ -164,7 +166,7 @@ impl Sink { .with_command_type(VDMCommandType::ResponderACK) .with_object_position(0) // 0 Must be used for descover identity .with_standard_or_vid(0xff00) // PD SID must be used with descover identity - .with_vdm_type(VDMType::Structured) + //.with_vdm_type(VDMType::Structured) .with_vdm_version_major(VDMVersionMajor::Version2x.into()) .with_vdm_version_minor(VDMVersionMinor::Version20.into()), ); From 8b146c390f13ef12f2ac1e81cab4d937f87c9634 Mon Sep 17 00:00:00 2001 From: Mike Panetta Date: Sun, 17 Mar 2024 15:26:16 -0400 Subject: [PATCH 08/10] Work on PPS stuff --- fusb302b/src/lib.rs | 19 ++++++++++++- usb-pd/src/messages/pdo.rs | 58 ++++++++++++++++++++++++++++++++++++++ usb-pd/src/sink.rs | 44 ++++++++++++++++++++++++++++- 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/fusb302b/src/lib.rs b/fusb302b/src/lib.rs index 0699552..73a7aec 100644 --- a/fusb302b/src/lib.rs +++ b/fusb302b/src/lib.rs @@ -129,6 +129,16 @@ impl SinkDriver for Fusb302b { self.registers .set_control0(Control0::default().with_int_mask(false).with_host_cur(01)) .await; + self.registers + .set_control3( + Control3::default() + .with_send_hard_reset(true) + .with_auto_hardreset(true) + .with_auto_softreset(true) + .with_auto_retry(true) + .with_n_retries(3), + ) + .await; self.state = State::Measuring { cc_pin: CcPin::CC1 }; self.message = None; self.did_change_protocol = false; @@ -386,7 +396,14 @@ impl Fusb302b { // Enable automatic retries self.registers - .set_control3(Control3::default().with_auto_retry(true).with_n_retries(3)) + .set_control3( + Control3::default() + .with_send_hard_reset(true) + .with_auto_hardreset(true) + .with_auto_softreset(true) + .with_auto_retry(true) + .with_n_retries(3), + ) .await; // Enable interrupts for CC activity and CRC_CHK diff --git a/usb-pd/src/messages/pdo.rs b/usb-pd/src/messages/pdo.rs index 1dd9aa8..0ba72ac 100644 --- a/usb-pd/src/messages/pdo.rs +++ b/usb-pd/src/messages/pdo.rs @@ -146,3 +146,61 @@ impl FixedVariableRequestDataObject { LittleEndian::write_u32(buf, self.0); } } + +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq, Format)] + pub struct BatteryRequestDataObject(pub u32): FromRaw, IntoRaw { + /// Object position (0000b and 1110b…1111b are Reserved and Shall Not be used) + pub object_position: u8 @ 28..=31, + /// GiveBackFlag = 0 + pub giveback_flag: bool @ 27, + /// Capability mismatch + pub capability_mismatch: bool @ 26, + /// USB communications capable + pub usb_communications_capable: bool @ 25, + /// No USB Suspend + pub no_usb_suspend: bool @ 24, + /// Unchunked extended messages supported + pub unchunked_extended_messages_supported: bool @ 23, + /// EPR mode capable + pub epr_mode_capable: bool @ 22, + /// Operating power in 250mW units + pub operating_power: u16 @ 10..=19, + /// Maximum operating power in 250mW units + pub maximum_operating_power: u16 @ 0..=9, + } +} + +impl BatteryRequestDataObject { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} + +bitfield!( + #[derive(Clone, Copy, PartialEq, Eq, Format)] + pub struct PPSRequestDataObject(pub u32): FromRaw, IntoRaw { + /// Object position (0000b and 1110b…1111b are Reserved and Shall Not be used) + pub object_position: u8 @ 28..=31, + /// Capability mismatch + pub capability_mismatch: bool @ 26, + /// USB communications capable + pub usb_communications_capable: bool @ 25, + /// No USB Suspend + pub no_usb_suspend: bool @ 24, + /// Unchunked extended messages supported + pub unchunked_extended_messages_supported: bool @ 23, + /// EPR mode capable + pub epr_mode_capable: bool @ 22, + /// Output voltage in 20mV units + pub output_voltage: u16 @ 9..=20, + /// Operating current in 50mA units + pub operating_current: u16 @ 0..=6, + } +); + +impl PPSRequestDataObject { + pub fn to_bytes(&self, buf: &mut [u8]) { + LittleEndian::write_u32(buf, self.0); + } +} diff --git a/usb-pd/src/sink.rs b/usb-pd/src/sink.rs index b5994c2..ee08516 100644 --- a/usb-pd/src/sink.rs +++ b/usb-pd/src/sink.rs @@ -2,7 +2,7 @@ use { crate::{ header::{DataMessageType, Header, SpecificationRevision}, messages::{ - pdo::{FixedVariableRequestDataObject, PowerDataObject}, + pdo::{FixedVariableRequestDataObject, PPSRequestDataObject, PowerDataObject}, vdo::{ CertStatVDO, ProductVDO, UFPTypeVDO, VDMCommand, VDMCommandType, VDMHeader, VDMHeaderStructured, VDMIdentityHeader, VDMType, VDMVersionMajor, VDMVersionMinor, @@ -56,6 +56,14 @@ pub enum Request { index: usize, current: u16, }, + RequestPPS { + /// Index of the desired PowerDataObject + index: usize, + /// Requested voltage (in mV) + voltage: u16, + /// Requested maximum current (in mA) + current: u16, + }, REQDiscoverIdentity, ACKDiscoverIdentity { identity: VDMIdentityHeader, @@ -142,6 +150,40 @@ impl Sink { pub async fn request(&mut self, request: Request) { match request { Request::RequestPower { index, current } => self.request_power(current, index).await, + + Request::RequestPPS { + index, + voltage, + current, + } => { + // Payload is 4 bytes + let mut payload = [0; 4]; + // Add one to index to account for array offsets starting at 0 and obj_pos + // starting at 1... + let obj_pos = index + 1; + assert!(obj_pos > 0b0000 && obj_pos <= 0b1110); + + // Create PPS request data object + let pps = PPSRequestDataObject(0) + .with_object_position(obj_pos as u8) + .with_operating_current(current / 50) // Convert current from millis to 50ma units + .with_output_voltage(voltage / 20) // Convert voltage from millis to 20mv units + .with_capability_mismatch(false) + .with_epr_mode_capable(false) + .with_usb_communications_capable(true); + pps.to_bytes(&mut payload[0..4]); + + // Create header + let header = Header(0) + .with_message_type_raw(DataMessageType::Request as u8) + .with_num_objects(1) + .with_spec_revision(SpecificationRevision::from(self.spec_rev)) + .with_port_power_role(PowerRole::Sink); + + // Send request message + self.driver.send_message(header, &payload).await + } + Request::ACKDiscoverIdentity { identity, cert_stat, From 2800c7e28b052b7ac1ad5bf73153b84040b850ae Mon Sep 17 00:00:00 2001 From: Ferdia McKeogh Date: Mon, 6 May 2024 15:25:41 +0100 Subject: [PATCH 09/10] Fixup --- pd-interceptor/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pd-interceptor/src/main.rs b/pd-interceptor/src/main.rs index f32f3d2..267cbde 100644 --- a/pd-interceptor/src/main.rs +++ b/pd-interceptor/src/main.rs @@ -16,7 +16,7 @@ use { fusb302b::Fusb302b, panic_probe as _, usb_pd::{ - pdo::PowerDataObject, + messages::pdo::PowerDataObject, sink::{Event, Request, Sink}, }, }; From 5b298edc6fd45b4050dfeef00879733ddff35f99 Mon Sep 17 00:00:00 2001 From: Mike Panetta Date: Mon, 6 May 2024 15:26:43 +0100 Subject: [PATCH 10/10] Timers from the usb IF --- usb-pd/src/lib.rs | 1 + usb-pd/src/timers/mod.rs | 189 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 usb-pd/src/timers/mod.rs diff --git a/usb-pd/src/lib.rs b/usb-pd/src/lib.rs index 1a4c02f..1d79695 100644 --- a/usb-pd/src/lib.rs +++ b/usb-pd/src/lib.rs @@ -4,6 +4,7 @@ pub mod header; pub mod messages; pub mod sink; pub mod source; +pub mod timers; pub mod token; #[derive(Clone, Copy, PartialEq, Eq)] diff --git a/usb-pd/src/timers/mod.rs b/usb-pd/src/timers/mod.rs new file mode 100644 index 0000000..3272c66 --- /dev/null +++ b/usb-pd/src/timers/mod.rs @@ -0,0 +1,189 @@ +use embassy_time::{Duration, Instant}; + +#[allow(dead_code)] +#[derive(Debug)] +pub enum TimerTypes { + BISTContMode, + ChunkingNotSupported, + ChunkSenderRequest, + ChunkSenderResponse, + CRCReceive, + DataResetFail, + DataResetFailUFP, + DiscoverIdentity, + HardResetComplete, + NoResponse, + PSHardReset, + PSSourceOff, + PSSourceOn, + PSTransition, + SenderResponse, + SinkEPREnter, + SinkEPRKeepAlive, + SinkPPSPeriodic, + SinkRequest, + SinkWaitCap, + SourceCapability, + SourceEPRKeepAlive, + SourcePPSComm, + SinkTx, + SwapSourceStart, + VCONNDischarge, + VCONNOn, + VDMModeEntry, + VDMModeExit, + VDMResponse, +} + +impl From for Timer { + fn from(ty: TimerTypes) -> Self { + match ty { + TimerTypes::BISTContMode => Self::new(ty, timer_values::tBISTContMode), + TimerTypes::ChunkingNotSupported => Self::new(ty, timer_values::tChunkingNotSupported), + TimerTypes::ChunkSenderRequest => Self::new(ty, timer_values::tChunkSenderRequest), + TimerTypes::ChunkSenderResponse => Self::new(ty, timer_values::tChunkSenderResponse), + TimerTypes::CRCReceive => Self::new(ty, timer_values::tReceive), + TimerTypes::DataResetFail => Self::new(ty, timer_values::tDataResetFail), + TimerTypes::DataResetFailUFP => Self::new(ty, timer_values::tDataResetFailUFP), + TimerTypes::DiscoverIdentity => Self::new(ty, timer_values::tDiscoverIdentity), + TimerTypes::HardResetComplete => Self::new(ty, timer_values::tHardResetComplete), + TimerTypes::NoResponse => Self::new(ty, timer_values::tNoResponse), + TimerTypes::PSHardReset => Self::new(ty, timer_values::tPSHardReset), + TimerTypes::PSSourceOff => Self::new(ty, timer_values::tPSSourceOffSPR), + TimerTypes::PSSourceOn => Self::new(ty, timer_values::tPSSourceOnSPR), + TimerTypes::PSTransition => Self::new(ty, timer_values::tPSTransitionSPR), + TimerTypes::SenderResponse => Self::new(ty, timer_values::tSenderResponse), + TimerTypes::SinkEPREnter => Self::new(ty, timer_values::tEnterEPR), + TimerTypes::SinkEPRKeepAlive => Self::new(ty, timer_values::tSinkEPRKeepAlive), + TimerTypes::SinkPPSPeriodic => Self::new(ty, timer_values::tPPSRequest), + TimerTypes::SinkRequest => Self::new(ty, timer_values::tSinkRequest), + TimerTypes::SinkWaitCap => Self::new(ty, timer_values::tTypeCSinkWaitCap), + TimerTypes::SourceCapability => Self::new(ty, timer_values::tTypeCSendSourceCap), + TimerTypes::SourceEPRKeepAlive => Self::new(ty, timer_values::tSourceEPRKeepAlive), + TimerTypes::SourcePPSComm => todo!(), + TimerTypes::SinkTx => todo!(), + TimerTypes::SwapSourceStart => todo!(), + TimerTypes::VCONNDischarge => todo!(), + TimerTypes::VCONNOn => todo!(), + TimerTypes::VDMModeEntry => todo!(), + TimerTypes::VDMModeExit => todo!(), + TimerTypes::VDMResponse => todo!(), + } + } +} + +#[allow(dead_code)] +pub struct Timer { + ty: TimerTypes, + start: Instant, + duration: Duration, + expires: Option, +} + +#[allow(dead_code)] +impl Timer { + pub fn new(ty: TimerTypes, duration: Duration) -> Self { + Self { + ty, + start: Instant::now(), + duration, + expires: Some(Instant::now().checked_add(duration).unwrap()), + } + } + + pub fn is_expired(&self) -> bool { + self.expires + .map_or(false, |expires| expires <= Instant::now()) + } + + pub fn reset(&mut self) { + self.start = Instant::now(); + self.expires = Some(self.start.checked_add(self.duration).unwrap()); + } + + pub fn elapsed(&self) -> Duration { + Instant::now().duration_since(self.start) + } + + pub fn remaining(&self) -> Option { + self.expires + .map(|expires| expires.duration_since(Instant::now())) + } + + pub fn ty(&self) -> &TimerTypes { + &self.ty + } +} + +#[allow(non_upper_case_globals)] +#[allow(dead_code)] +mod timer_values { + use super::Duration; + + const tACTemoUpdate: Duration = Duration::from_millis(500); + pub(crate) const tBISTContMode: Duration = Duration::from_millis(45); + const tBISTCarrierMode: Duration = Duration::from_millis(300); + const tBISTSharedTestMode: Duration = Duration::from_secs(1); + const tCableMessage: Duration = Duration::from_micros(750); + pub(crate) const tChunkingNotSupported: Duration = Duration::from_millis(45); + const tChunkReceiverRequest: Duration = Duration::from_millis(15); + const tChunkReceiverResponse: Duration = Duration::from_millis(15); + pub(crate) const tChunkSenderRequest: Duration = Duration::from_millis(27); + pub(crate) const tChunkSenderResponse: Duration = Duration::from_millis(27); + const tDataReset: Duration = Duration::from_millis(225); + pub(crate) const tDataResetFail: Duration = Duration::from_millis(350); + pub(crate) const tDataResetFailUFP: Duration = Duration::from_millis(500); + pub(crate) const tDiscoverIdentity: Duration = Duration::from_millis(45); + const tDRSwapHardReset: Duration = Duration::from_millis(15); + const tDRSwapWait: Duration = Duration::from_millis(100); + const tEnterUSB: Duration = Duration::from_millis(500); + const tEnterUSBWait: Duration = Duration::from_millis(100); + pub(crate) const tEnterEPR: Duration = Duration::from_millis(500); + const tEPRSourceCableDiscovery: Duration = Duration::from_secs(2); + const tFirstSourceCap: Duration = Duration::from_millis(250); + const tFRSwap5V: Duration = Duration::from_millis(15); + const tFRSwapComplete: Duration = Duration::from_millis(15); + const tFRSwapInit: Duration = Duration::from_millis(15); + const tHardReset: Duration = Duration::from_millis(5); + pub(crate) const tHardResetComplete: Duration = Duration::from_millis(5); + pub(crate) const tSourceEPRKeepAlive: Duration = Duration::from_millis(875); + pub(crate) const tSinkEPRKeepAlive: Duration = Duration::from_millis(375); + pub(crate) const tNoResponse: Duration = Duration::from_secs(5); + pub(crate) const tPPSRequest: Duration = Duration::from_secs(5); // Max is 10 seconds. + const tPPSTimeout: Duration = Duration::from_secs(13); + const tProtErrHardReset: Duration = Duration::from_millis(15); + const tProtErrSoftReset: Duration = Duration::from_millis(15); + const tPRSwapWait: Duration = Duration::from_millis(100); + pub(crate) const tPSHardReset: Duration = Duration::from_millis(30); + pub(crate) const tPSSourceOffSPR: Duration = Duration::from_millis(835); + const tPSSourceOffEPR: Duration = Duration::from_millis(1260); + pub(crate) const tPSSourceOnSPR: Duration = Duration::from_millis(435); + pub(crate) const tPSTransitionSPR: Duration = Duration::from_millis(500); + const tPSTransitionEPR: Duration = Duration::from_millis(925); + pub(crate) const tReceive: Duration = Duration::from_millis(1); + const tReceiverResponse: Duration = Duration::from_millis(15); + const tRetry: Duration = Duration::from_micros(195); + pub(crate) const tSenderResponse: Duration = Duration::from_millis(30); + const tSinkDelay: Duration = Duration::from_millis(5); + pub(crate) const tSinkRequest: Duration = Duration::from_millis(100); + const tSinkTx: Duration = Duration::from_millis(18); + const tSoftReset: Duration = Duration::from_millis(15); + const tSrcHoldsBus: Duration = Duration::from_millis(50); + const tSwapSinkReady: Duration = Duration::from_millis(15); + const tSwapSourceStart: Duration = Duration::from_millis(20); + const tTransmit: Duration = Duration::from_micros(195); + pub(crate) const tTypeCSendSourceCap: Duration = Duration::from_millis(150); + pub(crate) const tTypeCSinkWaitCap: Duration = Duration::from_millis(465); + const tVCONNSourceDischarge: Duration = Duration::from_millis(200); + const tVCONNSourceOff: Duration = Duration::from_millis(25); + const tVCONNSourceOn: Duration = Duration::from_millis(50); + const tVCONNSourceTimeout: Duration = Duration::from_millis(150); + const tVCONNSwapWait: Duration = Duration::from_millis(100); + const tVDMBusy: Duration = Duration::from_millis(50); + const tVDMEnterMode: Duration = Duration::from_millis(25); + const tVDMExitMode: Duration = Duration::from_millis(25); + const tVDMReceiverResponse: Duration = Duration::from_millis(15); + const tVDMSenderResponse: Duration = Duration::from_millis(27); + const tVDMWaitModeEntry: Duration = Duration::from_millis(45); + const tVDMWaitModeExit: Duration = Duration::from_millis(45); +}