/// <summary>Sends a SCSI command</summary> /// <returns>0 if no error occurred, otherwise, errno</returns> /// <param name="ptId">Platform ID for executing the command</param> /// <param name="fd">File handle</param> /// <param name="cdb">SCSI CDB</param> /// <param name="buffer">Buffer for SCSI command response</param> /// <param name="senseBuffer">Buffer with the SCSI sense</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="direction">SCSI command transfer direction</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"> /// <c>True</c> if SCSI error returned non-OK status and <paramref name="senseBuffer" /> contains SCSI /// sense /// </param> /// <exception cref="InvalidOperationException">If the specified platform is not supported</exception> internal static int SendScsiCommand(PlatformID ptId, object fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, ScsiDirection direction, out double duration, out bool sense) { switch (ptId) { case PlatformID.Win32NT: { ScsiIoctlDirection dir; switch (direction) { case ScsiDirection.In: dir = ScsiIoctlDirection.In; break; case ScsiDirection.Out: dir = ScsiIoctlDirection.Out; break; default: dir = ScsiIoctlDirection.Unspecified; break; } return(Windows.Command.SendScsiCommand((SafeFileHandle)fd, cdb, ref buffer, out senseBuffer, timeout, dir, out duration, out sense)); } case PlatformID.Linux: { Linux.ScsiIoctlDirection dir; switch (direction) { case ScsiDirection.In: dir = Linux.ScsiIoctlDirection.In; break; case ScsiDirection.Out: dir = Linux.ScsiIoctlDirection.Out; break; case ScsiDirection.Bidirectional: dir = Linux.ScsiIoctlDirection.Unspecified; break; case ScsiDirection.None: dir = Linux.ScsiIoctlDirection.None; break; default: dir = Linux.ScsiIoctlDirection.Unknown; break; } return(Linux.Command.SendScsiCommand((int)fd, cdb, ref buffer, out senseBuffer, timeout, dir, out duration, out sense)); } case PlatformID.FreeBSD: { CcbFlags flags = 0; switch (direction) { case ScsiDirection.In: flags = CcbFlags.CamDirIn; break; case ScsiDirection.Out: flags = CcbFlags.CamDirOut; break; case ScsiDirection.Bidirectional: flags = CcbFlags.CamDirBoth; break; case ScsiDirection.None: flags = CcbFlags.CamDirNone; break; } return(IntPtr.Size == 8 ? FreeBSD.Command.SendScsiCommand64((IntPtr)fd, cdb, ref buffer, out senseBuffer, timeout, flags, out duration, out sense) : FreeBSD.Command.SendScsiCommand((IntPtr)fd, cdb, ref buffer, out senseBuffer, timeout, flags, out duration, out sense)); } default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); } }
/// <summary>Sends a SCSI command (64-bit arch)</summary> /// <returns>0 if no error occurred, otherwise, errno</returns> /// <param name="dev">CAM device</param> /// <param name="cdb">SCSI CDB</param> /// <param name="buffer">Buffer for SCSI command response</param> /// <param name="senseBuffer">Buffer with the SCSI sense</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="direction">SCSI command transfer direction</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"> /// <c>True</c> if SCSI error returned non-OK status and <paramref name="senseBuffer" /> contains SCSI /// sense /// </param> internal static int SendScsiCommand64(IntPtr dev, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, CcbFlags direction, out double duration, out bool sense) { senseBuffer = null; duration = 0; sense = false; if (buffer == null) { return(-1); } IntPtr ccbPtr = cam_getccb(dev); IntPtr cdbPtr = IntPtr.Zero; if (ccbPtr.ToInt64() == 0) { sense = true; return(Marshal.GetLastWin32Error()); } var csio = (CcbScsiio64)Marshal.PtrToStructure(ccbPtr, typeof(CcbScsiio64)); csio.ccb_h.func_code = XptOpcode.XptScsiIo; csio.ccb_h.flags = direction; csio.ccb_h.xflags = 0; csio.ccb_h.retry_count = 1; csio.ccb_h.cbfcnp = IntPtr.Zero; csio.ccb_h.timeout = timeout; csio.data_ptr = Marshal.AllocHGlobal(buffer.Length); csio.dxfer_len = (uint)buffer.Length; csio.sense_len = 32; csio.cdb_len = (byte)cdb.Length; // TODO: Create enum? csio.tag_action = 0x20; csio.cdb_bytes = new byte[CAM_MAX_CDBLEN]; if (cdb.Length <= CAM_MAX_CDBLEN) { Array.Copy(cdb, 0, csio.cdb_bytes, 0, cdb.Length); } else { cdbPtr = Marshal.AllocHGlobal(cdb.Length); byte[] cdbPtrBytes = BitConverter.GetBytes(cdbPtr.ToInt64()); Array.Copy(cdbPtrBytes, 0, csio.cdb_bytes, 0, IntPtr.Size); csio.ccb_h.flags |= CcbFlags.CamCdbPointer; } csio.ccb_h.flags |= CcbFlags.CamDevQfrzdis; Marshal.Copy(buffer, 0, csio.data_ptr, buffer.Length); Marshal.StructureToPtr(csio, ccbPtr, false); DateTime start = DateTime.UtcNow; int error = cam_send_ccb(dev, ccbPtr); DateTime end = DateTime.UtcNow; if (error < 0) { error = Marshal.GetLastWin32Error(); } csio = (CcbScsiio64)Marshal.PtrToStructure(ccbPtr, typeof(CcbScsiio64)); if ((csio.ccb_h.status & CamStatus.CamStatusMask) != CamStatus.CamReqCmp && (csio.ccb_h.status & CamStatus.CamStatusMask) != CamStatus.CamScsiStatusError) { error = Marshal.GetLastWin32Error(); AaruConsole.DebugWriteLine("FreeBSD devices", "CAM status {0} error {1}", csio.ccb_h.status, error); sense = true; } if ((csio.ccb_h.status & CamStatus.CamStatusMask) == CamStatus.CamScsiStatusError) { sense = true; senseBuffer = new byte[1]; senseBuffer[0] = csio.scsi_status; } if ((csio.ccb_h.status & CamStatus.CamAutosnsValid) != 0) { if (csio.sense_len - csio.sense_resid > 0) { sense = (csio.ccb_h.status & CamStatus.CamStatusMask) == CamStatus.CamScsiStatusError; senseBuffer = new byte[csio.sense_len - csio.sense_resid]; senseBuffer[0] = csio.sense_data.error_code; Array.Copy(csio.sense_data.sense_buf, 0, senseBuffer, 1, senseBuffer.Length - 1); } } buffer = new byte[csio.dxfer_len]; cdb = new byte[csio.cdb_len]; Marshal.Copy(csio.data_ptr, buffer, 0, buffer.Length); if (csio.ccb_h.flags.HasFlag(CcbFlags.CamCdbPointer)) { Marshal.Copy(new IntPtr(BitConverter.ToInt64(csio.cdb_bytes, 0)), cdb, 0, cdb.Length); } else { Array.Copy(csio.cdb_bytes, 0, cdb, 0, cdb.Length); } duration = (end - start).TotalMilliseconds; if (csio.ccb_h.flags.HasFlag(CcbFlags.CamCdbPointer)) { Marshal.FreeHGlobal(cdbPtr); } Marshal.FreeHGlobal(csio.data_ptr); cam_freeccb(ccbPtr); return(error); }