private static void SendSCSIRequest <TCommand, TData>(UnixFileDescriptor fd, ref TCommand cmd, out TData data) where TCommand : struct where TData : struct { var cmdlen = Util.SizeOfStructure <TCommand>(); if (cmdlen > 16) { throw new InvalidOperationException("A SCSI command must not exceed 16 bytes in size."); } var req = new SCSIRequest { interface_id = 'S', dxfer_direction = SCSITransferDirection.FROM_DEV, timeout = NativeApi.DefaultSCSIRequestTimeOut, cmd_len = (byte)cmdlen, mx_sb_len = (byte)64, dxfer_len = (uint)Util.SizeOfStructure <TData>(), }; var memlen = (uint)(req.cmd_len + req.mx_sb_len + req.dxfer_len); var bytes = NativeApi.AllocZero(new UIntPtr(1), new UIntPtr(memlen)); try { req.cmdp = bytes; req.sbp = req.cmdp + req.cmd_len; req.dxferp = req.sbp + req.mx_sb_len; Marshal.StructureToPtr(cmd, req.cmdp, false); try { if (NativeApi.SendSCSIRequest(fd.Value, IOCTL.SG_IO, ref req) != 0) { throw new UnixException(); } if (req.status == SAM.StatusCode.CHECK_CONDITION || req.driver_status == SCSIDriverStatus.DRIVER_SENSE) { var response = Marshal.ReadByte(req.sbp) & 0x7f; switch (response) { case 0x70: case 0x71: // Fixed Format (Current or Deferred) throw new ScsiException(Util.MarshalPointerToStructure <SPC.FixedSenseData>(req.sbp)); case 0x72: case 0x73: // Descriptor Format (Current or Deferred) throw new ScsiException(Util.MarshalPointerToStructure <SPC.DescriptorSenseData>(req.sbp)); default: throw new IOException($"SCSI CHECK CONDITION: BAD RESPONSE CODE ({response:X2})"); } } data = Util.MarshalPointerToStructure <TData>(req.dxferp); } finally { Util.DestroyStructure <TCommand>(req.cmdp); } } finally { NativeApi.Free(bytes); } }
private static extern int SendSCSIRequest(int fd, IOCTL command, ref SCSIRequest request);