chipset_legacy/winbond83977_sio/
mod.rs1#![warn(missing_docs)]
12
13pub use self::maybe_floppy_disk_controller::MaybeStubFloppyDiskController;
14
15use self::super_io::SioController;
16use chipset_device::ChipsetDevice;
17use chipset_device::io::IoError;
18use chipset_device::io::IoResult;
19use chipset_device::pio::PortIoIntercept;
20use chipset_device::pio::RegisterPortIoIntercept;
21use chipset_device::poll_device::PollDevice;
22use floppy::DriveRibbon;
23use guestmem::GuestMemory;
24use inspect::InspectMut;
25use std::task::Context;
26use thiserror::Error;
27use vmcore::device_state::ChangeDeviceState;
28use vmcore::isa_dma_channel::IsaDmaChannel;
29use vmcore::line_interrupt::LineInterrupt;
30
31mod super_io;
32
33const PRI_FLOPPY_BASE_ADDR: u16 = 0x3F0;
34const SEC_FLOPPY_BASE_ADDR: u16 = 0x370;
35
36const PRI_EXT_FUNC_ENABLE_REG: u16 = 0x3F0;
37const SEC_EXT_FUNC_ENABLE_REG: u16 = 0x370;
38const PRI_EXT_FUNC_DATA_REG: u16 = 0x3F1;
39const SEC_EXT_FUNC_DATA_REG: u16 = 0x371;
40
41#[derive(InspectMut)]
51pub struct Winbond83977FloppySioDevice<FDC: MaybeStubFloppyDiskController> {
52 #[inspect(mut)]
54 sio: SioController,
55 #[inspect(mut)]
56 primary_fdc: FDC,
57 #[inspect(mut)]
58 secondary_fdc: FDC,
59}
60
61#[derive(Debug, Error)]
62#[expect(missing_docs)]
63pub enum NewWinbond83977FloppySioDeviceError<FdcError> {
64 #[error("failed to share interrupt line")]
65 LineShare(#[source] vmcore::line_interrupt::NewLineError),
66 #[error("failed to init primary floppy controller")]
67 BadPrimaryFdc(#[source] FdcError),
68 #[error("failed to init secondary floppy controller")]
69 BadSecondaryFdc(#[source] FdcError),
70}
71
72impl<FDC: MaybeStubFloppyDiskController> Winbond83977FloppySioDevice<FDC> {
73 pub fn new(
75 guest_memory: GuestMemory,
76 interrupt: LineInterrupt,
77 register_pio: &mut dyn RegisterPortIoIntercept,
78 primary_disk_drive: DriveRibbon,
79 secondary_disk_drive: DriveRibbon,
80 primary_dma: Box<dyn IsaDmaChannel>,
81 secondary_dma: Box<dyn IsaDmaChannel>,
82 ) -> Result<Self, NewWinbond83977FloppySioDeviceError<FDC::NewError>> {
83 let secondary_interrupt = interrupt
84 .new_shared("floppy secondary")
85 .map_err(NewWinbond83977FloppySioDeviceError::LineShare)?;
86
87 Ok(Self {
88 sio: SioController::default(),
89 primary_fdc: FDC::new(
90 guest_memory.clone(),
91 interrupt,
92 register_pio,
93 PRI_FLOPPY_BASE_ADDR,
94 primary_disk_drive,
95 primary_dma,
96 )
97 .map_err(NewWinbond83977FloppySioDeviceError::BadPrimaryFdc)?,
98 secondary_fdc: FDC::new(
99 guest_memory,
100 secondary_interrupt,
101 register_pio,
102 SEC_FLOPPY_BASE_ADDR,
103 secondary_disk_drive,
104 secondary_dma,
105 )
106 .map_err(NewWinbond83977FloppySioDeviceError::BadSecondaryFdc)?,
107 })
108 }
109}
110
111impl<FDC: MaybeStubFloppyDiskController> ChangeDeviceState for Winbond83977FloppySioDevice<FDC> {
112 fn start(&mut self) {}
113
114 async fn stop(&mut self) {}
115
116 async fn reset(&mut self) {
117 self.sio.reset().await;
118 self.primary_fdc.reset().await;
119 self.secondary_fdc.reset().await;
120 }
121}
122
123impl<FDC: MaybeStubFloppyDiskController> ChipsetDevice for Winbond83977FloppySioDevice<FDC> {
124 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
125 Some(self)
126 }
127
128 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
129 Some(self)
130 }
131}
132
133impl<FDC: MaybeStubFloppyDiskController> PollDevice for Winbond83977FloppySioDevice<FDC> {
134 fn poll_device(&mut self, cx: &mut Context<'_>) {
135 self.primary_fdc.poll_device(cx);
136 self.secondary_fdc.poll_device(cx);
137 }
138}
139
140impl<FDC: MaybeStubFloppyDiskController> PortIoIntercept for Winbond83977FloppySioDevice<FDC> {
141 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
142 if data.len() != 1 {
143 return IoResult::Err(IoError::InvalidAccessSize);
144 }
145
146 if io_port == PRI_EXT_FUNC_DATA_REG || io_port == SEC_EXT_FUNC_DATA_REG {
147 if let Ok(value) = self.sio.config_read() {
150 data[0] = value;
151 return IoResult::Ok;
152 }
153 }
154
155 if self.primary_fdc.offset_of(io_port).is_some() {
156 return self.primary_fdc.io_read(io_port, data);
157 }
158
159 if self.secondary_fdc.offset_of(io_port).is_some() {
160 return self.secondary_fdc.io_read(io_port, data);
161 }
162
163 IoResult::Err(IoError::InvalidRegister)
164 }
165
166 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
167 if data.len() != 1 {
168 return IoResult::Err(IoError::InvalidAccessSize);
169 }
170
171 match io_port {
174 PRI_EXT_FUNC_ENABLE_REG | SEC_EXT_FUNC_ENABLE_REG => {
175 self.sio.update_config_state(data[0]);
176 }
177 PRI_EXT_FUNC_DATA_REG | SEC_EXT_FUNC_DATA_REG => self.sio.config_write(data[0]),
178 _ => {
179 if self.primary_fdc.offset_of(io_port).is_some() {
180 return self.primary_fdc.io_write(io_port, data);
181 }
182
183 if self.secondary_fdc.offset_of(io_port).is_some() {
184 return self.secondary_fdc.io_write(io_port, data);
185 }
186
187 return IoResult::Err(IoError::InvalidRegister);
188 }
189 }
190
191 IoResult::Ok
192 }
193}
194
195mod maybe_floppy_disk_controller {
196 use chipset_device::ChipsetDevice;
197 use chipset_device::pio::PortIoIntercept;
198 use chipset_device::pio::RegisterPortIoIntercept;
199 use chipset_device::poll_device::PollDevice;
200 use floppy::DriveRibbon;
201 use guestmem::GuestMemory;
202 use inspect::InspectMut;
203 use vmcore::device_state::ChangeDeviceState;
204 use vmcore::isa_dma_channel::IsaDmaChannel;
205 use vmcore::line_interrupt::LineInterrupt;
206
207 pub trait MaybeStubFloppyDiskController:
213 Sized
214 + ChipsetDevice
215 + ChangeDeviceState
216 + InspectMut
217 + PollDevice
218 + PortIoIntercept
219 + vmcore::save_restore::SaveRestore
220 {
221 type NewError: std::error::Error + Send + Sync + 'static;
223
224 fn new(
226 guest_memory: GuestMemory,
227 interrupt: LineInterrupt,
228 register_pio: &mut dyn RegisterPortIoIntercept,
229 pio_base_addr: u16,
230 disk_drive: DriveRibbon,
231 dma: Box<dyn IsaDmaChannel>,
232 ) -> Result<Self, Self::NewError>;
233
234 fn offset_of(&self, io_port: u16) -> Option<u16>;
237 }
238
239 impl MaybeStubFloppyDiskController for floppy::FloppyDiskController {
240 type NewError = floppy::NewFloppyDiskControllerError;
241
242 fn new(
243 guest_memory: GuestMemory,
244 interrupt: LineInterrupt,
245 register_pio: &mut dyn RegisterPortIoIntercept,
246 pio_base_addr: u16,
247 disk_drive: DriveRibbon,
248 dma: Box<dyn IsaDmaChannel>,
249 ) -> Result<Self, Self::NewError> {
250 Self::new(
251 guest_memory,
252 interrupt,
253 register_pio,
254 pio_base_addr,
255 disk_drive,
256 dma,
257 )
258 }
259
260 fn offset_of(&self, io_port: u16) -> Option<u16> {
261 self.offset_of(io_port)
262 }
263 }
264
265 impl MaybeStubFloppyDiskController for floppy_pcat_stub::StubFloppyDiskController {
266 type NewError = std::convert::Infallible;
267
268 fn new(
269 _guest_memory: GuestMemory,
270 interrupt: LineInterrupt,
271 register_pio: &mut dyn RegisterPortIoIntercept,
272 pio_base_addr: u16,
273 _disk_drive: DriveRibbon,
274 _dma: Box<dyn IsaDmaChannel>,
275 ) -> Result<Self, Self::NewError> {
276 Ok(Self::new(interrupt, register_pio, pio_base_addr))
277 }
278
279 fn offset_of(&self, io_port: u16) -> Option<u16> {
280 self.offset_of(io_port)
281 }
282 }
283}
284
285mod save_restore {
286 use super::*;
287 use vmcore::save_restore::RestoreError;
288 use vmcore::save_restore::SaveError;
289 use vmcore::save_restore::SaveRestore;
290
291 mod state {
292 use super::super_io::SioController;
293 use mesh::payload::Protobuf;
294 use vmcore::save_restore::SaveRestore;
295 use vmcore::save_restore::SavedStateRoot;
296
297 #[derive(Protobuf, SavedStateRoot)]
305 #[mesh(package = "chipset.winbond83977_superio", rename = "SavedState")]
306 pub struct StubSavedState {
307 #[mesh(1)]
308 pub floppy1: <floppy_pcat_stub::StubFloppyDiskController as SaveRestore>::SavedState,
309 #[mesh(2)]
310 pub floppy2: <floppy_pcat_stub::StubFloppyDiskController as SaveRestore>::SavedState,
311 #[mesh(3)]
312 pub sio: <SioController as SaveRestore>::SavedState,
313 }
314
315 #[derive(Protobuf, SavedStateRoot)]
316 #[mesh(package = "chipset.winbond83977_superio_nonstub")]
317 pub struct FullSavedState {
318 #[mesh(1)]
319 pub floppy1: <floppy::FloppyDiskController as SaveRestore>::SavedState,
320 #[mesh(2)]
321 pub floppy2: <floppy::FloppyDiskController as SaveRestore>::SavedState,
322 #[mesh(3)]
323 pub sio: <SioController as SaveRestore>::SavedState,
324 }
325 }
326
327 macro_rules! impl_save_restore {
328 ($saved_sate:ident, $ty:path) => {
329 impl SaveRestore for Winbond83977FloppySioDevice<$ty> {
330 type SavedState = state::$saved_sate;
331
332 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
333 let saved_state = state::$saved_sate {
334 floppy1: self.primary_fdc.save()?,
335 floppy2: self.secondary_fdc.save()?,
336 sio: self.sio.save()?,
337 };
338 Ok(saved_state)
339 }
340
341 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
342 let state::$saved_sate {
343 floppy1,
344 floppy2,
345 sio,
346 } = state;
347
348 self.primary_fdc.restore(floppy1)?;
349 self.secondary_fdc.restore(floppy2)?;
350 self.sio.restore(sio)?;
351 Ok(())
352 }
353 }
354 };
355 }
356
357 impl_save_restore!(StubSavedState, floppy_pcat_stub::StubFloppyDiskController);
358 impl_save_restore!(FullSavedState, floppy::FloppyDiskController);
359}