use alloc::sync::Arc;
use sdio_host::{SdioCardIrq, SdioHost};
use spin::Mutex;
use crate::{
common::{
ChipVariant, SDIOWIFI_BLOCK_CNT_REG, SDIOWIFI_BYTEMODE_LEN_REG,
SDIOWIFI_BYTEMODE_LEN_REG_V3, SDIOWIFI_FLOW_CTRL_Q1_REG_V3, SDIOWIFI_FLOW_CTRL_REG,
SDIOWIFI_FLOWCTRL_MASK, SDIOWIFI_INTR_CONFIG_REG, SDIOWIFI_INTR_ENABLE_REG_V3,
SDIOWIFI_MISC_INT_STATUS_REG_V3, SDIOWIFI_RD_FIFO_ADDR, SDIOWIFI_RD_FIFO_ADDR_V3,
SDIOWIFI_SLEEP_REG_V3, SDIOWIFI_V3_SLEEP_READY_BIT, SDIOWIFI_V3_WAKEUP_VALUE,
SDIOWIFI_WAKEUP_REG_V3, SDIOWIFI_WR_FIFO_ADDR, SDIOWIFI_WR_FIFO_ADDR_V3,
},
fdrv::consts::BUFFER_SIZE,
};
pub struct SdioTransport {
sdio: Arc<Mutex<dyn SdioHost>>,
card_irq: Option<Arc<dyn SdioCardIrq>>,
is_v3: bool,
}
impl SdioTransport {
pub fn new<H: SdioHost + 'static>(sdio: H, chip: ChipVariant) -> Arc<Self> {
let card_irq = sdio.card_irq_ctrl();
Arc::new(Self {
sdio: Arc::new(Mutex::new(sdio)),
card_irq,
is_v3: chip.is_v3(),
})
}
pub fn is_v3(&self) -> bool {
self.is_v3
}
pub fn block_cnt_reg(&self) -> u32 {
if self.is_v3 {
SDIOWIFI_MISC_INT_STATUS_REG_V3
} else {
SDIOWIFI_BLOCK_CNT_REG
}
}
pub fn flow_ctrl_reg_addr(&self) -> u32 {
if self.is_v3 {
SDIOWIFI_FLOW_CTRL_Q1_REG_V3
} else {
SDIOWIFI_FLOW_CTRL_REG
}
}
pub fn rd_fifo_addr(&self) -> u32 {
if self.is_v3 {
SDIOWIFI_RD_FIFO_ADDR_V3
} else {
SDIOWIFI_RD_FIFO_ADDR
}
}
pub fn bytemode_len_reg(&self) -> u32 {
if self.is_v3 {
SDIOWIFI_BYTEMODE_LEN_REG_V3
} else {
SDIOWIFI_BYTEMODE_LEN_REG
}
}
pub fn wr_fifo_addr(&self) -> u32 {
if self.is_v3 {
SDIOWIFI_WR_FIFO_ADDR_V3
} else {
SDIOWIFI_WR_FIFO_ADDR
}
}
pub fn intr_config_reg_addr(&self) -> u32 {
if self.is_v3 {
SDIOWIFI_INTR_ENABLE_REG_V3
} else {
SDIOWIFI_INTR_CONFIG_REG
}
}
pub fn read_byte(&self, func: u8, addr: u32) -> Result<u8, sdio_host::error::SdioError> {
self.sdio.lock().read_byte(func, addr)
}
pub fn write_byte(
&self,
func: u8,
addr: u32,
val: u8,
) -> Result<(), sdio_host::error::SdioError> {
self.sdio.lock().write_byte(func, addr, val)
}
pub fn read_fifo(
&self,
func: u8,
addr: u32,
buf: &mut [u8],
) -> Result<(), sdio_host::error::SdioError> {
self.sdio.lock().read_fifo(func, addr, buf)
}
pub fn write_fifo(
&self,
func: u8,
addr: u32,
buf: &[u8],
) -> Result<(), sdio_host::error::SdioError> {
self.sdio.lock().write_fifo(func, addr, buf)
}
pub(crate) fn mask_card_irq(&self) {
if let Some(ref ctrl) = self.card_irq {
ctrl.mask_card_irq();
}
}
pub(crate) fn unmask_card_irq(&self) {
if let Some(ref ctrl) = self.card_irq {
ctrl.unmask_card_irq();
}
}
pub fn enable_irq(&self) {
self.sdio.lock().enable_irq();
}
pub fn disable_irq(&self) {
self.sdio.lock().disable_irq();
}
pub fn wakeup(&self) -> bool {
if !self.is_v3 {
return true;
}
if let Ok(val) = self.read_byte(1, SDIOWIFI_SLEEP_REG_V3)
&& val & SDIOWIFI_V3_SLEEP_READY_BIT != 0
{
return true;
}
if let Err(e) = self.write_byte(1, SDIOWIFI_WAKEUP_REG_V3, SDIOWIFI_V3_WAKEUP_VALUE) {
log::error!("[sdio] wakeup write failed: {:?}", e);
return false;
}
for _ in 0..200 {
match self.read_byte(1, SDIOWIFI_SLEEP_REG_V3) {
Ok(val) if val & SDIOWIFI_V3_SLEEP_READY_BIT != 0 => return true,
_ => crate::runtime::runtime().yield_now(),
}
}
log::warn!("[sdio] wakeup timeout, chip may still be sleeping");
false
}
pub fn read_flow_ctrl(&self) -> Result<u8, sdio_host::error::SdioError> {
self.sdio.lock().read_byte(1, self.flow_ctrl_reg_addr())
}
pub fn read_flow_ctrl_value(&self) -> Result<u8, sdio_host::error::SdioError> {
self.read_flow_ctrl().map(|fc| fc & SDIOWIFI_FLOWCTRL_MASK)
}
pub fn check_flow_ctrl_available(&self) -> bool {
matches!(self.read_flow_ctrl_value(), Ok(v) if v != 0)
}
pub fn check_flow_ctrl_for_size(&self, send_len: usize) -> bool {
match self.read_flow_ctrl_value() {
Ok(v) if v != 0 => (v as usize) * BUFFER_SIZE > send_len,
_ => false,
}
}
pub fn wait_flow_ctrl(&self, max_retries: u32) -> bool {
for _ in 0..max_retries {
if self.check_flow_ctrl_available() {
return true;
}
crate::runtime::runtime().yield_now();
}
false
}
pub fn wait_flow_ctrl_for_size(&self, send_len: usize, max_retries: u32) -> bool {
for _ in 0..max_retries {
if self.check_flow_ctrl_for_size(send_len) {
return true;
}
crate::runtime::runtime().yield_now();
}
false
}
}