From e2888eaf2787c17827a2ac99878013da9cd09603 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sun, 10 Dec 2017 21:00:20 +0000 Subject: [PATCH] Added support for sending SCSI commands in FreeBSD. Not working because CAM is leaving the buffer untouched. --- DiscImageChef.Devices/Command.cs | 28 +- DiscImageChef.Devices/Device/Constructor.cs | 17 +- DiscImageChef.Devices/FreeBSD/Command.cs | 222 ++++++++++++- DiscImageChef.Devices/FreeBSD/Enums.cs | 343 ++++++++++++++++++++ DiscImageChef.Devices/FreeBSD/Structs.cs | 163 +++++++++- 5 files changed, 762 insertions(+), 11 deletions(-) diff --git a/DiscImageChef.Devices/Command.cs b/DiscImageChef.Devices/Command.cs index 33d9b960b..157a334fd 100644 --- a/DiscImageChef.Devices/Command.cs +++ b/DiscImageChef.Devices/Command.cs @@ -55,7 +55,7 @@ namespace DiscImageChef.Devices { Interop.PlatformID ptID = DetectOS.GetRealPlatformID(); - return SendScsiCommand(ptID, (SafeFileHandle)fd, cdb, ref buffer, out senseBuffer, timeout, direction, out duration, out sense); + return SendScsiCommand(ptID, fd, cdb, ref buffer, out senseBuffer, timeout, direction, out duration, out sense); } /// @@ -119,6 +119,32 @@ namespace DiscImageChef.Devices return Linux.Command.SendScsiCommand((int)fd, cdb, ref buffer, out senseBuffer, timeout, dir, out duration, out sense); } + case Interop.PlatformID.FreeBSD: + { + FreeBSD.ccb_flags flags = 0; + + switch(direction) + { + case ScsiDirection.In: + flags = FreeBSD.ccb_flags.CAM_DIR_IN; + break; + case ScsiDirection.Out: + flags = FreeBSD.ccb_flags.CAM_DIR_OUT; + break; + case ScsiDirection.Bidirectional: + flags = FreeBSD.ccb_flags.CAM_DIR_BOTH; + break; + case ScsiDirection.None: + flags = FreeBSD.ccb_flags.CAM_DIR_NONE; + 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(string.Format("Platform {0} not yet supported.", ptID)); } diff --git a/DiscImageChef.Devices/Device/Constructor.cs b/DiscImageChef.Devices/Device/Constructor.cs index ea34ff5fd..8ed8feaf7 100644 --- a/DiscImageChef.Devices/Device/Constructor.cs +++ b/DiscImageChef.Devices/Device/Constructor.cs @@ -33,7 +33,6 @@ using System; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -using System.Text; using DiscImageChef.Console; using DiscImageChef.Decoders.ATA; @@ -82,6 +81,18 @@ namespace DiscImageChef.Devices break; } + case Interop.PlatformID.FreeBSD: + { + fd = FreeBSD.Extern.cam_open_device(devicePath, FreeBSD.FileFlags.ReadWrite); + + if(((IntPtr)fd).ToInt64() == 0) + { + error = true; + lastError = Marshal.GetLastWin32Error(); + } + + break; + } default: throw new InvalidOperationException(string.Format("Platform {0} not yet supported.", platformID)); } @@ -272,7 +283,7 @@ namespace DiscImageChef.Devices } } } - else + else if(platformID == Interop.PlatformID.Linux) { if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) scsiSense = ScsiInquiry(out inqBuf, out senseBuf); @@ -306,6 +317,8 @@ namespace DiscImageChef.Devices } } } + else + scsiSense = ScsiInquiry(out inqBuf, out senseBuf); #region SecureDigital / MultiMediaCard if(cachedCid != null) diff --git a/DiscImageChef.Devices/FreeBSD/Command.cs b/DiscImageChef.Devices/FreeBSD/Command.cs index 11e56cd2b..82b18f3ed 100644 --- a/DiscImageChef.Devices/FreeBSD/Command.cs +++ b/DiscImageChef.Devices/FreeBSD/Command.cs @@ -31,12 +31,230 @@ // Copyright © 2011-2017 Natalia Portillo // ****************************************************************************/ +using System; +using System.Runtime.InteropServices; +using DiscImageChef.Console; +using DiscImageChef.Devices.Linux; +using static DiscImageChef.Devices.FreeBSD.Extern; + namespace DiscImageChef.Devices.FreeBSD { - public class Command + static class Command { - public Command() + const int CAM_MAX_CDBLEN = 16; + + /// + /// Sends a SCSI command (64-bit arch) + /// + /// 0 if no error occurred, otherwise, errno + /// CAM device + /// SCSI CDB + /// Buffer for SCSI command response + /// Buffer with the SCSI sense + /// Timeout in seconds + /// SCSI command transfer direction + /// Time it took to execute the command in milliseconds + /// True if SCSI error returned non-OK status and contains SCSI sense + internal static int SendScsiCommand64(IntPtr dev, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, ccb_flags direction, out double duration, out bool sense) { + senseBuffer = null; + duration = 0; + sense = false; + + if(buffer == null) + return -1; + + IntPtr ccbPtr = cam_getccb(dev); + + if(ccbPtr.ToInt64() == 0) + { + sense = true; + return Marshal.GetLastWin32Error(); + } + + ccb_scsiio64 csio = (ccb_scsiio64)Marshal.PtrToStructure(ccbPtr, typeof(ccb_scsiio64)); + csio.ccb_h.func_code = xpt_opcode.XPT_SCSI_IO; + 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_io = new cdb_t64(); + if(cdb.Length <= CAM_MAX_CDBLEN) + { + csio.cdb_io.cdb_bytes = new byte[CAM_MAX_CDBLEN]; + Array.Copy(cdb, 0, csio.cdb_io.cdb_bytes, 0, cdb.Length); + } + else + { + csio.cdb_io.cdb_ptr = Marshal.AllocHGlobal(cdb.Length).ToInt64(); + csio.ccb_h.flags |= ccb_flags.CAM_CDB_POINTER; + } + 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 = (ccb_scsiio64)Marshal.PtrToStructure(ccbPtr, typeof(ccb_scsiio64)); + + if((csio.ccb_h.status & cam_status.CAM_STATUS_MASK) != cam_status.CAM_REQ_CMP && + (csio.ccb_h.status & cam_status.CAM_STATUS_MASK) != cam_status.CAM_SCSI_STATUS_ERROR) + { + error = Marshal.GetLastWin32Error(); + DicConsole.DebugWriteLine("FreeBSD devices", "CAM status {0} error {1}", csio.ccb_h.status, error); + sense = true; + } + + if((csio.ccb_h.status & cam_status.CAM_STATUS_MASK) == cam_status.CAM_SCSI_STATUS_ERROR) + { + sense = true; + senseBuffer = new byte[1]; + senseBuffer[0] = csio.scsi_status; + } + + if((csio.ccb_h.status & cam_status.CAM_AUTOSNS_VALID) != 0) + { + if(csio.sense_len - csio.sense_resid > 0) + { + sense = (csio.ccb_h.status & cam_status.CAM_STATUS_MASK) == cam_status.CAM_SCSI_STATUS_ERROR; + 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(ccb_flags.CAM_CDB_POINTER)) + Marshal.Copy(new IntPtr(csio.cdb_io.cdb_ptr), cdb, 0, cdb.Length); + else + Array.Copy(csio.cdb_io.cdb_bytes, 0, cdb, 0, cdb.Length); + duration = (end - start).TotalMilliseconds; + + Marshal.FreeHGlobal(csio.data_ptr); + cam_freeccb(ccbPtr); + + return error; + } + + /// + /// Sends a SCSI command (32-bit arch) + /// + /// 0 if no error occurred, otherwise, errno + /// CAM device + /// SCSI CDB + /// Buffer for SCSI command response + /// Buffer with the SCSI sense + /// Timeout in seconds + /// SCSI command transfer direction + /// Time it took to execute the command in milliseconds + /// True if SCSI error returned non-OK status and contains SCSI sense + internal static int SendScsiCommand(IntPtr dev, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, ccb_flags direction, out double duration, out bool sense) + { + senseBuffer = null; + duration = 0; + sense = false; + + if(buffer == null) + return -1; + + IntPtr ccbPtr = cam_getccb(dev); + + if(ccbPtr.ToInt32() == 0) + { + sense = true; + return Marshal.GetLastWin32Error(); + } + + ccb_scsiio csio = (ccb_scsiio)Marshal.PtrToStructure(ccbPtr, typeof(ccb_scsiio)); + csio.ccb_h.func_code = xpt_opcode.XPT_SCSI_IO; + 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_io = new cdb_t(); + if(cdb.Length <= CAM_MAX_CDBLEN) + { + csio.cdb_io.cdb_bytes = new byte[CAM_MAX_CDBLEN]; + Array.Copy(cdb, 0, csio.cdb_io.cdb_bytes, 0, cdb.Length); + } + else + { + csio.cdb_io.cdb_ptr = Marshal.AllocHGlobal(cdb.Length).ToInt32(); + csio.ccb_h.flags |= ccb_flags.CAM_CDB_POINTER; + } + 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 = (ccb_scsiio)Marshal.PtrToStructure(ccbPtr, typeof(ccb_scsiio)); + + if((csio.ccb_h.status & cam_status.CAM_STATUS_MASK) != cam_status.CAM_REQ_CMP && + (csio.ccb_h.status & cam_status.CAM_STATUS_MASK) != cam_status.CAM_SCSI_STATUS_ERROR) + { + error = Marshal.GetLastWin32Error(); + DicConsole.DebugWriteLine("FreeBSD devices", "CAM status {0} error {1}", csio.ccb_h.status, error); + sense = true; + } + + if((csio.ccb_h.status & cam_status.CAM_STATUS_MASK) == cam_status.CAM_SCSI_STATUS_ERROR) + { + sense = true; + senseBuffer = new byte[1]; + senseBuffer[0] = csio.scsi_status; + } + + if((csio.ccb_h.status & cam_status.CAM_AUTOSNS_VALID) != 0) + { + if(csio.sense_len - csio.sense_resid > 0) + { + sense = (csio.ccb_h.status & cam_status.CAM_STATUS_MASK) == cam_status.CAM_SCSI_STATUS_ERROR; + 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(ccb_flags.CAM_CDB_POINTER)) + Marshal.Copy(new IntPtr(csio.cdb_io.cdb_ptr), cdb, 0, cdb.Length); + else + Array.Copy(csio.cdb_io.cdb_bytes, 0, cdb, 0, cdb.Length); + duration = (end - start).TotalMilliseconds; + + Marshal.FreeHGlobal(csio.data_ptr); + cam_freeccb(ccbPtr); + + return error; } } } diff --git a/DiscImageChef.Devices/FreeBSD/Enums.cs b/DiscImageChef.Devices/FreeBSD/Enums.cs index a1e1023db..0437c8684 100644 --- a/DiscImageChef.Devices/FreeBSD/Enums.cs +++ b/DiscImageChef.Devices/FreeBSD/Enums.cs @@ -391,5 +391,348 @@ namespace DiscImageChef.Devices.FreeBSD CAMIOCOMMAND = 0xC4D81802, } + [Flags] + enum ccb_flags : uint + { + /// + /// The CDB field is a pointer + /// + CAM_CDB_POINTER = 0x00000001, + /// + /// SIM queue actions are enabled + /// + CAM_QUEUE_ENABLE = 0x00000002, + /// + /// CCB contains a linked CDB + /// + CAM_CDB_LINKED = 0x00000004, + /// + /// Perform transport negotiation with this command. + /// + CAM_NEGOTIATE = 0x00000008, + /// + /// Data type with physical addrs + /// + CAM_DATA_ISPHYS = 0x00000010, + /// + /// Disable autosense feature + /// + CAM_DIS_AUTOSENSE = 0x00000020, + /// + /// Data direction (00:IN/OUT) + /// + CAM_DIR_BOTH = 0x00000000, + /// + /// Data direction (01:DATA IN) + /// + CAM_DIR_IN = 0x00000040, + /// + /// Data direction (10:DATA OUT) + /// + CAM_DIR_OUT = 0x00000080, + /// + /// Data direction (11:no data) + /// + CAM_DIR_NONE = 0x000000C0, + /// + /// Data type (000:Virtual) + /// + CAM_DATA_VADDR = 0x00000000, + /// + /// Data type (001:Physical) + /// + CAM_DATA_PADDR = 0x00000010, + /// + /// Data type (010:sglist) + /// + CAM_DATA_SG = 0x00040000, + /// + /// Data type (011:sglist phys) + /// + CAM_DATA_SG_PADDR = 0x00040010, + /// + /// Data type (100:bio) + /// + CAM_DATA_BIO = 0x00200000, + /// + /// Use Soft reset alternative + /// + CAM_SOFT_RST_OP = 0x00000100, + /// + /// Flush resid bytes on complete + /// + CAM_ENG_SYNC = 0x00000200, + /// + /// Disable DEV Q freezing + /// + CAM_DEV_QFRZDIS = 0x00000400, + /// + /// Freeze DEV Q on execution + /// + CAM_DEV_QFREEZE = 0x00000800, + /// + /// Command takes a lot of power + /// + CAM_HIGH_POWER = 0x00001000, + /// + /// Sense data is a pointer + /// + CAM_SENSE_PTR = 0x00002000, + /// + /// Sense pointer is physical addr + /// + CAM_SENSE_PHYS = 0x00004000, + /// + /// Use the tag action in this ccb + /// + CAM_TAG_ACTION_VALID = 0x00008000, + /// + /// Pass driver does err. recovery + /// + CAM_PASS_ERR_RECOVER = 0x00010000, + /// + /// Disable disconnect + /// + CAM_DIS_DISCONNECT = 0x00020000, + /// + /// Message buffer ptr is physical + /// + CAM_MSG_BUF_PHYS = 0x00080000, + /// + /// Autosense data ptr is physical + /// + CAM_SNS_BUF_PHYS = 0x00100000, + /// + /// CDB poiner is physical + /// + CAM_CDB_PHYS = 0x00400000, + /// + /// SG list is for the HBA engine + /// + CAM_ENG_SGLIST = 0x00800000, + + /* Phase cognizant mode flags */ + /// + /// Disable autosave/restore ptrs + /// + CAM_DIS_AUTOSRP = 0x01000000, + /// + /// Disable auto disconnect + /// + CAM_DIS_AUTODISC = 0x02000000, + /// + /// Target CCB available + /// + CAM_TGT_CCB_AVAIL = 0x04000000, + /// + /// The SIM runs in phase mode + /// + CAM_TGT_PHASE_MODE = 0x08000000, + /// + /// Message buffer valid + /// + CAM_MSGB_VALID = 0x10000000, + /// + /// Status buffer valid + /// + CAM_STATUS_VALID = 0x20000000, + /// + /// Data buffer valid + /// + CAM_DATAB_VALID = 0x40000000, + /* Host target Mode flags */ + /// + /// Send sense data with status + /// + CAM_SEND_SENSE = 0x08000000, + /// + /// Terminate I/O Message sup. + /// + CAM_TERM_IO = 0x10000000, + /// + /// Disconnects are mandatory + /// + CAM_DISCONNECT = 0x20000000, + /// + /// Send status after data phase + /// + CAM_SEND_STATUS = 0x40000000, + /// + /// Call callback without lock. + /// + CAM_UNLOCKED = 0x80000000 + } + + enum cam_status : uint + { + /// CCB request is in progress + CAM_REQ_INPROG = 0x00, + + /// CCB request completed without error + CAM_REQ_CMP = 0x01, + + /// CCB request aborted by the host + CAM_REQ_ABORTED = 0x02, + + /// Unable to abort CCB request + CAM_UA_ABORT = 0x03, + + /// CCB request completed with an error + CAM_REQ_CMP_ERR = 0x04, + + /// CAM subsystem is busy + CAM_BUSY = 0x05, + + /// CCB request was invalid + CAM_REQ_INVALID = 0x06, + + /// Supplied Path ID is invalid + CAM_PATH_INVALID = 0x07, + + /// SCSI Device Not Installed/there + CAM_DEV_NOT_THERE = 0x08, + + /// Unable to terminate I/O CCB request + CAM_UA_TERMIO = 0x09, + + /// Target Selection Timeout + CAM_SEL_TIMEOUT = 0x0a, + + /// Command timeout + CAM_CMD_TIMEOUT = 0x0b, + + /// SCSI error, look at error code in CCB + CAM_SCSI_STATUS_ERROR = 0x0c, + + /// Message Reject Received + CAM_MSG_REJECT_REC = 0x0d, + + /// SCSI Bus Reset Sent/Received + CAM_SCSI_BUS_RESET = 0x0e, + + /// Uncorrectable parity error occurred + CAM_UNCOR_PARITY = 0x0f, + + /// Autosense: request sense cmd fail + CAM_AUTOSENSE_FAIL = 0x10, + + /// No HBA Detected error + CAM_NO_HBA = 0x11, + + /// Data Overrun error + CAM_DATA_RUN_ERR = 0x12, + + /// Unexpected Bus Free + CAM_UNEXP_BUSFREE = 0x13, + + /// Target Bus Phase Sequence Failure + CAM_SEQUENCE_FAIL = 0x14, + + /// CCB length supplied is inadequate + CAM_CCB_LEN_ERR = 0x15, + + /// Unable to provide requested capability + CAM_PROVIDE_FAIL = 0x16, + + /// A SCSI BDR msg was sent to target + CAM_BDR_SENT = 0x17, + + /// CCB request terminated by the host + CAM_REQ_TERMIO = 0x18, + + /// Unrecoverable Host Bus Adapter Error + CAM_UNREC_HBA_ERROR = 0x19, + + /// Request was too large for this host + CAM_REQ_TOO_BIG = 0x1a, + + /// This request should be requeued to preserve transaction ordering. This typically occurs when the SIM recognizes an error that should freeze the queue and must place additional requests for the target at the sim level back into the XPT queue. + CAM_REQUEUE_REQ = 0x1b, + + /// ATA error, look at error code in CCB + CAM_ATA_STATUS_ERROR = 0x1c, + + /// Initiator/Target Nexus lost. + CAM_SCSI_IT_NEXUS_LOST = 0x1d, + + /// SMP error, look at error code in CCB + CAM_SMP_STATUS_ERROR = 0x1e, + + /// Command completed without error but exceeded the soft timeout threshold. + CAM_REQ_SOFTTIMEOUT = 0x1f, + + /* + * 0x20 - 0x32 are unassigned + */ + + /// Initiator Detected Error + CAM_IDE = 0x33, + + /// Resource Unavailable + CAM_RESRC_UNAVAIL = 0x34, + + /// Unacknowledged Event by Host + CAM_UNACKED_EVENT = 0x35, + + /// Message Received in Host Target Mode + CAM_MESSAGE_RECV = 0x36, + + /// Invalid CDB received in Host Target Mode + CAM_INVALID_CDB = 0x37, + + /// Lun supplied is invalid + CAM_LUN_INVALID = 0x38, + + /// Target ID supplied is invalid + CAM_TID_INVALID = 0x39, + + /// The requested function is not available + CAM_FUNC_NOTAVAIL = 0x3a, + + /// Nexus is not established + CAM_NO_NEXUS = 0x3b, + + /// The initiator ID is invalid + CAM_IID_INVALID = 0x3c, + + /// The SCSI CDB has been received + CAM_CDB_RECVD = 0x3d, + + /// The LUN is already enabled for target mode + CAM_LUN_ALRDY_ENA = 0x3e, + + /// SCSI Bus Busy + CAM_SCSI_BUSY = 0x3f, + + + /* + * Flags + */ + + /// The DEV queue is frozen w/this err + CAM_DEV_QFRZN = 0x40, + + /// Autosense data valid for target + CAM_AUTOSNS_VALID = 0x80, + + /// SIM ready to take more commands + CAM_RELEASE_SIMQ = 0x100, + + /// SIM has this command in its queue + CAM_SIM_QUEUED = 0x200, + + /// Quality of service data is valid + CAM_QOS_VALID = 0x400, + + /// Mask bits for just the status # + CAM_STATUS_MASK = 0x3F, + + /* + * Target Specific Adjunct Status + */ + + /// sent sense with status + CAM_SENT_SENSE = 0x40000000 + } } diff --git a/DiscImageChef.Devices/FreeBSD/Structs.cs b/DiscImageChef.Devices/FreeBSD/Structs.cs index 31bced71a..c83dc9a6a 100644 --- a/DiscImageChef.Devices/FreeBSD/Structs.cs +++ b/DiscImageChef.Devices/FreeBSD/Structs.cs @@ -158,12 +158,12 @@ namespace DiscImageChef.Devices.FreeBSD public uint retry_count; public IntPtr cbfcnp; public xpt_opcode func_code; - public uint status; + public cam_status status; public IntPtr path; public uint path_id; public uint target_id; public ulong target_lun; - public uint flags; + public ccb_flags flags; public uint xflags; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public IntPtr[] periph_priv; @@ -177,16 +177,29 @@ namespace DiscImageChef.Devices.FreeBSD [StructLayout(LayoutKind.Sequential)] struct scsi_sense_data { + const int SSD_FULL_SIZE = 252; public byte error_code; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 251)] - public byte[] sense_buf; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = SSD_FULL_SIZE - 1)] public byte[] sense_buf; + } + + struct cdb_t + { + /// + /// Pointer to the CDB bytes to send + /// + public int cdb_ptr; + /// + /// Area for the CDB send + /// + const int IOCDBLEN = 16; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = IOCDBLEN)] public byte[] cdb_bytes; } /// /// SCSI I/O Request CCB used for the XPT_SCSI_IO and XPT_CONT_TARGET_IO function codes. /// [StructLayout(LayoutKind.Sequential)] - struct cdb_scsiio + struct ccb_scsiio { public ccb_hdr ccb_h; /// Ptr for next CCB for action @@ -212,7 +225,7 @@ namespace DiscImageChef.Devices.FreeBSD /// Transfer residual length: 2's comp public int resid; /// Union for CDB bytes/pointer - public IntPtr cdb_io; + public cdb_t cdb_io; /// Pointer to the message buffer public IntPtr msg_ptr; /// Number of bytes for the Message @@ -225,6 +238,61 @@ namespace DiscImageChef.Devices.FreeBSD public uint init_id; } + struct cdb_t64 + { + /// + /// Pointer to the CDB bytes to send + /// + public long cdb_ptr; + /// + /// Area for the CDB send + /// + const int IOCDBLEN = 16; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = IOCDBLEN)] public byte[] cdb_bytes; + } + + /// + /// SCSI I/O Request CCB used for the XPT_SCSI_IO and XPT_CONT_TARGET_IO function codes. + /// + [StructLayout(LayoutKind.Sequential)] + struct ccb_scsiio64 + { + public ccb_hdr ccb_h; + /// Ptr for next CCB for action + public IntPtr next_ccb; + /// Ptr to mapping info + public IntPtr req_map; + /// Ptr to the data buf/SG list + public IntPtr data_ptr; + /// Data transfer length + public uint dxfer_len; + /// Autosense storage + public scsi_sense_data sense_data; + /// Number of bytes to autosense + public byte sense_len; + /// Number of bytes for the CDB + public byte cdb_len; + /// Number of SG list entries + public short sglist_cnt; + /// Returned SCSI status + public byte scsi_status; + /// Autosense resid length: 2's comp + public sbyte sense_resid; + /// Transfer residual length: 2's comp + public int resid; + /// Union for CDB bytes/pointer + public cdb_t64 cdb_io; + /// Pointer to the message buffer + public IntPtr msg_ptr; + /// Number of bytes for the Message + public short msg_len; + /// What to do for tag queueing. The tag action should be either the define below (to send a non-tagged transaction) or one of the defined scsi tag messages from scsi_message.h. + public byte tag_action; + /// tag id from initator (target mode) + public uint tag_id; + /// initiator id of who selected + public uint init_id; + } /// /// ATA I/O Request CCB used for the XPT_ATA_IO function code. /// @@ -638,5 +706,88 @@ namespace DiscImageChef.Devices.FreeBSD public ccb_dev_position pos; } + + struct cam_device + { + private const int MAXPATHLEN = 1024; + private const int DEV_IDLEN = 16; + private const int SIM_IDLEN = 16; + /// + /// Pathname of the device given by the user. This may be null if the user states the device name and unit number separately. + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXPATHLEN)] public byte[] device_path; + /// + /// Device name given by the user. + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = DEV_IDLEN + 1)] public byte[] given_dev_name; + /// + /// Unit number given by the user. + /// + public uint given_unit_number; + /// + /// Name of the device, e.g. 'pass' + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = DEV_IDLEN + 1)] public byte[] device_name; + /// + /// Unit number of the passthrough device associated with this particular device. + /// + public uint dev_unit_num; + /// + /// Controller name, e.g. 'ahc' + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = SIM_IDLEN + 1)] public byte[] sim_name; + /// + /// Controller unit number + /// + public uint sim_unit_number; + /// + /// Controller bus number + /// + public uint bus_id; + /// + /// Logical Unit Number + /// + lun_id_t target_lun; + /// + /// Target ID + /// + target_id_t target_id; + /// + /// System SCSI bus number + /// + path_id_t path_id; + /// + /// type of peripheral device + /// + public ushort pd_type; + /// + /// SCSI Inquiry data + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public byte[] inq_data; + /// + /// device serial number + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 252)] public byte[] serial_num; + /// + /// length of the serial number + /// + public byte serial_num_len; + /// + /// Negotiated sync period + /// + public byte sync_period; + /// + /// Negotiated sync offset + /// + public byte sync_offset; + /// + /// Negotiated bus width + /// + public byte bus_width; + /// + /// file descriptor for device + /// + public int fd; + } }