16#include <mach/mach_error.h>
17#include <mach/mach_init.h>
18#include <sys/utsname.h>
19#include <IOKit/IOCFPlugIn.h>
20#include <IOKit/IOKitLib.h>
21#include <IOKit/IOReturn.h>
22#include <IOKit/IOBSD.h>
23#include <IOKit/storage/IOBlockStorageDevice.h>
24#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
25#include <IOKit/storage/IOMedia.h>
26#include <IOKit/storage/ata/IOATAStorageDefines.h>
27#include <IOKit/storage/ata/ATASMARTLib.h>
28#include <CoreFoundation/CoreFoundation.h>
39#define ARGUSED(x) ((void)(x))
46 "=================================================== SMARTCTL EXAMPLES =====\n\n"
47 " smartctl -a disk0 (Prints all SMART information)\n\n"
48 " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n"
49 " smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n"
50 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n"
51 " (Prints Self-Test & Attribute errors)\n\n"
52 " smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n"
53 " (You can use IOService: ...)\n\n"
54 " smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n"
55 " (... Or IODeviceTree:)\n"
88 virtual bool is_open()
const override;
90 virtual bool open()
override;
92 virtual bool close()
override;
118 CFTypeRef smartCapableKey = NULL;
119 CFDictionaryRef diskChars;
123 if (!strcmp(
"ATA", type)) {
124 smartCapableKey = IORegistryEntryCreateCFProperty
126 kCFAllocatorDefault, 0);
129 else if (!strcmp(
"NVME", type)) {
130 smartCapableKey = IORegistryEntryCreateCFProperty
132 kCFAllocatorDefault, 0);
137 CFRelease (smartCapableKey);
145 && (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty
146 (dev, CFSTR (kIOPropertyDeviceCharacteristicsKey),
147 kCFAllocatorDefault, kNilOptions)) != NULL)
149 CFNumberRef diskFeatures = NULL;
150 UInt32 ataFeatures = 0;
152 if (CFDictionaryGetValueIfPresent (diskChars, CFSTR (
"ATA Features"),
153 (
const void **)&diskFeatures))
154 CFNumberGetValue (diskFeatures, kCFNumberLongType,
156 CFRelease (diskChars);
158 CFRelease (diskFeatures);
160 return (ataFeatures & kIOATAFeatureSMART) != 0;
177 char *type =
const_cast<char*
>(
m_mode);
179 if (!(strcmp(
"ATA", type) || strcmp(
"NVME", type)))
186 for (devnum = 0; devnum <
sizeof (
devices) /
sizeof (
devices[0]); devnum++)
196 if (strncmp (pathname,
"/dev/rdisk", 10) == 0)
197 devname = pathname + 6;
198 else if (strncmp (pathname,
"/dev/disk", 9) == 0)
199 devname = pathname + 5;
200 else if (strncmp (pathname,
"disk", 4) == 0)
207 CFMutableDictionaryRef matcher;
208 matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
209 disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
213 disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname);
224 io_object_t prevdisk = disk;
227 err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk);
228 if (err != kIOReturnSuccess || ! disk)
231 IOObjectRelease (prevdisk);
242 devices[devnum].smartIf = NULL;
243 devices[devnum].smartIfNVMe = NULL;
245 CFUUIDRef pluginType = NULL;
246 CFUUIDRef smartInterfaceId = NULL;
247 void ** SMARTptr = NULL;
249 if (!strcmp(
"ATA", type)) {
250 pluginType = kIOATASMARTUserClientTypeID;
251 smartInterfaceId = kIOATASMARTInterfaceID;
254 else if (!strcmp(
"NVME", type)) {
261 if (IOCreatePlugInInterfaceForService (disk,
263 kIOCFPlugInInterfaceID,
265 &dummy) == kIOReturnSuccess)
268 CFUUIDGetUUIDBytes ( smartInterfaceId),
271 return set_err(ENOSYS,
"IOCreatePlugInInterfaceForService failed");
277 set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
293 devices[fd].ioob = MACH_PORT_NULL;
306 io_object_t device = MACH_PORT_NULL;
310 if (!(strcmp(
"ATA", name) || strcmp(
"NVME", name))) {
314 err = IOServiceGetMatchingServices
315 (kIOMasterPortDefault, IOServiceMatching (kIOBlockStorageDeviceClass), &i);
316 if (err != kIOReturnSuccess)
321 while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
324 IOObjectRelease (device);
331 *devlist = (
char**)calloc (result,
sizeof (
char *));
333 while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
337 IORegistryEntryGetPath(device, kIOServicePlane, devName);
338 (*devlist)[index] = strdup (devName);
339 if (! (*devlist)[index])
343 IOObjectRelease (device);
350 if (device != MACH_PORT_NULL)
351 IOObjectRelease (device);
355 for (index = 0; index < result; index++)
356 if ((*devlist)[index])
357 free ((*devlist)[index]);
399 IOATASMARTInterface **ifp =
devices[fd].smartIf;
401 io_object_t disk =
devices[fd].ioob;
403 int timeoutCount = 5;
415 err =
smartIf->GetATAIdentifyData (ifp,
data, 512, &dummy);
416 if (err != kIOReturnSuccess && err != kIOReturnTimeout
417 && err != kIOReturnNotResponding)
418 printf (
"identify failed: %#x\n", (
unsigned) rc);
423 for (i = 0; i < 256; i+=2)
438 CFNumberRef cfLevel = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &l);
439 kern_return_t r = IORegistryEntrySetCFProperty(disk, CFSTR(
"APM Level"), cfLevel);
443 case kIOReturnNotPrivileged:
444 return set_err(ENOSYS,
"Use superuser to manage APM");
446 case kIOReturnUnsupported:
447 return set_err(ENOSYS,
"APM not supported");
450 return set_err(ENOSYS,
"APM error: %u", r);
456 return set_err(ENOSYS,
"Unsupported ATA feature");
462 err =
smartIf->SMARTReadData (ifp, (ATASMARTData *)
data);
465 err =
smartIf->SMARTReadDataThresholds (ifp,
466 (ATASMARTDataThresholds *)
data);
482 err =
smartIf->SMARTReturnStatus (ifp, &is_failing);
483 if (err == kIOReturnSuccess && is_failing) {
495 err =
smartIf->SMARTEnableDisableAutosave (ifp,
503 return set_err(ENOSYS,
"Unsupported SMART self-test mode");
505 err =
smartIf->SMARTExecuteOffLineImmediate (ifp,
509 return set_err(ENOSYS,
"SMART command not supported");
511 return set_err(ENOSYS,
"Unknown SMART command");
515 return set_err(ENOSYS,
"Non-SMART commands not implemented");
517 }
while ((err == kIOReturnTimeout || err == kIOReturnNotResponding)
518 && timeoutCount-- > 0);
519 if (err == kIOReturnExclusiveAccess)
521 rc = err == kIOReturnSuccess ? 0 : -1;
542 const char * pattern = 0)
override;
550 unsigned nsid)
override;
565 const char * req_type,
unsigned nsid);
571 const char * req_type,
unsigned nsid)
585 unsigned int page = in.
cdw10 & 0xff;
595 return set_err(ENOSYS,
"GetIdentifyData failed: system=0x%x, sub=0x%x, code=%d",
596 err_get_system(err), err_get_sub(err), err_get_code(err));
601 return set_err(ENOSYS,
"GetLogPage failed: system=0x%x, sub=0x%x, code=%d",
602 err_get_system(err), err_get_sub(err), err_get_code(err));
605 return set_err(ENOSYS,
"NVMe admin command 0x%02x is not supported", in.
opcode);
615 struct utsname osname;
617 return strprintf(
"%s %s %s", osname.sysname, osname.release, osname.machine);
622 if (!strcmp(appname,
"smartctl"))
651 const char *devname = NULL;
654 if (strncmp (name,
"/dev/rdisk", 10) == 0)
656 else if (strncmp (name,
"/dev/disk", 9) == 0)
658 else if (strncmp (name,
"disk", 4) == 0)
663 CFMutableDictionaryRef matcher;
664 matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
665 disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
668 disk = IORegistryEntryFromPath (kIOMasterPortDefault, name);
673 io_registry_entry_t tmpdisk=disk;
679 io_object_t prevdisk = tmpdisk;
682 err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk);
683 if (err != kIOReturnSuccess || ! tmpdisk)
685 IOObjectRelease (prevdisk);
695 io_object_t prevdisk = tmpdisk;
698 err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk);
699 if (err != kIOReturnSuccess || ! tmpdisk)
701 IOObjectRelease (prevdisk);
714 for (
int i = 0; i < numdevs; i++)
720 const char * type,
const char * pattern )
723 set_err(EINVAL,
"DEVICESCAN with pattern not implemented yet");
728 char * * atanames = 0;
int numata = 0;
729 if (!type || !strcmp(type,
"ata")) {
736 char * * nvmenames = 0;
int numnvme = 0;
738#ifdef WITH_NVME_DEVICESCAN
743 !strcmp(type,
"nvme")) {
755 for (i = 0; i < numata; i++) {
762 for (i = 0; i < numnvme; i++) {
void swap2(char *location)
#define ATA_SMART_AUTO_OFFLINE
#define ATA_IDENTIFY_DEVICE
#define ATA_SMART_WRITE_LOG_SECTOR
#define ATA_IDENTIFY_PACKET_DEVICE
#define ATA_SMART_READ_VALUES
#define ATA_SMART_READ_THRESHOLDS
#define ATA_SMART_READ_LOG_SECTOR
#define ATA_SMART_IMMEDIATE_OFFLINE
#define ATA_SMART_AUTOSAVE
#define ATA_SMART_DISABLE
#define ATA_CHECK_POWER_MODE
bool ata_cmd_is_ok(const ata_cmd_in &in, bool data_out_support=false, bool multi_sector_support=false, bool ata_48bit_support=false)
Check command input parameters (old version).
Implement standard ATA support.
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out) override
ATA pass through.
darwin_ata_device(smart_interface *intf, const char *dev_name, const char *req_type)
virtual bool nvme_pass_through(const nvme_cmd_in &in, nvme_cmd_out &out) override
NVMe pass through.
darwin_nvme_device(smart_interface *intf, const char *dev_name, const char *req_type, unsigned nsid)
Implement shared open/close routines with old functions.
virtual ~darwin_smart_device()
darwin_smart_device(const char *mode)
virtual bool close() override
Close device, return false on error.
virtual bool is_open() const override
Return true if device is open.
virtual bool open() override
Open device, return false on error.
int m_fd
filedesc, -1 if not open.
const char * m_mode
Mode string for deviceopen().
int get_fd() const
Return filedesc for derived classes.
Implement platform interface.
virtual std::string get_app_examples(const char *appname) override
Return example string for program 'appname'.
virtual scsi_device * get_scsi_device(const char *name, const char *type) override
Return standard SCSI device.
virtual std::string get_os_version_str() override
Return info string about build host and/or OS version.
virtual ata_device * get_ata_device(const char *name, const char *type) override
Return standard ATA device.
virtual bool scan_smart_devices(smart_device_list &devlist, const char *type, const char *pattern=0) override
Fill 'devlist' with devices of some 'type' with device names specified by some optional 'pattern'.
virtual smart_device * autodetect_smart_device(const char *name) override
Autodetect device if no device type specified.
virtual nvme_device * get_nvme_device(const char *name, const char *type, unsigned nsid) override
Return standard NVMe device.
List of devices for DEVICESCAN.
void push_back(smart_device *dev)
Base class for all devices.
int get_errno() const
Get last error number.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
const char * get_dev_name() const
Get device (path)name.
void clear_err()
Clear last error info.
The platform interface abstraction.
static void set(smart_interface *intf)
Set interface to use, must be called from init().
static void init()
Initialize platform interface and register with smi().
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
#define DEV_INTERFACE_H_CVSID
static int make_device_names(char ***devlist, const char *name)
static void free_devnames(char **devnames, int numdevs)
static bool is_smart_capable(io_object_t dev, const char *type)
IONVMeSMARTInterface ** smartIfNVMe
static const char smartctl_examples[]
const char * dev_darwin_cpp_cvsid
const char * os_darwin_cpp_cvsid
static struct @44 devices[20]
IOCFPlugInInterface ** plugin
IOATASMARTInterface ** smartIf
#define kIOATABlockStorageDeviceClass
#define kIONVMeSMARTUserClientTypeID
#define OS_DARWIN_H_CVSID
#define kIONVMeSMARTInterfaceID
#define kIOPropertyNVMeSMARTCapableKey
#define kIOPropertySMARTCapableKey
IOReturn(* GetIdentifyData)(void *interface, struct nvme_id_ctrl *NVMeIdentifyControllerStruct, unsigned int ns)
IOReturn(* GetLogPage)(void *interface, void *data, unsigned int logPageId, unsigned int numDWords)
ATA pass through input parameters.
void * buffer
Pointer to data buffer.
ata_in_regs_48bit in_regs
Input registers.
ata_out_regs_flags out_needed
True if output register value needed.
ATA pass through output parameters.
ata_out_regs_48bit out_regs
Output registers.
ata_register sector_count
NVMe pass through input parameters.
unsigned char opcode
Opcode (CDW0 07:00)
unsigned size
Size of buffer.
unsigned nsid
Namespace ID.
void * buffer
Pointer to data buffer.
NVMe pass through output parameters.
std::string strprintf(const char *fmt,...)