diff --git a/DiscImageChef.Decoders/SCSI/Inquiry.cs b/DiscImageChef.Decoders/SCSI/Inquiry.cs index 7f48e5388..fc8881bfc 100644 --- a/DiscImageChef.Decoders/SCSI/Inquiry.cs +++ b/DiscImageChef.Decoders/SCSI/Inquiry.cs @@ -63,7 +63,7 @@ namespace DiscImageChef.Decoders.SCSI return null; } - if(SCSIInquiryResponse.Length != SCSIInquiryResponse[4] + 5) + if(SCSIInquiryResponse.Length < SCSIInquiryResponse[4] + 5) { DicConsole.DebugWriteLine("SCSI INQUIRY decoder", "INQUIRY response length ({0} bytes) is different than specified in length field ({1} bytes), decoded data can be incorrect, not decoding.", SCSIInquiryResponse.Length, SCSIInquiryResponse[4] + 4); return null; diff --git a/DiscImageChef.Devices/Device/List.cs b/DiscImageChef.Devices/Device/List.cs index 39d265806..9acd61ce8 100644 --- a/DiscImageChef.Devices/Device/List.cs +++ b/DiscImageChef.Devices/Device/List.cs @@ -53,6 +53,8 @@ namespace DiscImageChef.Devices return Windows.ListDevices.GetList(); case Interop.PlatformID.Linux: return Linux.ListDevices.GetList(); + case Interop.PlatformID.FreeBSD: + return FreeBSD.ListDevices.GetList(); default: throw new InvalidOperationException(string.Format("Platform {0} not yet supported.", Interop.DetectOS.GetRealPlatformID())); } diff --git a/DiscImageChef.Devices/DiscImageChef.Devices.csproj b/DiscImageChef.Devices/DiscImageChef.Devices.csproj index 5bb9b6afb..82aef527d 100644 --- a/DiscImageChef.Devices/DiscImageChef.Devices.csproj +++ b/DiscImageChef.Devices/DiscImageChef.Devices.csproj @@ -36,6 +36,7 @@ + diff --git a/DiscImageChef.Devices/FreeBSD/Enums.cs b/DiscImageChef.Devices/FreeBSD/Enums.cs index ba40aed66..a1e1023db 100644 --- a/DiscImageChef.Devices/FreeBSD/Enums.cs +++ b/DiscImageChef.Devices/FreeBSD/Enums.cs @@ -265,7 +265,7 @@ namespace DiscImageChef.Devices.FreeBSD enum dev_match_type { - DEV_MATCH_PERIPH, + DEV_MATCH_PERIPH = 0, DEV_MATCH_DEVICE, DEV_MATCH_BUS } @@ -385,5 +385,11 @@ namespace DiscImageChef.Devices.FreeBSD CAM_DEV_POS_EDT = 0x100, CAM_DEV_POS_PDRV = 0x200 } + + enum FreebsdIoctl : uint + { + CAMIOCOMMAND = 0xC4D81802, + } + } diff --git a/DiscImageChef.Devices/FreeBSD/Extern.cs b/DiscImageChef.Devices/FreeBSD/Extern.cs index 8aea0e741..b5cfac759 100644 --- a/DiscImageChef.Devices/FreeBSD/Extern.cs +++ b/DiscImageChef.Devices/FreeBSD/Extern.cs @@ -64,6 +64,9 @@ namespace DiscImageChef.Devices.FreeBSD [DllImport("libcam", CharSet = CharSet.Ansi, SetLastError = true)] internal static extern int cam_send_ccb(IntPtr dev, IntPtr ccb); + + [DllImport("libc")] + internal static extern int ioctl(int fd, FreebsdIoctl request, IntPtr argp); } } diff --git a/DiscImageChef.Devices/FreeBSD/ListDevices.cs b/DiscImageChef.Devices/FreeBSD/ListDevices.cs new file mode 100644 index 000000000..3b4edc5d4 --- /dev/null +++ b/DiscImageChef.Devices/FreeBSD/ListDevices.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using DiscImageChef.Console; +using path_id_t = System.UInt32; +using target_id_t = System.UInt32; +using lun_id_t = System.UInt32; + +namespace DiscImageChef.Devices.FreeBSD +{ + public static class ListDevices + { + const string XPT_DEVICE = "/dev/xpt0"; + const path_id_t CAM_XPT_PATH_ID = 0xFFFFFFFF; + const target_id_t CAM_TARGET_WILDCARD = 0xFFFFFFFF; + const lun_id_t CAM_LUN_WILDCARD = 0xFFFFFFFF; + const int ccb_size = 1240; + + public static DeviceInfo[] GetList() + { + int fd = Extern.open(XPT_DEVICE, FileFlags.ReadWrite); + + // MMC support was added to CAM in FreeBSD 12 + int not_mmc_data = Marshal.SizeOf(typeof(mmc_params)); + if(Environment.OSVersion.Version.Major >= 12) + not_mmc_data = 0; + + int dev_match_result_size = Marshal.SizeOf(typeof(dev_match_result)) - not_mmc_data; + + if(fd == -1) + { + DicConsole.ErrorWriteLine("Error {0} opening {1}", Marshal.GetLastWin32Error(), XPT_DEVICE); + return null; + } + + int bufsize = dev_match_result_size * 100; + ccb_dev_match cdm = new ccb_dev_match(); + cdm.ccb_h = new ccb_hdr(); + cdm.ccb_h.path_id = CAM_XPT_PATH_ID; + cdm.ccb_h.target_id = CAM_TARGET_WILDCARD; + cdm.ccb_h.target_lun = CAM_LUN_WILDCARD; + cdm.ccb_h.func_code = xpt_opcode.XPT_DEV_MATCH; + cdm.match_buf_len = (uint)bufsize; + cdm.matches = Marshal.AllocHGlobal(bufsize); + + IntPtr ccb = Marshal.AllocHGlobal(ccb_size); + Marshal.StructureToPtr(cdm, ccb, false); + int res = Extern.ioctl(fd, FreebsdIoctl.CAMIOCOMMAND, ccb); + + if(res == -1) + { + DicConsole.ErrorWriteLine("Error {0} sending ioctl to CAM", Marshal.GetLastWin32Error()); + Extern.close(fd); + Marshal.FreeHGlobal(cdm.matches); + Marshal.FreeHGlobal(ccb); + return null; + } + + cdm = (ccb_dev_match)Marshal.PtrToStructure(ccb, typeof(ccb_dev_match)); + DicConsole.DebugWriteLine("FreeBSD devices", "CAM returned {0} matches", cdm.num_matches); + + if(cdm.num_matches == 0) + return null; + + dev_match_result[] matches = new dev_match_result[cdm.num_matches]; + + byte[] buffer = new byte[bufsize]; + Marshal.Copy(cdm.matches, buffer, 0, bufsize); + + List listDevices = new List(); + DeviceInfo deviceInfo = new DeviceInfo(); + bool pathFound = false; + bool skipDevice = false; + + for(int i = 0; i < matches.Length; i++) + { + dev_match_type matchType = (dev_match_type)BitConverter.ToUInt32(buffer, i * dev_match_result_size); + + if(matchType == dev_match_type.DEV_MATCH_DEVICE) + { + byte[] data = new byte[Marshal.SizeOf(typeof(device_match_result))]; + Buffer.BlockCopy(buffer, (i * dev_match_result_size) + 4, data, 0, + Marshal.SizeOf(typeof(device_match_result))); + IntPtr matchPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(device_match_result))); + Marshal.Copy(data, 0, matchPtr, Marshal.SizeOf(typeof(device_match_result))); + device_match_result dmr = + (device_match_result)Marshal.PtrToStructure(matchPtr, typeof(device_match_result)); + Marshal.FreeHGlobal(matchPtr); + + if(dmr.flags.HasFlag(dev_result_flags.DEV_RESULT_UNCONFIGURED)) + { + skipDevice = true; + continue; + } + + if(pathFound) + { + listDevices.Add(deviceInfo); + deviceInfo = new DeviceInfo(); + pathFound = false; + } + + System.Console.WriteLine("{0}", dmr.protocol); + + skipDevice = false; + switch(dmr.protocol) + { + case cam_proto.PROTO_ATA: + case cam_proto.PROTO_ATAPI: + case cam_proto.PROTO_SATAPM: + { + // Little-endian FreeBSD gives it resorted + // Big-endian FreeBSD, no idea + byte[] atad_tneid = new byte[512]; + for(int aIndex = 0; aIndex < 512; aIndex += 2) + { + atad_tneid[aIndex] = dmr.ident_data[aIndex + 1]; + atad_tneid[aIndex + 1] = dmr.ident_data[aIndex]; + } + + Decoders.ATA.Identify.IdentifyDevice? idt = Decoders.ATA.Identify.Decode(atad_tneid); + if(idt.HasValue) + { + string[] separated = idt.Value.Model.Split(' '); + + if(separated.Length == 1) + { + deviceInfo.vendor = "ATA"; + deviceInfo.model = separated[0]; + } + else + { + deviceInfo.vendor = separated[0]; + deviceInfo.model = separated[separated.Length - 1]; + } + + deviceInfo.serial = idt.Value.SerialNumber; + deviceInfo.bus = "ATA"; + deviceInfo.supported = false; + } + if(dmr.protocol == cam_proto.PROTO_ATAPI) + goto case cam_proto.PROTO_SCSI; + break; + } + case cam_proto.PROTO_SCSI: + { + + Decoders.SCSI.Inquiry.SCSIInquiry? inq = Decoders.SCSI.Inquiry.Decode(dmr.inq_data); + if(inq.HasValue) + { + deviceInfo.vendor = StringHandlers.CToString(inq.Value.VendorIdentification).Trim(); + deviceInfo.model = StringHandlers.CToString(inq.Value.ProductIdentification).Trim(); + deviceInfo.bus = dmr.protocol == cam_proto.PROTO_ATAPI ? "ATAPI" : "SCSI"; + deviceInfo.supported = false; + } + break; + } + case cam_proto.PROTO_NVME: + deviceInfo.bus = "NVMe"; + deviceInfo.supported = false; + break; + case cam_proto.PROTO_MMCSD: + if(!ArrayHelpers.ArrayIsNullOrEmpty(dmr.mmc_ident_data.model)) + deviceInfo.model = StringHandlers.CToString(dmr.mmc_ident_data.model); + else + deviceInfo.model = string.Format("{0} card", + dmr.mmc_ident_data.card_features.HasFlag(mmc_card_features.CARD_FEATURE_SDIO) + ? "SDIO" + : "Unknown"); + + if(dmr.mmc_ident_data.card_features.HasFlag(mmc_card_features.CARD_FEATURE_SD20) || + dmr.mmc_ident_data.card_features.HasFlag(mmc_card_features.CARD_FEATURE_SDHC) || + dmr.mmc_ident_data.card_features.HasFlag(mmc_card_features.CARD_FEATURE_SDIO)) + deviceInfo.bus = "SD"; + else if(dmr.mmc_ident_data.card_features.HasFlag(mmc_card_features.CARD_FEATURE_MMC)) + deviceInfo.bus = "MMC"; + else + deviceInfo.bus = "MMC/SD"; + + deviceInfo.supported = false; + break; + default: + skipDevice = true; + break; + } + } + else if(matchType == dev_match_type.DEV_MATCH_PERIPH) + { + if(skipDevice) + continue; + + byte[] data = new byte[Marshal.SizeOf(typeof(periph_match_result))]; + Buffer.BlockCopy(buffer, (i * dev_match_result_size) + 4, data, 0, + Marshal.SizeOf(typeof(periph_match_result))); + IntPtr matchPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(periph_match_result))); + Marshal.Copy(data, 0, matchPtr, Marshal.SizeOf(typeof(periph_match_result))); + periph_match_result pmr = + (periph_match_result)Marshal.PtrToStructure(matchPtr, typeof(periph_match_result)); + Marshal.FreeHGlobal(matchPtr); + + if(deviceInfo.path == null || StringHandlers.CToString(pmr.periph_name) == "pass") + { + deviceInfo.path = string.Format("/dev/{0}{1}", StringHandlers.CToString(pmr.periph_name), + pmr.unit_number); + pathFound = true; + } + } + } + + if(pathFound) + listDevices.Add(deviceInfo); + + + Marshal.FreeHGlobal(cdm.matches); + Marshal.FreeHGlobal(ccb); + + Extern.close(fd); + + if(listDevices.Count > 0) + return listDevices.OrderBy(t => t.path).ToArray(); + + return null; + } + } +} \ No newline at end of file diff --git a/DiscImageChef.Devices/FreeBSD/Structs.cs b/DiscImageChef.Devices/FreeBSD/Structs.cs index 5d1d298ab..31bced71a 100644 --- a/DiscImageChef.Devices/FreeBSD/Structs.cs +++ b/DiscImageChef.Devices/FreeBSD/Structs.cs @@ -35,10 +35,11 @@ using System; using System.Runtime.InteropServices; using path_id_t = System.UInt32; using target_id_t = System.UInt32; -using lun_id_t = System.UInt64; +using lun_id_t = System.UInt32; namespace DiscImageChef.Devices.FreeBSD { + [StructLayout(LayoutKind.Sequential)] struct ata_cmd { public CamAtaIoFlags flags; @@ -57,6 +58,7 @@ namespace DiscImageChef.Devices.FreeBSD public byte control; } + [StructLayout(LayoutKind.Sequential)] struct ata_res { public CamAtaIoFlags flags; @@ -73,6 +75,7 @@ namespace DiscImageChef.Devices.FreeBSD public byte sector_count_exp; } + [StructLayout(LayoutKind.Sequential)] struct cam_pinfo { public uint priority; @@ -80,7 +83,7 @@ namespace DiscImageChef.Devices.FreeBSD public int index; } - struct camq_entry + struct LIST_ENTRY { /// /// LIST_ENTRY(ccb_hdr)=le->*le_next @@ -90,10 +93,18 @@ namespace DiscImageChef.Devices.FreeBSD /// LIST_ENTRY(ccb_hdr)=le->**le_prev /// public IntPtr le_prev; + } + + struct SLIST_ENTRY + { /// /// SLIST_ENTRY(ccb_hdr)=sle->*sle_next /// public IntPtr sle_next; + } + + struct TAILQ_ENTRY + { /// /// TAILQ_ENTRY(ccb_hdr)=tqe->*tqe_next /// @@ -102,12 +113,26 @@ namespace DiscImageChef.Devices.FreeBSD /// TAILQ_ENTRY(ccb_hdr)=tqe->**tqe_prev /// public IntPtr tqe_prev; + } + + struct STAILQ_ENTRY + { /// /// STAILQ_ENTRY(ccb_hdr)=stqe->*stqe_next /// public IntPtr stqe_next; } + [StructLayout(LayoutKind.Explicit)] + struct camq_entry + { + [FieldOffset(0)] public LIST_ENTRY le; + [FieldOffset(0)] public SLIST_ENTRY sle; + [FieldOffset(0)] public TAILQ_ENTRY tqe; + [FieldOffset(0)] public STAILQ_ENTRY stqe; + } + + [StructLayout(LayoutKind.Sequential)] struct timeval { public long tv_sec; @@ -115,6 +140,7 @@ namespace DiscImageChef.Devices.FreeBSD public IntPtr tv_usec; } + [StructLayout(LayoutKind.Sequential)] struct ccb_qos_area { public timeval etime; @@ -122,6 +148,7 @@ namespace DiscImageChef.Devices.FreeBSD public UIntPtr periph_data; } + [StructLayout(LayoutKind.Sequential)] struct ccb_hdr { public cam_pinfo pinfo; @@ -147,6 +174,7 @@ namespace DiscImageChef.Devices.FreeBSD public timeval softtimeout; } + [StructLayout(LayoutKind.Sequential)] struct scsi_sense_data { public byte error_code; @@ -157,6 +185,7 @@ namespace DiscImageChef.Devices.FreeBSD /// /// SCSI I/O Request CCB used for the XPT_SCSI_IO and XPT_CONT_TARGET_IO function codes. /// + [StructLayout(LayoutKind.Sequential)] struct cdb_scsiio { public ccb_hdr ccb_h; @@ -199,6 +228,7 @@ namespace DiscImageChef.Devices.FreeBSD /// /// ATA I/O Request CCB used for the XPT_ATA_IO function code. /// + [StructLayout(LayoutKind.Sequential)] struct ccb_ataio { public ccb_hdr ccb_h; @@ -222,6 +252,7 @@ namespace DiscImageChef.Devices.FreeBSD public uint init_id; } + [StructLayout(LayoutKind.Sequential)] struct nvme_command { private ushort opc_fuse_rsvd1; @@ -292,6 +323,7 @@ namespace DiscImageChef.Devices.FreeBSD public byte rsvd1 => (byte)(opc_fuse_rsvd1 & 0x3F); } + [StructLayout(LayoutKind.Sequential)] struct nvme_status { private ushort status; @@ -327,6 +359,7 @@ namespace DiscImageChef.Devices.FreeBSD public byte dnr => (byte)(status & 0x1); } + [StructLayout(LayoutKind.Sequential)] struct nvme_completion { /// @@ -360,6 +393,7 @@ namespace DiscImageChef.Devices.FreeBSD /// /// NVMe I/O Request CCB used for the XPT_NVME_IO and XPT_NVME_ADMIN function codes. /// + [StructLayout(LayoutKind.Sequential)] struct ccb_nvmeio { public ccb_hdr ccb_h; @@ -379,6 +413,7 @@ namespace DiscImageChef.Devices.FreeBSD public ushort unused; } + [StructLayout(LayoutKind.Sequential)] struct periph_match_pattern { private const int DEV_IDLEN = 16; @@ -391,12 +426,14 @@ namespace DiscImageChef.Devices.FreeBSD public periph_pattern_flags flags; } + [StructLayout(LayoutKind.Sequential)] struct device_id_match_pattern { public byte id_len; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public byte[] id; } + [StructLayout(LayoutKind.Sequential)] struct scsi_static_inquiry_pattern { private const int SID_VENDOR_SIZE = 8; @@ -409,12 +446,14 @@ namespace DiscImageChef.Devices.FreeBSD [MarshalAs(UnmanagedType.ByValArray, SizeConst = SID_REVISION_SIZE + 1)] public byte[] revision; } + [StructLayout(LayoutKind.Explicit)] struct device_match_pattern_data { [FieldOffset(0)] public scsi_static_inquiry_pattern inq_pat; [FieldOffset(0)] public device_id_match_pattern devid_pat; } + [StructLayout(LayoutKind.Sequential)] struct device_match_pattern { public path_id_t path_id; @@ -424,6 +463,7 @@ namespace DiscImageChef.Devices.FreeBSD public device_match_pattern_data data; } + [StructLayout(LayoutKind.Sequential)] struct bus_match_pattern { private const int DEV_IDLEN = 16; @@ -443,22 +483,24 @@ namespace DiscImageChef.Devices.FreeBSD [FieldOffset(0)] public bus_match_pattern bus_pattern; } + [StructLayout(LayoutKind.Sequential)] struct dev_match_pattern { public dev_match_type type; public match_pattern pattern; } + [StructLayout(LayoutKind.Sequential)] struct periph_match_result { - private const int DEV_IDLEN = 16; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = DEV_IDLEN)] public byte[] periph_name; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] periph_name; public uint unit_number; public path_id_t path_id; public target_id_t target_id; public lun_id_t target_lun; } + [StructLayout(LayoutKind.Sequential)] struct mmc_cid { public uint mid; @@ -471,6 +513,7 @@ namespace DiscImageChef.Devices.FreeBSD public byte fwrev; } + [StructLayout(LayoutKind.Sequential)] struct mmc_params { /// @@ -516,6 +559,7 @@ namespace DiscImageChef.Devices.FreeBSD public byte sdio_func_count; } + [StructLayout(LayoutKind.Sequential)] struct device_match_result { public path_id_t path_id; @@ -527,13 +571,17 @@ namespace DiscImageChef.Devices.FreeBSD public dev_result_flags flags; public mmc_params mmc_ident_data; } - struct bus_match_result { - public path_id_t path_id; + + [StructLayout(LayoutKind.Sequential)] + struct bus_match_result + { + public path_id_t path_id; private const int DEV_IDLEN = 16; [MarshalAs(UnmanagedType.ByValArray, SizeConst = DEV_IDLEN)] public byte[] dev_name; - public uint unit_number; - public uint bus_id; + public uint unit_number; + public uint bus_id; } + [StructLayout(LayoutKind.Explicit)] struct match_result { @@ -542,12 +590,14 @@ namespace DiscImageChef.Devices.FreeBSD [FieldOffset(0)] public bus_match_result bus_result; } + [StructLayout(LayoutKind.Sequential)] struct dev_match_result { public dev_match_type type; public match_result result; } + [StructLayout(LayoutKind.Sequential)] struct ccb_dm_cookie { public IntPtr bus; @@ -557,6 +607,7 @@ namespace DiscImageChef.Devices.FreeBSD public IntPtr pdrv; } + [StructLayout(LayoutKind.Sequential)] struct ccb_dev_position { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public cam_generations[] generations; @@ -564,6 +615,7 @@ namespace DiscImageChef.Devices.FreeBSD public ccb_dm_cookie cookie; } + [StructLayout(LayoutKind.Sequential)] struct ccb_dev_match { public ccb_hdr ccb_h;