use crate::{
errors::{CatBridgeError, FSError},
fsemul::{
HostFilesystem,
dlf::DiskLayoutFile,
sdio::{
data_stream::DataStream,
errors::{SdioNetworkError, SdioProtocolError},
proto::{SDIO_BLOCK_SIZE, read::SdioControlReadRequest},
server::SDIO_DATA_STREAMS,
},
},
net::{
additions::StreamID,
server::requestable::{Body, State},
},
};
use bytes::{Bytes, BytesMut};
use std::path::PathBuf;
use tokio::{
fs::{File, read as fs_read},
io::{AsyncReadExt, AsyncSeekExt, SeekFrom},
};
use tracing::{info, warn};
pub(super) async fn handle_read_request(
State(fs): State<HostFilesystem>,
stream_id: StreamID,
Body(request): Body<SdioControlReadRequest>,
) -> Result<(), CatBridgeError> {
let Some(data_stream) = SDIO_DATA_STREAMS.get_async(&stream_id.to_raw()).await else {
return Err(SdioNetworkError::DataStreamMissing(stream_id.to_raw()).into());
};
let address_to_read = request.lba();
if address_to_read == 0xFFFF_0000 {
info!("Requested special ppc_boot.bsf file address");
let ppc_boot = fs.boot1_sytstem_path().await?;
return serve_padded_file_sdio(
&ppc_boot,
SeekFrom::Start(0),
request.blocks(),
&data_stream,
)
.await;
} else if address_to_read == 0x00F9_0000 {
info!("Unknown special large address... serving 0 blocks");
return serve_zeroed_blocks(request.blocks(), &data_stream).await;
}
let dlf = DiskLayoutFile::try_from(Bytes::from(
fs_read(fs.ppc_boot_dlf_path().await?)
.await
.map_err(FSError::from)?,
))?;
if u128::from(address_to_read) > dlf.max_address() {
return Err(SdioProtocolError::AddressOutOfRange(
u128::from(address_to_read),
dlf.max_address(),
)
.into());
}
if let Some((path, offset)) = dlf
.get_path_and_offset_for_file(u128::from(address_to_read))
.await
{
info!(
sdio.blocks = request.blocks(),
sdio.path = %path.display(),
sdio.path_offset = format!("{:06x}", offset),
"Serving known file over SDIO",
);
serve_padded_file_sdio(
path,
SeekFrom::Start(offset),
request.blocks(),
&data_stream,
)
.await
} else {
warn!(
sdio.address = format!("{:02x}", address_to_read),
sdio.blocks = request.blocks(),
"Serving unknown address over SDIO",
);
serve_zeroed_blocks(request.blocks(), &data_stream).await
}
}
async fn serve_padded_file_sdio(
path: &PathBuf,
offset: SeekFrom,
blocks_requested: u32,
data_channel: &DataStream,
) -> Result<(), CatBridgeError> {
let mut fd = File::open(path).await.map_err(FSError::IO)?;
fd.seek(offset).await.map_err(FSError::IO)?;
let blocks_left_to_serve =
usize::try_from(blocks_requested).map_err(|_| CatBridgeError::UnsupportedBitsPerCore)?;
let total_byte_size = blocks_left_to_serve * SDIO_BLOCK_SIZE;
let mut file_buff = BytesMut::zeroed(total_byte_size);
let mut bytes_read = 0;
while bytes_read < total_byte_size {
let read_this_go = fd
.read(&mut file_buff[bytes_read..])
.await
.map_err(FSError::IO)?;
if read_this_go == 0 {
break;
}
bytes_read += read_this_go;
}
data_channel.send(file_buff.freeze()).await?;
Ok(())
}
async fn serve_zeroed_blocks(
blocks_requested: u32,
data_channel: &DataStream,
) -> Result<(), CatBridgeError> {
let blocks_as_size =
usize::try_from(blocks_requested).map_err(|_| CatBridgeError::UnsupportedBitsPerCore)?;
data_channel
.send(BytesMut::zeroed(blocks_as_size * SDIO_BLOCK_SIZE).freeze())
.await?;
Ok(())
}