[UCPD] Add support for non-SOP packets

Allow capturing (and distinguishing) non-SOP packets as well. The
default configuration will just configure SOP packets. For ease of use
the default receive function signature is unchanged as for PD sinks
(which is likely the common usage) just SOP is enough so no need to
differentiate.
This commit is contained in:
Sjoerd Simons 2024-08-18 21:06:13 +02:00
parent d8459685fd
commit 1b0661ebb1
3 changed files with 76 additions and 7 deletions

View File

@ -27,7 +27,7 @@ use crate::dma::{ChannelAndRequest, TransferOptions};
use crate::interrupt; use crate::interrupt;
use crate::interrupt::typelevel::Interrupt; use crate::interrupt::typelevel::Interrupt;
use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode};
pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState}; pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState};
use crate::rcc::{self, RccPeripheral}; use crate::rcc::{self, RccPeripheral};
pub(crate) fn init( pub(crate) fn init(
@ -86,6 +86,34 @@ pub enum CcPull {
Source3_0A, Source3_0A,
} }
/// UCPD configuration
#[non_exhaustive]
#[derive(Copy, Clone, Debug)]
pub struct Config {
/// Receive SOP packets
pub sop: bool,
/// Receive SOP' packets
pub sop_prime: bool,
/// Receive SOP'' packets
pub sop_double_prime: bool,
/// Receive SOP'_Debug packets
pub sop_prime_debug: bool,
/// Receive SOP''_Debug packets
pub sop_double_prime_debug: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
sop: true,
sop_prime: false,
sop_double_prime: false,
sop_prime_debug: false,
sop_double_prime_debug: false,
}
}
}
/// UCPD driver. /// UCPD driver.
pub struct Ucpd<'d, T: Instance> { pub struct Ucpd<'d, T: Instance> {
cc_phy: CcPhy<'d, T>, cc_phy: CcPhy<'d, T>,
@ -98,6 +126,7 @@ impl<'d, T: Instance> Ucpd<'d, T> {
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
cc1: impl Peripheral<P = impl Cc1Pin<T>> + 'd, cc1: impl Peripheral<P = impl Cc1Pin<T>> + 'd,
cc2: impl Peripheral<P = impl Cc2Pin<T>> + 'd, cc2: impl Peripheral<P = impl Cc2Pin<T>> + 'd,
config: Config,
) -> Self { ) -> Self {
into_ref!(cc1, cc2); into_ref!(cc1, cc2);
cc1.set_as_analog(); cc1.set_as_analog();
@ -129,9 +158,15 @@ impl<'d, T: Instance> Ucpd<'d, T> {
// 1.75us * 17 = ~30us // 1.75us * 17 = ~30us
w.set_ifrgap(17 - 1); w.set_ifrgap(17 - 1);
// TODO: Currently only hard reset and SOP messages can be received.
// UNDOCUMENTED: This register can only be written while UCPDEN=0 (found by testing). // UNDOCUMENTED: This register can only be written while UCPDEN=0 (found by testing).
w.set_rxordseten(0b1001); let rxordset = (config.sop as u16) << 0
| (config.sop_prime as u16) << 1
| (config.sop_double_prime as u16) << 2
// Hard reset
| 0x1 << 3
| (config.sop_prime_debug as u16) << 4
| (config.sop_double_prime_debug as u16) << 5;
w.set_rxordseten(rxordset);
// Enable DMA // Enable DMA
w.set_txdmaen(true); w.set_txdmaen(true);
@ -288,6 +323,22 @@ impl<'d, T: Instance> CcPhy<'d, T> {
} }
} }
/// Receive SOP.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Sop {
/// SOP
Sop,
/// SOP'
SopPrime,
/// SOP''
SopDoublePrime,
/// SOP'_Debug
SopPrimeDebug,
/// SOP''_Debug
SopDoublePrimeDebug,
}
/// Receive Error. /// Receive Error.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -340,6 +391,13 @@ impl<'d, T: Instance> PdPhy<'d, T> {
/// ///
/// Returns the number of received bytes or an error. /// Returns the number of received bytes or an error.
pub async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, RxError> { pub async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, RxError> {
self.receive_with_sop(buf).await.map(|(_sop, size)| size)
}
/// Receives SOP and a PD message into the provided buffer.
///
/// Returns the start of packet type and number of received bytes or an error.
pub async fn receive_with_sop(&mut self, buf: &mut [u8]) -> Result<(Sop, usize), RxError> {
let r = T::REGS; let r = T::REGS;
let dma = unsafe { let dma = unsafe {
@ -388,7 +446,18 @@ impl<'d, T: Instance> PdPhy<'d, T> {
} }
} }
Ok(r.rx_payszr().read().rxpaysz().into()) let sop = match r.rx_ordsetr().read().rxordset() {
Rxordset::SOP => Sop::Sop,
Rxordset::SOPPRIME => Sop::SopPrime,
Rxordset::SOPDOUBLEPRIME => Sop::SopDoublePrime,
Rxordset::SOPPRIMEDEBUG => Sop::SopPrimeDebug,
Rxordset::SOPDOUBLEPRIMEDEBUG => Sop::SopDoublePrimeDebug,
Rxordset::CABLERESET => return Err(RxError::HardReset),
// Extension headers are not supported
_ => unreachable!(),
};
Ok((sop, r.rx_payszr().read().rxpaysz().into()))
} }
fn enable_rx_interrupt(enable: bool) { fn enable_rx_interrupt(enable: bool) {

View File

@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) {
info!("Hello World!"); info!("Hello World!");
let mut ucpd = Ucpd::new(p.UCPD1, Irqs {}, p.PB6, p.PB4); let mut ucpd = Ucpd::new(p.UCPD1, Irqs {}, p.PB6, p.PB4, Default::default());
ucpd.cc_phy().set_pull(CcPull::Sink); ucpd.cc_phy().set_pull(CcPull::Sink);
info!("Waiting for USB connection..."); info!("Waiting for USB connection...");

View File

@ -106,8 +106,8 @@ async fn main(_spawner: Spawner) {
info!("Hello World!"); info!("Hello World!");
// Wire between PD0 and PA8 // Wire between PD0 and PA8
let ucpd1 = Ucpd::new(p.UCPD1, Irqs {}, p.PA8, p.PB15); let ucpd1 = Ucpd::new(p.UCPD1, Irqs {}, p.PA8, p.PB15, Default::default());
let ucpd2 = Ucpd::new(p.UCPD2, Irqs {}, p.PD0, p.PD2); let ucpd2 = Ucpd::new(p.UCPD2, Irqs {}, p.PD0, p.PD2, Default::default());
join( join(
source(ucpd1, p.DMA1_CH1, p.DMA1_CH2), source(ucpd1, p.DMA1_CH1, p.DMA1_CH2),