1#![forbid(unsafe_code)]
7
8use inspect::Inspect;
9use scsi_buffers::RequestBuffers;
10use scsi_defs::ScsiOp;
11use scsi_defs::ScsiStatus;
12use scsi_defs::srb::SrbStatus;
13use stackfuture::StackFuture;
14use std::sync::Arc;
15use vm_resource::CanResolveTo;
16use vm_resource::kind::ScsiDeviceHandleKind;
17use vmcore::save_restore::RestoreError;
18use vmcore::save_restore::SaveError;
19use vmcore::vm_task::VmTaskDriverSource;
20
21impl CanResolveTo<ResolvedScsiDevice> for ScsiDeviceHandleKind {
22 type Input<'a> = ResolveScsiDeviceHandleParams<'a>;
23}
24
25pub struct ResolvedScsiDevice(pub Arc<dyn AsyncScsiDisk>);
27
28impl<T: 'static + AsyncScsiDisk> From<T> for ResolvedScsiDevice {
29 fn from(value: T) -> Self {
30 Self(Arc::new(value))
31 }
32}
33
34pub struct ResolveScsiDeviceHandleParams<'a> {
36 pub driver_source: &'a VmTaskDriverSource,
38}
39
40pub const ASYNC_SCSI_DISK_STACK_SIZE: usize = 1256 + 336;
45
46pub trait AsyncScsiDisk: Send + Sync + Inspect + ScsiSaveRestore {
48 fn execute_scsi<'a>(
50 &'a self,
51 external_data: &'a RequestBuffers<'a>,
52 request: &'a Request,
53 ) -> StackFuture<'a, ScsiResult, { ASYNC_SCSI_DISK_STACK_SIZE }>;
54}
55
56#[derive(Debug)]
58pub struct Request {
59 pub cdb: [u8; 0x10],
61 pub srb_flags: u32,
65}
66
67impl Request {
68 pub fn scsiop(&self) -> ScsiOp {
70 ScsiOp(self.cdb[0])
71 }
72}
73
74#[derive(Debug)]
76pub struct ScsiResult {
77 pub scsi_status: ScsiStatus,
79 pub srb_status: SrbStatus,
83 pub tx: usize,
85 pub sense_data: Option<scsi_defs::SenseData>,
87}
88
89pub trait ScsiSaveRestore {
91 fn save(&self) -> Result<Option<save_restore::ScsiSavedState>, SaveError>;
93 fn restore(&self, state: &save_restore::ScsiSavedState) -> Result<(), RestoreError>;
95}
96
97pub mod save_restore {
98 #![expect(missing_docs)]
101
102 use mesh::payload::Protobuf;
103
104 #[derive(Debug, Copy, Clone, Eq, PartialEq, Protobuf)]
105 #[mesh(package = "storage.scsi.common")]
106 pub struct SavedSenseData {
107 #[mesh(1)]
108 pub sense_key: u8,
109 #[mesh(2)]
110 pub additional_sense_code: u8,
111 #[mesh(3)]
112 pub additional_sense_code_qualifier: u8,
113 }
114
115 #[derive(Debug, Copy, Clone, Protobuf)]
116 #[mesh(package = "storage.scsi.disk")]
117 pub struct ScsiDiskSavedState {
118 #[mesh(1)]
119 pub sector_count: u64,
120 #[mesh(2)]
121 pub sense_data: Option<SavedSenseData>,
122 }
123
124 #[derive(Debug, Protobuf, Copy, Clone)]
125 #[mesh(package = "storage.scsi")]
126 pub enum ScsiSavedState {
127 #[mesh(1)]
128 ScsiDvd(ScsiDvdSavedState),
129 #[mesh(2)]
130 ScsiDisk(ScsiDiskSavedState),
131 }
132
133 #[derive(Debug, Protobuf, Copy, Clone)]
134 #[mesh(package = "storage.scsi.dvd")]
135 pub struct ScsiDvdSavedState {
136 #[mesh(1)]
137 pub sense_data: Option<SavedSenseData>,
138 #[mesh(2)]
139 pub persistent: bool,
140 #[mesh(3)]
141 pub prevent: bool,
142 #[mesh(4)]
143 pub drive_state: DriveState,
144 #[mesh(5)]
145 pub pending_medium_event: IsoMediumEvent,
146 }
147
148 #[derive(Debug, Default, PartialEq, Eq, Copy, Clone, inspect::Inspect, Protobuf)]
149 #[expect(clippy::enum_variant_names)]
150 #[mesh(package = "storage.scsi.dvd")]
151 pub enum DriveState {
152 #[mesh(1)]
153 MediumPresentTrayOpen,
154 #[mesh(2)]
155 MediumPresentTrayClosed,
156 #[mesh(3)]
157 MediumNotPresentTrayOpen,
158 #[default]
159 #[mesh(4)]
160 MediumNotPresentTrayClosed,
161 }
162
163 impl DriveState {
164 pub fn tray_open(&self) -> bool {
165 *self == DriveState::MediumPresentTrayOpen
166 || *self == DriveState::MediumNotPresentTrayOpen
167 }
168
169 pub fn medium_present(&self) -> bool {
170 *self == DriveState::MediumPresentTrayOpen
171 || *self == DriveState::MediumPresentTrayClosed
172 }
173 }
174
175 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Protobuf)]
176 #[mesh(package = "storage.scsi.dvd")]
177 pub enum IsoMediumEvent {
178 #[default]
179 #[mesh(1)]
180 None = 0x00,
181 #[mesh(2)]
182 NoMediaToMedia = 0x01,
183 #[mesh(3)]
184 MediaToNoMedia = 0x02,
185 #[mesh(4)]
186 MediaToMedia = 0x03,
187 }
188}