@@ -115,7 +115,10 @@ internal ScsiSenseData(
115115 public override string ToString ( )
116116 {
117117 if ( KnownSenseCode is ScsiKnownSenseCode known )
118+ {
118119 return $ "{ KnownSenseCode } ({ Description } ) [SK={ SenseKey } , ASC=0x{ AdditionalSenseCode : X2} , ASCQ=0x{ AdditionalSenseCodeQualifier : X2} ]";
120+ }
121+
119122 return $ "{ Description } [SK={ SenseKey } , ASC=0x{ AdditionalSenseCode : X2} , ASCQ=0x{ AdditionalSenseCodeQualifier : X2} ]";
120123 }
121124}
@@ -173,31 +176,130 @@ private static readonly Dictionary<ScsiKnownSenseCode, string> KnownDescriptions
173176 { ScsiKnownSenseCode . AbortedCommand_ScsiParityError , "SCSI parity error (command aborted)" } ,
174177 } ;
175178
179+ /// <summary>
180+ /// Parse SCSI sense data from a byte array (fixed or descriptor format).
181+ /// </summary>
182+ /// <param name="buffer">Sense data buffer.</param>
183+ /// <param name="sense"></param>
184+ /// <param name="offset">Offset into the buffer where sense data starts.</param>
176185 public static bool TryParse ( byte [ ] buffer , out ScsiSenseData sense , int offset = 0 )
177186 {
178187 sense = null ;
179188
180- try
189+ // --- Top-level basic validation ---------------------------------------
190+ if ( buffer == null )
181191 {
182- // Basic validation — but never throw.
183- if ( buffer == null )
184- return false ;
185- if ( offset < 0 || offset >= buffer . Length )
186- return false ;
192+ return false ;
193+ }
194+
195+ if ( offset < 0 || offset >= buffer . Length )
196+ {
197+ return false ;
198+ }
199+
200+ // Need at least 4 bytes to even know format
201+ if ( buffer . Length - offset < 4 )
202+ {
203+ return false ;
204+ }
205+
206+ // --- Read response code safely ----------------------------------------
207+ var b0 = buffer [ offset ] ;
208+ var responseCode = ( byte ) ( b0 & 0x7F ) ;
209+
210+ var isDescriptorFormat = responseCode is 0x72 or 0x73 ;
211+ var isFixedFormat = responseCode is 0x70 or 0x71 ;
187212
188- // Very small sense buffers cannot be parsed.
213+ if ( ! isDescriptorFormat && ! isFixedFormat )
214+ {
215+ // Unknown/unsupported format
216+ return false ;
217+ }
218+
219+ ScsiSenseKey senseKey ;
220+ byte asc = 0 ;
221+ byte ascq = 0 ;
222+ uint ? information = null ;
223+
224+ // --- Descriptor format (0x72 or 0x73) ---------------------------------
225+ if ( isDescriptorFormat )
226+ {
227+ // Descriptor format header is at least 4 bytes
189228 if ( buffer . Length - offset < 4 )
229+ {
190230 return false ;
231+ }
191232
192- sense = Parse ( buffer , offset ) ;
193- return true ;
233+ // Byte 1: Sense key (low nibble)
234+ senseKey = ( ScsiSenseKey ) ( buffer [ offset + 1 ] & 0x0F ) ;
235+
236+ // Byte 2: ASC
237+ asc = buffer [ offset + 2 ] ;
238+
239+ // Byte 3: ASCQ
240+ ascq = buffer [ offset + 3 ] ;
241+
242+ // INFORMATION descriptor parsing optional and skipped here
194243 }
195- catch
244+ // --- Fixed format (0x70 or 0x71) --------------------------------------
245+ else
196246 {
197- // Ensure no exception leaks out.
198- sense = null ;
199- return false ;
247+ // Need at least 14 bytes for fixed‐format ASC/ASCQ + info
248+ if ( buffer . Length - offset < 14 )
249+ {
250+ return false ;
251+ }
252+
253+ // Byte 2: sense key (low nibble)
254+ senseKey = ( ScsiSenseKey ) ( buffer [ offset + 2 ] & 0x0F ) ;
255+
256+ // Bytes 3–6: INFORMATION (often LBA)
257+ unchecked
258+ {
259+ information =
260+ ( uint ) ( ( buffer [ offset + 3 ] << 24 ) |
261+ ( buffer [ offset + 4 ] << 16 ) |
262+ ( buffer [ offset + 5 ] << 8 ) |
263+ buffer [ offset + 6 ] ) ;
264+ }
265+
266+ // Byte 12: ASC
267+ asc = buffer [ offset + 12 ] ;
268+
269+ // Byte 13: ASCQ
270+ ascq = buffer [ offset + 13 ] ;
271+ }
272+
273+ // --- Resolve KCQ into known enum --------------------------------------
274+ ScsiKnownSenseCode ? knownCode = null ;
275+ string description ;
276+
277+ if ( KnownMap . TryGetValue ( ( senseKey , asc , ascq ) , out var found ) )
278+ {
279+ knownCode = found ;
280+ if ( ! KnownDescriptions . TryGetValue ( found , out description ) )
281+ {
282+ description = found . ToString ( ) ;
283+ }
200284 }
285+ else
286+ {
287+ // Fallback description
288+ description = GetDefaultDescription ( senseKey , asc , ascq ) ;
289+ }
290+
291+ sense = new ScsiSenseData (
292+ responseCode : responseCode ,
293+ isCurrent : responseCode is 0x70 or 0x72 ,
294+ isDescriptor : isDescriptorFormat ,
295+ senseKey : senseKey ,
296+ asc : asc ,
297+ ascq : ascq ,
298+ information : information ,
299+ known : knownCode ,
300+ description : description ) ;
301+
302+ return true ;
201303 }
202304
203305 /// <summary>
@@ -207,19 +309,27 @@ public static bool TryParse(byte[] buffer, out ScsiSenseData sense, int offset =
207309 /// <param name="offset">Offset into the buffer where sense data starts.</param>
208310 public static ScsiSenseData Parse ( byte [ ] buffer , int offset = 0 )
209311 {
210- if ( buffer == null ) throw new ArgumentNullException ( nameof ( buffer ) ) ;
312+ if ( buffer == null )
313+ {
314+ throw new ArgumentNullException ( nameof ( buffer ) ) ;
315+ }
316+
211317 if ( offset < 0 || offset >= buffer . Length )
318+ {
212319 throw new ArgumentOutOfRangeException ( nameof ( offset ) ) ;
320+ }
213321
214322 if ( buffer . Length - offset < 4 )
323+ {
215324 throw new ArgumentException ( "Sense buffer is too short." , nameof ( buffer ) ) ;
325+ }
216326
217327 // Byte 0: Response code (low 7 bits) and VALID bit (bit 7 in fixed format).
218- byte raw0 = buffer [ offset + 0 ] ;
219- byte responseCode = ( byte ) ( raw0 & 0x7F ) ;
328+ var raw0 = buffer [ offset + 0 ] ;
329+ var responseCode = ( byte ) ( raw0 & 0x7F ) ;
220330
221- bool isCurrent = responseCode == 0x70 || responseCode == 0x72 ;
222- bool isDescriptorFormat = responseCode == 0x72 || responseCode == 0x73 ;
331+ var isCurrent = responseCode is 0x70 or 0x72 ;
332+ var isDescriptorFormat = responseCode is 0x72 or 0x73 ;
223333
224334 ScsiSenseKey senseKey ;
225335 byte asc ;
@@ -233,7 +343,9 @@ public static ScsiSenseData Parse(byte[] buffer, int offset = 0)
233343 // Byte 2: ASC
234344 // Byte 3: ASCQ
235345 if ( buffer . Length - offset < 4 )
346+ {
236347 throw new ArgumentException ( "Descriptor sense buffer is too short." , nameof ( buffer ) ) ;
348+ }
237349
238350 senseKey = ( ScsiSenseKey ) ( buffer [ offset + 1 ] & 0x0F ) ;
239351 asc = buffer [ offset + 2 ] ;
@@ -250,7 +362,9 @@ public static ScsiSenseData Parse(byte[] buffer, int offset = 0)
250362 // Byte 12: ASC
251363 // Byte 13: ASCQ
252364 if ( buffer . Length - offset < 14 )
365+ {
253366 throw new ArgumentException ( "Fixed-format sense buffer is too short." , nameof ( buffer ) ) ;
367+ }
254368
255369 senseKey = ( ScsiSenseKey ) ( buffer [ offset + 2 ] & 0x0F ) ;
256370
@@ -273,7 +387,9 @@ public static ScsiSenseData Parse(byte[] buffer, int offset = 0)
273387 {
274388 knownCode = found ;
275389 if ( ! KnownDescriptions . TryGetValue ( found , out description ) )
390+ {
276391 description = found . ToString ( ) ;
392+ }
277393 }
278394 else
279395 {
@@ -295,9 +411,9 @@ public static ScsiSenseData Parse(byte[] buffer, int offset = 0)
295411
296412 private static ( ScsiSenseKey key , byte asc , byte ascq ) ScsiTriple ( ScsiKnownSenseCode code )
297413 {
298- int value = ( int ) code ;
299- byte asc = ( byte ) ( ( value >> 8 ) & 0xFF ) ;
300- byte ascq = ( byte ) ( value & 0xFF ) ;
414+ var value = ( int ) code ;
415+ var asc = ( byte ) ( ( value >> 8 ) & 0xFF ) ;
416+ var ascq = ( byte ) ( value & 0xFF ) ;
301417 ScsiSenseKey key = ( ScsiSenseKey ) ( ( value >> 16 ) & 0x0F ) ;
302418 return ( key , asc , ascq ) ;
303419 }
0 commit comments