use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
use esp_idf_sys::{esp, EspError, TickType_t};
#[cfg(esp_idf_version_major = "5")]
use esp_idf_sys::i2s_port_t;
#[cfg(not(esp_idf_version_major = "4"))]
use {
core::ptr::null_mut,
esp_idf_sys::{
i2s_chan_config_t, i2s_chan_handle_t, i2s_channel_disable, i2s_channel_enable,
i2s_channel_read, i2s_channel_register_event_callback, i2s_channel_write, i2s_del_channel,
i2s_event_callbacks_t, i2s_event_data_t, i2s_new_channel,
},
};
#[cfg(esp_idf_version_major = "4")]
use esp_idf_sys::{
i2s_config_t, i2s_driver_install, i2s_driver_uninstall, i2s_read, i2s_start, i2s_stop,
i2s_write,
};
#[cfg(not(esp_idf_version_major = "4"))]
use crate::interrupt::asynch::HalIsrNotification;
use crate::{delay, io::EspIOError};
#[cfg(any(
all(
not(esp_idf_version_major = "4"),
any(esp_idf_soc_i2s_supports_pdm_rx, esp_idf_soc_i2s_supports_pdm_tx)
),
all(esp_idf_version_major = "4", any(esp32, esp32s3, esp32c3, esp32c6))
))]
mod pdm;
mod std;
#[cfg(any(
all(not(esp_idf_version_major = "4"), esp_idf_soc_i2s_supports_tdm),
all(esp_idf_version_major = "4", any(esp32s3, esp32c3, esp32c6))
))]
mod tdm;
#[cfg(esp_idf_version_at_least_6_0_0)]
#[allow(non_camel_case_types)]
type i2s_port_t = i32;
pub type I2sConfig = config::Config;
pub mod config {
#[cfg(any(
all(
not(esp_idf_version_major = "4"),
any(esp_idf_soc_i2s_supports_pdm_rx, esp_idf_soc_i2s_supports_pdm_tx)
),
all(esp_idf_version_major = "4", any(esp32, esp32s3, esp32c3, esp32c6))
))]
pub use super::pdm::config::*;
pub use super::std::config::*;
#[cfg(any(
all(not(esp_idf_version_major = "4"), esp_idf_soc_i2s_supports_tdm),
all(esp_idf_version_major = "4", any(esp32s3, esp32c3, esp32c6))
))]
pub use super::tdm::config::*;
use esp_idf_sys::{
i2s_mclk_multiple_t, i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_128,
i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_256, i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_384,
EspError, ESP_ERR_INVALID_ARG,
};
#[cfg(esp_idf_version_major = "5")]
use esp_idf_sys::i2s_port_t;
#[cfg(not(esp_idf_version_major = "4"))]
use esp_idf_sys::{
i2s_chan_config_t, i2s_clock_src_t, i2s_data_bit_width_t,
i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_512, i2s_role_t, i2s_slot_bit_width_t,
i2s_slot_mode_t,
};
#[cfg(esp_idf_version_major = "4")]
use esp_idf_sys::{
i2s_bits_per_chan_t, i2s_bits_per_sample_t, i2s_mode_t, i2s_mode_t_I2S_MODE_MASTER,
i2s_mode_t_I2S_MODE_SLAVE,
};
#[cfg(not(any(
esp_idf_version_major = "4",
all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
)))] use esp_idf_sys::i2s_chan_config_t__bindgen_ty_1;
#[cfg(esp_idf_version_at_least_6_0_0)]
#[allow(non_camel_case_types)]
type i2s_port_t = i32;
pub const DEFAULT_DMA_BUFFER_COUNT: u32 = 6;
pub const DEFAULT_FRAMES_PER_DMA_BUFFER: u32 = 240;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub enum ClockSource {
#[cfg(not(any(esp32h2, esp32c2)))]
#[default]
Pll160M,
#[cfg(esp32c2)]
#[default]
Pll60M,
#[cfg(esp32h2)]
#[default]
Pll64M,
#[cfg(any(esp32, esp32s2))]
Apll,
}
impl ClockSource {
#[cfg(not(esp_idf_version_major = "4"))]
#[allow(clippy::unnecessary_cast)]
pub(super) fn as_sdk(&self) -> i2s_clock_src_t {
match self {
#[cfg(not(any(esp32h2, esp32c2)))]
Self::Pll160M => core::convert::TryInto::try_into(
esp_idf_sys::soc_module_clk_t_SOC_MOD_CLK_PLL_F160M,
)
.unwrap(),
#[cfg(esp32c2)]
Self::Pll60M => core::convert::TryInto::try_into(
esp_idf_sys::soc_module_clk_t_SOC_MOD_CLK_PLL_F60M,
)
.unwrap(),
#[cfg(esp32h2)]
Self::Pll64M => core::convert::TryInto::try_into(
esp_idf_sys::soc_module_clk_t_SOC_MOD_CLK_PLL_F64M,
)
.unwrap(),
#[cfg(any(esp32, esp32s2))]
Self::Apll => esp_idf_sys::soc_module_clk_t_SOC_MOD_CLK_APLL,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Config {
pub(super) role: Role,
pub(super) dma_buffer_count: u32,
pub(super) frames_per_buffer: u32,
pub(super) auto_clear: bool,
}
impl Default for Config {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl Config {
#[inline(always)]
pub const fn new() -> Self {
Self {
role: Role::Controller,
dma_buffer_count: DEFAULT_DMA_BUFFER_COUNT,
frames_per_buffer: DEFAULT_FRAMES_PER_DMA_BUFFER,
auto_clear: false,
}
}
#[must_use]
#[inline(always)]
pub fn role(mut self, role: Role) -> Self {
self.role = role;
self
}
#[must_use]
#[inline(always)]
pub fn dma_buffer_count(mut self, dma_buffer_count: u32) -> Self {
self.dma_buffer_count = dma_buffer_count;
self
}
#[must_use]
#[inline(always)]
pub fn frames_per_buffer(mut self, frames: u32) -> Self {
self.frames_per_buffer = frames;
self
}
#[must_use]
#[inline(always)]
pub fn auto_clear(mut self, auto_clear: bool) -> Self {
self.auto_clear = auto_clear;
self
}
#[allow(clippy::needless_update)]
#[cfg(not(esp_idf_version_major = "4"))]
#[inline(always)]
pub(super) fn as_sdk(&self, id: i2s_port_t) -> i2s_chan_config_t {
i2s_chan_config_t {
id,
role: self.role.as_sdk(),
dma_desc_num: self.dma_buffer_count,
dma_frame_num: self.frames_per_buffer,
#[cfg(any(
esp_idf_version_major = "4",
all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
))]
auto_clear: self.auto_clear,
#[cfg(not(any(
esp_idf_version_major = "4",
all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
all(esp_idf_version_major = "5", esp_idf_version_minor = "2"),
)))] __bindgen_anon_1: i2s_chan_config_t__bindgen_ty_1{
auto_clear_after_cb: self.auto_clear,
},
..Default::default()
}
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum DataBitWidth {
Bits8,
Bits16,
Bits24,
Bits32,
}
impl From<DataBitWidth> for u32 {
#[inline(always)]
fn from(value: DataBitWidth) -> Self {
match value {
DataBitWidth::Bits8 => 8,
DataBitWidth::Bits16 => 16,
DataBitWidth::Bits24 => 24,
DataBitWidth::Bits32 => 32,
}
}
}
impl DataBitWidth {
#[cfg(not(esp_idf_version_major = "4"))]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_data_bit_width_t {
match self {
Self::Bits8 => 8,
Self::Bits16 => 16,
Self::Bits24 => 24,
Self::Bits32 => 32,
}
}
#[cfg(esp_idf_version_major = "4")]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_bits_per_sample_t {
match self {
Self::Bits8 => 8,
Self::Bits16 => 16,
Self::Bits24 => 24,
Self::Bits32 => 32,
}
}
}
impl TryFrom<usize> for DataBitWidth {
type Error = EspError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
match value {
8 => Ok(Self::Bits8),
16 => Ok(Self::Bits16),
24 => Ok(Self::Bits24),
32 => Ok(Self::Bits32),
_ => Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum MclkMultiple {
M128,
M256,
M384,
#[cfg(not(esp_idf_version_major = "4"))]
M512,
}
impl MclkMultiple {
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_mclk_multiple_t {
match self {
Self::M128 => i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_128,
Self::M256 => i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_256,
Self::M384 => i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_384,
#[cfg(not(esp_idf_version_major = "4"))]
Self::M512 => i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_512,
}
}
}
impl From<MclkMultiple> for u32 {
#[inline(always)]
fn from(mclk_multiple: MclkMultiple) -> Self {
match mclk_multiple {
MclkMultiple::M128 => 128,
MclkMultiple::M256 => 256,
MclkMultiple::M384 => 384,
#[cfg(not(esp_idf_version_major = "4"))]
MclkMultiple::M512 => 512,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Role {
#[default]
Controller,
Target,
}
#[cfg(not(esp_idf_version_major = "4"))]
const I2S_ROLE_CONTROLLER: i2s_role_t = 0;
#[cfg(not(esp_idf_version_major = "4"))]
const I2S_ROLE_TARGET: i2s_role_t = 1;
impl Role {
#[cfg(not(esp_idf_version_major = "4"))]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_role_t {
match self {
Self::Controller => I2S_ROLE_CONTROLLER,
Self::Target => I2S_ROLE_TARGET,
}
}
#[cfg(esp_idf_version_major = "4")]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_mode_t {
match self {
Self::Controller => i2s_mode_t_I2S_MODE_MASTER,
Self::Target => i2s_mode_t_I2S_MODE_SLAVE,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum SlotBitWidth {
#[default]
Auto,
Bits8,
Bits16,
Bits24,
Bits32,
}
#[cfg(not(esp_idf_version_major = "4"))]
type SlotBitWidthSdkType = i2s_slot_bit_width_t;
#[cfg(esp_idf_version_major = "4")]
type SlotBitWidthSdkType = i2s_bits_per_chan_t;
impl SlotBitWidth {
#[inline(always)]
pub(super) fn as_sdk(&self) -> SlotBitWidthSdkType {
match self {
Self::Auto => 0,
Self::Bits8 => 8,
Self::Bits16 => 16,
Self::Bits24 => 24,
Self::Bits32 => 32,
}
}
}
impl TryFrom<u32> for SlotBitWidth {
type Error = EspError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Auto),
8 => Ok(Self::Bits8),
16 => Ok(Self::Bits16),
24 => Ok(Self::Bits24),
32 => Ok(Self::Bits32),
_ => Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()),
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum SlotMode {
Mono,
#[default]
Stereo,
}
impl SlotMode {
#[cfg(not(esp_idf_version_major = "4"))]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_slot_mode_t {
match self {
Self::Mono => 1,
Self::Stereo => 2,
}
}
}
}
pub trait I2s: Send + sealed::Sealed {
fn port() -> i2s_port_t;
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::I2S0<'_> {}
#[cfg(any(esp32, esp32s3))]
impl Sealed for super::I2S1<'_> {}
}
pub trait I2sPort {
fn port(&self) -> i2s_port_t;
}
pub trait I2sRxSupported {}
pub struct I2sRx {}
impl I2sRxSupported for I2sRx {}
pub trait I2sTxSupported {}
pub struct I2sTx {}
impl I2sTxSupported for I2sTx {}
pub struct I2sBiDir {}
impl I2sRxSupported for I2sBiDir {}
impl I2sTxSupported for I2sBiDir {}
pub struct I2sDriverRef<'d, Dir>(NonNull<I2sDriver<'d, Dir>>);
impl<'d, Dir> Deref for I2sDriverRef<'d, Dir> {
type Target = I2sDriver<'d, Dir>;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl<Dir> DerefMut for I2sDriverRef<'_, Dir> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.0.as_mut() }
}
}
pub struct I2sDriver<'d, Dir> {
#[cfg(not(esp_idf_version_major = "4"))]
rx_handle: i2s_chan_handle_t,
#[cfg(not(esp_idf_version_major = "4"))]
tx_handle: i2s_chan_handle_t,
port: u8,
_p: PhantomData<&'d ()>,
_dir: PhantomData<Dir>,
}
impl<Dir> I2sDriver<'_, Dir> {
#[cfg(not(esp_idf_version_major = "4"))]
fn internal_new<I2S: I2s>(
config: &i2s_chan_config_t,
rx: bool,
tx: bool,
) -> Result<Self, EspError> {
let port = I2S::port();
let mut rx_handle: i2s_chan_handle_t = null_mut();
let mut tx_handle: i2s_chan_handle_t = null_mut();
unsafe {
esp!(i2s_new_channel(
config,
if tx {
&mut tx_handle as _
} else {
core::ptr::null_mut()
},
if rx {
&mut rx_handle as _
} else {
core::ptr::null_mut()
},
))?
};
let mut this = Self {
port: port as u8,
rx_handle,
tx_handle,
_p: PhantomData,
_dir: PhantomData,
};
this.subscribe_channel(this.rx_handle)?;
this.subscribe_channel(this.tx_handle)?;
Ok(this)
}
#[cfg(esp_idf_version_major = "4")]
#[allow(clippy::too_many_arguments)]
pub fn internal_new<I2S: I2s>(config: &i2s_config_t) -> Result<Self, EspError> {
let port = I2S::port();
unsafe {
esp!(i2s_driver_install(port, config, 0, core::ptr::null_mut()))?;
}
Ok(Self {
port: port as u8,
_p: PhantomData,
_dir: PhantomData,
})
}
#[cfg(not(esp_idf_version_major = "4"))]
fn subscribe_channel(&mut self, handle: i2s_chan_handle_t) -> Result<(), EspError> {
if !handle.is_null() {
let callbacks = i2s_event_callbacks_t {
on_recv: Some(dispatch_recv),
on_recv_q_ovf: Some(dispatch_recv),
on_sent: Some(dispatch_send),
on_send_q_ovf: Some(dispatch_send),
};
esp!(unsafe {
i2s_channel_register_event_callback(
handle,
&callbacks,
self.port as u32 as *mut core::ffi::c_void,
)
})?;
}
Ok(())
}
#[cfg(not(esp_idf_version_major = "4"))]
fn unsubscribe_channel(&mut self, handle: i2s_chan_handle_t) -> Result<(), EspError> {
if !handle.is_null() {
let callbacks = i2s_event_callbacks_t {
on_recv: None,
on_recv_q_ovf: None,
on_sent: None,
on_send_q_ovf: None,
};
esp!(unsafe {
i2s_channel_register_event_callback(
handle,
&callbacks,
self.port as u32 as *mut core::ffi::c_void,
)
})?;
}
Ok(())
}
#[cfg(not(esp_idf_version_major = "4"))]
fn del_channel(&mut self, handle: i2s_chan_handle_t) -> Result<(), EspError> {
if !handle.is_null() {
let callbacks = i2s_event_callbacks_t {
on_recv: None,
on_recv_q_ovf: None,
on_sent: None,
on_send_q_ovf: None,
};
esp!(unsafe {
i2s_channel_register_event_callback(handle, &callbacks, core::ptr::null_mut())
})?;
esp!(unsafe { i2s_del_channel(handle) })?;
}
Ok(())
}
fn remap_result(
result: Result<(), EspError>,
bytes_processed: usize,
) -> Result<usize, EspError> {
match result {
Ok(_) => Ok(bytes_processed),
Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT && bytes_processed > 0 => {
Ok(bytes_processed)
}
Err(err) => Err(err),
}
}
pub fn as_ref(&mut self) -> I2sDriverRef<'_, Dir> {
I2sDriverRef(unsafe { NonNull::new_unchecked(self) })
}
}
impl<Dir> I2sDriver<'_, Dir>
where
Dir: I2sRxSupported,
{
#[cfg(esp_idf_version_major = "4")]
pub fn rx_enable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_start(self.port as _)) }
}
#[cfg(not(esp_idf_version_major = "4"))]
pub fn rx_enable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_channel_enable(self.rx_handle)) }
}
#[cfg(esp_idf_version_major = "4")]
pub fn rx_disable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_stop(self.port as _)) }
}
#[cfg(not(esp_idf_version_major = "4"))]
pub fn rx_disable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_channel_disable(self.rx_handle)) }
}
#[cfg(not(esp_idf_version_major = "4"))]
pub async fn read_async(&mut self, buffer: &mut [u8]) -> Result<usize, EspError> {
loop {
match self.read(buffer, crate::delay::NON_BLOCK) {
Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => {
RECV_NOTIFIER[self.port as usize].wait().await;
}
other => break other,
}
}
}
#[cfg(esp_idf_version_major = "4")]
pub fn read(&mut self, buffer: &mut [u8], timeout: TickType_t) -> Result<usize, EspError> {
if buffer.is_empty() {
Ok(0)
} else {
let mut bytes_read: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_read(
self.port as _,
buffer.as_mut_ptr() as *mut c_void,
buffer.len(),
&mut bytes_read,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_read,
)
}
}
#[cfg(not(esp_idf_version_major = "4"))]
pub fn read(&mut self, buffer: &mut [u8], timeout: TickType_t) -> Result<usize, EspError> {
if buffer.is_empty() {
Ok(0)
} else {
let mut bytes_read: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_channel_read(
self.rx_handle,
buffer.as_mut_ptr() as *mut c_void,
buffer.len(),
&mut bytes_read,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_read,
)
}
}
#[cfg(not(esp_idf_version_major = "4"))]
pub async fn read_uninit_async(
&mut self,
buffer: &mut [MaybeUninit<u8>],
) -> Result<usize, EspError> {
loop {
match self.read_uninit(buffer, crate::delay::NON_BLOCK) {
Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => {
RECV_NOTIFIER[self.port as usize].wait().await;
}
other => break other,
}
}
}
#[cfg(esp_idf_version_major = "4")]
pub fn read_uninit(
&mut self,
buffer: &mut [MaybeUninit<u8>],
timeout: TickType_t,
) -> Result<usize, EspError> {
if buffer.is_empty() {
Ok(0)
} else {
let mut bytes_read: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_read(
self.port as _,
buffer.as_mut_ptr() as *mut c_void,
buffer.len(),
&mut bytes_read,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_read,
)
}
}
#[cfg(not(esp_idf_version_major = "4"))]
pub fn read_uninit(
&mut self,
buffer: &mut [MaybeUninit<u8>],
timeout: TickType_t,
) -> Result<usize, EspError> {
if buffer.is_empty() {
Ok(0)
} else {
let mut bytes_read: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_channel_read(
self.rx_handle,
buffer.as_mut_ptr() as *mut c_void,
buffer.len(),
&mut bytes_read,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_read,
)
}
}
}
impl<Dir> I2sDriver<'_, Dir>
where
Dir: I2sTxSupported,
{
#[cfg(esp_idf_version_major = "4")]
pub fn tx_enable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_start(self.port as _)) }
}
#[cfg(not(esp_idf_version_major = "4"))]
pub fn tx_enable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_channel_enable(self.tx_handle)) }
}
#[cfg(esp_idf_version_major = "4")]
pub fn tx_disable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_stop(self.port())) }
}
#[cfg(not(esp_idf_version_major = "4"))]
pub fn tx_disable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_channel_disable(self.tx_handle)) }
}
#[cfg(all(
not(esp_idf_version_major = "4"),
not(all(esp_idf_version_major = "5", esp_idf_version_minor = "0"))
))]
pub fn preload_data(&mut self, data: &[u8]) -> Result<usize, EspError> {
let mut bytes_loaded: usize = 0;
unsafe {
esp!(esp_idf_sys::i2s_channel_preload_data(
self.tx_handle,
data.as_ptr() as *const c_void,
data.len(),
&mut bytes_loaded as *mut usize
))?;
}
Ok(bytes_loaded)
}
#[cfg(not(esp_idf_version_major = "4"))]
pub async fn write_async(&mut self, data: &[u8]) -> Result<usize, EspError> {
loop {
match self.write(data, crate::delay::NON_BLOCK) {
Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => {
SEND_NOTIFIER[self.port as usize].wait().await;
}
other => break other,
}
}
}
#[cfg(not(esp_idf_version_major = "4"))]
pub async fn write_all_async(&mut self, data: &[u8]) -> Result<(), EspError> {
let mut offset = 0;
while offset < data.len() {
offset += self.write_async(&data[offset..]).await?;
}
Ok(())
}
#[cfg(esp_idf_version_major = "4")]
pub fn write(&mut self, data: &[u8], timeout: TickType_t) -> Result<usize, EspError> {
if data.is_empty() {
Ok(0)
} else {
let mut bytes_written: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_write(
self.port(),
data.as_ptr() as *mut c_void,
data.len(),
&mut bytes_written,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_written,
)
}
}
#[cfg(not(esp_idf_version_major = "4"))]
pub fn write(&mut self, data: &[u8], timeout: TickType_t) -> Result<usize, EspError> {
if data.is_empty() {
Ok(0)
} else {
let mut bytes_written: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_channel_write(
self.tx_handle,
data.as_ptr() as *mut c_void,
data.len(),
&mut bytes_written,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_written,
)
}
}
pub fn write_all(&mut self, data: &[u8], timeout: TickType_t) -> Result<(), EspError> {
let mut offset = 0;
while offset < data.len() {
offset += self.write(&data[offset..], timeout)?;
}
Ok(())
}
}
impl I2sDriver<'_, I2sBiDir> {
pub fn split(&mut self) -> (I2sDriverRef<'_, I2sRx>, I2sDriverRef<'_, I2sTx>) {
let this = unsafe { NonNull::new_unchecked(self) };
(I2sDriverRef(this.cast()), I2sDriverRef(this.cast()))
}
}
impl<Dir> Drop for I2sDriver<'_, Dir> {
fn drop(&mut self) {
#[cfg(esp_idf_version_major = "4")]
{
let _ = unsafe { esp!(i2s_stop(self.port as _)) };
esp!(unsafe { i2s_driver_uninstall(self.port as _) }).unwrap();
}
#[cfg(not(esp_idf_version_major = "4"))]
{
if !self.rx_handle.is_null() {
let _ = unsafe { esp!(i2s_channel_disable(self.rx_handle)) };
}
if !self.tx_handle.is_null() {
let _ = unsafe { esp!(i2s_channel_disable(self.tx_handle)) };
}
self.unsubscribe_channel(self.rx_handle).unwrap();
self.unsubscribe_channel(self.tx_handle).unwrap();
if !self.rx_handle.is_null() {
self.del_channel(self.rx_handle).unwrap();
}
if !self.tx_handle.is_null() {
self.del_channel(self.tx_handle).unwrap();
}
SEND_NOTIFIER[self.port as usize].reset();
RECV_NOTIFIER[self.port as usize].reset();
}
}
}
unsafe impl<Dir> Send for I2sDriver<'_, Dir> {}
impl<Dir> I2sPort for I2sDriver<'_, Dir> {
fn port(&self) -> i2s_port_t {
self.port as _
}
}
impl<Dir> embedded_io::ErrorType for I2sDriver<'_, Dir> {
type Error = EspIOError;
}
impl<Dir> embedded_io::Read for I2sDriver<'_, Dir>
where
Dir: I2sRxSupported,
{
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.read(buf, delay::BLOCK).map_err(EspIOError)
}
}
impl<Dir> embedded_io::Write for I2sDriver<'_, Dir>
where
Dir: I2sTxSupported,
{
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.write(buf, delay::BLOCK).map_err(EspIOError)
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
#[cfg(not(esp_idf_version_major = "4"))]
impl<Dir> embedded_io_async::Read for I2sDriver<'_, Dir>
where
Dir: I2sRxSupported,
{
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.read_async(buf).await.map_err(EspIOError)
}
}
#[cfg(not(esp_idf_version_major = "4"))]
impl<Dir> embedded_io_async::Write for I2sDriver<'_, Dir>
where
Dir: I2sTxSupported,
{
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.write_async(buf).await.map_err(EspIOError)
}
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
#[cfg(not(esp_idf_version_major = "4"))]
unsafe extern "C" fn dispatch_send(
_handle: i2s_chan_handle_t,
_raw_event: *mut i2s_event_data_t,
user_ctx: *mut c_void,
) -> bool {
let port = user_ctx as u32 as i2s_port_t;
SEND_NOTIFIER[port as usize].notify_lsb()
}
#[cfg(not(esp_idf_version_major = "4"))]
unsafe extern "C" fn dispatch_recv(
_handle: i2s_chan_handle_t,
_raw_event: *mut i2s_event_data_t,
user_ctx: *mut c_void,
) -> bool {
let port = user_ctx as u32 as i2s_port_t;
RECV_NOTIFIER[port as usize].notify_lsb()
}
macro_rules! impl_i2s {
($i2s:ident: $port:expr) => {
crate::impl_peripheral!($i2s);
impl I2s for $i2s<'_> {
#[inline(always)]
fn port() -> i2s_port_t {
$port
}
}
};
}
impl_i2s!(I2S0: 0);
#[cfg(any(esp32, esp32s3))]
impl_i2s!(I2S1: 1);
#[cfg(not(esp_idf_version_major = "4"))]
#[cfg(not(any(esp32, esp32s3)))]
static SEND_NOTIFIER: [HalIsrNotification; 1] = [HalIsrNotification::new()];
#[cfg(not(esp_idf_version_major = "4"))]
#[cfg(not(any(esp32, esp32s3)))]
static RECV_NOTIFIER: [HalIsrNotification; 1] = [HalIsrNotification::new()];
#[cfg(not(esp_idf_version_major = "4"))]
#[cfg(any(esp32, esp32s3))]
static SEND_NOTIFIER: [HalIsrNotification; 2] =
[HalIsrNotification::new(), HalIsrNotification::new()];
#[cfg(not(esp_idf_version_major = "4"))]
#[cfg(any(esp32, esp32s3))]
static RECV_NOTIFIER: [HalIsrNotification; 2] =
[HalIsrNotification::new(), HalIsrNotification::new()];