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
64#[derive(Debug)]
66pub struct Request {
67 pub cdb: [u8; 0x10],
69 pub srb_flags: u32,
73}
74
75impl Request {
76 pub fn scsiop(&self) -> ScsiOp {
78 ScsiOp(self.cdb[0])
79 }
80}
81
82#[derive(Debug)]
84pub struct ScsiResult {
85 pub scsi_status: ScsiStatus,
87 pub srb_status: SrbStatus,
91 pub tx: usize,
93 pub sense_data: Option<scsi_defs::SenseData>,
95}
96
97pub trait ScsiSaveRestore {
99 fn save(&self) -> Result<Option<save_restore::ScsiSavedState>, SaveError>;
101 fn restore(&self, state: &save_restore::ScsiSavedState) -> Result<(), RestoreError>;
103}
104
105pub mod save_restore {
106 #![expect(missing_docs)]
109
110 use mesh::payload::Protobuf;
111
112 #[derive(Debug, Copy, Clone, Eq, PartialEq, Protobuf)]
113 #[mesh(package = "storage.scsi.common")]
114 pub struct SavedSenseData {
115 #[mesh(1)]
116 pub sense_key: u8,
117 #[mesh(2)]
118 pub additional_sense_code: u8,
119 #[mesh(3)]
120 pub additional_sense_code_qualifier: u8,
121 }
122
123 #[derive(Debug, Copy, Clone, Protobuf)]
124 #[mesh(package = "storage.scsi.disk")]
125 pub struct ScsiDiskSavedState {
126 #[mesh(1)]
127 pub sector_count: u64,
128 #[mesh(2)]
129 pub sense_data: Option<SavedSenseData>,
130 }
131
132 #[derive(Debug, Protobuf, Copy, Clone)]
133 #[mesh(package = "storage.scsi")]
134 pub enum ScsiSavedState {
135 #[mesh(1)]
136 ScsiDvd(ScsiDvdSavedState),
137 #[mesh(2)]
138 ScsiDisk(ScsiDiskSavedState),
139 }
140
141 #[derive(Debug, Protobuf, Copy, Clone)]
142 #[mesh(package = "storage.scsi.dvd")]
143 pub struct ScsiDvdSavedState {
144 #[mesh(1)]
145 pub sense_data: Option<SavedSenseData>,
146 #[mesh(2)]
147 pub persistent: bool,
148 #[mesh(3)]
149 pub prevent: bool,
150 #[mesh(4)]
151 pub drive_state: DriveState,
152 #[mesh(5)]
153 pub pending_medium_event: IsoMediumEvent,
154 }
155
156 #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, inspect::Inspect, Protobuf)]
157 #[expect(clippy::enum_variant_names)]
158 #[mesh(package = "storage.scsi.dvd")]
159 pub enum DriveState {
160 #[mesh(1)]
161 MediumPresentTrayOpen,
162 #[mesh(2)]
163 MediumPresentTrayClosed,
164 #[mesh(3)]
165 MediumNotPresentTrayOpen,
166 #[default]
167 #[mesh(4)]
168 MediumNotPresentTrayClosed,
169 }
170
171 impl DriveState {
172 pub fn tray_open(&self) -> bool {
173 *self == DriveState::MediumPresentTrayOpen
174 || *self == DriveState::MediumNotPresentTrayOpen
175 }
176
177 pub fn medium_present(&self) -> bool {
178 *self == DriveState::MediumPresentTrayOpen
179 || *self == DriveState::MediumPresentTrayClosed
180 }
181 }
182
183 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Protobuf)]
184 #[mesh(package = "storage.scsi.dvd")]
185 pub enum IsoMediumEvent {
186 #[default]
187 #[mesh(1)]
188 None = 0x00,
189 #[mesh(2)]
190 NoMediaToMedia = 0x01,
191 #[mesh(3)]
192 MediaToNoMedia = 0x02,
193 #[mesh(4)]
194 MediaToMedia = 0x03,
195 }
196}