1#![forbid(unsafe_code)]
15
16use inspect::Inspect;
17use scsi_buffers::RequestBuffers;
18use scsi_defs::ScsiOp;
19use scsi_defs::ScsiStatus;
20use scsi_defs::srb::SrbStatus;
21use stackfuture::StackFuture;
22use std::sync::Arc;
23use vm_resource::CanResolveTo;
24use vm_resource::kind::ScsiDeviceHandleKind;
25use vmcore::save_restore::RestoreError;
26use vmcore::save_restore::SaveError;
27use vmcore::vm_task::VmTaskDriverSource;
28
29impl CanResolveTo<ResolvedScsiDevice> for ScsiDeviceHandleKind {
30 type Input<'a> = ResolveScsiDeviceHandleParams<'a>;
31}
32
33pub struct ResolvedScsiDevice(pub Arc<dyn AsyncScsiDisk>);
35
36impl<T: 'static + AsyncScsiDisk> From<T> for ResolvedScsiDevice {
37 fn from(value: T) -> Self {
38 Self(Arc::new(value))
39 }
40}
41
42pub struct ResolveScsiDeviceHandleParams<'a> {
44 pub driver_source: &'a VmTaskDriverSource,
46}
47
48pub const ASYNC_SCSI_DISK_STACK_SIZE: usize = 1256 + 336;
53
54pub trait AsyncScsiDisk: Send + Sync + Inspect + ScsiSaveRestore {
56 fn execute_scsi<'a>(
58 &'a self,
59 external_data: &'a RequestBuffers<'a>,
60 request: &'a Request,
61 ) -> StackFuture<'a, ScsiResult, { ASYNC_SCSI_DISK_STACK_SIZE }>;
62
63 fn clear_nexus_state(&self) {}
77}
78
79#[derive(Debug)]
81pub struct Request {
82 pub cdb: [u8; 0x10],
84 pub srb_flags: u32,
88}
89
90impl Request {
91 pub fn scsiop(&self) -> ScsiOp {
93 ScsiOp(self.cdb[0])
94 }
95}
96
97#[derive(Debug)]
99pub struct ScsiResult {
100 pub scsi_status: ScsiStatus,
102 pub srb_status: SrbStatus,
106 pub tx: usize,
108 pub sense_data: Option<scsi_defs::SenseData>,
110}
111
112pub trait ScsiSaveRestore {
114 fn save(&self) -> Result<Option<save_restore::ScsiSavedState>, SaveError>;
116 fn restore(&self, state: &save_restore::ScsiSavedState) -> Result<(), RestoreError>;
118}
119
120pub mod save_restore {
121 #![expect(missing_docs)]
124
125 use mesh::payload::Protobuf;
126
127 #[derive(Debug, Copy, Clone, Eq, PartialEq, Protobuf)]
128 #[mesh(package = "storage.scsi.common")]
129 pub struct SavedSenseData {
130 #[mesh(1)]
131 pub sense_key: u8,
132 #[mesh(2)]
133 pub additional_sense_code: u8,
134 #[mesh(3)]
135 pub additional_sense_code_qualifier: u8,
136 }
137
138 #[derive(Debug, Copy, Clone, Protobuf)]
139 #[mesh(package = "storage.scsi.disk")]
140 pub struct ScsiDiskSavedState {
141 #[mesh(1)]
142 pub sector_count: u64,
143 #[mesh(2)]
144 pub sense_data: Option<SavedSenseData>,
145 }
146
147 #[derive(Debug, Protobuf, Copy, Clone)]
148 #[mesh(package = "storage.scsi")]
149 pub enum ScsiSavedState {
150 #[mesh(1)]
151 ScsiDvd(ScsiDvdSavedState),
152 #[mesh(2)]
153 ScsiDisk(ScsiDiskSavedState),
154 }
155
156 #[derive(Debug, Protobuf, Copy, Clone)]
157 #[mesh(package = "storage.scsi.dvd")]
158 pub struct ScsiDvdSavedState {
159 #[mesh(1)]
160 pub sense_data: Option<SavedSenseData>,
161 #[mesh(2)]
162 pub persistent: bool,
163 #[mesh(3)]
164 pub prevent: bool,
165 #[mesh(4)]
166 pub drive_state: DriveState,
167 #[mesh(5)]
168 pub pending_medium_event: IsoMediumEvent,
169 }
170
171 #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, inspect::Inspect, Protobuf)]
172 #[expect(clippy::enum_variant_names)]
173 #[mesh(package = "storage.scsi.dvd")]
174 pub enum DriveState {
175 #[mesh(1)]
176 MediumPresentTrayOpen,
177 #[mesh(2)]
178 MediumPresentTrayClosed,
179 #[mesh(3)]
180 MediumNotPresentTrayOpen,
181 #[default]
182 #[mesh(4)]
183 MediumNotPresentTrayClosed,
184 }
185
186 impl DriveState {
187 pub fn tray_open(&self) -> bool {
188 *self == DriveState::MediumPresentTrayOpen
189 || *self == DriveState::MediumNotPresentTrayOpen
190 }
191
192 pub fn medium_present(&self) -> bool {
193 *self == DriveState::MediumPresentTrayOpen
194 || *self == DriveState::MediumPresentTrayClosed
195 }
196 }
197
198 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Protobuf)]
199 #[mesh(package = "storage.scsi.dvd")]
200 pub enum IsoMediumEvent {
201 #[default]
202 #[mesh(1)]
203 None = 0x00,
204 #[mesh(2)]
205 NoMediaToMedia = 0x01,
206 #[mesh(3)]
207 MediaToNoMedia = 0x02,
208 #[mesh(4)]
209 MediaToMedia = 0x03,
210 }
211}