#![warn(missing_docs)]
pub use self::maybe_floppy_disk_controller::MaybeStubFloppyDiskController;
use self::super_io::SioController;
use chipset_device::io::IoError;
use chipset_device::io::IoResult;
use chipset_device::pio::PortIoIntercept;
use chipset_device::pio::RegisterPortIoIntercept;
use chipset_device::poll_device::PollDevice;
use chipset_device::ChipsetDevice;
use floppy::DriveRibbon;
use guestmem::GuestMemory;
use inspect::InspectMut;
use std::task::Context;
use thiserror::Error;
use vmcore::device_state::ChangeDeviceState;
use vmcore::isa_dma_channel::IsaDmaChannel;
use vmcore::line_interrupt::LineInterrupt;
mod super_io;
const PRI_FLOPPY_BASE_ADDR: u16 = 0x3F0;
const SEC_FLOPPY_BASE_ADDR: u16 = 0x370;
const PRI_EXT_FUNC_ENABLE_REG: u16 = 0x3F0;
const SEC_EXT_FUNC_ENABLE_REG: u16 = 0x370;
const PRI_EXT_FUNC_DATA_REG: u16 = 0x3F1;
const SEC_EXT_FUNC_DATA_REG: u16 = 0x371;
#[derive(InspectMut)]
pub struct Winbond83977FloppySioDevice<FDC: MaybeStubFloppyDiskController> {
#[inspect(mut)]
sio: SioController,
#[inspect(mut)]
primary_fdc: FDC,
#[inspect(mut)]
secondary_fdc: FDC,
}
#[derive(Debug, Error)]
#[allow(missing_docs)]
pub enum NewWinbond83977FloppySioDeviceError<FdcError> {
#[error("failed to share interrupt line")]
LineShare(#[source] vmcore::line_interrupt::NewLineError),
#[error("failed to init primary floppy controller")]
BadPrimaryFdc(#[source] FdcError),
#[error("failed to init secondary floppy controller")]
BadSecondaryFdc(#[source] FdcError),
}
impl<FDC: MaybeStubFloppyDiskController> Winbond83977FloppySioDevice<FDC> {
pub fn new(
guest_memory: GuestMemory,
interrupt: LineInterrupt,
register_pio: &mut dyn RegisterPortIoIntercept,
primary_disk_drive: DriveRibbon,
secondary_disk_drive: DriveRibbon,
primary_dma: Box<dyn IsaDmaChannel>,
secondary_dma: Box<dyn IsaDmaChannel>,
) -> Result<Self, NewWinbond83977FloppySioDeviceError<FDC::NewError>> {
let secondary_interrupt = interrupt
.new_shared("floppy secondary")
.map_err(NewWinbond83977FloppySioDeviceError::LineShare)?;
Ok(Self {
sio: SioController::default(),
primary_fdc: FDC::new(
guest_memory.clone(),
interrupt,
register_pio,
PRI_FLOPPY_BASE_ADDR,
primary_disk_drive,
primary_dma,
)
.map_err(NewWinbond83977FloppySioDeviceError::BadPrimaryFdc)?,
secondary_fdc: FDC::new(
guest_memory,
secondary_interrupt,
register_pio,
SEC_FLOPPY_BASE_ADDR,
secondary_disk_drive,
secondary_dma,
)
.map_err(NewWinbond83977FloppySioDeviceError::BadSecondaryFdc)?,
})
}
}
impl<FDC: MaybeStubFloppyDiskController> ChangeDeviceState for Winbond83977FloppySioDevice<FDC> {
fn start(&mut self) {}
async fn stop(&mut self) {}
async fn reset(&mut self) {
self.sio.reset().await;
self.primary_fdc.reset().await;
self.secondary_fdc.reset().await;
}
}
impl<FDC: MaybeStubFloppyDiskController> ChipsetDevice for Winbond83977FloppySioDevice<FDC> {
fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
Some(self)
}
fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
Some(self)
}
}
impl<FDC: MaybeStubFloppyDiskController> PollDevice for Winbond83977FloppySioDevice<FDC> {
fn poll_device(&mut self, cx: &mut Context<'_>) {
self.primary_fdc.poll_device(cx);
self.secondary_fdc.poll_device(cx);
}
}
impl<FDC: MaybeStubFloppyDiskController> PortIoIntercept for Winbond83977FloppySioDevice<FDC> {
fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
if data.len() != 1 {
return IoResult::Err(IoError::InvalidAccessSize);
}
if io_port == PRI_EXT_FUNC_DATA_REG || io_port == SEC_EXT_FUNC_DATA_REG {
if let Ok(value) = self.sio.config_read() {
data[0] = value;
return IoResult::Ok;
}
}
if self.primary_fdc.offset_of(io_port).is_some() {
return self.primary_fdc.io_read(io_port, data);
}
if self.secondary_fdc.offset_of(io_port).is_some() {
return self.secondary_fdc.io_read(io_port, data);
}
IoResult::Err(IoError::InvalidRegister)
}
fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
if data.len() != 1 {
return IoResult::Err(IoError::InvalidAccessSize);
}
match io_port {
PRI_EXT_FUNC_ENABLE_REG | SEC_EXT_FUNC_ENABLE_REG => {
self.sio.update_config_state(data[0]);
}
PRI_EXT_FUNC_DATA_REG | SEC_EXT_FUNC_DATA_REG => self.sio.config_write(data[0]),
_ => {
if self.primary_fdc.offset_of(io_port).is_some() {
return self.primary_fdc.io_write(io_port, data);
}
if self.secondary_fdc.offset_of(io_port).is_some() {
return self.secondary_fdc.io_write(io_port, data);
}
return IoResult::Err(IoError::InvalidRegister);
}
}
IoResult::Ok
}
}
mod maybe_floppy_disk_controller {
use chipset_device::pio::PortIoIntercept;
use chipset_device::pio::RegisterPortIoIntercept;
use chipset_device::poll_device::PollDevice;
use chipset_device::ChipsetDevice;
use floppy::DriveRibbon;
use guestmem::GuestMemory;
use inspect::InspectMut;
use vmcore::device_state::ChangeDeviceState;
use vmcore::isa_dma_channel::IsaDmaChannel;
use vmcore::line_interrupt::LineInterrupt;
pub trait MaybeStubFloppyDiskController:
Sized
+ ChipsetDevice
+ ChangeDeviceState
+ InspectMut
+ PollDevice
+ PortIoIntercept
+ vmcore::save_restore::SaveRestore
{
type NewError: std::error::Error + Send + Sync + 'static;
fn new(
guest_memory: GuestMemory,
interrupt: LineInterrupt,
register_pio: &mut dyn RegisterPortIoIntercept,
pio_base_addr: u16,
disk_drive: DriveRibbon,
dma: Box<dyn IsaDmaChannel>,
) -> Result<Self, Self::NewError>;
fn offset_of(&self, io_port: u16) -> Option<u16>;
}
impl MaybeStubFloppyDiskController for floppy::FloppyDiskController {
type NewError = floppy::NewFloppyDiskControllerError;
fn new(
guest_memory: GuestMemory,
interrupt: LineInterrupt,
register_pio: &mut dyn RegisterPortIoIntercept,
pio_base_addr: u16,
disk_drive: DriveRibbon,
dma: Box<dyn IsaDmaChannel>,
) -> Result<Self, Self::NewError> {
Self::new(
guest_memory,
interrupt,
register_pio,
pio_base_addr,
disk_drive,
dma,
)
}
fn offset_of(&self, io_port: u16) -> Option<u16> {
self.offset_of(io_port)
}
}
impl MaybeStubFloppyDiskController for floppy_pcat_stub::StubFloppyDiskController {
type NewError = std::convert::Infallible;
fn new(
_guest_memory: GuestMemory,
interrupt: LineInterrupt,
register_pio: &mut dyn RegisterPortIoIntercept,
pio_base_addr: u16,
_disk_drive: DriveRibbon,
_dma: Box<dyn IsaDmaChannel>,
) -> Result<Self, Self::NewError> {
Ok(Self::new(interrupt, register_pio, pio_base_addr))
}
fn offset_of(&self, io_port: u16) -> Option<u16> {
self.offset_of(io_port)
}
}
}
mod save_restore {
use super::*;
use vmcore::save_restore::RestoreError;
use vmcore::save_restore::SaveError;
use vmcore::save_restore::SaveRestore;
mod state {
use super::super_io::SioController;
use mesh::payload::Protobuf;
use vmcore::save_restore::SaveRestore;
use vmcore::save_restore::SavedStateRoot;
#[derive(Protobuf, SavedStateRoot)]
#[mesh(package = "chipset.winbond83977_superio", rename = "SavedState")]
pub struct StubSavedState {
#[mesh(1)]
pub floppy1: <floppy_pcat_stub::StubFloppyDiskController as SaveRestore>::SavedState,
#[mesh(2)]
pub floppy2: <floppy_pcat_stub::StubFloppyDiskController as SaveRestore>::SavedState,
#[mesh(3)]
pub sio: <SioController as SaveRestore>::SavedState,
}
#[derive(Protobuf, SavedStateRoot)]
#[mesh(package = "chipset.winbond83977_superio_nonstub")]
pub struct FullSavedState {
#[mesh(1)]
pub floppy1: <floppy::FloppyDiskController as SaveRestore>::SavedState,
#[mesh(2)]
pub floppy2: <floppy::FloppyDiskController as SaveRestore>::SavedState,
#[mesh(3)]
pub sio: <SioController as SaveRestore>::SavedState,
}
}
macro_rules! impl_save_restore {
($saved_sate:ident, $ty:path) => {
impl SaveRestore for Winbond83977FloppySioDevice<$ty> {
type SavedState = state::$saved_sate;
fn save(&mut self) -> Result<Self::SavedState, SaveError> {
let saved_state = state::$saved_sate {
floppy1: self.primary_fdc.save()?,
floppy2: self.secondary_fdc.save()?,
sio: self.sio.save()?,
};
Ok(saved_state)
}
fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
let state::$saved_sate {
floppy1,
floppy2,
sio,
} = state;
self.primary_fdc.restore(floppy1)?;
self.secondary_fdc.restore(floppy2)?;
self.sio.restore(sio)?;
Ok(())
}
}
};
}
impl_save_restore!(StubSavedState, floppy_pcat_stub::StubFloppyDiskController);
impl_save_restore!(FullSavedState, floppy::FloppyDiskController);
}