]> git.proxmox.com Git - mirror_smartmontools-debian.git/commitdiff
Imported Upstream version 6.5+svn4324
authorGiuseppe Iuculano <iuculano@debian.org>
Sat, 30 Jul 2016 16:33:10 +0000 (18:33 +0200)
committerGiuseppe Iuculano <iuculano@debian.org>
Sat, 30 Jul 2016 16:33:10 +0000 (18:33 +0200)
102 files changed:
AUTHORS
ChangeLog
INSTALL
Makefile.am
NEWS
README
WARNINGS [deleted file]
aacraid.h
atacmdnames.cpp
atacmdnames.h
atacmds.cpp
atacmds.h
ataidentify.cpp
ataidentify.h
ataprint.cpp
ataprint.h
autogen.sh
cciss.cpp
configure.ac
dev_areca.cpp
dev_areca.h
dev_ata_cmd_set.cpp
dev_ata_cmd_set.h
dev_interface.cpp
dev_interface.h
dev_legacy.cpp
dev_tunnelled.h
do_release
drivedb.h
examplescripts/README
freebsd_nvme_ioctl.h [new file with mode: 0644]
int64.h
knowndrives.cpp
knowndrives.h
linux_nvme_ioctl.h [new file with mode: 0644]
nvmecmds.cpp [new file with mode: 0644]
nvmecmds.h [new file with mode: 0644]
nvmeprint.cpp [new file with mode: 0644]
nvmeprint.h [new file with mode: 0644]
os_darwin.cpp
os_darwin.h
os_darwin/English_Localizable.strings [deleted file]
os_darwin/SMART.in [deleted file]
os_darwin/StartupParameters.plist [deleted file]
os_darwin/com.smartmontools.smartd.plist.in [new file with mode: 0644]
os_darwin/pkg/Distribution.in [new file with mode: 0644]
os_darwin/pkg/PackageInfo.in [new file with mode: 0644]
os_darwin/pkg/installer/README.html [new file with mode: 0644]
os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall [new file with mode: 0755]
os_freebsd.cpp
os_freebsd.h
os_generic.cpp
os_generic.h
os_linux.cpp
os_linux.h
os_netbsd.cpp
os_netbsd.h
os_openbsd.cpp
os_openbsd.h
os_os2.cpp
os_os2.h
os_qnxnto.h
os_solaris.cpp
os_solaris.h
os_solaris_ata.s [deleted file]
os_win32.cpp
os_win32/daemon_win32.cpp
os_win32/daemon_win32.h
os_win32/default.manifest [new file with mode: 0644]
os_win32/installer.nsi
os_win32/runcmd.c
os_win32/runcmda.exe.manifest [deleted file]
os_win32/runcmdu.exe.manifest [deleted file]
os_win32/syslog.h
os_win32/syslog_win32.cpp
os_win32/syslogevt.mc
os_win32/update-smart-drivedb.nsi
os_win32/vc10/smartctl.vcxproj
os_win32/vc10/smartctl.vcxproj.filters
os_win32/vc10/smartd.vcxproj
os_win32/vc10/smartd.vcxproj.filters
os_win32/wbemcli_small.h
os_win32/wmiquery.cpp
os_win32/wmiquery.h
os_win32/wtssendmsg.c
scsiata.cpp
scsicmds.cpp
scsicmds.h
scsiprint.cpp
scsiprint.h
smartctl.8.in
smartctl.cpp
smartctl.h
smartd.8.in
smartd.conf
smartd.conf.5.in
smartd.cpp
smartd.initd.in
update-smart-drivedb.8.in
update-smart-drivedb.in
utility.cpp
utility.h

diff --git a/AUTHORS b/AUTHORS
index 74af81593cc9e4ac282f6455ab5e9327c4dfdc05..62cd8f2812dc7ce0c3bf8a8a2e73861d543acee1 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,39 +1,41 @@
-$Id: AUTHORS 3751 2013-01-18 21:19:43Z chrfranke $
+$Id: AUTHORS 4285 2016-04-10 13:17:11Z chrfranke $
 
-This code was originally developed as a Senior Thesis by Michael
-Cornwell at the Concurrent Systems Laboratory (now part of the Storage
-Systems Research Center), Jack Baskin School of Engineering, University
-of California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+Developers / Maintainers / Contributors:
 
-This package is meant to be an up-to-date replacement for the
-ucsc-smartsuite and smartsuite packages, and is derived from that code.
-
-Maintainers / Developers:
-
-Bruce Allen             <smartmontools-support@lists.sourceforge.net>
-Erik Inge Bolsø         <knan@mo.himolde.no>
+Raghava Aditya          <...>
+Bruce Allen             <...>
+Erik Inge Bolsø         <...>
 Stanislav Brabec        <sbrabec@suse.cz>
 Peter Cassidy           <pcassidy@mac.com>
 Praveen Chidambaram     <bunchofmails@gmail.com>
 Yuri Dario              <mc6530@mclink.it>
-Casper Dik              <casper@holland.sun.com>
+Casper Dik              <...>
 Christian Franke        <franke@computer.org>
-Guilhem Frézou          <guilhem.frezou@catii.fr>
+Guilhem Frézou          <...>
+Thomas Gatterweh        <thomas_gatterweh@hotmail.com>
 Douglas Gilbert         <dgilbert@interlog.com>
 Guido Guenther          <agx@sigxcpu.org>
 Jordan Hargrave         <jordan_hargrave@dell.com>
-Joerg Hering            <hering.ruegen@gmx.de>
+Joerg Hering            <...>
 Geoff Keating           <geoffk@geoffk.org>
-Dr. David Kirkby        <drkirkby@ntlworld.com>
+Dr. David Kirkby        <...>
+Dan Lukes               <dan+smartmontools.changelog@obluda.cz>
 Kai Mäkisara            <kai.makisara@kolumbus.fi>
+Nidhi Malhotra          <nidhi.malhotra@pmcs.com>
 Eduard Martinescu       <martines@rochester.rr.com>
-Frédéric L. W. Meunier  <http://www.pervalidus.net/contact.html>
+Frédéric L. W. Meunier  <...>
 Alex Samorukov          <samm@os2.kiev.ua>
 Keiji Sawada            <card_captor@users.sourceforge.net>
 Manfred Schwarb         <manfred99@gmx.ch>
 Tomas Smetana           <tsmetana@redhat.com>
 David Snyder            <dasnyderx@yahoo.com>
 Sergey Svishchev        <svs@ropnet.ru>
-Phil Williams           <phil@subbacultcha.demon.co.uk>
+Tommy Vestermark        <tommy.vestermark@gmail.com>
+Roger Willcocks         <roger@filmlight.ltd.uk>
+Phil Williams           <...>
+Hank Wu                 <hank@areca.com.tw>
 Shengfeng Zhou          <linux@highpoint-tech.com>
 Richard Zybert          <richard.zybert@zybert.co.uk>
+
+The first smartmontools code was derived from the smartsuite package,
+written by Michael Cornwell and Andre Hedrick.
index b334db936d7277d4fcc8a331f215429e66bcbfb5..107304e35eb41bd3942984070ee26cfdefa4eed1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-$Id: ChangeLog 3990 2014-09-29 17:59:37Z samm2 $
+$Id: ChangeLog 4324 2016-05-31 20:45:50Z chrfranke $
+
+2016-05-31  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Intel 311/313 Series SSDs: mSATA, *H (HP) variant
+       - Intel 520 Series SSDs: *L (Lenovo) variant
+       - HGST Ultrastar He6/He8: attribute 22 "Helium_Level"
+       - Western Digital Red: 8TB, attribute 22 "Helium_Level"
+       - USB: WD My Passport Ultra (0x1058:0x0837) (ticket #696)
+       - USB: WD My Passport (0x1058:0x083a)
+       - USB: WD My Book (0x1058:0x111d)
+
+2016-05-10  Christian Franke  <franke@computer.org>
+
+       os_openbsd.cpp: Compile fix (regression from r4156).
+
+       os_netbsd.cpp: Apply patch-os__netbsd.cpp 1.3 (2016-05-08) from
+       pkgsrc.se/sysutils/smartmontools:
+       - Compile fix (regression from r4156).
+       - Use a raw disk device file on NetBSD.
+
+2016-05-07  Christian Franke  <franke@computer.org>
+
+       smartmontools 6.5
+
+2016-05-06  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Samsung SpinPoint P80 SD: *J/P variant
+       - Seagate Samsung SpinPoint M7E
+       - Hitachi/HGST Travelstar Z5K500: *E680 variant
+       - Hitachi Travelstar 7K500: HITACHI variant
+       - Hitachi Ultrastar 7K3000: *A641 variant
+       - HGST Ultrastar He8
+       - Toshiba 2.5" HDD MQ01ABD...: *V variant
+       - Seagate Desktop HDD.15: 5TB
+       - Seagate SV35.3
+       - Seagate SV35: *0001 variant
+       - Seagate DB35: SATA variant
+       - Western Digital Blue: 2-6TB, *Z variant
+       - Western Digital RE4-GP: *2003* variant
+       - Western Digital Re: Rename, 2-6TB
+       - Western Digital Caviar Green: SATA 6Gb/s variant
+       - Western Digital Caviar Black: *7501AAES*
+       - Western Digital Blue Mobile: 2TB
+       - Western Digital Elements / My Passport (USB, AF): *7500B*, 3TB
+
+2016-05-01  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Samsung based SSDs: 840 EVO 750GB (ticket #692), 850 EVO M.2,
+         SM843T *HCFV* variant
+       - USB: WD My Passport (0x1058:0x07ae) (ticket #686)
+       - USB: JMicron JMS561 (0x152d:0x9561)
+
+       nvmecmds.cpp: Enhance debug hex dump to sizeof Identify structs.
+       Do not dump trailing zero bytes.
+
+2016-04-27  Christian Franke  <franke@computer.org>
+
+       nvmeprint.cpp, nvmeprint.h, smartctl.cpp, smartctl.8.in:
+       Add NVMe support for 'smartctl -c'.  Print various drive and
+       namespace capabilites.  Remove related info from '-i' output.
+
+2016-04-24  Christian Franke  <franke@computer.org>
+
+       nvmeprint.cpp: Fix formatting of error log with unset LBA fields.
+
+       utility.cpp, utility.h: Skip leading blanks in format_char_array().
+       Some NVMe devices return right aligned text fields.
+
+       configure.ac, smartd.cpp: Remove include of netdb.h.
+       No longer needed since r3712.
+
+       smartd.cpp, smartd.conf.5.in: Remove support for '-m [sys]msgbox'.
+
+2016-04-23  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Innodisk 3ME SSDs
+       - Innodisk 3IE2/3MG2/3SE2-P SSDs: Rename, add 3SE2-P
+       - Innodisk 3IE3/3ME3 SSDs: Rename, add 3IE3
+       - USB: Buffalo MiniStation HD-PNFU3 (0x0411:0x0251) (ticket #683)
+       - USB: Renesas uPD720231A (0x045b:0x022a)
+       - USB: Toshiba Canvio (0x0480:0x0210, 0x0480:0xa20c)
+       - USB: Samsung G2 Portable (0x04e8:0x6032): 2nd entry with -d sat
+       - USB: Iomega LDHD-UPS (0x059b:0x0278)
+       - USB: Iomega LPHD-UP (0x059b:0x0470)
+       - USB: LaCie Desktop Hard Drive (0x059f:0x1016)
+       - USB: SanDisk SDCZ80 Flash Drive (0x0781:0x5588)
+       - USB: Seagate Backup Plus USB 3.0 (0x0bc2:0xab2[05])
+       - USB: WD My Passport Ultra (0x1058:0x0822)
+       - USB: WD Elements (0x1058:0x25a2)
+       - USB: JMicron JMS561 (0x152d:0x1561)
+       - USB: VIA VL711 (0x2109:0x0711): change to -d sat (ticket #594)
+       - USB: Sharkoon QuickPort XT USB 3.0 (0x357d:0x7788)
+
+2016-04-16  Christian Franke  <franke@computer.org>
+
+       smartctl.cpp: Allow NVMe debug messages during --scan.
+       Suppress "Device open changed type ..." message unless debug
+       mode is enabled.
+
+       atacmds.cpp: Remove duplicate POWER MODE error message.
+
+       smartd.cpp: Remove dead increment (cppcheck: unreadVariable).
+       Do not write localized decimal point to syslog().
+
+       configure.ac, Makefile.am: Add '--with-update-smart-drivedb=no'
+       option to disable drive database update script.  Useful if
+       maintainers do not want the script due to security concerns
+       and/or want to provide database updates as a separate package
+       (Debian bug 804299, FreeBSD Bugzilla 208398).
+       smartctl.8.in, smartd.8.in: Hide references to script if disabled.
+
+       nvmeprint.cpp: Add Power State and Namespace info to '-i' output.
+       Do not print unset or duplicate info unless debug mode is enabled.
+       nvmecmds.cpp, nvmecmds.h: Add Identify Namespace support.
+
+2016-04-15  Christian Franke  <franke@computer.org>
+
+       os_linux.cpp: Fix harmless bug in errno check of HPTIO_CTL ioctl()
+       calls.  Bug was introduced 10 years ago in r2237.
+
+2016-04-15  Yuriy M. Kaminskiy  <yumkam@gmail.com>
+
+       os_linux.cpp: Fix harmless bug in errno check of HDIO_DRIVE_TASK*
+       ioctl() calls.  Bug was introduced 12 years ago in r1609, the fix
+       in r4003 was incomplete.
+
+2016-04-14  Christian Franke  <franke@computer.org>
+
+       nvmeprint.cpp: Fix size factor of Data Units Read/Written counters.
+       os_win32.cpp: Fix device count in win_nvme_device::open().
+
+       Thanks to Oliver Bruchmann for bug reports and testing.
+
+2016-04-12  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsiprint.cpp: improve handling when no tape cartridge is
+       in the tape drive.
+
+2016-04-12  Alex Samorukov  <samm@os2.kiev.ua>
+
+       scsiprint.cpp, smartd.cpp: workaround for the buggy ST8000NM0075/E001,
+       request log page list with a fixed length (ticket #678).
+
+2016-04-11  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: add Samsung SM863 series, ticket #681
+
+2016-04-10  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Include also unknown and unsupported USB devices
+       in device scan result.  Move USB device handling to new function.
+       Add Windows Server 2016 to get_os_version_str().
+
+       AUTHORS: Add Thomas Gatterweh.
+       smartd.cpp: Check is_powered_down() also with '-n sleep'.
+
+2016-04-10  Thomas Gatterweh  <thomas_gatterweh@hotmail.com>
+
+       Prevent drive spin up by '-n standby' check on Windows (ticket #677):
+       dev_interface.cpp, dev_interface.h:
+       Add smart_device::is_powered_down().
+       os_win32.cpp: Add win_ata_device::is_powered_down().  Open device
+       without READ or WRITE access to prevent spin up.
+       smartctl.cpp, smartd.cpp: Add check for is_powered_down().
+
+2016-04-09  Christian Franke  <franke@computer.org>
+
+       configure.ac, os_win32.cpp, smartd.8.in: Add NVMe DEVICESCAN
+       support for Windows.
+
+       smartctl.8.in, smartd.8.in, smartd.conf.5.in: Document NVMe
+       support for Windows.
+
+       nvmecmds.cpp, os_win32.cpp: Use NSID=0 for Identify Controller
+       command.  This fixes NVMe access via Samsung driver on Windows.
+
+2016-04-08  Christian Franke  <franke@computer.org>
+
+       os_win.cpp: Add initial NVMe support for Windows.
+       Successfully tested with Intel driver.
+       Does not work with Samsung driver.
+
+       Thanks to Minkyu Kim for testing.
+
+2016-04-02  Christian Franke  <franke@computer.org>
+
+       Fix memory leak if get_sat_device() is called with unknown 'type':
+       scsiata.cpp: get_sat_device(): Delete 'scsidev' on error.
+       dev_interface.h: Update documentation of get_sat_device().
+       dev_interface.cpp: Fix use of get_sat_device().
+       (All other uses of get_sat_device() are already sane).
+
+       dev_interface.cpp, dev_interface.h: Add counter for objects derived
+       from 'smart_device'.
+       smartctl.cpp, smartd.cpp: Print error message if any objects remain
+       on exit.
+
+       os_linux.cpp: linux_megaraid_device: Remove unused member variable
+       'm_busnum' (clang++: -Wunused-private-field) and the related ctor
+       parameter.
+
+       os_linux.cpp: Fixes suggested by clang analyser:
+       Add or remove inconsistent nullptr checks.
+       Remove dead increments.
+
+2016-04-01  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsiprint.cpp: add missing commas in peripheral_dt_arr and
+        add number of elements (2**5) so that won't happen again.
+
+2016-03-31  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: 
+        - add samsung SAMSUNG-MZ7PC series (ticket #679)
+        - add KINGSTON SKC400S37128G (SSDNow KC400) (ticket #673, patch provided
+          by the reporter)
+        - add SanDisk SSD Plus series (ticket #674)
+        - add XceedIOPS SSD series (ticket #672)
+        - add Crucial BX200 SSD (ticket #643)
+
+2016-03-30  Christian Franke  <franke@computer.org>
+
+       Add support for multiple '-d TYPE' options for device scanning:
+       dev_interface.cpp, dev_interface.cpp: Add new version of
+       scan_smart_devices() which accepts list of types.
+       smartctl.cpp, smartd.cpp: Allow multiple '-d TYPE' options.
+       Use new scan_smart_devices().
+       smartctl.8.in, smartd.conf.5.in: Document it.
+
+       Makefile.am: Add man page support for --with-nvme-devicescan.
+       smartd.8.in: Document NVMe DEVICESCAN for Linux.
+
+       configure.ac: Use `...` instead of $(...) due to possible parsing
+       problems since r4260.  Remove workaround for related bash bug.
+
+2016-03-28  Christian Franke  <franke@computer.org>
+
+       Add NVMe DEVICESCAN support for Linux:
+       configure.ac: Add --with-nvme-devicescan option.
+       os_linux.cpp: Scan for '/dev/nvme[0-99]' if '-d nvme' is specified
+       or --with-nvme-devicescan is set.
+       smartctl.cpp: Add "NVMe" to --scan info.
+
+       smartctl.8.in, smartd.8.in, smartd.conf.5.in: Enable NVMe
+       sections also for FreeBSD.
+
+       configure.ac: Write configuration summary also to config.log.
+
+2016-03-28  Alex Samorukov  <samm@os2.kiev.ua>
+
+       os_freebsd.cpp: Add initial FreeBSD NVMe support (ticket #657)
+
+2016-03-27  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Support POWER MODE values introduced in ATA ACS-2
+       (ticket #184, smartctl only).
+
+2016-03-27  Thomas Gatterweh  <thomas_gatterweh@hotmail.com>
+
+       atacmds.cpp, smartd.cpp: Support POWER MODE values introduced
+       in ATA ACS-2 (ticket #184, smartd only).
+
+2016-03-26  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Rearrange code such that no forward declarations
+       are needed.
+
+       os_freebsd.cpp, os_netbsd.cpp, os_openbsd.cpp, os_solaris.cpp,
+       utility.cpp: Remove variable 'bytes'.  Only used for a memory
+       leak check which was removed in r2629 (2008-08-29).
+
+       os_solaris.cpp, utility.cpp, utility.h:
+       Remove CustomStrDup(), use strdup() instead.
+
+       dev_legacy.cpp, utility.cpp, utility.h:
+       Remove FreeNonZero(), use free() instead.
+
+       smartctl.cpp, smartd.cpp, utility.cpp, utility.h:
+       Remove split_report_arg(), use sscanf() instead.
+
+       Add basic NVMe support for smartd (-H -l error -W):
+       Makefile.am, os_win32/vc10/smartd.vcxproj: Add nvmecmds.cpp to smartd.
+       smartd.cpp: Add NVMeDeviceScan() and NVMeCheckDevice().
+       smartd.8.in, smartd.conf.5.in: Document NVMe support.
+
+       nvmeprint.cpp: Remove ary_to_str().
+       utility.cpp, utility.h: Add format_char_array().
+
+2016-03-24  Christian Franke  <franke@computer.org>
+
+       dev_interface.cpp: Add missing 'usbprolific' to help text.
+
+       nvmecmds.cpp, nvmeprint.cpp: Add support for '-q noserial'.
+
+       smartd.cpp: Remove outdated declaration of getdomainname().
+
+       utility.cpp: Add C++ language version to output of -V option.
+
+2016-03-20  Christian Franke  <franke@computer.org>
+
+       nvmecmds.cpp, nvmecmds.h, nvmeprint.cpp, nvmeprint.h, smartctl.cpp:
+       Add options '-l error[,NUM]' and '-l nvmelog,PAGE,SIZE' for NVMe
+       devices.
+       scsicmds.cpp: dStrHex(): Don't print trailing spaces.
+       smartctl.8.in: Document '-l error[,NUM]', '-l nvmelog,PAGE,SIZE'
+       and '-r nvmeioctl'.
+
+2016-03-18  Christian Franke  <franke@computer.org>
+
+       Add basic NVMe support for smartctl (-i -H -A) on Linux:
+       Makefile.am: Add new files.
+       dev_interface.cpp, dev_interface.h: Add class nvme_device.
+       linux_nvme_ioctl.h: New file imported from Linux kernel sources
+       (include/uapi/linux/nvme_ioctl.h 9d99a8d 2015-10-09).
+       nvmecmds.cpp, nvmecmds.h: New module with NVMe command wrapper
+       functions for smartctl and smartd.
+       nvmeprint.cpp, nvmeprint.h: New module with nvmePrintMain().
+       smartctl.cpp: Add nvmePrintMain() support.
+       os_linux.cpp: Add class linux_nvme_device.
+       os_win32/vc10/smart*.vcxproj*: Add new files.
+       smartctl.8.in: Document NVMe support.
+
+2016-03-14  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsiprint.cpp: work on LB provisioning corner cases; LBPRZ now
+       3 bits wide (in response to ticket #664)
+
+2016-03-14  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h:
+        - extend Apple SSD regexp (ticket #668)
+        - Add OCZ VeloDrive R (ticket #667)
+
+2016-03-12  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: Add Phison Driven SSDs:
+        - Kingston UV300 SSD series (ticket #663)
+        - Kingston SSDNow KC310/V310
+        - HyperX Savage
+
+2016-03-11  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: Add Kingston UV300 SSD series
+
+2016-03-06  Christian Franke  <franke@computer.org>
+
+       drivedb.h: Samsung based SSDs: Fix PM863 regexp, attribute IDs and
+       name length (regression from r4227).
+
+2016-03-03  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: Adata HD710 1TB USB3 (ticket #662)
+
+2016-02-29  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: PM863 Series (ticket #661)
+
+2016-02-28  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: OWC Aura Pro 480 GB (ticket #660)
+
+2016-02-26  Christian Franke  <franke@computer.org>
+
+       update-smart-drivedb.in: Use HTTPS for '-u sf' (ticket #659).
+       Improve file modification check.
+       update-smart-drivedb.8.in: Document changed URL.
+
+       os_win32/vc10/smartctl.vcxproj: Workaround for missing support of
+       '__func__' (included in C99 and C++11, but not in C++03).
+
+2016-02-15  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: APPLE SSD TS064E (ticket #655)
+
+2016-02-02  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsiprint.cpp: output unavailable rather than 255C for Drive
+        Trip temperature; skip background scan lpage for tape drives
+
+2016-02-02  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Crucial/Micron MX100/MX200/M5x0/M600 Client SSDs: 250GB MX200
+         (ticket #644), M500 mSATA and M.2
+       - OCZ Trion SSDs: Rename, add Trion 150
+       - Innodisk 3ME3 SSDs: SATADOM-SL 3IE3
+
+2016-01-25  Alex Samorukov  <samm@os2.kiev.ua>
+
+       os_darwin: add launchctl script for the smartd and remove depricated one.
+       "On current systems there is only one recommend way: launchd"
+
+2016-01-24  Alex Samorukov  <samm@os2.kiev.ua>
+
+       os_freebsd.cpp: fix possible reallocf with 0 bytes arg (ticket #640)
+       drivedb.h: add Corsair Extreme SSD (ticket #642)
+       os_darwin.cpp: fix error reporting if open fails
+
+2016-01-23  Alex Samorukov  <samm@os2.kiev.ua>
+
+       os_darwin.cpp: do not print bogus memory allocation error message if
+         there are no devices found
+
+2016-01-22  Christian Franke  <franke@computer.org>
+
+       Various fixes suggested by clang analyser (ticket #640):
+       dev_areca.cpp: Fix check of ARCMSR_READ_RQBUFFER result.
+       knowndrives.cpp: Add missing member initialization.
+       smartd.cpp: Fix crash on missing argument to '-s' directive.
+       Add missing variable initialization.  Remove redundant assignment.
+
+2016-01-21  Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h: Added ADATA SP550 SSD (ticket #638)
+       os_freebsd.cpp: Reduce variable scope where possible (cppcheck: variableScope)
+       os_openbsd/os_netbsd - removed never used warning code defines (cppcheck)
+
+2016-01-21  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp, smartd.cpp: Don't issue SCT commands if ATA Security
+       is locked (ticket #637).
+
+2016-01-19  Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h:
+        - Samsung PM871 SSD family (ticket #636)
+        - Fixed detection for Samsung SSD 850 EVO mSATA 120GB (ticket #635)
+        - Fixed Western Digital Caviar Black regexp, extended WD Black (ticket #631)
+
+2016-01-06  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - SandForce Driven SSDs: Extra warning entry for buggy Corsair Force LS
+         (ticket #628)
+       - Innodisk 3MG2-P SSDs: 1.8" variant
+       - Innodisk 3ME3 SSDs
+       - USB: Seagate Expansion Portable (0x0bc2:0x2322) (ticket #627)
+       - USB: Jess-Link (0x0dbf:0x9001)
+
+2016-01-01  Christian Franke  <franke@computer.org>
+
+       Happy New Year! Update copyright year in version info.
+
+2015-12-19  Christian Franke  <franke@computer.org>
+
+       Makefile.am: Fix path of 'smart-pkg-uninstall' (Regression from r4190).
+
+       update-smart-drivedb.8.in: Fix platform specific formatting.
+
+2015-12-18  Alex Samorukov <samm@os2.kiev.ua>
+
+       os_netbsd.cpp, os_openbsd.cpp: fix ioctl returtn value check
+       os_darwin.cpp: fix error handling
+       os_darwin: use /usr/local/ prefix to install on 10.11 (El Capitan)
+
+2015-12-16  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsiprint.cpp: stop tape drive looking for Solid State media
+       log page (ticket #314).
+
+2015-12-14  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsiprint.cpp: fix compiler warning for is_tape. Clean code around
+       handling of tape drives.
+
+2015-12-14  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Intel 320 Series SSDs: 1.8" microSATA
+       - Intel 53x and Pro 2500 Series SSDs: Rename, add 535 (ticket #625),
+         add Pro 2500
+       - Intel 730 and DC S35x0/3610/3700 Series SSDs: Rename,
+         add S3510/3610, 1.2TB, 1.6TB
+       - USB: LaCie (0x059f:0x106f) (ticket #624)
+       - USB: WD My Passport (0x1058:0x071a, 0x1058:0x0816)
+       - USB: Initio (0x13fd:0x1650)
+       - USB: Unknown (0xabcd:0x6103)
+
+       update-smart-drivedb.in: Add '-s SMARTCTL' option.
+       update-smart-drivedb.8.in: Document it.
+
+2015-12-07  Christian Franke  <franke@computer.org>
+
+       configure.ac: Append 'svn' to list of download tools.
+
+       update-smart-drivedb.in: Use HTTPS download by default.
+       Add options '-t TOOL', '-u LOCATION', '--cacert FILE',
+       '--capath DIR', '--insecure' and '--dryrun'.
+       Add 'svn' as new download tool.
+       Ignore differences in SVN Id string (re-added).
+       Remove usage of 'which' command.
+
+       update-smart-drivedb.8.in: Document the new options.
+
+2015-11-23  Christian Franke  <franke@computer.org>
+
+       atacmds.cpp: parse_attribute_def(): Init buffers before sscanf() call
+       (cppcheck-1.71: uninitvar).
+
+       scsiprint.cpp: Fix GLTSD bit set/cleared info messages (ticket #621).
+
+2015-11-22  Christian Franke  <franke@computer.org>
+
+       Makefile.am: Add NEWS file to svnversion.h target.
+
+       os_win32/installer.nsi: Select 64-bit version on 64-bit Windows.
+       Fix installation of runcmda.exe.  Update links.
+
+2015-11-15  Christian Franke  <franke@computer.org>
+
+       configure.ac: Check whether MinGW adds an application manifest.
+
+       Makefile.am: Add default manifest for MinGW builds.
+
+       os_win32/default.manifest: New default application manifest.
+       Remove external application manifests.
+
+       os_win32/installer.nsi: Use macros from 'LogicLib.nsh' where possible.
+       Add missing MessageBox /SD options.
+       Remove external application manifests.
+
+2015-11-07  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Micron M500DC/M510DC Enterprise SSDs: Rename, add M510DC
+       - SandForce Driven SSDs: Mushkin Chronos 7mm/MX/G2, Enhanced ECO2
+       - Innodisk 3MG2-P SSDs
+       - SiliconMotion based SSDs: Crucial BX100 (ticket #597)
+
+2015-10-31  Christian Franke  <franke@computer.org>
+
+       atacmds.cpp, atacmds.h, knowndrives.cpp, knowndrives.h:
+       Read default SMART attribute settings from drivedb.h (ticket #465).
+       Remove hard-coded attribute names and format settings.
+
+       drivedb.h: Uncomment default settings to create the "DEFAULT" entry.
+       Add ",HDD" or ",SSD" to HDD/SSD specific settings.
+
+       smartctl.cpp, smartd.cpp: Use new database initialization function.
+
+       Create branch RELEASE_6_4_DRIVEDB with last drivedb.h file
+       compatible with smartmontools 6.4.
+
+2015-10-22  Paul Grabinar  <pgrabinar@ocz.com>
+
+       drivedb.h:
+       - SandForce Driven SSDs: OCZ RevoDrive 350, Z-Drive 4500
+       - Indilinx Barefoot 3 based SSDs: Add attributes,
+         OCZ ARC 100, Saber 1000, Vector 180, Vertex 460A
+       - OCZ Intrepid 3000 SSDs: Intrepid 3700
+       - OCZ Trion
+
+2015-10-20  Christian Franke  <franke@computer.org>
+
+       Reduce variable scope where possible (cppcheck: variableScope).
+
+       Makefile.am: Remove *.s from files used to generate svnversion.h.
+
+2015-10-18  Alex Samorukov <samm@os2.kiev.ua>
+
+       fixes suggested by cppcheck:
+       Check realloc result to avoid memory leak (memleakOnRealloc)
+       Fix printf() signednsess (invalidPrintfArgType_sint)
+
+2015-10-17  Christian Franke  <franke@computer.org>
+
+       Various fixes suggested by cppcheck:
+       Close FILE pointer before reopening it (cppcheck: publicAllocationError).
+       Add missing member initializations to ctors (cppcheck: uninitMemberVar).
+       Remove redundant nullptr check (cppcheck: nullPointerRedundantCheck).
+       Remove redundant assignments (cppcheck: redundantAssignment).
+       Clarify calculation precedence (cppcheck: clarifyCalculation).
+       Use C++-style casts for pointer types (cppcheck: cstyleCast).
+       Remove duplicate on both sides of '||' (cppcheck: duplicateExpression).
+       Declare ctors with one argument as 'explicit'
+       (cppcheck: noExplicitConstructor).
+       Remove unread variables and assignments (cppcheck: unreadVariable).
+       Fix signedness of sscanf() formats strings
+       (cppcheck: invalidScanfArgType_int).
+
+2015-10-14  Christian Franke  <franke@computer.org>
+
+       configure.ac: Disable os_solaris_ata.o by default.
+       Add --with-solaris-sparc-ata option to enable.
+       Makefile.am: Exclude os_solaris_ata.s from source tarball
+       (Debian bug 729842).
+       os_solaris.cpp: Check for WITH_SOLARIS_SPARC_ATA instead of __sparc.
+
+2015-10-13  Christian Franke  <franke@computer.org>
+
+       Makefile.am: Fix error handling in various shell scripts.
+
+2015-10-13  Casper Dik  <...>
+
+       os_solaris.cpp: Detect SATA devices as SCSI devices.  This adds
+       support for auto detection of SATA devices behind SAT layer.
+       Set USCSI_SILENT flag to suppress /dev/console messages on command
+       error.
+
+2015-10-11  Christian Franke  <franke@computer.org>
+
+       drivedb.h: SiliconMotion based SSDs: Transcend SSD370S, SSD420,
+       update attribute 245 (ticket #595, ticket #602).
+
+2015-10-10  Christian Franke  <franke@computer.org>
+
+       Makefile.am: Use MKDIR_P to create directories
+       (available since automake 1.10).
+
+       os_win32.cpp: Detect USB ID if WMI reports type name "SCSI" instead
+       of "USBSTOR".
+       Detect USB ID also if drive letter is specified as device name.
+
+2015-10-04  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - USB: Genesys Logic (0x05e3:0x0735)
+       - USB: Addonics (0x0bf6:0x1001): unsupported (ticket #609)
+       - USB: Initio (0x13fd:0x3920)
+       - USB: JMicron JMS539 (0x152d:0x0539, 0x0100): Set from -d usbjmicron to
+         unsupported because some devices may require -d sat instead (ticket #552).
+       - USB: JMicron (0x152d:0x0565) (ticket #607)
+       - USB: VIA VL711 (0x2109:0x0711): unsupported (ticket #594)
+       - USB: Hitachi Touro Mobile (0x4971:0x1024)
+
+2015-09-25  Christian Franke  <franke@computer.org>
+
+       scsiata.cpp: Ignore SAT ATA PASS-THROUGH fixed format sense data if no
+       ATA status bit is set (ticket #612).
+
+2015-09-23 Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h: Innostor USB3.0 to SATAIII bridge (#611)
+
+2015-09-21 Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h: decode 188 attribute for the "Seagate Enterprise Capacity
+       3.5 HDD" drives family, (see #551).
+
+2015-09-04 Alex Samorukov <samm@os2.kiev.ua>
+
+       Makefile.am: integrate darwin dmg build process to the Makefile
+
+2015-09-03 Alex Samorukov <samm@os2.kiev.ua>
+
+       os_darwin: Initial import of the files required to build
+       OSX/smartmontools native package (see #555).
+
+2015-08-27  Alex Samorukov <samm@os2.kiev.ua>
+
+       Homepage URL updated from the sourceforge to smartmontools.org (r4120)
+
+2015-08-26  Alex Samorukov <samm@os2.kiev.ua>
+
+       os_darwin.cpp: Implement get_os_version_str() for the darwin.
+
+2015-08-17  Christian Franke  <franke@computer.org>
+
+       scsiata.cpp: Ignore bogus SCSI sense_key if ATA status in
+       SAT ATA Return Descriptor indicates success (ticket #548).
+
+2015-08-08  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Fix get_os_version_str() for Windows >= 8.1.
+       Add Windows 10 Final.
+
+2015-08-02  Christian Franke  <franke@computer.org>
+
+       configure.ac: Remove '--disable-drivedb',
+       '--enable-savestates', '--enable-attributelog'.
+       Print error message if used.
+
+2015-07-15  Christian Franke  <franke@computer.org>
+
+       autogen.sh: Drop support for automake 1.7 - 1.9.x.
+       Rework search for automake-VERSION.
+       configure.ac: Drop support for autoconf 2.5x.
+       Drop support for automake 1.7 - 1.9.x.
+       Remove --with-docdir option.
+
+2015-06-24  Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h:
+       - USB: SimpleTech 3.0 bridge (0x4971:0x8017), reported in #554
+
+2015-06-04  Christian Franke  <franke@computer.org>
+
+       smartmontools 6.4
+
+2015-06-03  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - InnoDisk iCF 9000 CompactFlash Cards
+       - SanDisk based SSDs: ReadyCache SSD
+       - Seagate Barracuda 7200.14 (AF): Apple OEM
+       - USB: Toshiba Canvio Basics (0x0480:0xa200)
+
+       ataprint.cpp: Read General Purpose Log Directory only if GPL
+       feature set is supported.  Improve support check of old logs
+       for older drives which return empty SMART Log Directory.
+
+2015-06-01  Christian Franke  <franke@computer.org>
+
+       Makefile.am, smartd.8.in: Hide initscript documentation if
+       initscriptdir is not configured.
+       smartd.conf.5.in: Remove outdated info about default shell.
+
+2015-05-30  Christian Franke  <franke@computer.org>
+
+       Fixes for aacraid patch:
+       aacraid.h: Fix _WIN32/_WIN64 checks.
+       os_win32.cpp: Clarify copyright info in GPL header.
+       Improve source code formatting.
+       Fix build on Cygwin.  Fix HKEY leak.
+       Fix member initialization order.
+       Fix info_name and dev_type parameter order.
+       Improve error handling.  Avoid unsafe sprintf().
+       Remove unused variables.  Add help text.
+       Use 0 as number of first aacraid controller as on Linux.
+
+       smartctl.8.in, smartd.conf.5.in: Update '-d aacraid' documentation.
+       AUTHORS: Add Nidhi Malhotra.
+
+2015-05-30  Nidhi Malhotra  <nidhi.malhotra@pmcs.com>
+
+       aacraid.h, os_win32.cpp:
+       Add aacraid support for Windows (ticket #496).
+
+2015-05-27  Christian Franke  <franke@computer.org>
+
+       INSTALL: Update ./configure description.
+       Remove info about old Linux kernel series.
+       Update Windows info.
+
+2015-05-19  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Print the Additional Product Identifier (OEM Id)
+       regardless of '-q noserial' option.
+
+       smartctl.8.in, smartd.conf.5.in: Clarify '-H' option and directive.
+
+2015-05-17  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - USB: ViPowER USB3.0 Storage (0x0350:0x0038)
+       - USB: Buffalo DriveStation HD-LBU2 (0x0411:0x01ea)
+       - USB: Toshiba Stor.E Basics; (0x0480:0xa00e)
+       - USB: Toshiba Canvio Desktop (0x0480:0xd011)
+       - USB: Samsung M3 Portable USB 3.0 (0x04e8:0x61b3)
+       - USB: Iomega (0x059b:0x0575)
+       - USB: Genesys Logic GL3310 (0x05e3:0x0731)
+       - USB: Freecom HD (0x07ab:0xfcd6)
+       - USB: Apricorn SATA Wire (0x0984:0x0040)
+       - USB: WD My Passport (0x1058:0x0830)
+       - USB: WD My Book: Merge entries, add 0x1058:0x0900, 0x1058:0x1104
+       - USB: Initio (0x13fd:0x3940)
+       - USB: Super Top (0x14cd:0x6116): change to -d sat
+       - USB: JMicron (0x152d:0x2590) (ticket #550)
+       - USB: ASMedia ASM1053/1153 (0x174c:0x1[01]53)
+       - USB: Verbatim Pocket Hard Drive (0x18a5:0x0237)
+       - USB: Verbatim External Hard Drive (0x18a5:0x0400)
+       - USB: VIA VL701 (0x2109:0x0701)
+       - USB: Unknown (0x2537:0x106[68])
+       - USB: Hitachi Touro Mobile (0x4971:0x1020)
+
+2015-05-16  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Samsung SpinPoint T166: 250GB
+       - Seagate Samsung SpinPoint M8 (AF): Rename, add Apple OEM
+       - Seagate Samsung SpinPoint M9T
+       - Seagate Samsung SpinPoint M9TU (USB)
+       - Hitachi/HGST Travelstar Z5K320
+       - HGST Travelstar Z5K1000
+       - HGST Deskstar NAS: 128MB cache variants
+       - HGST Ultrastar He6
+       - Toshiba 2.5" HDD MK..51GSY
+       - Toshiba 2.5" HDD MK..61GSY[N]: -v 9,minutes
+       - Toshiba 2.5" HDD MK..61GSYB
+       - Toshiba 2.5" HDD MK..75GSX
+       - Toshiba 2.5" HDD MQ01ABB...
+       - Toshiba 2.5" HDD MQ01ABC...
+       - Toshiba 2.5" HDD MQ01ABF...
+       - Toshiba 2.5" HDD MQ01UBB... (USB 3.0)
+       - Toshiba 3.5" MD04ACA... Enterprise HDD
+       - Toshiba 3.5" DT01ABA... Desktop HDD
+       - Seagate Laptop Thin HDD: 7200 rpm variants
+       - Seagate Constellation ES.2 (SATA 6Gb/s): HP OEM
+       - Seagate Constellation.2 (SATA): HP OEM
+       - Seagate Enterprise Capacity 3.5 HDD
+       - Seagate Archive HDD
+       - Western Digital AV-GP (AF): 500MB, EURX variants
+       - Western Digital Red Pro
+       - Western Digital Purple
+
+2015-05-14  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Crucial/Micron MX100/MX200/M5x0/M600 Client SSDs:
+         MX200 *00 sizes (ticket #545)
+       - Samsung based SSDs: PM851, SM841N, 850 EVO
+       - Marvell based SanDisk SSDs: Extreme Pro, Ultra II (ticket #544)
+       - Marvell based SanDisk SSDs: X110 mSATA, X300
+       - SanDisk based SSDs: pSSD (USB), U110
+       - USB: Samsung D3 Station 4TB (0x04e8:0x6125) (ticket #549)
+       - USB: Seagate Backup Plus USB 3.0 (0x0bc2:0xa003)
+       - USB: Seagate Backup Plus Desktop USB 3.0 5TB (0x0bc2:0xab31)
+       - USB: JMicron (0x152d:0x3569) (ticket #546)
+
+2015-05-10  Christian Franke  <franke@computer.org>
+
+       scsicmds.cpp, scsicmds.h: Remove unused functions
+       scsiReceiveDiagnostic() and scsiSmartIBMOfflineTest().
+       Found by cppcheck.
+
+2015-05-05  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Print ACS-3 device statistics DSN flags.
+       Print device statistics page numbers in hex.
+       smartctl.cpp: Allow hex argument for '-l devstat,PAGE'.
+
+2015-05-02  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Print Transport Type for PATA and PCIe.
+       Print diagnostic values if SATA version or speed is unknown.
+       smartctl.8.in, smartd.8.in: Add Volker Kuhlmann to AUTHORS section.
+
+2015-05-01  Christian Franke  <franke@computer.org>
+
+       ataidentify.cpp: ACS-3/4 updates.
+       ataprint.cpp: Add recent ACS-3/4 minor revisions.
+       Add ACS-4 log 0x0f.  Add ACS-4 device statistics values and
+       vendor specific statistics page.
+
+2015-04-28  Christian Franke  <franke@computer.org>
+
+       os_win32/installer.nsi: Fix possible loss of user PATH environment
+       variable with length greater than NSIS max string length.
+
+2015-04-26  Christian Franke  <franke@computer.org>
+
+       do_release: New Signing Key.
+       Makefile.am: Use make variables instead of autoconf variables
+       if possible.
+
+2015-04-24  Christian Franke  <franke@computer.org>
+
+       smartctl.8.in, smartd.8.in: Rework AUTHORS section.
+       INSTALL, Makefile.am, os_win32/installer.nsi:
+       Remove WARNINGS file.
+       WARNINGS: Remove this file.
+
+2015-04-23  Christian Franke  <franke@computer.org>
+
+       configure.ac: Add '--with-systemdenvfile=auto' option as new default.
+       Remove no longer needed ENABLE_CAPABILITIES conditional.
+       Makefile.am: Silence build of smartd.service file.
+       Integrate all ENABLE_* conditionals in MAN_FILTER script.
+
+2015-04-21  Christian Franke  <franke@computer.org>
+
+       configure.ac: Print 'deprecated' warning for '--disable-drivedb',
+       '--enable-savestates', '--enable-attributelog' options.
+       Add 'yes|no' support to corresponding '--with-...' options.
+
+2015-04-19  Christian Franke  <franke@computer.org>
+
+       AUTHORS: Remove smartmontools-support list address.
+       Remove defunct mail addresses.  Update smartsuite info.
+       Add recent contributors.
+       README: Refer to AUTHORS.
+
+2015-04-18  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Add SAT autodetection based on vendor string from
+       IOCTL_STORAGE_QUERY_PROPERTY.
+       smartd.cpp: If SMART ENABLE command failed, continue if SMART is
+       already enabled.
+
+2015-04-17  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Detect SAT layer of certain Intel AHCI drivers.
+
+2015-04-15  Christian Franke  <franke@computer.org>
+
+       smartctl.8.in, smartd.8.in, update-smart-drivedb.8.in:
+       Add REPORTING BUGS section.
+       smartctl.8.in, smartd.8.in:
+       Rename RETURN VALUE section to EXIT STATUS.
+       smartd.8.in: Remove no longer used exit status 9.
+
+2015-04-14  Christian Franke  <franke@computer.org>
+
+       autogen.sh: automake 1.15 works.
+       Print 'deprecated' warning if automake < 1.10 is used.
+
+2015-04-08  Christian Franke  <franke@computer.org>
+
+       configure.ac: Print 'deprecated' warning if autoconf 2.5x or
+       --with-docdir option is used.
+       Add comments to fix vim syntax coloring.
+       smartctl.8.in, smartd.8.in, smartd.conf.5.in:
+       Remove EXPERIMENTAL notes for features added before 6.3.
+
+2015-03-29  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Read only required log pages of Extended Comprehensive
+       Error log.  This adds support for logs with many pages (ticket #498).
+       atacmds.cpp, atacmds.h, smartd.cpp: Add 'page' parameter to function
+       ataReadExtErrorLog().
+
+2015-03-22  Christian Franke  <franke@computer.org>
+
+       os_linux.cpp, smartctl.8.in, smartd.8.in, smartd.conf, smartd.conf.5.in,
+       smartd.cpp: Remove old Linux IDE device names (/dev/hdX) in man pages
+       and help texts.
+
+2015-03-21  Christian Franke  <franke@computer.org>
+
+       smartd.8.in, smartd.cpp: Clarify smartd '--capabilities' option
+       (ticket #523).
+
+2015-03-20  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Crucial/Micron MX100/MX200/M5x0/M600 Client SSDs: Rename, add MX200
+       - Sandforce Driven SSDs: ATP Velocity MIV, Mushkin Chronos Enhanced
+       - Indilinx Barefoot 3 based SSDs: OCZ VERTEX 460, OCZ AMD Radeon R7
+       - Intel 530 Series SSDs: mSATA variant
+       - JMicron based SSDs: ADATA SP310
+       - Plextor M3/M5/M6 Series SSDs: Rename, add M6M, M6S
+
+2015-03-13  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsiata.cpp
+       - SCSI to ATA translation: from SAT-2 and later a SAT layer may
+         return ATA registers via fixed format sense data. Change to
+         additionally accept (partial) fixed format sense. In response
+         to ticket #296 and FreeBSD Bug 191717.
+
+2015-03-10  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsicmds.cpp, scsiprint.cpp
+       - SCSI: when READ DEFECT yields sense of "... defect list not found"
+         bypass the corresponding report quietly. (ticket #343)
+
+2015-02-08  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - USB: Buffalo Drivestation Duo (0x0411:0x01ce)
+       - USB: Toshiba Canvio Basics (0x0480:0x0201, 0xa00d)
+       - USB: Toshiba Stor.E Basics (0x0480:0xa00c)
+       - USB: Toshiba Canvio ALU (0x0480:0xa100)
+       - USB: Toshiba Canvio Desktop (0x0480:0xd000)
+       - USB: Samsung S2 Portable (0x04e8:0x1f0a)
+       - USB: Samsung S3 Portable (0x04e8:0x61c8)
+       - USB: LaCie Rugged Triple Interface (0x059f:0x100c)
+       - USB: Initio (0x13fd:0x3910)
+       - USB: ASMedia (0x174c:0x5516)
+       - USB: Innostor IS611 (0x1f75:0x0611)
+
+2015-02-02  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - USB: Seagate FreeAgent XTreme (0x0bc2:0x3101)
+       - USB: Seagate Expansion Portable (0x0bc2:0x232[01])
+       - USB: Seagate Expansion External (0x0bc2:0x3321)
+       - USB: Seagate FreeAgent GoFlex (0x0bc2:0x5070, 0x50a7, 0x6121)
+       - USB: Seagate Slim Portable Drive (0x0bc2:0xab00) (ticket #517)
+       - USB: Seagate Backup Plus Slim (0x0bc2:0xab21)
+       - USB: ADATA HD650 (0x125f:0xa35a)
+       - USB: JMicron JMS567 (0x152d:0x3562) (ticket #508)
+       - USB: Innostor IS621 (0x1f75:0x0621) (ticket #517)
+
+2015-01-25  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - JMicron based SSDs: Transcend SSD340 (ticket #348)
+       - SiliconMotion based SSDs: Transcend SSD370 (ticket #468)
+
+2015-01-24  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Add Windows 10 to get_os_version_str().
+
+2015-01-01  Christian Franke  <franke@computer.org>
+
+       Happy New Year! Update copyright year in version info.
+
+2014-12-13  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - USB: SanDisk SDCZ80 Flash Drive (0x0781:0x5580)
+       - USB: WD My Passport: Merge entries, add 0x1058:0x0810
+       - USB: WD Elements Desktop: Merge entries, add 0x1058:0x107c
+       - USB: WD Elements: Merge entries
+       - USB: JMicron JMS539 (0x152d:0x0539): 2.06 and 28.03 support SAT
+         (ticket #504)
+       - USB: JMicron JMS567 (0x152d:0x0567) (ticket #504)
+       - USB: JMicron JMS566 (0x152d:0x2566)
+       - USB: Hitachi Touro (0x4971:0x1014)
+
+2014-12-13  Christian Franke  <franke@computer.org>
+
+       utility.cpp, utility.h: Remove unused functions Calloc() and
+       CheckFree().
+
+2014-12-10  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Western Digital Blue: Rename, *AZLX variant
+       - Western Digital RE4: *FBYZ variant
+       - Western Digital Green: Rename, add 5TB, 6TB
+       - Western Digital AV: Rename, add 1TB, *BUCT variant
+       - Western Digital Red: Rename, add 750GB, 5TB, 6TB
+       - Western Digital Black Mobile
+
+2014-12-08  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Hitachi Travelstar 5K500.B: *SA00 variant
+       - Hitachi/HGST Travelstar Z5K500: Hitachi variant, Apple OEM
+       - HGST Travelstar 5K1000
+       - HGST Travelstar 5K1500
+       - Hitachi Travelstar 7K500: *A360 variant
+       - Hitachi CinemaStar 5K320
+       - Hitachi Deskstar 7K1000.C: SATA 6Gb/s variants
+       - HGST Deskstar NAS
+       - Hitachi/HGST Ultrastar 7K4000: Rename, add HGST
+       - HGST MegaScale 4000
+
+2014-12-07  Christian Franke  <franke@computer.org>
+
+       os_linux.cpp: Fix fd leak in megasas_dcmd_cmd().  Found by cppcheck.
+
+2014-12-07  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Crucial/Micron MX100/M500/M510/M550/M600 Client SSDs: M600 EE variant
+       - SandForce Driven SSDs: Kingston KC300 180GB
+       - Indilinx Barefoot 3 based SSDs: OCZ Vector 150
+       - JMicron based SSDs: Kingston SSDNow V+
+       - Plextor M3/M5 (Pro) Series SSDs: M5P
+       - Samsung based SSDs: 850 PRO, SM853T Series
+
+2014-12-06  Christian Franke  <franke@computer.org>
+
+       Makefile.am: Add quotes to parameters of INSTALL commands to allow path
+       names with spaces (this is supported since automake 1.8).
+       update-smart-drivedb.in: Add quotes to SMARTCTL variable (ticket #502).
+
+2014-11-30  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Crucial/Micron RealSSD m4/C400/P400: C400 *MAM variant
+       - Crucial/Micron MX100/M500/M510/M550/M600 Client SSDs: Rename,
+       add Crucial M500/M550, Micron M600
+       - SandForce Driven SSDs: ADATA SX900 (ticket #490), Mushkin Atlas
+       - Intel 311/313 Series SSDs: Rename, add 311 Series
+
+2014-11-30  Christian Franke  <franke@computer.org>
+
+       drivedb.h: USB: Prolific PL2571, PL2771, PL2775 (0x067b:0x2.7.)
+       (ticket #499).
+       smartctl.8.in, smartd.conf.5.in: Update '-d usbprolific' documentation.
+
+2014-11-29  Christian Franke  <franke@computer.org>
+
+       smartctl.8.in, smartd.8.in, smartd.conf.5.in, update-smart-drivedb.8.in:
+       Add package title to page header.  Move PACKAGE VERSION section to bottom
+       of page.  Remove SVN ID section header.
+
+2014-11-29  Tommy Vestermark  <tommy.vestermark@gmail.com>
+
+       scsiata.cpp: Add DATA OUT support for Prolific (ticket #482).
+       Add more ATA output registers.  SCT commands are now supported.
+
+2014-11-29  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Add strnicmp() compatibility macro for newer Cygwin
+       releases.
+
+2014-11-16  Tommy Vestermark  <tommy.vestermark@gmail.com>
+
+       drivedb.h: USB: Prolific PL2773 (0x067b:0x2773) (ticket #482).
+
+2014-11-16  Christian Franke  <franke@computer.org>
+
+       Create branches RELEASE_6_[1-3]_DRIVEDB with last drivedb.h file
+       compatible with smartmontools 6.[1-3].
+
+2014-11-10  Tommy Vestermark  <tommy.vestermark@gmail.com>
+
+       scsiata.cpp: Add class usbprolific_device to support Prolific PL2773
+       USB bridges (ticket #482).
+       smartctl.8.in, smartd.conf.5.in: Document '-d usbprolific'.
+
+2014-11-09  Roger Willcocks  <roger@filmlight.ltd.uk>
+
+       os_linux.cpp: linux_aacraid_device: Fix ioctl data count
+       if dxfer_len == 0.  Return scsi sense data.  Together these
+       allow the SMART STATUS command to operate correctly.
+       Improve SRB status checks.
+       linux_ata_device: Fix very old bug in the error handling
+       of HDIO_DRIVE_TASKFILE.
+
+2014-10-07  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: Added more attributes for SanDisk based SSDs based on SSD
+       Dashboard tool data (#463)
+
+2014-10-06  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Add form factors from ACS-4.
+       Add ACS-2 and ACS-3 minor versions.
+       Update SATA log names.  Add SATA 3.2.
+       Avoid crash on device statistics page 0xff if SMART READ LOG is used.
+       Print vendor specific bytes from SCT Status.
+       atacmds.cpp, atacmds.h, ataprint.cpp: Print SMART STATUS info
+       from SCT Status.
+
+2014-10-06  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: Exteneded regexp for SanDisk X300s (#463)
 
 2014-09-29  Alex Samorukov  <samm@os2.kiev.ua>
 
diff --git a/INSTALL b/INSTALL
index 3335f63efa9eecf8da9933009a8d3aa508374e53..011622ac358ae950d2902ed5392313767f3faee6 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,10 +1,10 @@
 Smartmontools installation instructions
 =======================================
 
-$Id: INSTALL 3935 2014-07-05 16:28:06Z chrfranke $
+$Id: INSTALL 4120 2015-08-27 16:12:21Z samm2 $
 
 Please also see the smartmontools home page:
-http://smartmontools.sourceforge.net/
+http://www.smartmontools.org/
 
 Table of contents:
 
@@ -32,49 +32,6 @@ Table of contents:
     kernel version greater than or equal to 2.2.14. So any recent
     Linux distribution should support smartmontools.
 
-    There are two parts of smartmontools that may require a patched or
-    nonstandard kernel:
-
-    (1) To get the ATA RETURN SMART STATUS command, the kernel needs
-    to support the HDIO_DRIVE_TASK ioctl().
-
-    (2) To run Selective Self-tests, the kernel needs to support the
-    HDIO_DRIVE_TASKFILE ioctl().
-
-    If your kernel does not support one or both of these ioctls, then
-    smartmontools will "mostly" work.  The things that don't work will
-    give you harmless warning messages.
-
-    For item (1) above, any 2.4 or 2.6 series kernel will provide
-    HDIO_DRIVE_TASK support.  Some 2.2.20 and later kernels also
-    provide this support IF they're properly patched and
-    configured. [Andre Hedrick's IDE patches may be found at
-    http://www.nic.funet.fi/pub/linux/kernel/people/hedrick/ide-2.2.20/
-    or are available from your local kernel.org mirror.  They are not
-    updated for 2.2.21 or later, and may contain a few bugs.].
-    If the configuration option CONFIG_IDE_TASK_IOCTL
-    exists in your 2.2.X kernel source code tree, then your 2.2.X
-    kernel will probably support this ioctl. [Note that this kernel
-    configuration option does NOT need to be enabled. Its presence
-    merely indicates that the required HDIO_DRIVE_TASK ioctl() is
-    supported.]
-
-    For item (2) above, your kernel must be configured with the kernel
-    configuration option CONFIG_IDE_TASKFILE_IO enabled.  This
-    configuration option is present in all 2.4 and 2.6 series
-    kernels. Some 2.2.20 and later kernels also provide this support
-    IF they're properly patched and configured as described above.
-
-    Please see FAQ section of the URL above for additional details.
-
-    If you are using 3ware controllers, for full functionality you
-    must either use version 1.02.00.037 or greater of the 3w-xxxx
-    driver, or patch earlier 3ware 3w-xxxx drivers.  See
-    http://smartmontools.sourceforge.net/3w-xxxx.txt
-    for the patch.  The version 1.02.00.037 3w-xxxx.c driver was
-    incorporated into kernel 2.4.23-bk2 on 3 December 2003 and into
-    kernel 2.6.0-test5-bk11 on 23 September 2003.
-
     B) FreeBSD
 
     For FreeBSD support, a 5-current kernel that includes ATAng is
@@ -106,9 +63,9 @@ Table of contents:
 
     F) Windows
 
-    The code was tested on Windows XP SP3, 2003, Vista, Windows 7 and
-    Windows 8 Release Preview.  Support von Windows 9x/ME and NT4 was removed
-    after smartmontools 5.43.
+    The code was tested on Windows XP SP3, 2003, Vista, Windows 7, 8, 8.1
+    and Windows 10 Release Preview.  Support von Windows 9x/ME and NT4 was
+    removed after smartmontools 5.43.
 
     ATA or SATA devices are supported if the device driver implements
     the SMART IOCTLs or IOCTL_IDE_PASS_THROUGH or IOCTL_ATA_PASS_THROUGH.
@@ -179,7 +136,7 @@ Table of contents:
     The code was tested on eComStation 1.1, but it should work on all versions
     of OS/2.
     Innotek LibC 0.5 runtime is required.
-    Currently only ATA disks are supported, SCSI support will be added.
+    Only ATA disks are supported.
 
 [2] Installing from SVN
 =======================
@@ -210,22 +167,31 @@ Table of contents:
     make install (you may need to be root to do this)
 
     As shown (with no options to ./configure) this defaults to the
-    following set of installation directories:
+    following set of installation directories and other settings:
+
     --prefix=/usr/local
-    --sbindir=/usr/local/sbin
-    --sysconfdir=/usr/local/etc
-    --mandir=/usr/local/share/man
-    --docdir=/usr/local/share/doc/smartmontools
-    --with-exampledir=/usr/local/share/doc/smartmontools/examplescripts
-    --with-drivedbdir=/usr/local/share/smartmontools
-    --with-initscriptdir=auto
-    --with-systemdsystemunitdir=auto
-    --enable-drivedb
-    --disable-attributelog
+    --exec-prefix='${prefix}'
+    --sbindir='${exec_prefix}/sbin'
+    --sysconfdir='${prefix}/etc'
+    --localstatedir='${prefix}/var'
+    --datarootdir='${prefix}/share'
+    --datadir='${datarootdir}'
+    --mandir='${datarootdir}/man'
+    --docdir='${datarootdir}/doc/smartmontools'
     --disable-sample
-    --disable-savestates
-    --with-libcap-ng=auto
+    --with-systemdsystemunitdir=auto
+    --with-systemdenvfile=auto
+    --with-initscriptdir=auto
+    --with-exampledir='${docdir}/examplescripts'
+    --with-drivedbdir='${datadir}/smartmontools'
+    --with-smartdscriptdir='${sysconfdir}'
+    --with-smartdplugindir='${smartdscriptdir}/smartd_warning.d'
+    --without-savestates
+    --without-attributelog
+    --with-os-deps='os_linux.o dev_areca.o' (platform specific)
     --without-selinux
+    --with-libcap-ng=auto
+    --with-working-snprintf
 
     These will usually not overwrite existing "distribution" installations on
     Linux Systems since the FHS reserves this area for use by the system
@@ -415,35 +381,17 @@ To compile the Windows release with MinGW gcc on MSYS, use:
   Instead of using "make install", copy the .exe files into
   some directory in the PATH.
 
-Cross-compile statically linked 32-bit version with MinGW-w64:
+Cross-compile statically linked 32-bit and 64-bit versions with MinGW-w64:
 
   ./configure --build=$(./config.guess) \
               --host=i686-w64-mingw32 \
               LDFLAGS=-static
 
-  Tested on Cygwin and Debian Linux.
-
-Cross-compile statically linked 64-bit version with MinGW-w64:
-
   ./configure --build=$(./config.guess) \
               --host=x86_64-w64-mingw32 \
               LDFLAGS=-static
 
-  Tested on Cygwin and Debian Linux with MinGW-w64 from
-  http://mingw-w64.sourceforge.net/.
-
-Cross-compile on Cygwin with old gcc-mingw 3.x:
-
-  ./configure --build=$(./config.guess) \
-              --host=i686-pc-mingw32 \
-               CC='gcc-3 -mno-cygwin' \
-              CXX='g++-3 -mno-cygwin' \
-              CXXFLAGS='-g -O2 -Wall -W -Wno-format'
-
-Cross-compile on Debian Linux with gcc-mingw32:
-
-  ./configure --build=$(./config.guess) \
-              --host=i586-mingw32msvc
+  Tested on Cygwin, Debian and Fedora.
 
 
 To build the Windows binary distribution, use:
@@ -472,7 +420,8 @@ To create a Windows installer, use:
   download location.
 
   It is also possible to (cross-)build the installer on Linux.
-  This was successfully tested on Debian with package "nsis".
+  This was successfully tested on Debian and Fedora with package
+  "nsis".
 
 To create a combined 32-/64-bit installer, use this in 32-bit build
 directory if 64-build directory is at ../build64:
@@ -488,14 +437,6 @@ To both create and run the (interactive) installer, use:
 
   The binary distribution includes all documentation files converted
   to DOS text file format and *.html and *.txt preformatted man pages.
-  The tools unix2dos.exe (package cygutils) and zip.exe (package zip
-  or a native Win32 release of Info-ZIP, http://www.info-zip.org) are
-  necessary but may be not installed by Cygwin's default settings.
-
-  The event message file tool syslogevt.exe (see smartd man page) is
-  included in the binary distribution if message compiler (windmc)
-  and resource compiler (windres) are available. This may be disabled
-  by passing 'WINDMC=no' to configure.
 
 To prepare os_win32 directory for MS Visual Studio C++ 2010 [Express],
 use the following on MSYS or Cygwin:
@@ -506,9 +447,9 @@ use the following on MSYS or Cygwin:
 
   The MSVC project files (os_win32/vc10/*) are included in SVN (but not
   in source tarball). The target config-vc10 from a Makefile configured
-  for MinGW creates os_win32/vc10/{config,svnversion}.h from
-  ./{config,svnversion}.h. The configure skript must be run outside
-  of the source directory to avoid inclusion of the original config.h.
+  for MinGW creates os_win32/vc10/{config.h,smart*.rc,svnversion.h}.
+  The configure skript must be run outside of the source directory to
+  avoid inclusion of the original config.h.
 
 
 [11] Guidelines for OS/2, eComStation
@@ -567,8 +508,8 @@ man smartd.conf
 man smartctl
 man smartd
 
-/usr/sbin/smartctl -s on -o on -S on /dev/hda (only root can do this)
-/usr/sbin/smartctl -a /dev/hda (only root can do this)
+/usr/sbin/smartctl -s on -o on -S on /dev/sda (only root can do this)
+/usr/sbin/smartctl -a /dev/sda (only root can do this)
 
 Note that the default location for the manual pages are
 /usr/share/man/man5 and /usr/share/man/man8.  If "man" doesn't find
@@ -587,22 +528,30 @@ The following files are installed if ./configure has no options:
 /usr/local/sbin/smartd                                  [Executable daemon]
 /usr/local/sbin/update-smart-drivedb                    [Drive database update script]
 /usr/local/etc/smartd.conf                              [Configuration file for smartd daemon]
-/usr/local/etc/rc.d/init.d/smartd                       [Init/Startup script for smartd]
+/usr/local/etc/smartd_warning.sh                        [Warning skript for smartd daemon]
 /usr/local/share/man/man5/smartd.conf.5                 [Manual page]
 /usr/local/share/man/man8/smartctl.8                    [Manual page]
 /usr/local/share/man/man8/smartd.8                      [Manual page]
+/usr/local/share/man/man8/update-smart-drivedb.8        [Manual page]
 /usr/local/share/doc/smartmontools/AUTHORS              [Information about the authors and developers]
 /usr/local/share/doc/smartmontools/ChangeLog            [A log of changes. Also see SVN]
 /usr/local/share/doc/smartmontools/COPYING              [GNU General Public License Version 2]
 /usr/local/share/doc/smartmontools/INSTALL              [Installation instructions: what you're reading!]
-/usr/local/share/doc/smartmontools/NEWS                 [Significant bugs discovered in old versions]
+/usr/local/share/doc/smartmontools/NEWS                 [Significant enhancements and fixes]
 /usr/local/share/doc/smartmontools/README               [Overview]
 /usr/local/share/doc/smartmontools/TODO                 [Things that need to be done/fixed]
-/usr/local/share/doc/smartmontools/WARNINGS             [Systems where lockups or other serious problems were reported]
 /usr/local/share/doc/smartmontools/smartd.conf          [Example configuration file for smartd]
 /usr/local/share/doc/smartmontools/examplescripts/      [Executable scripts for -M exec of smartd.conf (4 files)]
 /usr/local/share/smartmontools/drivedb.h                [Drive database]
 
+Due to checks done by '--with-systemdsystemunitdir=auto' and '--with-initscriptdir=auto',
+one of the following files may also be installed:
+
+/usr/local/lib/systemd/system/smartd.service            [Systemd service file for smartd]
+/usr/local/etc/rc.d/init.d/smartd                       [Init/Startup script for smartd]
+/usr/local/etc/init.d/smartd                            [Init/Startup script for smartd]
+/usr/local/etc/rc.d/smartd                              [Init/Startup script for smartd]
+
 If /usr/local/etc/smartd.conf exists and differs from the
 default then the default configuration file is installed as
 /usr/local/etc/smartd.conf.sample instead.
@@ -640,52 +589,23 @@ a documentation file doc/latex/refman.pdf:
 ===========================================================
 
 When you type:
-./configure [options]
-there are six particularly important variables that affect where the
-smartmontools software is installed.  The variables are listed here,
-with their default values in square brackets, and the quantities that
-they affect described following that.  This is a very wide table: please read
-it in a wide window.
-
-OPTIONS              DEFAULT                                      AFFECTS
--------              -------                                      -------
---prefix             /usr/local                                   Please see below
---sbindir            ${prefix}/sbin                               Directory for smartd/smartctl executables;
-                                                                  Contents of smartd/smartctl man pages
---docdir             ${prefix}/share/doc/smartmontools            Location of the documentation
-                                                                  (autoconf >= 2.60 only, see also --with-docdir below)
---mandir             ${prefix}/share/man                          Directory for smartctl/smartd/smartd.conf man pages
---sysconfdir         ${prefix}/etc                                Directory for smartd.conf;
-                                                                  Contents of smartd executable;
-                                                                  Contents of smartd/smartd.conf man pages;
-                                                                  Directory for rc.d/init.d/smartd init script
---with-initscriptdir           auto                               Location of init scripts
---with-systemdsystemunitdir    auto                               Location of systemd service files
---with-systemdenvfile ${sysconfdir}/sysconfig/smartmontools       Path of environment file for system service
---with-docdir         ${prefix}/share/doc/smartmontools           Location of the documentation
---with-exampledir     ${docdir}/examplescripts                    Location of example scripts
---enable-sample       --disable-sample                            Adds the string '.sample' to the names of the smartd.conf file and the smartd RC file
---with-os-deps        os_<guessed>.o                              OS dependent module(s)
---with-selinux        --without-selinux                           Enables SELinux support.  If smartmontools has to create the /dev/tw[ae] device
-                                                                  nodes for 3ware/AMCC controllers, this option ensures that the nodes are created
-                                                                  with correct SELinux file contexts.
---with-libcap-ng      --with-libcap-ng=auto                       Enables/disables libcap-ng support. If enabled and libcap-ng is
-                                                                  available, option --capabilities is added to smartd.
---disable-drivedb     --enable-drivedb                            Disables default drive database file '${drivedbdir}/drivedb.h'
---with-drivedbdir     ${prefix}/share/smartmontools               Directory for 'drivedb.h' (implies --enable-drivedb)
---with-smartdscriptdir    ${sysconfdir}                           Directory for 'smartd_warning.sh' script
---with-smartdplugindir    ${sysconfdir}/smartd_warning.d          Directory for 'smartd_warning.sh' plugin scripts
---enable-savestates   --disable-savestates                        Enables default smartd state files '${savestates}MODEL-SERIAL.ata.state'
---with-savestates     ${prefix}/var/lib/smartmontools/smartd.     Prefix for smartd state files (implies --enable-savestates)
---enable-attributelog --disable-attributelog                      Enables default smartd attribute log files
---with-attributelog   ${prefix}/var/lib/smartmontools/attrlog.    Prefix for smartd attribute log files (implies --enable-attributelog)
---with-working-snprintf    MinGW:guessed,others:yes               Function snprintf() handles output truncation as specified by C99
-
-Please note that in previous versions of smartmontools (<= 5.39) the
-default for --with-docdir was
-  ${prefix}/share/doc/smartmontools-VERSION
-This was changed to make it consistent with the default of the
-new --docdir option added in autoconf 2.60.
+./configure --help
+a description of available configure options is printed
+[with defaults in square brackets].  See also section [3] above.
+
+
+The following old configure options are deprecated and will be removed
+in a future release of smartmontools:
+
+Old option                 Replacement
+--with-docdir=DIR          --docdir=DIR (autoconf >= 2.60)
+--enable-drivedb           [no option needed]
+--disable-drivedb          --without-drivedbdir
+--enable-savestates        --with-savestates[=yes]
+--disable-savestates       [no option needed]
+--enable-attrbutelog       --with-attributelog[=yes]
+--disable-savestates       [no option needed]
+
 
 The defaults for --with-initscriptdir and --with-systemdsystemunitdir are
 guessed such that the following rules apply:
@@ -732,30 +652,6 @@ else
 --with-initscriptdir         [disabled]
 --with-systemdsystemunitdir  [disabled]
 
-This is useful for test installs in a harmless subdirectory somewhere.
-
-Here are the four possible cases for the four variables above:
-
-Case 1:
---prefix not set
---variable not set
-===> VARIABLE gets default value above
-
-Case 2:
---prefix set
---variable not set
-===> VARIABLE gets PREFIX/ prepended to default value above
-
-Case 3:
---prefix not set
---variable set
-===> VARIABLE gets value that is set
-
-Case 4:
---prefix is set
---variable is set
-===> PREFIX is IGNORED, VARIABLE gets value that is set
-
 
 Here are the differences with and without --enable-sample, assuming
 that initscript location is set and no other options specified 
index 440153711469fea1531749cd2fa44056231ffdf5..c496bc9e202cfe42295cacb8592f05fcb2d22d2e 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 #
-# $Id: Makefile.am 3957 2014-07-18 18:39:06Z chrfranke $
+# $Id: Makefile.am 4299 2016-04-16 19:45:57Z chrfranke $
 #
 
 @SET_MAKE@
@@ -42,7 +42,7 @@ sbin_PROGRAMS = \
         smartctl \
         smartd
 
-if ENABLE_DRIVEDB
+if ENABLE_UPDATE_SMART_DRIVEDB
 if OS_WIN32_MINGW
 else
 sbin_SCRIPTS = update-smart-drivedb
@@ -70,6 +70,10 @@ smartctl_SOURCES = \
         int64.h \
         knowndrives.cpp \
         knowndrives.h \
+        nvmecmds.cpp \
+        nvmecmds.h \
+        nvmeprint.cpp \
+        nvmeprint.h \
         scsicmds.cpp \
         scsicmds.h \
         scsiata.cpp \
@@ -78,8 +82,8 @@ smartctl_SOURCES = \
         utility.cpp \
         utility.h
 
-smartctl_LDADD = @os_deps@ @os_libs@
-smartctl_DEPENDENCIES = @os_deps@
+smartctl_LDADD = $(os_deps) $(os_libs)
+smartctl_DEPENDENCIES = $(os_deps)
 
 EXTRA_smartctl_SOURCES = \
         os_darwin.cpp \
@@ -96,7 +100,6 @@ EXTRA_smartctl_SOURCES = \
         os_qnxnto.h \
         os_solaris.cpp \
         os_solaris.h \
-        os_solaris_ata.s \
         os_win32.cpp \
         os_generic.cpp \
         os_generic.h \
@@ -107,12 +110,13 @@ EXTRA_smartctl_SOURCES = \
         dev_areca.cpp \
         dev_areca.h \
         dev_legacy.cpp \
+        linux_nvme_ioctl.h \
         megaraid.h
 
 if OS_WIN32_MINGW
 
-smartctl_LDADD        += smartctl_res.o
-smartctl_DEPENDENCIES += smartctl_res.o
+smartctl_LDADD        += smartctl_res.o $(os_win32_manifest)
+smartctl_DEPENDENCIES += smartctl_res.o $(os_win32_manifest)
 
 endif
 
@@ -132,14 +136,16 @@ smartd_SOURCES = \
         int64.h \
         knowndrives.cpp \
         knowndrives.h \
+        nvmecmds.cpp \
+        nvmecmds.h \
         scsicmds.cpp \
         scsicmds.h \
         scsiata.cpp \
         utility.cpp \
         utility.h
 
-smartd_LDADD = @os_deps@ @os_libs@ @CAPNG_LDADD@
-smartd_DEPENDENCIES = @os_deps@
+smartd_LDADD = $(os_deps) $(os_libs) $(CAPNG_LDADD)
+smartd_DEPENDENCIES = $(os_deps)
 
 EXTRA_smartd_SOURCES = \
         os_darwin.cpp \
@@ -156,7 +162,6 @@ EXTRA_smartd_SOURCES = \
         os_qnxnto.h \
         os_solaris.cpp \
         os_solaris.h \
-        os_solaris_ata.s \
         os_win32.cpp \
         os_generic.cpp \
         os_generic.h \
@@ -167,6 +172,8 @@ EXTRA_smartd_SOURCES = \
         dev_areca.cpp \
         dev_areca.h \
         dev_legacy.cpp \
+        linux_nvme_ioctl.h \
+        freebsd_nvme_ioctl.h \
         megaraid.h
 
 if OS_WIN32_MINGW
@@ -177,11 +184,14 @@ smartd_SOURCES += \
         os_win32/syslog_win32.cpp \
         os_win32/syslog.h
 
-smartd_LDADD        += smartd_res.o
-smartd_DEPENDENCIES += smartd_res.o
+smartd_LDADD        += smartd_res.o $(os_win32_manifest)
+smartd_DEPENDENCIES += smartd_res.o $(os_win32_manifest)
 
 endif
 
+# Exclude from source tarball
+nodist_EXTRA_smartctl_SOURCES = os_solaris_ata.s
+nodist_EXTRA_smartd_SOURCES   = os_solaris_ata.s
 
 if NEED_GETOPT_LONG
 
@@ -248,15 +258,15 @@ if OS_SOLARIS
 extra_MANS =      smartd.conf.4 \
                   smartctl.1m   \
                   smartd.1m
-if ENABLE_DRIVEDB
+if ENABLE_UPDATE_SMART_DRIVEDB
 extra_MANS += update-smart-drivedb.1m
 endif
 
 all-local: $(extra_MANS)
 install-man: $(extra_MANS)
        @$(NORMAL_INSTALL)
-       $(mkinstalldirs) $(DESTDIR)$(mandir)/man4
-       $(mkinstalldirs) $(DESTDIR)$(mandir)/man1m
+       $(MKDIR_P) '$(DESTDIR)$(mandir)/man4'
+       $(MKDIR_P) '$(DESTDIR)$(mandir)/man1m'
        for i in $(extra_MANS); do \
          if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
          else file=$$i; fi; \
@@ -264,8 +274,8 @@ install-man: $(extra_MANS)
          inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
          inst=`echo $$inst | sed -e 's/^.*\///'`; \
          inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-         echo " $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst"; \
-         $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst; \
+         echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(mandir)/man$$ext/$$inst'"; \
+         $(INSTALL_DATA) "$$file" "$(DESTDIR)$(mandir)/man$$ext/$$inst"; \
        done
 uninstall-man:
        @$(NORMAL_UNINSTALL)
@@ -276,8 +286,8 @@ uninstall-man:
          inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
          inst=`echo $$inst | sed -e 's/^.*\///'`; \
          inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-         echo " rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst"; \
-         rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst; \
+         echo " rm -f '$(DESTDIR)$(mandir)/man$$ext/$$inst'"; \
+         rm -f "$(DESTDIR)$(mandir)/man$$ext/$$inst"; \
        done
 else
 # For systems that adopts traditional manner
@@ -286,7 +296,7 @@ man_MANS =        smartd.conf.5 \
                   smartctl.8    \
                   smartd.8
 
-if ENABLE_DRIVEDB
+if ENABLE_UPDATE_SMART_DRIVEDB
 man_MANS += update-smart-drivedb.8
 endif
 
@@ -302,7 +312,6 @@ docs_DATA = \
         NEWS \
         README \
         TODO \
-        WARNINGS \
         smartd.conf
 
 examplesdir=$(exampledir)
@@ -320,7 +329,7 @@ sysconf_DATA = smartd.conf
 
 # If modified smartd.conf exists install smartd.conf.sample instead
 install-sysconfDATA: $(sysconf_DATA)
-       $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
+       $(MKDIR_P) '$(DESTDIR)$(sysconfdir)'
        @s="$(srcdir)/smartd.conf"; \
        f="$(DESTDIR)$(sysconfdir)/smartd.conf$(smartd_suffix)"; \
        if test -z "$(smartd_suffix)" && test -f "$$f"; then \
@@ -332,7 +341,7 @@ install-sysconfDATA: $(sysconf_DATA)
            f="$$f".sample; \
          fi; \
        fi; \
-       echo " $(INSTALL_DATA) $$s $$f"; \
+       echo " $(INSTALL_DATA) '$$s' '$$f'"; \
        $(INSTALL_DATA) "$$s" "$$f"
 
 # If smartd.conf.sample exists preserve smartd.conf
@@ -345,7 +354,7 @@ uninstall-sysconfDATA:
          echo "************************************************************"; \
          f="$$f".sample; \
        fi; \
-       echo " rm -f $$f"; \
+       echo " rm -f '$$f'"; \
        rm -f "$$f"
 
 smartdscript_SCRIPTS = smartd_warning.sh
@@ -363,13 +372,14 @@ EXTRA_DIST = \
         update-smart-drivedb.in \
         update-smart-drivedb.8.in \
         m4/pkg.m4 \
-        os_darwin/SMART.in \
-        os_darwin/StartupParameters.plist \
-        os_darwin/English_Localizable.strings \
+        os_darwin/com.smartmontools.smartd.plist.in \
+        os_darwin/pkg/PackageInfo.in \
+        os_darwin/pkg/Distribution.in \
+        os_darwin/pkg/installer/README.html \
+        os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall \
+        os_win32/default.manifest \
         os_win32/installer.nsi \
         os_win32/runcmd.c \
-        os_win32/runcmda.exe.manifest \
-        os_win32/runcmdu.exe.manifest \
         os_win32/smartctl_res.rc.in \
         os_win32/smartd_res.rc.in \
         os_win32/smartd_warning.cmd \
@@ -387,12 +397,15 @@ CLEANFILES = \
         smartd.8 \
         smartd.1m \
         smartd.8.html \
+        smartd.8.html.tmp \
         smartd.8.txt \
         smartctl.8 \
         smartctl.1m \
         smartctl.8.html \
+        smartctl.8.html.tmp \
         smartctl.8.txt \
         smartd.conf.5.html \
+        smartd.conf.5.html.tmp \
         smartd.conf.5.txt \
         smartd.initd \
         smartd.freebsd.initd \
@@ -417,7 +430,6 @@ MAINTAINERCLEANFILES = \
         $(srcdir)/depcomp \
         $(srcdir)/install-sh \
         $(srcdir)/missing \
-        $(srcdir)/mkinstalldirs \
         $(srcdir)/m4/pkg.m4
 
 utility.o: svnversion.h
@@ -425,7 +437,7 @@ utility.o: svnversion.h
 if IS_SVN_BUILD
 # Get version info from SVN
 svnversion.h: ChangeLog Makefile $(svn_deps)
-       @echo '  svn info | $$(VERSION_FROM_SVN_INFO) > $@'
+       @echo ' svn info | $$(VERSION_FROM_SVN_INFO) > $@'
        @echo '/* svnversion.h.  Generated by Makefile from svn info.  */' > $@
        @(cd $(srcdir) \
         && svnversion 2>/dev/null | sed -n 's,^\([0-9].*\),REV  "\1",p' \
@@ -435,10 +447,10 @@ svnversion.h: ChangeLog Makefile $(svn_deps)
 else
 
 # SVN not available, guess version info from Id strings
-svnversion.h: ChangeLog Makefile
-       @echo '  cat ChangeLog $$(SOURCES) | $$(VERSION_FROM_SVN_IDS) > $@'
+svnversion.h: ChangeLog Makefile NEWS
+       @echo ' cat ChangeLog NEWS $$(SOURCES) | $$(VERSION_FROM_SVN_IDS) > $@'
        @echo '/* svnversion.h.  Generated by Makefile from Id strings.  */' > $@
-       @(cd $(srcdir) && cat ChangeLog Makefile.am configure.ac smart*.in *.cpp *.h *.s) \
+       @(cd $(srcdir) && cat ChangeLog NEWS Makefile.am configure.ac smart*.in *.cpp *.h) \
        | sed -n 's,^.*\$$[I][d]: [^ ]* \([0-9][0-9]* [0-9][-0-9]* [0-9][:0-9]*\)[^:0-9][^$$]*\$$.*$$,\1,p' \
        | sort -n -r \
        | sed -n 'h;s,^\([^ ]*\) .*$$,REV  "\1",p;g;s,^[^ ]* \([^ ]*\) .*$$,DATE "\1",p;g;s,^[^ ]* [^ ]* \([^ ]*\)$$,TIME "\1",p;q' \
@@ -460,41 +472,27 @@ smartd_warning.sh: smartd_warning.sh.in config.status
 
 if INSTALL_INITSCRIPT
 if OS_DARWIN
-initd_DATA = SMART                            \
-       os_darwin/StartupParameters.plist     \
-       os_darwin/English_Localizable.strings
-
-initd_install_name = SMART
+initd_DATA = com.smartmontools.smartd.plist
 
 initd_DATA_install = install-initdDATA-darwin
 initd_DATA_uninstall = uninstall-initdDATA-darwin
 
-SMART : os_darwin/SMART.in
+com.smartmontools.smartd.plist : os_darwin/com.smartmontools.smartd.plist.in
        sed "s|/usr/sbin/|$(sbindir)/|" $< > $@
 
 install-initdDATA-darwin: $(initd_DATA)
-       $(mkinstalldirs) $(DESTDIR)$(initddir)
-       $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART
-       $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART/Resources
-       $(INSTALL_SCRIPT) $(top_builddir)/SMART $(DESTDIR)$(initddir)/SMART
-       $(INSTALL_DATA) $(srcdir)/os_darwin/StartupParameters.plist \
-           $(DESTDIR)$(initddir)/SMART/StartupParameters.plist
-       for i in English ; do \
-         RDIR=$(DESTDIR)$(initddir)/SMART/Resources/$${i}.lproj ; \
-         $(mkinstalldirs) $$RDIR ;\
-         $(INSTALL_DATA) $(srcdir)/os_darwin/$${i}_Localizable.strings \
-           $$RDIR/Localizable.strings ; \
-       done
+       $(MKDIR_P) '$(DESTDIR)$(initddir)'
+       $(INSTALL_DATA) $(top_builddir)/$(initd_DATA) $(DESTDIR)$(initddir)/$(initd_DATA)
 
 uninstall-initdDATA-darwin:
-       rm -rf $(DESTDIR)$(initddir)/$(initd_install_name)
+       rm -f $(DESTDIR)$(initddir)/$(initd_DATA)
 
 else
 
-initd_DATA = @initdfile@
+initd_DATA = $(initdfile)
 
-@initdfile@: $(srcdir)/@initdfile@.in Makefile
-       sed "s|/usr/local/sbin/|$(sbindir)/|g" $(srcdir)/@initdfile@.in > $@
+$(initdfile): $(srcdir)/$(initdfile).in Makefile
+       sed 's|/usr/local/sbin/|$(sbindir)/|g' $(srcdir)/$(initdfile).in > $@
 
 initd_install_name = smartd$(smartd_suffix)
 
@@ -502,12 +500,11 @@ initd_DATA_install = install-initdDATA-generic
 initd_DATA_uninstall = uninstall-initdDATA-generic
 
 install-initdDATA-generic: $(initd_DATA)
-       $(mkinstalldirs) $(DESTDIR)$(initddir)
-       $(INSTALL_SCRIPT) $(top_builddir)/@initdfile@ $(DESTDIR)$(initddir)/smartd$(smartd_suffix)
-
+       $(MKDIR_P) '$(DESTDIR)$(initddir)'
+       $(INSTALL_SCRIPT) '$(top_builddir)/$(initdfile)' '$(DESTDIR)$(initddir)/smartd$(smartd_suffix)'
 
 uninstall-initdDATA-generic:
-       rm -rf $(DESTDIR)$(initddir)/$(initd_install_name)
+       rm -f '$(DESTDIR)$(initddir)/$(initd_install_name)'
 endif
 else
 
@@ -527,23 +524,23 @@ systemdsystemunit_DATA = smartd.service
 endif
 
 smartd.service: smartd.service.in Makefile
-       cat $(srcdir)/smartd.service.in | \
+       @echo ' $$(SMARTD_SERVICE_FILTER) < $(srcdir)/smartd.service.in > $@'
+       @{ \
        sed 's|/usr/local/sbin/smartd|$(sbindir)/smartd|' | \
        if test -n '$(systemdenvfile)'; then \
          sed 's|/usr/local/etc/sysconfig/smartmontools|$(systemdenvfile)|'; \
        else \
          sed -e '/^EnvironmentFile=/d' -e 's| *\$$smartd[_a-z]* *||g'; \
-       fi > $@
+       fi; } < $(srcdir)/smartd.service.in > $@
 
 
 # Create empty directories if configured.
 # Default install rules no longer create empty directories since automake 1.11.
-# Uses $(mkinstalldirs) instead of $(MKDIR_P) to preserve support for automake 1.7 - 1.9.
 installdirs-local:
        @for d in '$(smartdplugindir)' '$(savestatesdir)' '$(attributelogdir)'; do \
          test -n "$$d" || continue; \
-         echo "$(mkinstalldirs) $(DESTDIR)$$d"; \
-         $(mkinstalldirs) "$(DESTDIR)$$d" || exit 1; \
+         echo " $(MKDIR_P) '$(DESTDIR)$$d'"; \
+         $(MKDIR_P) "$(DESTDIR)$$d" || exit 1; \
        done
 
 install-data-local: installdirs-local
@@ -551,37 +548,12 @@ install-data-local: installdirs-local
 #
 # Build man pages
 #
-if ENABLE_CAPABILITIES
-MAN_CAPABILITIES = cat
-else
-MAN_CAPABILITIES = sed '/^\.\\" %IF ENABLE_CAPABILITIES/,/^\.\\" %ENDIF ENABLE_CAPABILITIES/ s,^,.\\"\# ,'
-endif
-
-if ENABLE_DRIVEDB
-MAN_DRIVEDB = sed "s|/usr/local/share/smartmontools/drivedb\\.h|$(drivedbdir)/drivedb.h|g"
-else
-MAN_DRIVEDB = sed '/^\.\\" %IF ENABLE_DRIVEDB/,/^\.\\" %ENDIF ENABLE_DRIVEDB/ s,^,.\\"\# ,'
-endif
-
-if ENABLE_SAVESTATES
-MAN_SAVESTATES = sed "s|/usr/local/var/lib/smartmontools/smartd\\.|$(savestates)|g"
-else
-MAN_SAVESTATES = sed '/^\.\\" %IF ENABLE_SAVESTATES/,/^\.\\" %ENDIF ENABLE_SAVESTATES/ s,^,.\\"\# ,'
-endif
-
-if ENABLE_ATTRIBUTELOG
-MAN_ATTRIBUTELOG = sed "s|/usr/local/var/lib/smartmontools/attrlog\\.|$(attributelog)|g"
-else
-MAN_ATTRIBUTELOG = sed '/^\.\\" %IF ENABLE_ATTRIBUTELOG/,/^\.\\" %ENDIF ENABLE_ATTRIBUTELOG/ s,^,.\\"\# ,'
-endif
-
-MAN_FILTER = \
+MAN_FILTER = { \
     sed -e 's|CURRENT_SVN_VERSION|$(releaseversion)|g' \
         -e "s|CURRENT_SVN_DATE|`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|g" \
         -e "s|CURRENT_SVN_REV|`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h`|g" \
         -e 's|/usr/local/share/man/|$(mandir)/|g' \
         -e 's|/usr/local/sbin/|$(sbindir)/|g' \
-        -e 's|/usr/local/etc/rc\.d/init.d/|$(initddir)/|g' \
         -e 's|/usr/local/share/doc/smartmontools/examplescripts/|!exampledir!|g' \
         -e 's|/usr/local/share/doc/smartmontools/|$(docsdir)/|g' \
         -e 's|!exampledir!|$(exampledir)/|g' \
@@ -591,16 +563,47 @@ MAN_FILTER = \
         -e 's|\\fBmail\\fP|\\fB$(os_mailer)\\fP|g' \
         -e 's|\\'\''mail\\'\''|\\'\''$(os_mailer)\\'\''|g' \
         -e 's|/usr/bin/mail|/usr/bin/$(os_mailer)|g' \
-        -e 's|RELEASE_6_0_DRIVEDB|@DRIVEDB_BRANCH@|g' | \
+        -e 's|RELEASE_6_0_DRIVEDB|$(DRIVEDB_BRANCH)|g' | \
+    if test -n '$(drivedbdir)'; then \
+      sed 's|/usr/local/share/smartmontools/drivedb\.h|$(drivedbdir)/drivedb.h|g' ; \
+    else \
+      sed '/^\.\\" %IF ENABLE_DRIVEDB/,/^\.\\" %ENDIF ENABLE_DRIVEDB/ s,^,.\\"\# ,' ; \
+    fi | \
+    if test '$(with_update_smart_drivedb)' = 'yes'; then \
+      cat; \
+    else \
+      sed '/^\.\\" %IF ENABLE_UPDATE_SMART_DRIVEDB/,/^\.\\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB/ s,^,.\\"\# ,' ; \
+    fi | \
+    if test -n '$(initddir)'; then \
+      sed 's|/usr/local/etc/rc\.d/init\.d/|$(initddir)/|g' ; \
+    else \
+      sed '/^\.\\" %IF ENABLE_INITSCRIPT/,/^\.\\" %ENDIF ENABLE_INITSCRIPT/ s,^,.\\"\# ,' ; \
+    fi | \
+    if test -n '$(savestates)'; then \
+      sed 's|/usr/local/var/lib/smartmontools/smartd\.|$(savestates)|g' ; \
+    else \
+      sed '/^\.\\" %IF ENABLE_SAVESTATES/,/^\.\\" %ENDIF ENABLE_SAVESTATES/ s,^,.\\"\# ,' ; \
+    fi | \
+    if test -n '$(attributelog)'; then \
+      sed 's|/usr/local/var/lib/smartmontools/attrlog\.|$(attributelog)|g' ; \
+    else \
+      sed '/^\.\\" %IF ENABLE_ATTRIBUTELOG/,/^\.\\" %ENDIF ENABLE_ATTRIBUTELOG/ s,^,.\\"\# ,' ; \
+    fi | \
     if test -n '$(smartdplugindir)'; then \
       sed 's|/usr/local/etc/smartd_warning\.d|$(smartdplugindir)|g' ; \
     else \
       sed '/^\.\\" %IF ENABLE_SMARTDPLUGINDIR/,/^\.\\" %ENDIF ENABLE_SMARTDPLUGINDIR/ s,^,.\\"\# ,' ; \
     fi | \
-    $(MAN_ATTRIBUTELOG) | \
-    $(MAN_CAPABILITIES) | \
-    $(MAN_DRIVEDB) | \
-    $(MAN_SAVESTATES) | \
+    if test -n '$(CAPNG_LDADD)'; then \
+      cat; \
+    else \
+      sed '/^\.\\" %IF ENABLE_CAPABILITIES/,/^\.\\" %ENDIF ENABLE_CAPABILITIES/ s,^,.\\"\# ,' ; \
+    fi | \
+    if test '$(with_nvme_devicescan)' = 'yes'; then \
+      cat; \
+    else \
+      sed '/^\.\\" %IF ENABLE_NVME_DEVICESCAN/,/^\.\\" %ENDIF ENABLE_NVME_DEVICESCAN/ s,^,.\\"\# ,' ; \
+    fi | \
     if test -n '$(os_man_filter)'; then \
       sed -e 's,OS_MAN_FILTER,$(os_man_filter),g' \
           -e '/^\.\\" %IF NOT OS .*$(os_man_filter)/,/^.\\" %ENDIF NOT OS .*$(os_man_filter)/ s,^,.\\"\# ,' \
@@ -611,24 +614,24 @@ MAN_FILTER = \
           -e 's,^!!!*,,' ; \
     else \
       cat; \
-    fi
+    fi; }
 
 # Implicit rule 'smart%: smart%.in ...' does not work with BSD make
 smartctl.8: smartctl.8.in Makefile svnversion.h
-       @echo '  cat $(srcdir)/smartctl.8.in | $$(MAN_FILTER) > $@'
-       @cat $(srcdir)/smartctl.8.in | $(MAN_FILTER) > $@
+       @echo ' $$(MAN_FILTER) < $(srcdir)/smartctl.8.in > $@'
+       @$(MAN_FILTER) < $(srcdir)/smartctl.8.in > $@
 
 smartd.8: smartd.8.in Makefile svnversion.h
-       @echo '  cat $(srcdir)/smartd.8.in | $$(MAN_FILTER) > $@'
-       @cat $(srcdir)/smartd.8.in | $(MAN_FILTER) > $@
+       @echo ' $$(MAN_FILTER) < $(srcdir)/smartd.8.in > $@'
+       @$(MAN_FILTER) < $(srcdir)/smartd.8.in > $@
 
 smartd.conf.5: smartd.conf.5.in Makefile svnversion.h
-       @echo '  cat $(srcdir)/smartd.conf.5.in | $$(MAN_FILTER) > $@'
-       @cat $(srcdir)/smartd.conf.5.in | $(MAN_FILTER) > $@
+       @echo ' $$(MAN_FILTER) < $(srcdir)/smartd.conf.5.in > $@'
+       @$(MAN_FILTER) < $(srcdir)/smartd.conf.5.in > $@
 
 update-smart-drivedb.8: update-smart-drivedb.8.in Makefile svnversion.h
-       @echo '  cat $(srcdir)/update-smart-drivedb.8.in | $$(MAN_FILTER) > $@'
-       @cat $(srcdir)/update-smart-drivedb.8.in | $(MAN_FILTER) > $@
+       @echo ' $$(MAN_FILTER) < $(srcdir)/update-smart-drivedb.8.in > $@'
+       @$(MAN_FILTER) < $(srcdir)/update-smart-drivedb.8.in > $@
 
 # Build Solaris specific man pages
 SOLARIS_MAN_FILTER = \
@@ -639,20 +642,20 @@ SOLARIS_MAN_FILTER = \
         -e 's,/var/log/messages,/var/adm/messages,g'
 
 smartctl.1m: smartctl.8
-       @echo '  cat smartctl.8 | $$(SOLARIS_MAN_FILTER) > $@'
-       @cat smartctl.8 | $(SOLARIS_MAN_FILTER) > $@
+       @echo ' $$(SOLARIS_MAN_FILTER) < smartctl.8 > $@'
+       @$(SOLARIS_MAN_FILTER) < smartctl.8 > $@
 
 smartd.1m: smartd.8
-       @echo '  cat smartd.8 | $$(SOLARIS_MAN_FILTER) > $@'
-       @cat smartd.8 | $(SOLARIS_MAN_FILTER) > $@
+       @echo ' $$(SOLARIS_MAN_FILTER) < smartd.8 > $@'
+       @$(SOLARIS_MAN_FILTER) < smartd.8 > $@
 
 smartd.conf.4: smartd.conf.5
-       @echo '  cat smartd.conf.5 | $$(SOLARIS_MAN_FILTER) > $@'
-       @cat smartd.conf.5 | $(SOLARIS_MAN_FILTER) > $@
+       @echo ' $$(SOLARIS_MAN_FILTER) < smartd.conf.5 > $@'
+       @$(SOLARIS_MAN_FILTER) < smartd.conf.5 > $@
 
 update-smart-drivedb.1m: update-smart-drivedb.8
-       @echo '  cat update-smart-drivedb.8 | $$(SOLARIS_MAN_FILTER) > $@'
-       @cat update-smart-drivedb.8 | $(SOLARIS_MAN_FILTER) > $@
+       @echo ' $$(SOLARIS_MAN_FILTER) < update-smart-drivedb.8 > $@'
+       @$(SOLARIS_MAN_FILTER) < update-smart-drivedb.8 > $@
 
 
 # Commands to convert man pages into .html and .txt
@@ -674,21 +677,15 @@ htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html
 
 txtman:  smartctl.8.txt smartd.8.txt smartd.conf.5.txt
 
-if OS_WIN32_MINGW
-
 %.5.html: %.5
-       $(DOS2UNIX) < $< | $(MAN2HTML) | $(FIXHTML) > $@
+       $(MAN2HTML) $< > $@.tmp
+       @echo ' $$(FIXHTML) < $@.tmp > $@'
+       @$(FIXHTML) < $@.tmp > $@
 
 %.8.html: %.8
-       $(DOS2UNIX) < $< | $(MAN2HTML) | $(FIXHTML) > $@
-else
-
-%.5.html: %.5
-       $(MAN2HTML) $< | $(FIXHTML) > $@
-
-%.8.html: %.8
-       $(MAN2HTML) $< | $(FIXHTML) > $@
-endif
+       $(MAN2HTML) $< > $@.tmp
+       @echo ' $$(FIXHTML) < $@.tmp > $@'
+       @$(FIXHTML) < $@.tmp > $@
 
 %.5.txt: %.5
        $(MAN2TXT) $< > $@
@@ -716,22 +713,35 @@ smartd_res.o: smartd_res.rc syslogevt.rc
        $(WINDRES) -I. $< $@
 
 # Convert version for VERSIONINFO resource: 6.1 r3754 -> 6.1.0.3754, set Copyright year
-WIN_RC_FILTER = \
-    ( ver=`echo '$(PACKAGE_VERSION).0' | sed -n 's,^\([0-9]*\.[0-9]*\.[0-9]*\).*$$,\1,p'`; \
-      rev=`sed -n 's,^.*REV[^"]*"\([0-9]*\).*$$,\1,p' svnversion.h`; \
-      txtver="$${ver:-0.0.0}.$${rev:-0}"; binver=`echo "$$txtver" | sed 's|\.|,|g'`; \
-      yy=`sed -n 's,^.*DATE[^"]*"20\([0-9][0-9]\).*$$,\1,p' svnversion.h`; yy="$${yy:-XX}"; \
-      sed -e "s|@BINARY_VERSION@|$$binver|g" -e "s|@TEXT_VERSION@|$$txtver|g" -e "s|@YY@|$$yy|g"; )
+WIN_RC_FILTER = \
+    ver=`echo '$(PACKAGE_VERSION).0' | sed -n 's,^\([0-9]*\.[0-9]*\.[0-9]*\).*$$,\1,p'` && \
+    rev=`sed -n 's,^.*REV[^"]*"\([0-9]*\).*$$,\1,p' svnversion.h` && \
+    txtver="$${ver:-0.0.0}.$${rev:-0}" && binver=`echo "$$txtver" | sed 's|\.|,|g'` && \
+    yy=`sed -n 's,^.*DATE[^"]*"20\([0-9][0-9]\).*$$,\1,p' svnversion.h` && yy="$${yy:-XX}" && \
+    sed -e "s|@BINARY_VERSION@|$$binver|g" -e "s|@TEXT_VERSION@|$$txtver|g" -e "s|@YY@|$$yy|g"; }
 
 smartctl_res.rc: os_win32/smartctl_res.rc.in Makefile svnversion.h
-       cat $< | $(WIN_RC_FILTER) > $@
+       @echo ' $$(WIN_RC_FILTER) < $< > $@'
+       @$(WIN_RC_FILTER) < $< > $@
 
 smartd_res.rc: os_win32/smartd_res.rc.in Makefile svnversion.h
-       cat $< | $(WIN_RC_FILTER) > $@
+       @echo ' $$(WIN_RC_FILTER) < $< > $@'
+       @$(WIN_RC_FILTER) < $< > $@
 
 syslogevt.rc: os_win32/syslogevt.mc
        $(WINDMC) -b $<
 
+# Application manifests
+
+default.manifest.o: os_win32/default.manifest
+       echo '1 24 "$<"' | $(WINDRES) -J rc -o $@
+
+defadmin.manifest.o: defadmin.manifest
+       echo '1 24 "$<"' | $(WINDRES) -J rc -o $@
+
+defadmin.manifest: os_win32/default.manifest
+       sed 's,"asInvoker","requireAdministrator",' $< > $@
+
 # Definitions for Windows distribution
 
 if OS_WIN64
@@ -773,7 +783,6 @@ FILES_WIN32 = \
         $(docdir_win32)/NEWS.txt \
         $(docdir_win32)/README.txt \
         $(docdir_win32)/TODO.txt \
-        $(docdir_win32)/WARNINGS.txt \
         $(docdir_win32)/checksums$(win_bits).txt \
         $(docdir_win32)/smartd.conf \
         $(docdir_win32)/smartctl.8.html \
@@ -781,9 +790,7 @@ FILES_WIN32 = \
         $(docdir_win32)/smartd.8.html \
         $(docdir_win32)/smartd.8.txt \
         $(docdir_win32)/smartd.conf.5.html \
-        $(docdir_win32)/smartd.conf.5.txt \
-        $(exedir_win32)/runcmda.exe.manifest \
-        $(exedir_win32)/runcmdu.exe.manifest
+        $(docdir_win32)/smartd.conf.5.txt
 
 if ENABLE_DRIVEDB
 FILES_WIN32 += \
@@ -792,20 +799,18 @@ endif
 
 CLEANFILES += \
         $(FILES_WIN32) \
-        runcmdu.exe \
+        defadmin.manifest \
+        distdir.mkdir \
+        runcmda.exe runcmdu.exe \
         smartctl-nc.exe smartctl-nc.exe.tmp \
-        smartctl_res.rc smartctl_res.o \
-        smartd_res.rc smartd_res.o \
-        syslogevt.h syslogevt.o \
+        smartctl_res.rc smartd_res.rc \
+        syslogevt.h \
         syslogevt.rc syslogevt_*.bin \
-        wtssendmsg.exe \
         update-smart-drivedb.exe \
-        distdir.mkdir
+        wtssendmsg.exe
 
-# Textfile converter from package cygutils or tofrodos
-# Note: Only use without options to be compatible with both packages
+# Note: Only use without options to be compatible with all variants
 UNIX2DOS = unix2dos
-DOS2UNIX = dos2unix
 
 # Build Windows distribution
 
@@ -830,11 +835,11 @@ if OS_WIN32_NSIS
 # Note: Only option character '-' is also compatible with Linux version of makensis
 $(distinst_win32): os_win32/installer.nsi distdir.mkdir $(FILES_WIN32)
        test -z '$(builddir_win64)' || ( cd $(builddir_win64) && make distdir-win32 )
-       @date=`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`; \
-       rev=`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h`; \
-       verstr="$(PACKAGE_VERSION) $$date $$rev "$(BUILD_INFO); \
-       d64=; test -z '$(builddir_win64)' || d64='-DINPDIR64=$(builddir_win64)/$(PACKAGE)-$(VERSION).win64'; \
-       echo "'$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSTR='$$verstr' $<"; \
+       @date=`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h` && \
+       rev=`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h` && \
+       verstr="$(PACKAGE_VERSION) $$date $$rev "$(BUILD_INFO) && \
+       d64= && if [ -n '$(builddir_win64)' ]; then d64='-DINPDIR64=$(builddir_win64)/$(PACKAGE)-$(VERSION).win64'; fi && \
+       echo "'$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSTR='$$verstr' $<" && \
        '$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSTR="$$verstr" $<
        md5sum $@ > $@.md5
        sha1sum $@ > $@.sha1
@@ -867,18 +872,10 @@ $(exedir_win32)/%.exe: %.exe
 $(exedir_win32)/update-smart-drivedb.exe: update-smart-drivedb.exe
        cp -p $< $@
 
-# runcmd?.exe only differ by .exe.manifest files
-$(exedir_win32)/runcmda.exe: $(exedir_win32)/runcmdu.exe
-       cp -p $< $@
-
 $(exedir_win32)/%.h: $(srcdir)/%.h
        $(UNIX2DOS) < $< > $@
        touch -r $< $@
 
-$(exedir_win32)/%.exe.manifest: $(srcdir)/os_win32/%.exe.manifest
-       $(UNIX2DOS) < $< > $@
-       touch -r $< $@
-
 $(exedir_win32)/%.cmd: $(srcdir)/os_win32/%.cmd
        $(UNIX2DOS) < $< > $@
        touch -r $< $@
@@ -920,11 +917,17 @@ smartctl-nc.exe: smartctl.exe
        mv -f $@.tmp $@
 
 # Build runcmd?.exe and wtssendmsg.exe
-runcmdu.exe: os_win32/runcmd.c
-       $(CC) -Os -o $@ $<
+runcmd.o: os_win32/runcmd.c
+       $(CC) -c -Os $<
+
+runcmdu.exe: runcmd.o $(os_win32_manifest)
+       $(CC) -o $@ $^
 
-wtssendmsg.exe: os_win32/wtssendmsg.c
-       $(CC) -Os -o $@ $< -lwtsapi32
+runcmda.exe: runcmd.o defadmin.manifest.o
+       $(CC) -o $@ $^
+
+wtssendmsg.exe: os_win32/wtssendmsg.c $(os_win32_manifest)
+       $(CC) -Os -o $@ $< $(os_win32_manifest) -lwtsapi32
 
 # Build os_win32/vc10/{config.h,smart*.rc,svnversion.h} for MSVC10 from MinGW files
 
@@ -948,3 +951,44 @@ $(srcdir)/os_win32/vc10/smartd_res.rc: smartd_res.rc
        cp $< $@
 
 endif
+if OS_DARWIN
+# Definitions for OSX distribution
+distdir_darwin  = $(PACKAGE)-$(VERSION).darwin
+dmg_darwin =  $(PACKAGE)-$(VERSION).dmg
+pkg_darwin = $(PACKAGE)-$(VERSION).pkg
+
+# build darwin installer 
+$(pkg_darwin):
+       ${MAKE} install DESTDIR=$(distdir_darwin)/root
+       @cp $(srcdir)/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall $(distdir_darwin)/root$(sbindir)
+       @mkdir -p $(distdir_darwin)/pkg
+       @( cd $(distdir_darwin)/root && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > $(distdir_darwin)/pkg/Payload
+       PAYLOAD_FILES=`find $(distdir_darwin)/root | wc -l` &&\
+       PAYLOAD_SIZEKB=`du -BK  -s $(distdir_darwin)/root|${AWK} '{print $$1}'|tr -d 'K'` &&\
+       sed -e "s|@version@|$(VERSION)|" -e "s|@files@|$${PAYLOAD_FILES}|" \
+               -e "s|@size@|$${PAYLOAD_SIZEKB}|" $(srcdir)/os_darwin/pkg/PackageInfo.in \
+               > $(distdir_darwin)/pkg/PackageInfo &&\
+       sed -e "s|@version@|$(VERSION)|" -e "s|@files@|$${PAYLOAD_FILES}|" -e "s|@size@|$${PAYLOAD_SIZEKB}|" \
+               -e "s|@pkgname@|$(pkg_darwin)|" \
+               $(srcdir)/os_darwin/pkg/Distribution.in > $(distdir_darwin)/pkg/Distribution
+       @mkdir -p $(distdir_darwin)/pkg/Resources/English.lproj
+       @cp $(srcdir)/COPYING $(distdir_darwin)/pkg/Resources/English.lproj/license.txt
+       @mkbom -u 0 -g 80 $(distdir_darwin)/root $(distdir_darwin)/pkg/Bom
+       @mkdir -p $(distdir_darwin)/dmg
+       @( cd $(distdir_darwin)/pkg && xar --compression none -cf "../dmg/$(pkg_darwin)" * )
+
+# build darwon dmg image
+$(dmg_darwin):
+       @cp $(srcdir)/os_darwin/pkg/installer/README.html $(distdir_darwin)/dmg
+       @mkisofs -V 'smartmontools' -no-pad -r -apple -o $(distdir_darwin)/smartmontools-$(VERSION).iso \
+               -hfs-bless "$(distdir_darwin)/dmg/" "$(distdir_darwin)/dmg/"
+       @dmg dmg $(distdir_darwin)/smartmontools-$(VERSION).iso $(dmg_darwin)
+       md5sum $@ > $@.md5
+       sha1sum $@ > $@.sha1
+       sha256sum $@ > $@.sha256
+
+install-darwin: install-darwin-cleanup $(pkg_darwin) $(dmg_darwin)
+
+install-darwin-cleanup: 
+       @rm -rf $(distdir_darwin)
+endif
diff --git a/NEWS b/NEWS
index 03cd432a5c3d077b7730e4e434f1d2c04105ea8f..6db7d288586c2ab90b81653a14d8054163621a8e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,11 +1,82 @@
 smartmontools NEWS
 ------------------
-$Id: NEWS 3979 2014-08-15 11:09:41Z samm2 $
+$Id: NEWS 4318 2016-05-07 11:18:20Z chrfranke $
 
 The most up-to-date version of this file is:
 http://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/NEWS
 
-- darwin: '-S' command implemented, '-l devstat' should work now
+Date 2016-05-07
+Summary: smartmontools release 6.5
+-----------------------------------------------------------
+- Experimental support for NVMe devices on FreeBSD, Linux and Windows.
+- smartctl '-i', '-c', '-H' and '-l error': NVMe support.
+- smartctl '-l nvmelog': New option for NVMe.
+- smartd.conf '-H', '-l error' and '-W': NVMe support.
+- Optional NVMe device scanning support on Linux and Windows.
+- configure option '--with-nvme-devicescan' to include NVMe in
+  default device scanning result.
+- Device scanning now allows to specify multiple '-d TYPE' options.
+- ATA: Added new POWER MODE values introduced in ATA ACS-2.
+- ATA: SCT commands are no longer issued if ATA Security is locked.
+- SCSI: LB provisioning improvements.
+- SCSI: Fixed GLTSD bit set/cleared info messages.
+- SCSI: Solid State media log page is no longer checked for tapes.
+- SCSI: Improved handling when no tape cartridge in drive.
+- SCSI: Workaround for buggy Seagate firmware.
+- SAT: Improved heuristics to detect bogus sense data from SAT layer.
+- smartd: Fixed crash on missing argument to '-s' directive.
+- update-smart-drivedb: Now uses HTTPS for download by default.
+- update-smart-drivedb: New options to select URL and download tool.
+- update-smart-drivedb: New download tool 'svn'.
+- configure option '--without-update-smart-drivedb' to disable
+  update-smart-drivedb script.
+- configure options '--disable-drivedb', '--enable-savestates',
+  '--enable-attributelog' and '--with-docdir' are no longer supported.
+- autoconf < 2.60 and automake < 1.10 are no longer supported.
+- Drive database file now also includes the DEFAULT setting
+  for each attribute.
+- HDD, SSD and USB additions to drive database.
+- Darwin: New support files for package installer.
+  New makefile target 'install-darwin' builds DMG image.
+- Solaris: Auto detection of SATA devices behind SAT layer.
+- Solaris SPARC: Legacy ATA support disabled by default.
+  New configure option '--with-solaris-sparc-ata' enables it.
+  File os_solaris_ata.s is no longer included in source tarball.
+- Windows: Auto detection of USB devices specified by drive letter.
+- Windows: Device scanning does no longer ignore unknown USB devices.
+- Windows: Prevent drive spin up by '-n standby' check.
+- Windows: New application manifests indicating Win 10 support.
+- Windows smartd: '-m [sys]msgbox' is no longer supported.
+- Windows installer: Defaults to 64-bit version on 64-bit Windows.
+- Various code changes suggested by Clang Static Analyser and Cppcheck.
+
+Date 2015-06-04
+Summary: smartmontools release 6.4
+-----------------------------------------------------------
+- Device type '-d usbprolific' for Prolific PL2571/277x USB bridges.
+- SAT: Support for ATA registers returned in fixed format sense data.
+- smartctl '-i' and '--identify': ATA ACS-4 and SATA 3.2 enhancements.
+- smartctl '-l xerror': Support for logs with more than 255 pages.
+- smartctl '-l devstat': Prints ACS-3 DSN flags.
+- smartctl '-l devstat': Read via SMART command if GP log is not
+  available.
+- smartctl '-l scttempsts': Prints SCT SMART STATUS (ACS-4) and
+  vendor specific SCT bytes.
+- configure option '--with-systemdenvfile=auto' as new default.
+- configure options '--disable-drivedb', '--enable-savestates'
+  and '--enable-attributelog' are deprecated.
+- Corresponding '--with-*' options are enhanced accordingly.
+- Configure option '--with-docdir' is deprecated.
+- autoconf < 2.60 and automake < 1.10 are deprecated.
+  (all of the above still work but a warning is printed if used)
+- HDD, SSD and USB additions to drive database.
+- Linux: AACRAID fixes, SMART STATUS should work now.
+- Linux: '/dev/megaraid_sas_ioctl_node' fd leak fix.
+- Darwin: '-S' command implemented, '-l devstat' should work now.
+- Cygwin: Compile fix.
+- Windows: Device type '-d aacraid' for AACRAID controllers.
+- Windows: SAT autodetection based on IOCTL_STORAGE_QUERY_PROPERTY.
+- Windows installer: Fix possible loss of user PATH environment variable.
 
 Date 2014-07-26
 Summary: smartmontools release 6.3
diff --git a/README b/README
index 02c1b741437d06850dd15498d4159f008bdd6777..6d20c011416592a689c10aa87aa0b16481c28824 100644 (file)
--- a/README
+++ b/README
@@ -3,12 +3,12 @@ smartmontools - S.M.A.R.T. utility toolset for Darwin/Mac
 OSX, FreeBSD, Linux, NetBSD, OpenBSD, Solaris, and Windows.
 ==========================================================
 
-$Id: README 3949 2014-07-13 17:23:40Z chrfranke $
+$Id: README 4120 2015-08-27 16:12:21Z samm2 $
 
 == HOME ==
 The home for smartmontools is located at:
     
-    http://smartmontools.sourceforge.net/
+    http://www.smartmontools.org/
 
 Please see this web site for updates, documentation, and for submitting
 patches and bug reports.
@@ -19,8 +19,8 @@ You will find a mailing list for support and other questions at:
 
 
 == COPYING ==
-Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-Copyright (C) 2004-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+Copyright (C) 2002-9 Bruce Allen
+Copyright (C) 2004-15 Christian Franke
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the Free
@@ -32,10 +32,7 @@ example COPYING).  If not, see <http://www.gnu.org/licenses/>.
 
 
 == CREDITS ==
-This code was originally developed as a Senior Thesis by Michael Cornwell
-at the Concurrent Systems Laboratory (now part of the Storage Systems
-Research Center), Jack Baskin School of Engineering, University of
-California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+See AUTHORS file.
 
 
 == OVERVIEW ==
@@ -85,11 +82,8 @@ Refer to the "INSTALL" file for detailed installation instructions.
 == GETTING STARTED ==
 
 To examine SMART data from a disk, try:
-  smartctl -a /dev/hda
-for ATA disks, or
   smartctl -a /dev/sda
-for SCSI disks.  See the manual page 'man smartctl' for more
-information.
+See the manual page 'man smartctl' for more information.
 
 To start automatic monitoring of your disks with the smartd daemon,
 try:
diff --git a/WARNINGS b/WARNINGS
deleted file mode 100644 (file)
index 0cfce2a..0000000
--- a/WARNINGS
+++ /dev/null
@@ -1,4 +0,0 @@
-$Id: WARNINGS 3904 2014-06-15 14:21:15Z chrfranke $
-
-This file is no longer maintained, please see:
-http://www.smartmontools.org/wiki/Warnings
index de7527a7259d30988220234e9b410c0f186a4440..d62fbca0ca35dad41e56857b518144e1e95e3428 100644 (file)
--- a/aacraid.h
+++ b/aacraid.h
@@ -1,5 +1,6 @@
 /* aacraid.h
  * Copyright (C) 2014 Raghava Aditya <Raghava.Aditya@pmcs.com>
+ * Copyright (C) 2015 Nidhi Malhotra <Nidhi.Malhotra@pmcs.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -12,8 +13,8 @@
  */
 
 // Check windows
-#if _WIN32 || _WIN64
-#if _WIN64
+#if defined(_WIN32) || defined(_WIN64)
+#ifdef _WIN64
   #define ENVIRONMENT64
 #else
   #define ENVIRONMENT32
 #define METHOD_BUFFERED 0
 #define METHOD_NEITHER  3
 
+#if defined(_WIN32) || defined(_WIN64)
+#define FSAMPCTL_SCSI_BASE          IOCTL_SCSI_BASE
+#define ARCIOCTL_SEND_RAW_SRB       CTL_CODE(FSAMPCTL_SCSI_BASE, 2201, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define AACRAID_SAS_SIGNATURE       "ARCSAS"
+#define SRB_FLAGS_DATA_IN           0x00000040
+#define SRB_FLAGS_DATA_OUT          0x00000080
+#define SRB_FLAGS_NO_DATA_TRANSFER  0x00000000
+#else
 #define CTL_CODE(function, method) ((4<< 16) | ((function) << 2) | (method) )
 
 #define FSACTL_SEND_RAW_SRB  CTL_CODE(2067, METHOD_BUFFERED)
-
+#endif
 #define  SRB_FUNCTION_EXECUTE_SCSI 0X00
 
 #define  SRB_DataIn      0x0040
@@ -67,6 +76,45 @@ typedef struct {
   user_sgentry32   sg32[1];
   }  user_sgmap32;
 
+#if defined(_WIN32) || defined(_WIN64)
+typedef struct _SCSI_REQUEST_BLOCK {
+    USHORT Length;                  // offset 0
+    UCHAR Function;                 // offset 2
+    UCHAR SrbStatus;                // offset 3
+    UCHAR ScsiStatus;               // offset 4
+    UCHAR PathId;                   // offset 5
+    UCHAR TargetId;                 // offset 6
+    UCHAR Lun;                      // offset 7
+    UCHAR QueueTag;                 // offset 8
+    UCHAR QueueAction;              // offset 9
+    UCHAR CdbLength;                // offset a
+    UCHAR SenseInfoBufferLength;    // offset b
+    ULONG SrbFlags;                 // offset c
+    ULONG DataTransferLength;       // offset 10
+    ULONG TimeOutValue;             // offset 14
+    PVOID DataBuffer;               // offset 18
+    PVOID SenseInfoBuffer;          // offset 1c
+    struct _SCSI_REQUEST_BLOCK *NextSrb; // offset 20
+    PVOID OriginalRequest;          // offset 24
+    PVOID SrbExtension;             // offset 28
+    union {
+        ULONG InternalStatus;       // offset 2c
+        ULONG QueueSortKey;         // offset 2c
+    };
+
+#if defined(_WIN64)
+    //
+    // Force PVOID alignment of Cdb
+    //
+    ULONG Reserved;
+#endif
+
+    UCHAR Cdb[16];                  // offset 30
+} SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK;
+
+#define SCSI_REQUEST_BLOCK_SIZE sizeof(SCSI_REQUEST_BLOCK)
+
+#else
 typedef struct {
   uint32_t function;           //SRB_FUNCTION_EXECUTE_SCSI 0x00
   uint32_t channel;            //bus
@@ -103,3 +151,4 @@ typedef struct {
   uint32_t sense_data_size;
   uint8_t  sense_data[30];
   }  user_aac_reply;
+#endif
index 9ba90f65a35961e0248da4deeff876f3a51b9a7c..9a57c175c797e3cb2a7154e55f6f8f9e31214e07 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * atacmdnames.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-8 Philip Williams
  * Copyright (C) 2012 Christian Franke <smartmontools-support@lists.sourceforge.net>
@@ -20,7 +20,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 
-const char * atacmdnames_cpp_cvsid = "$Id: atacmdnames.cpp 3670 2012-10-31 22:00:50Z chrfranke $"
+const char * atacmdnames_cpp_cvsid = "$Id: atacmdnames.cpp 4120 2015-08-27 16:12:21Z samm2 $"
   ATACMDNAMES_H_CVSID;
 
 const char cmd_reserved[]        = "[RESERVED]";
index ac0e446a0bb0ab9591962719e163eaf96267e1c1..20f99eda2ace97efab4ae853d11c427ef14c89a2 100644 (file)
@@ -4,7 +4,7 @@
  * This module is based on the T13/1532D Volume 1 Revision 3 (ATA/ATAPI-7)
  * specification, which is available from http://www.t13.org/#FTP_site
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  * Address of support mailing list: smartmontools-support@lists.sourceforge.net
  *
  * Copyright (C) 2003-8 Philip Williams
@@ -23,7 +23,7 @@
 #ifndef ATACMDNAMES_H_
 #define ATACMDNAMES_H_
 
-#define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 /* Returns the name of the command (and possibly sub-command) with the given
    command code and feature register values. */
index c27e593d3c37fa96f700b167c3042b13fcb4411b..f1974916c8c08a7b2517198c60b0679a35e77c5b 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * atacmds.cpp
  * 
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
+ * Copyright (C) 2008-15 Christian Franke
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
  * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
  *
 #include "config.h"
 #include "int64.h"
 #include "atacmds.h"
+#include "knowndrives.h"  // get_default_attr_defs()
 #include "utility.h"
 #include "dev_ata_cmd_set.h" // for parsed_ata_device
 
-const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 3971 2014-07-23 18:57:55Z chrfranke $"
+const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 4301 2016-04-16 20:48:29Z chrfranke $"
                                  ATACMDS_H_CVSID;
 
 // Print ATA debug messages?
@@ -170,7 +171,9 @@ bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs,
   // Parse option
   int len = strlen(opt);
   int id = 0, n1 = -1, n2 = -1;
-  char fmtname[32+1], attrname[32+1];
+  char fmtname[32+1], attrname[32+1], hddssd[3+1];
+  attrname[0] = hddssd[0] = 0;
+
   if (opt[0] == 'N') {
     // "N,format[,name]"
     if (!(   sscanf(opt, "N,%32[^,]%n,%32[^,]%n", fmtname, &n1, attrname, &n2) >= 1
@@ -178,13 +181,16 @@ bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs,
       return false;
   }
   else {
-    // "id,format[+][,name]"
-    if (!(   sscanf(opt, "%d,%32[^,]%n,%32[^,]%n", &id, fmtname, &n1, attrname, &n2) >= 2
-          && 1 <= id && id <= 255 && (n1 == len || n2 == len)))
+    // "id,format[+][,name[,HDD|SSD]]"
+    int n3 = -1;
+    if (!(   sscanf(opt, "%d,%32[^,]%n,%32[^,]%n,%3[DHS]%n",
+                    &id, fmtname, &n1, attrname, &n2, hddssd, &n3) >= 2
+          && 1 <= id && id <= 255
+          && (    n1 == len || n2 == len
+                  // ",HDD|SSD" for DEFAULT settings only
+              || (n3 == len && priority == PRIOR_DEFAULT))))
       return false;
   }
-  if (n1 == len)
-    attrname[0] = 0;
 
   unsigned flags = 0;
   // For "-v 19[78],increasing" above
@@ -196,6 +202,9 @@ bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs,
   // Split "format[:byteorder]"
   char byteorder[8+1] = "";
   if (strchr(fmtname, ':')) {
+    if (priority == PRIOR_DEFAULT)
+      // TODO: Allow Byteorder in DEFAULT entry
+      return false;
     n1 = n2 = -1;
     if (!(   sscanf(fmtname, "%*[^:]%n:%8[012345rvwz]%n", &n1, byteorder, &n2) >= 1
           && n2 == (int)strlen(fmtname)))
@@ -220,6 +229,16 @@ bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs,
   if (!*byteorder && (format == RAWFMT_RAW64 || format == RAWFMT_HEX64))
     flags |= (ATTRFLAG_NO_NORMVAL|ATTRFLAG_NO_WORSTVAL);
 
+  // ",HDD|SSD" suffix for DEFAULT settings
+  if (hddssd[0]) {
+    if (!strcmp(hddssd, "HDD"))
+      flags |= ATTRFLAG_HDD_ONLY;
+    else if (!strcmp(hddssd, "SSD"))
+      flags |= ATTRFLAG_SSD_ONLY;
+    else
+      return false;
+  }
+
   if (!id) {
     // "N,format" -> set format for all entries
     for (i = 0; i < MAX_ATTRIBUTE_NUM; i++) {
@@ -617,7 +636,7 @@ int smartcommandhandler(ata_device * device, smart_command_set command, int sele
 
   // If requested, invalidate serial number before any printing is done
   if ((command == IDENTIFY || command == PIDENTIFY) && !retval && dont_print_serial_number)
-    invalidate_serno((ata_identify_device *)data);
+    invalidate_serno( reinterpret_cast<ata_identify_device *>(data) );
 
   // If reporting is enabled, say what output was produced by the command
   if (ata_debugmode) {
@@ -778,9 +797,6 @@ int ataCheckPowerMode(ata_device * device) {
   if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result)))
     return -1;
 
-  if (result!=0 && result!=0x80 && result!=0xff)
-    pout("ataCheckPowerMode(): ATA CHECK POWER MODE returned unknown Sector Count Register value %02x\n", result);
-
   return (int)result;
 }
 
@@ -831,9 +847,9 @@ int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_s
     packet = true;
   }
 
-  unsigned i;
   if (fix_swapped_id) {
     // Swap ID strings
+    unsigned i;
     for (i = 0; i < sizeof(buf->serial_no)-1; i += 2)
       swap2((char *)(buf->serial_no+i));
     for (i = 0; i < sizeof(buf->fw_rev)-1; i += 2)
@@ -851,14 +867,12 @@ int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_s
   // NetBSD kernel delivers IDENTIFY data in host byte order
   // TODO: Handle NetBSD case in os_netbsd.cpp
   if (isbigendian()){
-    
     // swap various capability words that are needed
+    unsigned i;
     for (i=0; i<33; i++)
       swap2((char *)(buf->words047_079+i));
-    
     for (i=80; i<=87; i++)
       swap2((char *)(rawshort+i));
-    
     for (i=0; i<168; i++)
       swap2((char *)(buf->words088_255+i));
   }
@@ -1466,9 +1480,9 @@ static void fix_exterrlog_lba(ata_smart_exterrlog * log, unsigned nsectors)
 
 // Read Extended Comprehensive Error Log
 bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log,
-                        unsigned nsectors, firmwarebug_defs firmwarebugs)
+                        unsigned page, unsigned nsectors, firmwarebug_defs firmwarebugs)
 {
-  if (!ataReadLogExt(device, 0x03, 0x00, 0, log, nsectors))
+  if (!ataReadLogExt(device, 0x03, 0x00, page, log, nsectors))
     return false;
 
   check_multi_sector_sum(log, nsectors, "SMART Extended Comprehensive Error Log Structure");
@@ -1859,35 +1873,12 @@ ata_attr_state ata_get_attr_state(const ata_smart_attribute & attr,
   return ATTRSTATE_OK;
 }
 
-// Get default raw value print format
-static ata_attr_raw_format get_default_raw_format(unsigned char id)
-{
-  switch (id) {
-  case 3:   // Spin-up time
-    return RAWFMT_RAW16_OPT_AVG16;
-
-  case 5:   // Reallocated sector count
-  case 196: // Reallocated event count
-    return RAWFMT_RAW16_OPT_RAW16;
-
-  case 9:   // Power on hours
-  case 240: // Head flying hours
-    return RAWFMT_RAW24_OPT_RAW8;
-
-  case 190: // Temperature
-  case 194:
-    return RAWFMT_TEMPMINMAX;
-
-  default:
-    return RAWFMT_RAW48;
-  }
-}
-
 // Get attribute raw value.
 uint64_t ata_get_attr_raw_value(const ata_smart_attribute & attr,
                                 const ata_vendor_attr_defs & defs)
 {
   const ata_vendor_attr_defs::entry & def = defs[attr.id];
+  // TODO: Allow Byteorder in DEFAULT entry
 
   // Use default byteorder if not specified
   const char * byteorder = def.byteorder;
@@ -1978,8 +1969,13 @@ std::string ata_format_attr_raw_value(const ata_smart_attribute & attr,
 
   // Get print format
   ata_attr_raw_format format = defs[attr.id].raw_format;
-  if (format == RAWFMT_DEFAULT)
-    format = get_default_raw_format(attr.id);
+  if (format == RAWFMT_DEFAULT) {
+     // Get format from DEFAULT entry
+     format = get_default_attr_defs()[attr.id].raw_format;
+     if (format == RAWFMT_DEFAULT)
+       // Unknown Attribute
+       format = RAWFMT_RAW48;
+  }
 
   // Print
   std::string s;
@@ -2158,212 +2154,23 @@ std::string ata_format_attr_raw_value(const ata_smart_attribute & attr,
   return s;
 }
 
-// Attribute names shouldn't be longer than 23 chars, otherwise they break the
-// output of smartctl.
-static const char * get_default_attr_name(unsigned char id, int rpm)
-{
-  bool hdd = (rpm > 1), ssd = (rpm == 1);
-
-  static const char Unknown_HDD_Attribute[] = "Unknown_HDD_Attribute";
-  static const char Unknown_SSD_Attribute[] = "Unknown_SSD_Attribute";
-
-  switch (id) {
-  case 1:
-    return "Raw_Read_Error_Rate";
-  case 2:
-    return "Throughput_Performance";
-  case 3:
-    return "Spin_Up_Time";
-  case 4:
-    return "Start_Stop_Count";
-  case 5:
-    return "Reallocated_Sector_Ct";
-  case 6:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Read_Channel_Margin";
-  case 7:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Seek_Error_Rate";
-  case 8:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Seek_Time_Performance";
-  case 9:
-    return "Power_On_Hours";
-  case 10:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Spin_Retry_Count";
-  case 11:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Calibration_Retry_Count";
-  case 12:
-    return "Power_Cycle_Count";
-  case 13:
-    return "Read_Soft_Error_Rate";
-  case 175:
-    if (hdd) return Unknown_HDD_Attribute;
-    return "Program_Fail_Count_Chip";
-  case 176:
-    if (hdd) return Unknown_HDD_Attribute;
-    return "Erase_Fail_Count_Chip";
-  case 177:
-    if (hdd) return Unknown_HDD_Attribute;
-    return "Wear_Leveling_Count";
-  case 178:
-    if (hdd) return Unknown_HDD_Attribute;
-    return "Used_Rsvd_Blk_Cnt_Chip";
-  case 179:
-    if (hdd) return Unknown_HDD_Attribute;
-    return "Used_Rsvd_Blk_Cnt_Tot";
-  case 180:
-    if (hdd) return Unknown_HDD_Attribute;
-    return "Unused_Rsvd_Blk_Cnt_Tot";
-  case 181:
-    return "Program_Fail_Cnt_Total";
-  case 182:
-    if (hdd) return Unknown_HDD_Attribute;
-    return "Erase_Fail_Count_Total";
-  case 183:
-    return "Runtime_Bad_Block";
-  case 184:
-    return "End-to-End_Error";
-  case 187:
-    return "Reported_Uncorrect";
-  case 188:
-    return "Command_Timeout";
-  case 189:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "High_Fly_Writes";
-  case 190:
-    // Western Digital uses this for temperature.
-    // It's identical to Attribute 194 except that it
-    // has a failure threshold set to correspond to the
-    // max allowed operating temperature of the drive, which 
-    // is typically 55C.  So if this attribute has failed
-    // in the past, it indicates that the drive temp exceeded
-    // 55C sometime in the past.
-    return "Airflow_Temperature_Cel";
-  case 191:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "G-Sense_Error_Rate";
-  case 192:
-    return "Power-Off_Retract_Count";
-  case 193:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Load_Cycle_Count";
-  case 194:
-    return "Temperature_Celsius";
-  case 195:
-    // Fujitsu: "ECC_On_The_Fly_Count";
-    return "Hardware_ECC_Recovered";
-  case 196:
-    return "Reallocated_Event_Count";
-  case 197:
-    return "Current_Pending_Sector";
-  case 198:
-    return "Offline_Uncorrectable";
-  case 199:
-    return "UDMA_CRC_Error_Count";
-  case 200:
-    if (ssd) return Unknown_SSD_Attribute;
-    // Western Digital
-    return "Multi_Zone_Error_Rate";
-  case 201:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Soft_Read_Error_Rate";
-  case 202:
-    if (ssd) return Unknown_SSD_Attribute;
-    // Fujitsu: "TA_Increase_Count"
-    return "Data_Address_Mark_Errs";
-  case 203:
-    // Fujitsu
-    return "Run_Out_Cancel";
-    // Maxtor: ECC Errors
-  case 204:
-    // Fujitsu: "Shock_Count_Write_Opern"
-    return "Soft_ECC_Correction";
-  case 205:
-    // Fujitsu: "Shock_Rate_Write_Opern"
-    return "Thermal_Asperity_Rate";
-  case 206:
-    // Fujitsu
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Flying_Height";
-  case 207:
-    // Maxtor
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Spin_High_Current";
-  case 208:
-    // Maxtor
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Spin_Buzz";
-  case 209:
-    // Maxtor
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Offline_Seek_Performnce";
-  case 220:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Disk_Shift";
-  case 221:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "G-Sense_Error_Rate";
-  case 222:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Loaded_Hours";
-  case 223:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Load_Retry_Count";
-  case 224:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Load_Friction";
-  case 225:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Load_Cycle_Count";
-  case 226:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Load-in_Time";
-  case 227:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Torq-amp_Count";
-  case 228:
-    return "Power-off_Retract_Count";
-  case 230:
-    // seen in IBM DTPA-353750
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Head_Amplitude";
-  case 231:
-    return "Temperature_Celsius";
-  case 232:
-    // seen in Intel X25-E SSD
-    return "Available_Reservd_Space";
-  case 233:
-    // seen in Intel X25-E SSD
-    if (hdd) return Unknown_HDD_Attribute;
-    return "Media_Wearout_Indicator";
-  case 240:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Head_Flying_Hours";
-  case 241:
-    return "Total_LBAs_Written";
-  case 242:
-    return "Total_LBAs_Read";
-  case 250:
-    return "Read_Error_Retry_Rate";
-  case 254:
-    if (ssd) return Unknown_SSD_Attribute;
-    return "Free_Fall_Sensor";
-  default:
-    return "Unknown_Attribute";
-  }
-}
-
 // Get attribute name
 std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs & defs,
                                     int rpm /* = 0 */)
 {
   if (!defs[id].name.empty())
     return defs[id].name;
-  else
-    return get_default_attr_name(id, rpm);
+  else {
+     const ata_vendor_attr_defs::entry & def = get_default_attr_defs()[id];
+     if (def.name.empty())
+       return "Unknown_Attribute";
+     else if ((def.flags & ATTRFLAG_HDD_ONLY) && rpm == 1)
+       return "Unknown_SSD_Attribute";
+     else if ((def.flags & ATTRFLAG_SSD_ONLY) && rpm > 1)
+       return "Unknown_HDD_Attribute";
+     else
+       return def.name;
+  }
 }
 
 // Find attribute index for attribute id, -1 if not found.
@@ -2428,6 +2235,7 @@ int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts)
     swapx(&sts->function_code);
     swapx(&sts->over_limit_count);
     swapx(&sts->under_limit_count);
+    swapx(&sts->smart_status);
   }
 
   // Check format version
index 21d809584a9522f788a21fc786157066b9939a3f..5768677acd2fec3b18b13572ab1a2a2ec14d08f0 100644 (file)
--- a/atacmds.h
+++ b/atacmds.h
@@ -1,10 +1,10 @@
 /*
  * atacmds.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
+ * Copyright (C) 2008-15 Christian Franke
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,7 @@
 #ifndef ATACMDS_H_
 #define ATACMDS_H_
 
-#define ATACMDS_H_CVSID "$Id: atacmds.h 3825 2013-07-06 21:38:25Z samm2 $"
+#define ATACMDS_H_CVSID "$Id: atacmds.h 4162 2015-10-31 16:36:16Z chrfranke $"
 
 #include "dev_interface.h" // ata_device
 
@@ -521,7 +521,7 @@ ASSERT_SIZEOF_STRUCT(ata_selective_self_test_log, 512);
 //   T13/1699-D Revision 3f (Working Draft), December 11, 2006.
 
 // SCT Status response (read with SMART_READ_LOG page 0xe0)
-// Table 60 of T13/1699-D Revision 3f 
+// Table 182 of T13/BSR INCITS 529 (ACS-4) Revision 04, August 25, 2014
 #pragma pack(1)
 struct ata_sct_status_response
 {
@@ -545,7 +545,8 @@ struct ata_sct_status_response
   unsigned char byte205;            // 205: reserved (T13/e06152r0-2: Average lifetime temperature)
   unsigned int over_limit_count;    // 206-209: # intervals since last reset with temperature > Max Op Limit
   unsigned int under_limit_count;   // 210-213: # intervals since last reset with temperature < Min Op Limit
-  unsigned char bytes214_479[266];  // 214-479: reserved
+  unsigned short smart_status;      // 214-215: LBA(32:8) of SMART RETURN STATUS (0, 0x2cf4, 0xc24f) (ACS-4)
+  unsigned char bytes216_479[479-216+1]; // 216-479: reserved
   unsigned char vendor_specific[32];// 480-511: vendor specific
 } ATTR_PACKED;
 #pragma pack()
@@ -683,9 +684,11 @@ enum ata_attr_raw_format
 
 // Attribute flags
 enum {
-  ATTRFLAG_INCREASING = 0x01,   // Value not reset (for reallocated/pending counts)
-  ATTRFLAG_NO_NORMVAL = 0x02,   // Normalized value not valid
-  ATTRFLAG_NO_WORSTVAL = 0x04   // Worst value not valid
+  ATTRFLAG_INCREASING  = 0x01, // Value not reset (for reallocated/pending counts)
+  ATTRFLAG_NO_NORMVAL  = 0x02, // Normalized value not valid
+  ATTRFLAG_NO_WORSTVAL = 0x04, // Worst value not valid
+  ATTRFLAG_HDD_ONLY    = 0x08, // DEFAULT setting for HDD only
+  ATTRFLAG_SSD_ONLY    = 0x10, // DEFAULT setting for SSD only
 };
 
 // Vendor attribute display defs for all attribute ids
@@ -785,7 +788,7 @@ bool ataReadSmartLog(ata_device * device, unsigned char logaddr,
                      void * data, unsigned nsectors);
 // Read SMART Extended Comprehensive Error Log
 bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log,
-                        unsigned nsectors, firmwarebug_defs firwarebugs);
+                        unsigned page, unsigned nsectors, firmwarebug_defs firmwarebugs);
 // Read SMART Extended Self-test Log
 bool ataReadExtSelfTestLog(ata_device * device, ata_smart_extselftestlog * log,
                            unsigned nsectors);
index fb47ae1aede6a36e81b9ded4f098ff61279c99f8..ec3c40edd7d3f5256442ce468748aec2d1598809 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * ataidentify.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2012-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2012-15 Christian Franke
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 #include "config.h"
 #include "ataidentify.h"
 
-const char * ataidentify_cpp_cvsid = "$Id: ataidentify.cpp 3851 2013-08-17 20:10:11Z chrfranke $"
+const char * ataidentify_cpp_cvsid = "$Id: ataidentify.cpp 4120 2015-08-27 16:12:21Z samm2 $"
   ATAIDENTIFY_H_CVSID;
 
 #include "int64.h"
@@ -33,7 +33,8 @@ const char * ataidentify_cpp_cvsid = "$Id: ataidentify.cpp 3851 2013-08-17 20:10
 // Tables 16 and 18 of T13/1532D (ATA/ATAPI-7) Volume 1 Revision 4b, April 21, 2004
 // Tables 29 and 39 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008
 // Tables 50 and 61 of T13/2015-D (ACS-2) Revision 7, June 22, 2011
-// Tables 51 and 56 of T13/2161-D (ACS-3) Revision 4g, February 27, 2013
+// Tables 45 and 50 of T13/2161-D (ACS-3) Revision 5, October 28, 2013
+// Table 44 of T13/BSR INCITS 529 (ACS-4) Revision 08, April 28, 2015 (ATAPI removed)
 
 const char * const identify_descriptions[] = {
   "  0 General configuration",
@@ -42,7 +43,7 @@ const char * const identify_descriptions[] = {
     ". 14 ATAPI: Must be set to 0",
     ". 13 ATAPI: Reserved",
     ". 12:8 ATAPI: Command set: 0x05 = CD/DVD",
-    ". 7 Removable media device",
+    ". 7 Removable media device [OBS-8]",
     ". 6 ATA: Not removable controller and/or device [OBS-6]",
     ". 5:3 ATA: Vendor specific [RET-3]",
     ". 6:5 ATAPI: DRQ after PACKET cmd: 0x0 = 3ms, 0x2 = 50us",
@@ -88,7 +89,8 @@ const char * const identify_descriptions[] = {
     ". 10 IORDY may be disabled",
     ". 9 LBA supported",
     ". 8 DMA supported",
-    ". 7:0 Vendor specific [RET-4]",
+    ". 7:2 Reserved", // ATA-3: Vendor specific, ATA-8: Retired
+    ". 1:0 Long Phy Sector Alignment Error reporting", // ACS-2
 
   " 50 Capabilities",
     ". 15:14 Must be set to 0x1",
@@ -116,7 +118,9 @@ const char * const identify_descriptions[] = {
     ". 14 OVERWRITE EXT supported",
     ". 13 CRYPTO SCRAMBLE EXT supported",
     ". 12 Sanitize Device feature set supported",
-    ". 11:9 Reserved",
+    ". 11 Cmds during sanitize as specified by this standard", // ACS-3
+    ". 10 SANITIZE ANTIFREEZE LOCK EXT supported", // ACS-3
+    ". 9 Reserved",
     ". 8 Bits 7:0 are valid",
     ". 7:0 Current sectors per DRQ on READ/WRITE MULTIPLE",
 
@@ -157,8 +161,8 @@ const char * const identify_descriptions[] = {
     ". 5 Trimmed LBA range(s) returning zeroed data supported",
     ". 4 Device encrypts all user data",
     ". 3 Extended number of user addressable sectors supported",
-    ". 2 All write cache is non-volatile",
-    ". 1:0 Reserved",
+    ". 2 All write cache is non-volatile", // ACS-3
+    ". 1:0 Zoned Capabilities", // ACS-4
 
   " 70 Reserved",
   " 71-74 ATA: Reserved for IDENTIFY PACKET DEVICE",
@@ -216,7 +220,8 @@ const char * const identify_descriptions[] = {
     ". 0 Must be set to 0",
 
   " 80 Major version number",
-    ". 15:11 Reserved",
+    ". 15:12 Reserved",
+    ". 11 ACS-4 supported",
     ". 10 ACS-3 supported",
     ". 9 ACS-2 supported",
     ". 8 ATA8-ACS supported",
@@ -257,7 +262,7 @@ const char * const identify_descriptions[] = {
     ". 10 48-bit Address feature set supported",
     ". 9 AAM feature set supported [OBS-ACS-2]",
     ". 8 SET MAX security extension supported [OBS-ACS-3]",
-    ". 7 Reserved for Address Offset Reserved Area Boot Method",
+    ". 7 Reserved for Addr Offset Resvd Area Boot [OBS-ACS-3]",
     ". 6 SET FEATURES subcommand required to spin-up",
     ". 5 PUIS feature set supported",
     ". 4 Removable Media Status Notification supported [OBS-8]",
@@ -278,7 +283,7 @@ const char * const identify_descriptions[] = {
     ". 5 GPL feature set supported",
     ". 4 Streaming feature set supported [OBS-ACS-3]",
     ". 3 Media Card Pass Through Command supported [OBS-ACS-2]",
-    ". 2 Media serial number supported", // ACS-3 r3 or later: Reserved
+    ". 2 Media serial number supported [RES-ACS-3]",
     ". 1 SMART self-test supported",
     ". 0 SMART error logging supported",
 
@@ -309,7 +314,7 @@ const char * const identify_descriptions[] = {
     ". 10 48-bit Address features set supported",
     ". 9 AAM feature set enabled [OBS-ACS-2]",
     ". 8 SET MAX security extension enabled [OBS-ACS-3]",
-    ". 7 Reserved for Address Offset Reserved Area Boot Method",
+    ". 7 Reserved for Addr Offset Resvd Area Boot [OBS-ACS-3]",
     ". 6 SET FEATURES subcommand required to spin-up",
     ". 5 PUIS feature set enabled",
     ". 4 Removable Media Status Notification enabled [OBS-8]",
@@ -353,9 +358,18 @@ const char * const identify_descriptions[] = {
     ". 0 Ultra DMA mode 0 supported",
 
   " 89 SECURITY ERASE UNIT time",
+    ". 15 Bits 14:8 of value are valid", // ACS-3
+    ". 14:0 SECURITY ERASE UNIT time value", // value*2 minutes
+
   " 90 ENHANCED SECURITY ERASE UNIT time",
+    ". 15 Bits 14:8 of value are valid", // ACS-3
+    ". 14:0 ENHANCED SECURITY ERASE UNIT time value", // value*2 minutes
+
   " 91 Current APM level",
-  " 92 Master password revision code",
+    ". 15:8 Reserved", // ACS-3
+    ". 7:0 Current APM level value",
+
+  " 92 Master Password Identifier", // ATA-7: Master Password Revision Code
 
   " 93 Hardware reset result (PATA)",
     ". 15:14 Must be set to 0x1",
@@ -393,7 +407,7 @@ const char * const identify_descriptions[] = {
 
   "107 Inter-seek delay for ISO 7779 acoustic testing",
   "108-111 64-bit World Wide Name",
-  "112-115 Reserved for a 128-bit World Wide Name",
+  "112-115 Reserved", // ATA-7: Reserved for world wide name extension to 128 bits
   "116 Reserved for TLC [OBS-ACS-3]",
   "117-118 Logical sector size (DWord)",
 
@@ -459,7 +473,7 @@ const char * const identify_descriptions[] = {
     ". 15:4 Reserved",
     ". 3:0 Nominal form factor: -, 5.25, 3.5, 2.5, 1.8, <1.8",
 
-  "169 Data Set Management support",
+  "169 DATA SET MANAGEMENT command support",
     ". 15:1 Reserved",
     ". 0 Trim bit in DATA SET MANAGEMENT command supported",
 
@@ -468,7 +482,7 @@ const char * const identify_descriptions[] = {
   "176-205 Current media serial number (String)",
 
   "206 SCT Command Transport",
-    ". 15:12 Vendor Specific",
+    ". 15:12 Vendor specific",
     ". 11:8 Reserved",
     ". 7 Reserved for Serial ATA",
     ". 6 Reserved",
@@ -479,7 +493,7 @@ const char * const identify_descriptions[] = {
     ". 1 SCT Read/Write Long supported [OBS-ACS-2]",
     ". 0 SCT Command Transport supported",
 
-  "207-208 Reserved for CE-ATA",
+  "207-208 Reserved", // ATA-8: Reserved for CE-ATA
 
   "209 Alignment of logical sectors",
     ". 15:14 Must be set to 0x1",
@@ -512,8 +526,9 @@ const char * const identify_descriptions[] = {
   "221 Reserved",
 
   "222 Transport major version number",
-    ". 15:12 Transport type: 0x0 = Parallel, 0x1 = Serial",
-    ". 11:7 Reserved    | Reserved",
+    ". 15:12 Transport: 0x0 = Parallel, 0x1 = Serial, 0xe = PCIe", // PCIe: ACS-4
+    ". 11:8 Reserved    | Reserved",
+    ". 7 Reserved    | SATA 3.2",
     ". 6 Reserved    | SATA 3.1",
     ". 5 Reserved    | SATA 3.0",
     ". 4 Reserved    | SATA 2.6",
index 6375388a45d847f3afdf42a4345080e351169803..76593b061d787e33b253d998bcc741e33755d35b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * ataidentify.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2012 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -18,7 +18,7 @@
 #ifndef ATAIDENTIFY_H
 #define ATAIDENTIFY_H
 
-#define ATAIDENTIFY_H_CVSID "$Id: ataidentify.h 3610 2012-09-20 21:27:19Z chrfranke $"
+#define ATAIDENTIFY_H_CVSID "$Id: ataidentify.h 4120 2015-08-27 16:12:21Z samm2 $"
 
 void ata_print_identify_data(const void * id, bool all_words, int bit_level);
 
index 7b6ed2cc6c8b6f47668b877a15a10ba8913d16cb..fe94593caa344a86c67956c44a95628c4c170285 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * ataprint.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
+ * Copyright (C) 2008-16 Christian Franke
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -40,7 +40,7 @@
 #include "utility.h"
 #include "knowndrives.h"
 
-const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 3982 2014-08-16 21:07:19Z samm2 $"
+const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 4256 2016-03-27 16:51:32Z chrfranke $"
                                   ATAPRINT_H_CVSID;
 
 
@@ -412,6 +412,24 @@ static inline std::string format_st_er_desc(
 }
 
 
+static const char * get_form_factor(unsigned short word168)
+{
+  // Table A.32 of T13/2161-D (ACS-3) Revision 4p, September 19, 2013
+  // Table 236 of T13/BSR INCITS 529 (ACS-4) Revision 04, August 25, 2014
+  switch (word168) {
+    case 0x1: return "5.25 inches";
+    case 0x2: return "3.5 inches";
+    case 0x3: return "2.5 inches";
+    case 0x4: return "1.8 inches";
+    case 0x5: return "< 1.8 inches";
+    case 0x6: return "mSATA"; // ACS-4
+    case 0x7: return "M.2"; // ACS-4
+    case 0x8: return "MicroSSD"; // ACS-4
+    case 0x9: return "CFast"; // ACS-4
+    default : return 0;
+  }
+}
+
 static int find_msb(unsigned short word)
 {
   for (int bit = 15; bit >= 0; bit--)
@@ -439,6 +457,10 @@ static const char * get_ata_major_version(const ata_identify_device * drive)
 
 static const char * get_ata_minor_version(const ata_identify_device * drive)
 {
+  // Table 10 of X3T13/2008D (ATA-3) Revision 7b, January 27, 1997
+  // Table 28 of T13/1410D (ATA/ATAPI-6) Revision 3b, February 26, 2002
+  // Table 31 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008
+  // Table 46 of T13/BSR INCITS 529 (ACS-4) Revision 08, April 28, 2015
   switch (drive->minor_rev_num) {
     case 0x0001: return "ATA-1 X3T9.2/781D prior to revision 4";
     case 0x0002: return "ATA-1 published, ANSI X3.221-1994";
@@ -489,21 +511,41 @@ static const char * get_ata_minor_version(const ata_identify_device * drive)
 
     case 0x0052: return "ATA8-ACS T13/1699-D revision 3b";
 
+    case 0x005e: return "ACS-4 T13/BSR INCITS 529 revision 5";
+
+    case 0x006d: return "ACS-3 T13/2161-D revision 5";
+
+    case 0x0082: return "ACS-2 published, ANSI INCITS 482-2012";
+
     case 0x0107: return "ATA8-ACS T13/1699-D revision 2d";
 
+    case 0x010a: return "ACS-3 published, ANSI INCITS 522-2014";
+
     case 0x0110: return "ACS-2 T13/2015-D revision 3";
 
+    case 0x011b: return "ACS-3 T13/2161-D revision 4";
+
     default:     return 0;
   }
 }
 
-static const char * get_sata_version(const ata_identify_device * drive)
+static const char * get_pata_version(unsigned short word222, char (& buf)[32])
+{
+  switch (word222 & 0x0fff) {
+    default: snprintf(buf, sizeof(buf),
+                       "Unknown (0x%03x)", word222 & 0x0fff); return buf;
+    case 0x001:
+    case 0x003: return "ATA8-APT";
+    case 0x002: return "ATA/ATAPI-7";
+  }
+}
+
+static const char * get_sata_version(unsigned short word222, char (& buf)[32])
 {
-  unsigned short word222 = drive->words088_255[222-88];
-  if ((word222 & 0xf000) != 0x1000)
-    return 0;
   switch (find_msb(word222 & 0x0fff)) {
-    default: return "SATA >3.1";
+    default: snprintf(buf, sizeof(buf),
+                    "SATA >3.2 (0x%03x)", word222 & 0x0fff); return buf;
+    case 7:  return "SATA 3.2";
     case 6:  return "SATA 3.1";
     case 5:  return "SATA 3.0";
     case 4:  return "SATA 2.6";
@@ -511,7 +553,7 @@ static const char * get_sata_version(const ata_identify_device * drive)
     case 2:  return "SATA II Ext";
     case 1:  return "SATA 1.0a";
     case 0:  return "ATA8-AST";
-    case -1: return 0;
+    case -1: return "Unknown";
   }
 }
 
@@ -520,7 +562,10 @@ static const char * get_sata_speed(int level)
   if (level <= 0)
     return 0;
   switch (level) {
-    default: return ">6.0 Gb/s";
+    default: return ">6.0 Gb/s (7)";
+    case 6:  return ">6.0 Gb/s (6)";
+    case 5:  return ">6.0 Gb/s (5)";
+    case 4:  return ">6.0 Gb/s (4)";
     case 3:  return "6.0 Gb/s";
     case 2:  return "3.0 Gb/s";
     case 1:  return "1.5 Gb/s";
@@ -559,6 +604,7 @@ static void print_drive_info(const ata_identify_device * drive,
     pout("Model Family:     %s\n", dbentry->modelfamily);
 
   pout("Device Model:     %s\n", infofound(model));
+
   if (!dont_print_serial_number) {
     pout("Serial Number:    %s\n", infofound(serial));
 
@@ -566,16 +612,17 @@ static void print_drive_info(const ata_identify_device * drive,
     int naa = ata_get_wwn(drive, oui, unique_id);
     if (naa >= 0)
       pout("LU WWN Device Id: %x %06x %09" PRIx64 "\n", naa, oui, unique_id);
+  }
 
-    // Additional Product Identifier (OEM Id) string in words 170-173
-    // (e08130r1, added in ACS-2 Revision 1, December 17, 2008)
-    if (0x2020 <= drive->words088_255[170-88] && drive->words088_255[170-88] <= 0x7e7e) {
-      char add[8+1];
-      ata_format_id_string(add, (const unsigned char *)(drive->words088_255+170-88), sizeof(add)-1);
-      if (add[0])
-        pout("Add. Product Id:  %s\n", add);
-    }
+  // Additional Product Identifier (OEM Id) string in words 170-173
+  // (e08130r1, added in ACS-2 Revision 1, December 17, 2008)
+  if (0x2020 <= drive->words088_255[170-88] && drive->words088_255[170-88] <= 0x7e7e) {
+    char add[8+1];
+    ata_format_id_string(add, (const unsigned char *)(drive->words088_255+170-88), sizeof(add)-1);
+    if (add[0])
+      pout("Add. Product Id:  %s\n", add);
   }
+
   pout("Firmware Version: %s\n", infofound(firmware));
 
   if (sizes.capacity) {
@@ -610,17 +657,9 @@ static void print_drive_info(const ata_identify_device * drive,
   // Print form factor if reported
   unsigned short word168 = drive->words088_255[168-88];
   if (word168) {
-    const char * inches;
-    switch (word168) {
-      case 0x1: inches = "5.25"; break;
-      case 0x2: inches = "3.5"; break;
-      case 0x3: inches = "2.5"; break;
-      case 0x4: inches = "1.8"; break;
-      case 0x5: inches = "< 1.8"; break;
-      default : inches = 0;
-    }
-    if (inches)
-      pout("Form Factor:      %s inches\n", inches);
+    const char * form_factor = get_form_factor(word168);
+    if (form_factor)
+      pout("Form Factor:      %s\n", form_factor);
     else
       pout("Form Factor:      Unknown (0x%04x)\n", word168);
   }
@@ -657,15 +696,31 @@ static void print_drive_info(const ata_identify_device * drive,
   }
   pout("ATA Version is:   %s\n", infofound(ataver.c_str()));
 
-  // If SATA drive print SATA version and speed
-  const char * sataver = get_sata_version(drive);
-  if (sataver) {
-    const char * maxspeed = get_sata_maxspeed(drive);
-    const char * curspeed = get_sata_curspeed(drive);
-    pout("SATA Version is:  %s%s%s%s%s%s\n", sataver,
-         (maxspeed ? ", " : ""), (maxspeed ? maxspeed : ""),
-         (curspeed ? " (current: " : ""), (curspeed ? curspeed : ""),
-         (curspeed ? ")" : ""));
+  // Print Transport specific version
+    // cppcheck-suppress variableScope
+  char buf[32] = "";
+  unsigned short word222 = drive->words088_255[222-88];
+  if (word222 != 0x0000 && word222 != 0xffff) switch (word222 >> 12) {
+    case 0x0: // PATA
+      pout("Transport Type:   Parallel, %s\n", get_pata_version(word222, buf));
+      break;
+    case 0x1: // SATA
+      {
+        const char * sataver = get_sata_version(word222, buf);
+        const char * maxspeed = get_sata_maxspeed(drive);
+        const char * curspeed = get_sata_curspeed(drive);
+        pout("SATA Version is:  %s%s%s%s%s%s\n", sataver,
+             (maxspeed ? ", " : ""), (maxspeed ? maxspeed : ""),
+             (curspeed ? " (current: " : ""), (curspeed ? curspeed : ""),
+             (curspeed ? ")" : ""));
+      }
+      break;
+    case 0xe: // PCIe (ACS-4)
+      pout("Transport Type:   PCIe (0x%03x)\n", word222 & 0x0fff);
+      break;
+    default:
+      pout("Transport Type:   Unknown (0x%04x)\n", word222);
+      break;
   }
 
   // print current time and date and timezone
@@ -1126,9 +1181,10 @@ static unsigned GetNumLogSectors(const ata_smart_log_directory * logdir, unsigne
 }
 
 // Get name of log.
-// Table A.2 of T13/2161-D (ACS-3) Revision 4, September 4, 2012
 static const char * GetLogName(unsigned logaddr)
 {
+    // Table 205 of T13/BSR INCITS 529 (ACS-4) Revision 08, April 28, 2015
+    // Table 112 of Serial ATA Revision 3.2, August 7, 2013
     switch (logaddr) {
       case 0x00: return "Log Directory";
       case 0x01: return "Summary SMART error log";
@@ -1145,12 +1201,14 @@ static const char * GetLogName(unsigned logaddr)
       case 0x0c: return "Pending Defects log"; // ACS-4
       case 0x0d: return "LPS Mis-alignment log"; // ACS-2
 
-      case 0x10: return "NCQ Command Error log";
-      case 0x11: return "SATA Phy Event Counters";
-      case 0x12: return "SATA NCQ Queue Management log"; // ACS-3
-      case 0x13: return "SATA NCQ Send and Receive log"; // ACS-3
-      case 0x14:
-      case 0x15:
+      case 0x0f: return "Sense Data for Successful NCQ Cmds log"; // ACS-4
+      case 0x10: return "SATA NCQ Queued Error log";
+      case 0x11: return "SATA Phy Event Counters log";
+    //case 0x12: return "SATA NCQ Queue Management log"; // SATA 3.0/3.1
+      case 0x12: return "SATA NCQ NON-DATA log"; // SATA 3.2
+      case 0x13: return "SATA NCQ Send and Receive log"; // SATA 3.1
+      case 0x14: return "SATA Hybrid Information log"; // SATA 3.2
+      case 0x15: return "SATA Rebuild Assist log"; // SATA 3.2
       case 0x16:
       case 0x17: return "Reserved for Serial ATA";
 
@@ -1182,13 +1240,14 @@ static const char * get_log_rw(unsigned logaddr)
 {
    if (   (                   logaddr <= 0x08)
        || (0x0c <= logaddr && logaddr <= 0x0d)
-       || (0x10 <= logaddr && logaddr <= 0x13)
+       || (0x0f <= logaddr && logaddr <= 0x14)
        || (0x19 == logaddr)
        || (0x20 <= logaddr && logaddr <= 0x25)
        || (0x30 == logaddr))
       return "R/O";
 
    if (   (0x09 <= logaddr && logaddr <= 0x0a)
+       || (0x15 == logaddr)
        || (0x80 <= logaddr && logaddr <= 0x9f)
        || (0xe0 <= logaddr && logaddr <= 0xe1))
       return "R/W";
@@ -1311,9 +1370,8 @@ static void PrintLogPages(const char * type, const unsigned char * data,
 ///////////////////////////////////////////////////////////////////////
 // Device statistics (Log 0x04)
 
-// See Section A.5 of
-//   ATA/ATAPI Command Set - 3 (ACS-3)
-//   T13/2161-D Revision 2, February 21, 2012.
+// Section A.5 of T13/2161-D (ACS-3) Revision 5, October 28, 2013
+// Section 9.5 of T13/BSR INCITS 529 (ACS-4) Revision 08, April 28, 2015
 
 struct devstat_entry_info
 {
@@ -1329,12 +1387,15 @@ const devstat_entry_info devstat_info_0x00[] = {
 const devstat_entry_info devstat_info_0x01[] = {
   {  2, "General Statistics" },
   {  4, "Lifetime Power-On Resets" },
-  {  4, "Power-on Hours" }, // spec says no flags(?)
+  {  4, "Power-on Hours" },
   {  6, "Logical Sectors Written" },
   {  6, "Number of Write Commands" },
   {  6, "Logical Sectors Read" },
   {  6, "Number of Read Commands" },
   {  6, "Date and Time TimeStamp" }, // ACS-3
+  {  4, "Pending Error Count" }, // ACS-4
+  {  2, "Workload Utilization" }, // ACS-4
+  {  6, "Utilization Usage Rate" }, // ACS-4 (TODO: field provides 3 values)
   {  0, 0 }
 };
 
@@ -1354,6 +1415,7 @@ const devstat_entry_info devstat_info_0x03[] = {
   {  4, "Read Recovery Attempts" },
   {  4, "Number of Mechanical Start Failures" },
   {  4, "Number of Realloc. Candidate Logical Sectors" }, // ACS-3
+  {  4, "Number of High Priority Unload Events" }, // ACS-3
   {  0, 0 }
 };
 
@@ -1410,24 +1472,32 @@ const devstat_entry_info * devstat_infos[] = {
 
 const int num_devstat_infos = sizeof(devstat_infos)/sizeof(devstat_infos[0]);
 
-static void print_device_statistics_page(const unsigned char * data, int page,
-  bool & need_trailer)
+static const char * get_device_statistics_page_name(int page)
+{
+  if (page < num_devstat_infos)
+    return devstat_infos[page][0].name;
+  if (page == 0xff)
+    return "Vendor Specific Statistics"; // ACS-4
+  return "Unknown Statistics";
+}
+
+static void print_device_statistics_page(const unsigned char * data, int page)
 {
   const devstat_entry_info * info = (page < num_devstat_infos ? devstat_infos[page] : 0);
-  const char * name = (info ? info[0].name : "Unknown Statistics");
+  const char * name = get_device_statistics_page_name(page);
 
   // Check page number in header
-  static const char line[] = "  =====  =                =  == ";
+  static const char line[] = "  =====  =               =  ===  == ";
   if (!data[2]) {
-    pout("%3d%s%s (empty) ==\n", page, line, name);
+    pout("0x%02x%s%s (empty) ==\n", page, line, name);
     return;
   }
   if (data[2] != page) {
-    pout("%3d%s%s (invalid page %d in header) ==\n", page, line, name, data[2]);
+    pout("0x%02x%s%s (invalid page 0x%02x in header) ==\n", page, line, name, data[2]);
     return;
   }
 
-  pout("%3d%s%s (rev %d) ==\n", page, line, name, data[0]);
+  pout("0x%02x%s%s (rev %d) ==\n", page, line, name, data[0] | (data[1] << 8));
 
   // Print entries
   for (int i = 1, offset = 8; offset < 512-7; i++, offset+=8) {
@@ -1442,7 +1512,7 @@ static void print_device_statistics_page(const unsigned char * data, int page,
 
     // Stop if unknown entries contain garbage data due to buggy firmware
     if (!info && (data[offset+5] || data[offset+6])) {
-      pout("%3d  0x%03x  -                -  [Trailing garbage ignored]\n", page, offset);
+      pout("0x%02x  0x%03x  -               -  [Trailing garbage ignored]\n", page, offset);
       break;
     }
 
@@ -1469,15 +1539,17 @@ static void print_device_statistics_page(const unsigned char * data, int page,
       valstr[0] = '-'; valstr[1] = 0;
     }
 
-    pout("%3d  0x%03x  %d%c %15s%c %s\n",
+    pout("0x%02x  0x%03x  %d %15s  %c%c%c%c %s\n",
       page, offset,
       abs(size),
-      (flags & 0x1f ? '+' : ' '), // unknown flags
       valstr,
-      (flags & 0x20 ? '~' : ' '), // normalized flag
-      (info ? info[i].name : "Unknown"));
-    if (flags & 0x20)
-      need_trailer = true;
+      ((flags & 0x20) ? 'N' : '-'), // normalized statistics
+      ((flags & 0x10) ? 'D' : '-'), // supports DSN (ACS-3)
+      ((flags & 0x08) ? 'C' : '-'), // monitored condition met (ACS-3)
+      ((flags & 0x07) ? '+' : ' '), // reserved flags
+      ( info          ? info[i].name :
+       (page == 0xff) ? "Vendor Specific" // ACS-4
+                      : "Unknown"        ));
   }
 }
 
@@ -1494,13 +1566,13 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
   else
     rc = ataReadSmartLog(device, 0x04, page_0, 1);
   if (!rc) {
-    pout("Read Device Statistics page 0 failed\n\n");
+    pout("Read Device Statistics page 0x00 failed\n\n");
     return false;
   }
 
   unsigned char nentries = page_0[8];
   if (!(page_0[2] == 0 && nentries > 0)) {
-    pout("Device Statistics page 0 is invalid (page=%d, nentries=%d)\n\n", page_0[2], nentries);
+    pout("Device Statistics page 0x00 is invalid (page=0x%02x, nentries=%d)\n\n", page_0[2], nentries);
     return false;
   }
 
@@ -1519,14 +1591,14 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
   // Add manually specified pages
   bool print_page_0 = false;
   for (i = 0; i < single_pages.size() || ssd_page; i++) {
-    int page = (i < single_pages.size() ? single_pages[i] : 7);
+    int page = (i < single_pages.size() ? single_pages[i] : 0x07);
     if (!page)
       print_page_0 = true;
     else if (page >= (int)nsectors)
-      pout("Device Statistics Log has only %u pages\n", nsectors);
+      pout("Device Statistics Log has only 0x%02x pages\n", nsectors);
     else
       pages.push_back(page);
-    if (page == 7)
+    if (page == 0x07)
       ssd_page = false;
   }
 
@@ -1534,11 +1606,10 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
   if (print_page_0) {
     pout("Device Statistics (%s Log 0x04) supported pages\n", 
       use_gplog ? "GP" : "SMART");
-    pout("Page Description\n");
+    pout("Page  Description\n");
     for (i = 0; i < nentries; i++) {
       int page = page_0[8+1+i];
-      pout("%3d  %s\n", page,
-        (page < num_devstat_infos ? devstat_infos[page][0].name : "Unknown Statistics"));
+      pout("0x%02x  %s\n", page, get_device_statistics_page_name(page));
     }
     pout("\n");
   }
@@ -1547,38 +1618,41 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
   if (!pages.empty()) {
     pout("Device Statistics (%s Log 0x04)\n",
       use_gplog ? "GP" : "SMART");
-    pout("Page Offset Size         Value  Description\n");
-    bool need_trailer = false;
+    pout("Page  Offset Size        Value Flags Description\n");
     int max_page = 0;
-    
+
     if (!use_gplog)
-    for (i = 0; i < pages.size(); i++) {
-      int page = pages[i];
-      if (max_page < page && page < 0xff)
-        max_page = page;
-    }
-    
+      for (i = 0; i < pages.size(); i++) {
+        int page = pages[i];
+        if (max_page < page && page < 0xff)
+          max_page = page;
+      }
+
     raw_buffer pages_buf((max_page+1) * 512);
 
-     if (!use_gplog && !ataReadSmartLog(device, 0x04, pages_buf.data(), max_page+1)) {
-      pout("Read Device Statistics pages 0-%d failed\n\n", max_page);
+    if (!use_gplog && !ataReadSmartLog(device, 0x04, pages_buf.data(), max_page+1)) {
+      pout("Read Device Statistics pages 0x00-0x%02x failed\n\n", max_page);
       return false;
     }
 
     for (i = 0; i <  pages.size(); i++) {
       int page = pages[i];
-      if (use_gplog && !ataReadLogExt(device, 0x04, 0, page, pages_buf.data(), 1)) {
-        pout("Read Device Statistics page %d failed\n\n", page);
-        return false;
+      if (use_gplog) {
+        if (!ataReadLogExt(device, 0x04, 0, page, pages_buf.data(), 1)) {
+          pout("Read Device Statistics page 0x%02x failed\n\n", page);
+          return false;
+        }
       }
+      else if (page > max_page)
+        continue;
 
       int offset = (use_gplog ? 0 : page * 512);
-      print_device_statistics_page(pages_buf.data() + offset, page, need_trailer);
+      print_device_statistics_page(pages_buf.data() + offset, page);
     }
 
-    if (need_trailer)
-      pout("%30s|_ ~ normalized value\n", "");
-    pout("\n");
+    pout("%32s|||_ C monitored condition met\n", "");
+    pout("%32s||__ D supports DSN\n", "");
+    pout("%32s|___ N normalized value\n\n", "");
   }
 
   return true;
@@ -1641,7 +1715,7 @@ static void PrintSataPhyEventCounters(const unsigned char * data, bool reset)
       case 0x010: name = "R_ERR response for host-to-device data FIS, non-CRC"; break;
       case 0x012: name = "R_ERR response for host-to-device non-data FIS, CRC"; break;
       case 0x013: name = "R_ERR response for host-to-device non-data FIS, non-CRC"; break;
-      default:    name = (id & 0x8000 ? "Vendor specific" : "Unknown"); break;
+      default:    name = ((id & 0x8000) ? "Vendor specific" : "Unknown"); break;
     }
 
     // Counters stop at max value, add '+' in this case
@@ -1739,7 +1813,7 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data,
   for (int k = 4; k >= 0; k-- ) {
 
     // The error log data structure entries are a circular buffer
-    int j, i=(data->error_log_pointer+k)%5;
+    int i = (data->error_log_pointer + k) % 5;
     const ata_smart_errorlog_struct * elog = data->errorlog_struct+i;
     const ata_smart_errorlog_error_struct * summary = &(elog->error_struct);
 
@@ -1775,7 +1849,7 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data,
       pout("  Commands leading to the command that caused the error were:\n"
            "  CR FR SC SN CL CH DH DC   Powered_Up_Time  Command/Feature_Name\n"
            "  -- -- -- -- -- -- -- --  ----------------  --------------------\n");
-      for ( j = 4; j >= 0; j--){
+      for (int j = 4; j >= 0; j--) {
         const ata_smart_errorlog_command_struct * thiscommand = elog->commands+j;
 
         // Spec says: unused data command structures shall be zero filled
@@ -1804,7 +1878,9 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data,
 }
 
 // Print SMART Extended Comprehensive Error Log (GP Log 0x03)
-static int PrintSmartExtErrorLog(const ata_smart_exterrlog * log,
+static int PrintSmartExtErrorLog(ata_device * device,
+                                 const firmwarebug_defs & firmwarebugs,
+                                 const ata_smart_exterrlog * log,
                                  unsigned nsectors, unsigned max_errors)
 {
   pout("SMART Extended Comprehensive Error Log Version: %u (%u sectors)\n",
@@ -1865,11 +1941,30 @@ static int PrintSmartExtErrorLog(const ata_smart_exterrlog * log,
        "DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,\n"
        "SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n");
 
+  // Recently read log page
+  ata_smart_exterrlog log_buf;
+  unsigned log_buf_page = ~0;
+
   // Iterate through circular buffer in reverse direction
   for (unsigned i = 0, errnum = log->device_error_count;
        i < errcnt; i++, errnum--, erridx = (erridx > 0 ? erridx - 1 : nentries - 1)) {
 
-    const ata_smart_exterrlog_error_log & entry = log[erridx / 4].error_logs[erridx % 4];
+    // Read log page if needed
+    const ata_smart_exterrlog * log_p;
+    unsigned page = erridx / 4;
+    if (page == 0)
+      log_p = log;
+    else {
+      if (page != log_buf_page) {
+        memset(&log_buf, 0, sizeof(log_buf));
+        if (!ataReadExtErrorLog(device, &log_buf, page, 1, firmwarebugs))
+          break;
+        log_buf_page = page;
+      }
+      log_p = &log_buf;
+    }
+
+    const ata_smart_exterrlog_error_log & entry = log_p->error_logs[erridx % 4];
 
     // Skip unused entries
     if (!nonempty(&entry, sizeof(entry))) {
@@ -2215,6 +2310,7 @@ static int ataPrintSCTStatus(const ata_sct_status_response * sts)
     // T13/e06152r0-3 (Additional SCT Temperature Statistics), August - October 2006
     // Table 60 of T13/1699-D (ATA8-ACS) Revision 3f, December 2006  (format version 2)
     // Table 80 of T13/1699-D (ATA8-ACS) Revision 6a, September 2008 (format version 3)
+    // Table 182 of T13/BSR INCITS 529 (ACS-4) Revision 02a, May 22, 2014 (smart_status field)
     pout("Current Temperature:                    %s Celsius\n",
       sct_ptemp(sts->hda_temp, buf1));
     pout("Power Cycle Min/Max Temperature:     %s/%s Celsius\n",
@@ -2226,6 +2322,17 @@ static int ataPrintSCTStatus(const ata_sct_status_response * sts)
       pout("Lifetime    Average Temperature:        %2d Celsius\n", avg);
     pout("Under/Over Temperature Limit Count:  %2u/%u\n",
       sts->under_limit_count, sts->over_limit_count);
+
+    if (sts->smart_status) // ACS-4
+      pout("SMART Status:                        0x%04x (%s)\n", sts->smart_status,
+           (sts->smart_status == 0x2cf4 ? "FAILED" :
+            sts->smart_status == 0xc24f ? "PASSED" : "Reserved"));
+
+    if (nonempty(sts->vendor_specific, sizeof(sts->vendor_specific))) {
+      pout("Vendor specific:\n");
+      for (unsigned i = 0; i < sizeof(sts->vendor_specific); i++)
+        pout("%02x%c", sts->vendor_specific[i], ((i & 0xf) != 0xf ? ' ' : '\n'));
+    }
   }
   return 0;
 }
@@ -2425,6 +2532,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
   if (options.powermode) {
     unsigned char powerlimit = 0xff;
     int powermode = ataCheckPowerMode(device);
+    // TODO: Move to new function used by smartctl and smartd.
     switch (powermode) {
       case -1:
         if (device->is_syscall_unsup()) {
@@ -2432,12 +2540,28 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
         }
         powername = "SLEEP";   powerlimit = 2;
         break;
-      case 0:
+      // Table 215 of T13/2015-D (ACS-2) Revision 7, June 22, 2011
+      // Table 293 of T13/BSR INCITS 529 (ACS-4) Revision 12, February 18, 2016
+      case 0x00: // PM2:Standby, EPC unavailable or Standby_z power condition
         powername = "STANDBY"; powerlimit = 3; break;
-      case 0x80:
+      case 0x01: // PM2:Standby, Standby_y power condition
+        powername = "STANDBY_Y"; powerlimit = 3; break;
+      case 0x80: // PM1:Idle, EPC unavailable
         powername = "IDLE";    powerlimit = 4; break;
-      case 0xff:
+      case 0x81: // PM1:Idle, Idle_a power condition
+        powername = "IDLE_A";  powerlimit = 4; break;
+      case 0x82: // PM1:Idle, Idle_b power condition
+        powername = "IDLE_B";  powerlimit = 4; break;
+      case 0x83: // PM1:Idle, Idle_c power condition
+        powername = "IDLE_C";  powerlimit = 4; break;
+      // 0x40/41 were declared obsolete in ACS-3 Revision 1
+      case 0x40: // PM0:Active, NV Cache power mode enabled, spun down
+        powername = "ACTIVE_NV_DOWN"; break;
+      case 0x41: // PM0:Active, NV Cache power mode enabled, spun up
+        powername = "ACTIVE_NV_UP"  ; break;
+      case 0xff: // PM0:Active or PM1:Idle
         powername = "ACTIVE or IDLE"; break;
+
       default:
         pout("CHECK POWER MODE returned unknown value 0x%02x, ignoring -n option\n", powermode);
         break;
@@ -2669,13 +2793,21 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
        !(drive.cfs_enable_1 & 0x0020) ? "Disabled" : "Enabled"); // word085
   }
 
-  // Print ATA security status
+  // Check for ATA Security LOCK
+  unsigned short word128 = drive.words088_255[128-88];
+  bool locked = ((word128 & 0x0007) == 0x0007); // LOCKED|ENABLED|SUPPORTED
+
+  // Print ATA Security status
   if (options.get_security)
-    print_ata_security_status("ATA Security is:  ", drive.words088_255[128-88]);
+    print_ata_security_status("ATA Security is:  ", word128);
 
   // Print write cache reordering status
   if (options.sct_wcache_reorder_get) {
-    if (isSCTFeatureControlCapable(&drive)) {
+    if (!isSCTFeatureControlCapable(&drive))
+      pout("Wt Cache Reorder: Unavailable\n");
+    else if (locked)
+      pout("Wt Cache Reorder: Unknown (SCT not supported if ATA Security is LOCKED)\n");
+    else {
       int wcache_reorder = ataGetSetSCTWriteCacheReordering(device,
         false /*enable*/, false /*persistent*/, false /*set*/);
 
@@ -2687,8 +2819,6 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
       else
         pout("Wt Cache Reorder: Unknown (0x%02x)\n", wcache_reorder);
     }
-    else
-      pout("Wt Cache Reorder: Unavailable\n");
   }
 
   // Print remaining drive info
@@ -2780,6 +2910,9 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     if (!isSCTFeatureControlCapable(&drive))
       pout("Write cache reordering %sable failed: SCT Feature Control command not supported\n",
         (enable ? "en" : "dis"));
+    else if (locked)
+      pout("Write cache reordering %sable failed: SCT not supported if ATA Security is LOCKED\n",
+        (enable ? "en" : "dis"));
     else if (ataGetSetSCTWriteCacheReordering(device,
                enable, false /*persistent*/, true /*set*/) < 0) {
       pout("Write cache reordering %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg());
@@ -3046,7 +3179,8 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
   // If GP Log is supported use smart log directory for
   // error and selftest log support check.
-  if (   isGeneralPurposeLoggingCapable(&drive)
+  bool gp_log_supported = !!isGeneralPurposeLoggingCapable(&drive);
+  if (   gp_log_supported
       && (   options.smart_error_log || options.smart_selftest_log
           || options.retry_error_log || options.retry_selftest_log))
     need_smart_logdir = true;
@@ -3070,6 +3204,10 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
   if (need_gp_logdir) {
     if (firmwarebugs.is_set(BUG_NOLOGDIR))
       gplogdir = fake_logdir(&gplogdir_buf, options);
+    else if (!gp_log_supported && !is_permissive()) {
+      if (options.gp_logdir)
+        pout("General Purpose Log Directory not supported\n\n");
+    }
     else if (ataReadLogDirectory(device, &gplogdir_buf, true)) {
       pout("Read GP Log Directory failed\n\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
@@ -3142,17 +3280,16 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     unsigned nsectors = GetNumLogSectors(gplogdir, 0x03, true);
     if (!nsectors)
       pout("SMART Extended Comprehensive Error Log (GP Log 0x03) not supported\n\n");
-    else if (nsectors >= 256)
-      pout("SMART Extended Comprehensive Error Log size %u not supported\n\n", nsectors);
     else {
-      raw_buffer log_03_buf(nsectors * 512);
-      ata_smart_exterrlog * log_03 = (ata_smart_exterrlog *)log_03_buf.data();
-      if (!ataReadExtErrorLog(device, log_03, nsectors, firmwarebugs)) {
+      // Read only first sector to get error count and index
+      // Print function will read more sectors as needed
+      ata_smart_exterrlog log_03; memset(&log_03, 0, sizeof(log_03));
+      if (!ataReadExtErrorLog(device, &log_03, 0, 1, firmwarebugs)) {
         pout("Read SMART Extended Comprehensive Error Log failed\n\n");
         failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
       }
       else {
-        if (PrintSmartExtErrorLog(log_03, nsectors, options.smart_ext_error_log))
+        if (PrintSmartExtErrorLog(device, firmwarebugs, &log_03, nsectors, options.smart_ext_error_log))
           returnval |= FAILERR;
         ok = true;
       }
@@ -3168,9 +3305,10 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
   // Print SMART error log
   if (do_smart_error_log) {
-    if (!(   ( smartlogdir && GetNumLogSectors(smartlogdir, 0x01, false))
-          || (!smartlogdir && isSmartErrorLogCapable(&smartval, &drive) )
-          || is_permissive()                                             )) {
+    if (!(   GetNumLogSectors(smartlogdir, 0x01, false)
+          || (   !(smartlogdir && gp_log_supported)
+              && isSmartErrorLogCapable(&smartval, &drive))
+          || is_permissive()                               )) {
       pout("SMART Error Log not supported\n\n");
     }
     else {
@@ -3199,7 +3337,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
       pout("SMART Extended Self-test Log size %u not supported\n\n", nsectors);
     else {
       raw_buffer log_07_buf(nsectors * 512);
-      ata_smart_extselftestlog * log_07 = (ata_smart_extselftestlog *)log_07_buf.data();
+      ata_smart_extselftestlog * log_07 = reinterpret_cast<ata_smart_extselftestlog *>(log_07_buf.data());
       if (!ataReadExtSelfTestLog(device, log_07, nsectors)) {
         pout("Read SMART Extended Self-test Log failed\n\n");
         failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
@@ -3221,9 +3359,10 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
   // Print SMART self-test log
   if (do_smart_selftest_log) {
-    if (!(   ( smartlogdir && GetNumLogSectors(smartlogdir, 0x06, false))
-          || (!smartlogdir && isSmartTestLogCapable(&smartval, &drive)  )
-          || is_permissive()                                             )) {
+    if (!(   GetNumLogSectors(smartlogdir, 0x06, false)
+          || (   !(smartlogdir && gp_log_supported)
+              && isSmartTestLogCapable(&smartval, &drive))
+          || is_permissive()                              )) {
       pout("SMART Self-test Log not supported\n\n");
     }
     else {
@@ -3266,9 +3405,15 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
   // Check if SCT commands available
   bool sct_ok = isSCTCapable(&drive);
-  if(!sct_ok && (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int
-                 || options.sct_erc_get || options.sct_erc_set                        ))
-    pout("SCT Commands not supported\n\n");
+  if (   options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int
+      || options.sct_erc_get  || options.sct_erc_set                          ) {
+    if (!sct_ok)
+      pout("SCT Commands not supported\n\n");
+    else if (locked) {
+      pout("SCT Commands not supported if ATA Security is LOCKED\n\n");
+      sct_ok = false;
+    }
+  }
 
   // Print SCT status and temperature history table
   if (sct_ok && (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int)) {
index e83020fa4ea6398c803b745908bb4fbdae6b5992..b0b2f1bdbaa7ff00ab60dad2358568bf06716789 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * ataprint.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
@@ -25,7 +25,7 @@
 #ifndef ATAPRINT_H_
 #define ATAPRINT_H_
 
-#define ATAPRINT_H_CVSID "$Id: ataprint.h 3825 2013-07-06 21:38:25Z samm2 $\n"
+#define ATAPRINT_H_CVSID "$Id: ataprint.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 #include <vector>
 
index 8fe3859bf372e1fcba28e02387f8bb36794a0606..f73610bf706599e327d348812090df5e1cf8a3fe 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
-# $Id: autogen.sh 3917 2014-06-20 19:57:41Z chrfranke $
+# $Id: autogen.sh 4115 2015-07-15 20:52:26Z chrfranke $
 #
-# Generate ./configure from config.in and Makefile.in from Makefile.am.
+# Generate ./configure from configure.ac and Makefile.in from Makefile.am.
 # This also adds files like missing,depcomp,install-sh to the source
 # directory. To update these files at a later date use:
 #      autoreconf -f -i -v
@@ -23,81 +23,44 @@ test -x /usr/bin/uname && /usr/bin/uname | grep -i CYGWIN >/dev/null &&
     rm -f dostest.tmp
 }
 
-typep()
-{
-    cmd=$1 ; TMP=$IFS ; IFS=: ; set $PATH
-    for dir
-    do
-       if [ -x "$dir/$cmd" ]; then
-           echo "$dir/$cmd"
-           IFS=$TMP
-           return 0
-        fi
-    done
-    IFS=$TMP
-    return 1
-}
+# Find automake
+if [ -n "$AUTOMAKE" ]; then
+  ver=$("$AUTOMAKE" --version) || exit 1
+else
+  maxver=
+  for v in 1.15 1.14 1.13 1.12 1.11 1.10; do
+    minver=$v; test -n "$maxver" || maxver=$v
+    ver=$(automake-$v --version 2>/dev/null) || continue
+    AUTOMAKE="automake-$v"
+    break
+  done
+  if [ -z "$AUTOMAKE" ]; then
+    echo "GNU Automake $minver (up to $maxver) is required to bootstrap smartmontools from SVN."
+    exit 1;
+  fi
+fi
 
-test -x "$AUTOMAKE" ||
-    AUTOMAKE=`typep automake-1.14` ||
-    AUTOMAKE=`typep automake-1.13` || AUTOMAKE=`typep automake-1.12` ||
-    AUTOMAKE=`typep automake-1.11` || AUTOMAKE=`typep automake-1.10` ||
-    AUTOMAKE=`typep automake-1.9` || AUTOMAKE=`typep automake-1.8` ||
-    AUTOMAKE=`typep automake-1.7` || AUTOMAKE=`typep automake17` ||
-{
-echo
-echo "You must have at least GNU Automake 1.7 (up to 1.14) installed"
-echo "in order to bootstrap smartmontools from SVN. Download the"
-echo "appropriate package for your distribution, or the source tarball"
-echo "from ftp://ftp.gnu.org/gnu/automake/ ."
-echo
-echo "Also note that support for new Automake series (anything newer"
-echo "than 1.14) is only added after extensive tests. If you live in"
-echo "the bleeding edge, you should know what you're doing, mainly how"
-echo "to test it before the developers. Be patient."
-exit 1;
-}
+ver=$(echo "$ver" | sed -n '1s,^.*[^.0-9]\([12]\.[0-9][-.0-9pl]*\).*$,\1,p')
+if [ -z "$ver" ]; then
+  echo "$AUTOMAKE: Unable to determine automake version."
+  exit 1
+fi
 
-test -x "$ACLOCAL" || ACLOCAL="aclocal`echo "$AUTOMAKE" | sed 's/.*automake//'`" && ACLOCAL=`typep "$ACLOCAL"` ||
-{
-echo
-echo "autogen.sh found automake-1.X, but not the respective aclocal-1.X."
-echo "Your installation of GNU Automake is broken or incomplete."
-exit 2;
-}
+# Check aclocal
+if [ -z "$ACLOCAL" ]; then
+  ACLOCAL="aclocal$(echo "$AUTOMAKE" | sed -n 's,^.*automake\(-[.0-9]*\),\1,p')"
+fi
 
-# Detect Automake version
-case "$AUTOMAKE" in
-  *automake-1.7|*automake17)
-    ver=1.7 ;;
-  *automake-1.8)
-    ver=1.8 ;;
-  *)
-    ver="`$AUTOMAKE --version | sed -n '1s,^.*[^.0-9]\([12]\.[0-9][-.0-9pl]*\).*$,\1,p'`"
-    ver="${ver:-?.?.?}"
-esac
+"$ACLOCAL" --version >/dev/null || exit 1
 
-# Warn if Automake version was not tested or does not support filesystem
+# Warn if Automake version was not tested
 amwarnings=$warnings
 case "$ver" in
-  1.[78]|1.[78].*)
-    # Check for case sensitive filesystem
-    # (to avoid e.g. "DIST_COMMON = ... ChangeLog ..." in Makefile.in on Cygwin)
-    rm -f CASETEST.TMP
-    echo > casetest.tmp
-    test -f CASETEST.TMP &&
-    {
-      echo "Warning: GNU Automake version ${ver} does not properly handle case"
-      echo "insensitive filesystems. Some make targets may not work."
-    }
-    rm -f casetest.tmp
-    ;;
-
-  1.9.[1-6]|1.10|1.10.[123]|1.11|1.11.[1-6]|1.12.[2-6]|1.13.[34])
+  1.10|1.10.[123]|1.11|1.11.[1-6]|1.12.[2-6]|1.13.[34])
     # OK
     ;;
 
-  1.14|1.14.1)
+  1.14|1.14.1|1.15)
     # TODO: Enable 'subdir-objects' in configure.ac
     # For now, suppress 'subdir-objects' forward-incompatibility warning
     test -n "$warnings" || amwarnings="--warnings=no-unsupported"
@@ -108,24 +71,14 @@ case "$ver" in
     echo "Please report success/failure to the smartmontools-support mailing list."
 esac
 
-# Install pkg-config macros
-# (Don't use 'aclocal -I m4 --install' to keep support for automake < 1.10)
+# required for aclocal-1.10 --install
 test -d m4 || mkdir m4 || exit 1
-test -z "$force" || rm -f m4/pkg.m4
-test -f m4/pkg.m4 || acdir=`${ACLOCAL} --print-ac-dir` &&
-  test -n "$acdir" && test -f "$acdir/pkg.m4" &&
-{
-  echo "$0: installing \`m4/pkg.m4' from \`$acdir/pkg.m4'"
-  cp "$acdir/pkg.m4" m4/pkg.m4
-}
-test -f m4/pkg.m4 ||
-  echo "Warning: cannot install m4/pkg.m4, 'make dist' and systemd detection will not work."
 
 set -e # stops on error status
 
 test -z "$warnings" || set -x
 
-${ACLOCAL} -I m4 $force $warnings
+${ACLOCAL} -I m4 --install $force $warnings
 autoheader $force $warnings
 ${AUTOMAKE} --add-missing --copy ${force:+--force-missing} $amwarnings
 autoconf $force $warnings
index bf074ed88baceb258e9286e132b4c8473c64564f..f5871dd75786f959bfdf9457cce6644fb0e18c0a 100644 (file)
--- a/cciss.cpp
+++ b/cciss.cpp
@@ -38,7 +38,7 @@
 #include "scsicmds.h"
 #include "utility.h"
 
-const char * cciss_cpp_cvsid = "$Id: cciss.cpp 3945 2014-07-13 15:29:05Z chrfranke $"
+const char * cciss_cpp_cvsid = "$Id: cciss.cpp 4156 2015-10-18 12:20:40Z samm2 $"
   CCISS_H_CVSID;
 
 typedef struct _ReportLUNdata_struct
@@ -73,10 +73,9 @@ int cciss_io_interface(int device, int target, struct scsi_cmnd_io * iop, int re
      unsigned char pBuf[512] = {0};
      unsigned char phylun[8] = {0};
      int iBufLen = 512;
-     int status = -1;
      int len = 0; // used later in the code.
  
-     status = cciss_getlun(device, target, phylun, report);
+     int status = cciss_getlun(device, target, phylun, report);
      if (report > 0)
          printf("  cciss_getlun(%d, %d) = 0x%x; scsi3addr: %02x %02x %02x %02x %02x %02x %02x %02x\n", 
             device, target, status, 
@@ -181,7 +180,7 @@ static int cciss_sendpassthru(unsigned int cmdtype, unsigned char *CDB,
 
     if ((err = ioctl(fd, CCISS_PASSTHRU, &iocommand))) 
     {
-        fprintf(stderr, "CCISS ioctl error %d (fd %d CDBLen %d buf_size %d)\n",
+        fprintf(stderr, "CCISS ioctl error %d (fd %d CDBLen %u buf_size %u)\n",
            fd, err, CDBlen, size);
     }
     return err;
index e7f083f27b2e12c6c28134b47bebc287158e862c..f0d47338198bb05a0c40b02c1604918c2a64e7e9 100644 (file)
@@ -1,25 +1,24 @@
 #
-# $Id: configure.ac 3977 2014-07-26 11:03:24Z chrfranke $
+# $Id: configure.ac 4319 2016-05-07 12:14:20Z chrfranke $
 #
 dnl Process this file with autoconf to produce a configure script.
-AC_PREREQ(2.50)
-AC_INIT(smartmontools, 6.4, smartmontools-support@lists.sourceforge.net)
-AC_CONFIG_SRCDIR(smartctl.cpp)
+AC_PREREQ([2.60])
+AC_INIT(smartmontools, 6.6, smartmontools-support@lists.sourceforge.net)
+AM_INIT_AUTOMAKE([1.10 foreign])
 
-smartmontools_cvs_tag=`echo '$Id: configure.ac 3977 2014-07-26 11:03:24Z chrfranke $'`
-smartmontools_release_date=2014-07-26
-smartmontools_release_time="09:49:11 UTC"
+smartmontools_cvs_tag=`echo '$Id: configure.ac 4319 2016-05-07 12:14:20Z chrfranke $'`
+smartmontools_release_date=2016-05-07
+smartmontools_release_time="11:17:46 UTC"
 
 AC_DEFINE_UNQUOTED(SMARTMONTOOLS_CONFIGURE_ARGS, "$ac_configure_args",            [smartmontools Configure Arguments])
 AC_DEFINE_UNQUOTED(SMARTMONTOOLS_RELEASE_DATE,   "$smartmontools_release_date",   [smartmontools Release Date])
 AC_DEFINE_UNQUOTED(SMARTMONTOOLS_RELEASE_TIME,   "$smartmontools_release_time",   [smartmontools Release Time])
 AC_DEFINE_UNQUOTED(CONFIG_H_CVSID,               "$smartmontools_cvs_tag",        [smartmontools CVS Tag])
-AC_DEFINE_UNQUOTED(PACKAGE_HOMEPAGE,             "http://smartmontools.sourceforge.net/", [smartmontools Home Page])
+AC_DEFINE_UNQUOTED(PACKAGE_HOMEPAGE,             "http://www.smartmontools.org/", [smartmontools Home Page])
 
+AC_CONFIG_SRCDIR([smartctl.cpp])
 AC_CONFIG_HEADER([config.h])
 
-AM_INIT_AUTOMAKE([foreign])
-
 AM_MAINTAINER_MODE
 
 AC_LANG([C++])
@@ -84,7 +83,6 @@ AC_MSG_RESULT([$is_svn_build])
 dnl Checks for header files.
 AC_CHECK_HEADERS([locale.h])
 AC_CHECK_HEADERS([dev/ata/atavar.h])
-AC_CHECK_HEADERS([netdb.h])
 dnl we need [u]int64_t and friends.
 AC_CHECK_HEADERS([inttypes.h])         dnl C99, UNIX98, solaris 2.6+
 AC_CHECK_HEADERS([stdint.h])           dnl C99
@@ -149,16 +147,9 @@ AC_SUBST(CPPFLAGS)
 AC_SUBST(LDFLAGS)
 AC_SUBST(ASFLAGS)
 
-AC_ARG_WITH(systemdenvfile,
-  [AS_HELP_STRING([--with-systemdenvfile=@<:@FILE|no@:>@],
-    [Path of systemd EnvironmentFile (implies --with-systemdsystemunitdir=yes) [SYSCONFDIR/sysconfig/smartmontools]])],
-  [systemdenvfile=; test "$withval" != "no" && systemdenvfile="$withval"; systemd_default=yes],
-  [systemdenvfile='${sysconfdir}/sysconfig/smartmontools'; systemd_default=auto])
-AC_SUBST(systemdenvfile)
-
 AC_ARG_WITH(systemdsystemunitdir,
   [AS_HELP_STRING([--with-systemdsystemunitdir@<:@=DIR|auto|yes|no@:>@], [Location of systemd service files [auto]])],
-  [], [with_systemdsystemunitdir=$systemd_default])
+  [], [with_systemdsystemunitdir=auto])
 
 systemdsystemunitdir=
 case "$with_systemdsystemunitdir" in
@@ -179,6 +170,30 @@ esac
 AC_SUBST(systemdsystemunitdir)
 AM_CONDITIONAL(INSTALL_SYSTEMDUNIT, [test -n "$systemdsystemunitdir"])
 
+AC_ARG_WITH(systemdenvfile,
+  [AS_HELP_STRING([--with-systemdenvfile@<:@=FILE|auto|yes|no@:>@], [Path of systemd EnvironmentFile [auto]])],
+  [], [with_systemdenvfile=auto])
+
+systemdenvfile=
+case "$with_systemdenvfile:$cross_compiling:$systemdsystemunitdir" in
+  auto:no:?*|yes:*:?*)
+    AC_MSG_CHECKING([for path of systemd EnvironmentFile])
+    for dir in sysconfig default; do
+      if test -d /etc/$dir; then
+        systemdenvfile='${sysconfdir}'/$dir/smartmontools
+        break
+      fi
+    done
+    AC_MSG_RESULT([${systemdenvfile:-no}])
+    case "$with_systemdenvfile:$systemdenvfile" in
+      yes:) AC_MSG_ERROR([Path of systemd EnvironmentFile not found]) ;;
+    esac ;;
+  auto:*|no:*) ;;
+  *:*:) AC_MSG_ERROR([Location of systemd service files not found]) ;;
+  *) systemdenvfile="$with_systemdenvfile"
+esac
+AC_SUBST(systemdenvfile)
+
 AC_ARG_WITH(initscriptdir,
   [AS_HELP_STRING([--with-initscriptdir@<:@=DIR|auto|yes|no@:>@], [Location of init scripts [auto]])],
   [], [with_initscriptdir=auto])
@@ -208,37 +223,33 @@ case "${host}" in
   *-*-freebsd*)
     initdfile="smartd.freebsd.initd"
     ;;
+  *-apple-darwin*)
+    initdfile="com.smartmontools.smartd.plist"
+    ;;
   *)
     initdfile="smartd.initd"
     ;;
 esac
 AC_SUBST(initdfile)
 
-AC_ARG_WITH(docdir,
-  [AS_HELP_STRING([--with-docdir=DIR], [Location of documentation [DATADIR/doc/smartmontools]])],
-  [docdir="$withval"],
-  [ if test -z "$docdir"; then
-      # autoconf 2.5x without '--docdir' support
-      docdir='${datadir}/doc/${PACKAGE}'
-    fi
-  ])
-AC_SUBST(docdir)
-
 AC_ARG_WITH(exampledir,
   [AS_HELP_STRING([--with-exampledir=DIR], [Location of example scripts [DOCDIR/examplescripts]])],
   [exampledir="$withval"], [exampledir='${docdir}/examplescripts'])
 AC_SUBST(exampledir)
 
-AC_ARG_ENABLE(drivedb,
-  [AS_HELP_STRING([--disable-drivedb], [Disables drive database file])],
-  [], [enable_drivedb=yes])
-
+drivedbdir='${datadir}/${PACKAGE}'
 AC_ARG_WITH(drivedbdir,
-  [AS_HELP_STRING([--with-drivedbdir=DIR], [Location of drive database file [DATADIR/smartmontools]])],
-  [drivedbdir="$withval"; enable_drivedb=yes],
-  [drivedbdir=; test "$enable_drivedb" = "yes" && drivedbdir='${datadir}/${PACKAGE}'])
+  [AS_HELP_STRING([--with-drivedbdir@<:@=DIR|yes|no@:>@], [Location of drive database file [DATADIR/smartmontools]])],
+  [case "$withval" in yes) ;; no) drivedbdir= ;; *) drivedbdir="$withval" ;; esac])
 AC_SUBST(drivedbdir)
-AM_CONDITIONAL(ENABLE_DRIVEDB, [test "$enable_drivedb" = "yes"])
+AM_CONDITIONAL(ENABLE_DRIVEDB, [test -n "$drivedbdir"])
+
+AC_ARG_WITH(update-smart_drivedb,
+  [AS_HELP_STRING([--with-update-smart-drivedb@<:@=yes|no@:>@], [Install update-smart-drivedb script [yes]])],
+  [], [with_update_smart_drivedb=yes])
+test -n "$drivedbdir" || with_update_smart_drivedb=no
+AC_SUBST(with_update_smart_drivedb)
+AM_CONDITIONAL(ENABLE_UPDATE_SMART_DRIVEDB, [test "$with_update_smart_drivedb" = "yes"])
 
 AC_ARG_WITH(smartdscriptdir,
   [AS_HELP_STRING([--with-smartdscriptdir=DIR], [Location of smartd_warning.sh script [SYSCONFDIR]])],
@@ -252,29 +263,27 @@ AC_ARG_WITH(smartdplugindir,
   [smartdplugindir='${smartdscriptdir}/smartd_warning.d'])
 AC_SUBST(smartdplugindir)
 
-AC_ARG_ENABLE(savestates, [AS_HELP_STRING([--enable-savestates], [Enables default smartd state files])])
-
+savestates=
 AC_ARG_WITH(savestates,
-  [AS_HELP_STRING([--with-savestates=PREFIX],
-    [Prefix for default smartd state files (implies --enable-savestates) [LOCALSTATEDIR/lib/smartmontools/smartd.]])],
-  [savestates="$withval"; enable_savestates="yes"],
-  [savestates=; test "$enable_savestates" = "yes" && savestates='${localstatedir}/lib/${PACKAGE}/smartd.'])
+  [AS_HELP_STRING([--with-savestates@<:@=PREFIX|yes|no@:>@],
+    [Enable default smartd state files [no] (yes=LOCALSTATEDIR/lib/smartmontools/smartd.)])],
+  [case "$withval" in yes) savestates='${localstatedir}/lib/${PACKAGE}/smartd.' ;;
+                      no) ;; *) savestates="$withval" ;; esac])
 savestatesdir="${savestates%/*}"
 AC_SUBST(savestates)
 AC_SUBST(savestatesdir)
-AM_CONDITIONAL(ENABLE_SAVESTATES, [test "$enable_savestates" = "yes"])
-
-AC_ARG_ENABLE(attributelog, [AS_HELP_STRING([--enable-attributelog], [Enables default smartd attribute log files])])
+AM_CONDITIONAL(ENABLE_SAVESTATES, [test -n "$savestates"])
 
+attributelog=
 AC_ARG_WITH(attributelog,
-  [AS_HELP_STRING([--with-attributelog=PREFIX],
-    [Prefix for default smartd attribute log files (implies --enable-attributelog) [LOCALSTATEDIR/lib/smartmontools/attrlog.]])],
-  [attributelog="$withval"; enable_attributelog="yes"],
-  [attributelog=; test "$enable_attributelog" = "yes" && attributelog='${localstatedir}/lib/${PACKAGE}/attrlog.'])
+  [AS_HELP_STRING([--with-attributelog@<:@=PREFIX|yes|no@:>@],
+    [Enable default smartd attribute log files [no] (yes=LOCALSTATEDIR/lib/smartmontools/attrlog.)])],
+  [case "$withval" in yes) attributelog='${localstatedir}/lib/${PACKAGE}/attrlog.' ;;
+                      no) ;; *) attributelog="$withval" ;; esac])
 attributelogdir="${attributelog%/*}"
 AC_SUBST(attributelog)
 AC_SUBST(attributelogdir)
-AM_CONDITIONAL(ENABLE_ATTRIBUTELOG, [test "$enable_attributelog" = "yes"])
+AM_CONDITIONAL(ENABLE_ATTRIBUTELOG, [test -n "$attributelog"])
 
 AC_ARG_ENABLE(sample,
   [AS_HELP_STRING([--enable-sample], [Enables appending .sample to the installed smartd rc script and configuration file])],
@@ -311,7 +320,7 @@ AC_ARG_WITH(libcap-ng,
 use_libcap_ng=no
 if test "$with_libcap_ng" != "no"; then
   AC_CHECK_LIB(cap-ng, capng_clear,
-    [AC_DEFINE(HAVE_LIBCAP_NG, 1, [Define to 1 if you have the `cap-ng' library (-lcap-ng).])
+    [AC_DEFINE(HAVE_LIBCAP_NG, 1, [Define to 1 if you have the `cap-ng' library (-lcap-ng).]) dnl `vim syntax
      CAPNG_LDADD="-lcap-ng"; use_libcap_ng=yes])
 
   if test "$use_libcap_ng" = "yes"; then
@@ -323,9 +332,32 @@ fi
 
 AC_MSG_CHECKING([whether to use libcap-ng])
 AC_SUBST(CAPNG_LDADD)
-AM_CONDITIONAL(ENABLE_CAPABILITIES, [test "$use_libcap_ng" = "yes"])
 AC_MSG_RESULT([$use_libcap_ng])
 
+# TODO: Remove when NVMe support is no longer EXPERIMENTAL
+AC_ARG_WITH(nvme-devicescan,
+  [AS_HELP_STRING([--with-nvme-devicescan@<:@=yes|no@:>@],
+    [Include NVMe devices in smartd DEVICESCAN [no]])])
+AC_SUBST(with_nvme_devicescan)
+if test "$with_nvme_devicescan" = "yes"; then
+  AC_DEFINE(WITH_NVME_DEVICESCAN, 1, [Define to 1 to include NVMe devices in smartd DEVICESCAN.])
+fi
+
+AC_ARG_WITH(solaris-sparc-ata,
+  [AS_HELP_STRING([--with-solaris-sparc-ata@<:@=yes|no@:>@],
+    [Enable legacy ATA support on Solaris SPARC (requires os_solaris_ata.s from SVN repository) [no]])])
+
+case "$host:$with_solaris_sparc_ata" in
+  sparc-*-solaris*:yes)
+    if test ! -f "$srcdir/os_solaris_ata.s"; then
+      AC_MSG_ERROR([Missing source file: $srcdir/os_solaris_ata.s
+This file is no longer included in the source tarball but still
+available in the SVN repository.])
+    fi
+    AC_DEFINE(WITH_SOLARIS_SPARC_ATA, 1, [Define to 1 to enable legacy ATA support on Solaris SPARC.])
+    ;;
+esac
+
 # Assume broken snprintf only on Windows with MSVCRT (MinGW without ANSI stdio support)
 libc_have_working_snprintf=yes
 
@@ -357,23 +389,52 @@ AC_ARG_WITH(working-snprintf,
   [libc_have_working_snprintf=$withval])
 
 if test "$libc_have_working_snprintf" = "yes"; then
-  AC_DEFINE(HAVE_WORKING_SNPRINTF, 1, [Define to 1 if the `snprintf' function is sane])
+  AC_DEFINE(HAVE_WORKING_SNPRINTF, 1, [Define to 1 if the `snprintf' function is sane.]) dnl `vim syntax
 fi
 
-if test "$prefix" = "NONE"; then
-    dnl no prefix and no mandir, so use ${prefix}/share/man as default
-    if test "$mandir" = '${prefix}/man'; then
-       AC_SUBST([mandir], ['${prefix}/share/man'])
-    fi
+os_win32_manifest=
+case "$host" in
+  *-*-mingw*)
+    # Newer MinGW may add a default manifest
+    AC_MSG_CHECKING([whether $CC adds an application manifest])
+    cc_adds_manifest=no
+    AC_LINK_IFELSE([AC_LANG_PROGRAM()], [
+        if "$WINDRES" -O rc conftest.exe 2>/dev/null | grep '^1.*RT_MANIFEST' >/dev/null 2>&1; then
+          cc_adds_manifest=incomplete
+          # Manifest must provide a Win 10 compatibility ID
+          if "$WINDRES" -O rc conftest.exe 2>/dev/null | grep '{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' >/dev/null 2>&1; then
+            cc_adds_manifest=yes
+          fi
+        fi],
+      [AC_MSG_ERROR([test compile failed])])
+    AC_MSG_RESULT([$cc_adds_manifest])
+    test "$cc_adds_manifest" = "yes" || os_win32_manifest='default.manifest.o'
+    ;;
+esac
+
+# TODO: Remove after smartmontools 6.5
+AC_ARG_WITH(docdir,
+  [AS_HELP_STRING([--with-docdir=DIR], [(removed, use --docdir=DIR instead)])],
+  [AC_MSG_ERROR([--with-docdir is no longer supported, use --docdir instead])])
+AC_ARG_ENABLE(drivedb,
+  [AS_HELP_STRING([--disable-drivedb], [(removed, use --without-drivedbdir instead)])])
+AC_ARG_ENABLE(savestates,
+  [AS_HELP_STRING([--enable-savestates], [(removed, use --with-savestates@<:@=yes@:>@ instead)])])
+AC_ARG_ENABLE(attributelog,
+  [AS_HELP_STRING([--enable-attributelog], [(removed, use --with-attributelog@<:@=yes@:>@ instead)])])
+if test -n "${enable_drivedb+set}${enable_savestates+set}${enable_attributelog+set}"; then
+  AC_MSG_ERROR([Options --disable-drivedb, --enable-savestates, --enable-attributelog are no longer supported.
+Use --without-drivedbdir, --with-savestates, --with-attributelog instead.])
 fi
 
+
 AC_SUBST(releaseversion,['${PACKAGE}-${VERSION}'])
 AC_SUBST(smartmontools_release_date)
 AC_SUBST(smartmontools_release_time)
 
 # Set platform-specific modules and symbols
 os_libs=
-os_dltools='curl wget lynx'
+os_dltools='curl wget lynx svn'
 os_mailer=mail
 os_hostname="'hostname'"
 os_dnsdomainname=
@@ -394,12 +455,14 @@ case "${host}" in
   *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*)
     os_deps='os_freebsd.o cciss.o dev_areca.o'
     os_libs='-lcam'
-    os_dltools='curl wget lynx fetch'
+    os_dltools='curl wget lynx fetch svn'
     AC_CHECK_LIB(usb, libusb20_dev_get_device_desc)
     os_man_filter=FreeBSD
     ;;
   sparc-*-solaris*)
-    os_deps='os_solaris.o os_solaris_ata.o'
+    os_deps='os_solaris.o'
+    test "$with_solaris_sparc_ata" = "yes" \
+      && os_deps="$os_deps os_solaris_ata.o"
     os_mailer='mailx'
     os_solaris=yes
     os_man_filter=Solaris
@@ -418,7 +481,7 @@ case "${host}" in
   *-*-openbsd*)
     os_deps='os_openbsd.o'
     os_libs='-lutil'
-    os_dltools='curl wget lynx ftp'
+    os_dltools='curl wget lynx ftp svn'
     os_man_filter=OpenBSD
     ;;
   *-*-cygwin*)
@@ -480,6 +543,7 @@ AC_SUBST([os_hostname])
 AC_SUBST([os_dnsdomainname])
 AC_SUBST([os_nisdomainname])
 AC_SUBST([os_man_filter])
+AC_SUBST([os_win32_manifest])
 
 # Create drivedb.h update branch name from version: 5.41[.X] -> RELEASE_5_41_DRIVEDB
 DRIVEDB_BRANCH=`echo $VERSION | sed 's,^\([[0-9]]*\.[[0-9]]*\)\..*$,\1,' \
@@ -538,85 +602,125 @@ AC_CONFIG_FILES(Makefile)
 AC_OUTPUT
 AC_PROG_MAKE_SET
 
-echo "-----------------------------------------------------------------------------" >&AS_MESSAGE_FD
-echo "${PACKAGE}-${VERSION} configuration:" >&AS_MESSAGE_FD
-echo "host operating system:  $host" >&AS_MESSAGE_FD
-echo "C++ compiler:           $CXX" >&AS_MESSAGE_FD
-echo "C compiler:             $CC" >&AS_MESSAGE_FD
-echo "preprocessor flags:     $CPPFLAGS" >&AS_MESSAGE_FD
-echo "C++ compiler flags:     $CXXFLAGS" >&AS_MESSAGE_FD
-echo "C compiler flags:       $CFLAGS" >&AS_MESSAGE_FD
-echo "linker flags:           $LDFLAGS" >&AS_MESSAGE_FD
-echo "OS specific modules:    $os_deps $os_libs $LIBS" >&AS_MESSAGE_FD
-
-case "$host_os" in
-  mingw*)
-    echo "resource compiler:      $WINDRES" >&AS_MESSAGE_FD
-    echo "message compiler:       $WINDMC" >&AS_MESSAGE_FD
-    echo "NSIS compiler:          $MAKENSIS" >&AS_MESSAGE_FD
-    if test -n "$drivedbdir"; then
-      echo "drive database file:    EXEDIR/drivedb.h" >&AS_MESSAGE_FD
-      if test -n "$MAKENSIS"; then
-        echo "database update tool:   EXEDIR/update-smart-drivedb.exe" >&AS_MESSAGE_FD
+# Note: Use `...` here as some shells do not properly parse '$(... case $x in X) ...)'
+info=`
+  echo "-----------------------------------------------------------------------------"
+  echo "${PACKAGE}-${VERSION} configuration:"
+  echo "host operating system:  $host"
+  echo "C++ compiler:           $CXX"
+  echo "C compiler:             $CC"
+  echo "preprocessor flags:     $CPPFLAGS"
+  echo "C++ compiler flags:     $CXXFLAGS"
+  echo "C compiler flags:       $CFLAGS"
+  echo "linker flags:           $LDFLAGS"
+  echo "OS specific modules:    $os_deps $os_libs $LIBS"
+
+  case "$host_os" in
+    mingw*)
+      echo "application manifest:   ${os_win32_manifest:-built-in}"
+      echo "resource compiler:      $WINDRES"
+      echo "message compiler:       $WINDMC"
+      echo "NSIS compiler:          $MAKENSIS"
+      if test -n "$drivedbdir"; then
+        echo "drive database file:    EXEDIR/drivedb.h"
+        if test -n "$MAKENSIS"; then
+          echo "database update tool:   EXEDIR/update-smart-drivedb.exe"
+        fi
+      else
+        echo "drive database file:    [[disabled]]"
       fi
-    else
-      echo "drive database file:    [[disabled]]" >&AS_MESSAGE_FD
-    fi
-    if test -n "$savestates"; then
-      echo "smartd save files:      `eval eval eval echo $savestates`MODEL-SERIAL.TYPE.state" >&AS_MESSAGE_FD
-    fi
-    if test -n "$attributelog"; then
-      echo "smartd attribute logs:  `eval eval eval echo $attributelog`MODEL-SERIAL.TYPE.csv" >&AS_MESSAGE_FD
-    fi
-    ;;
-
-  *)
-    echo "binary install path:    `eval eval eval echo $sbindir`" >&AS_MESSAGE_FD
-    echo "man page install path:  `eval eval eval echo $mandir`" >&AS_MESSAGE_FD
-    echo "doc file install path:  `eval eval eval echo $docdir`" >&AS_MESSAGE_FD
-    echo "examples install path:  `eval eval eval echo $exampledir`" >&AS_MESSAGE_FD
-    if test -n "$drivedbdir"; then
-      echo "drive database file:    `eval eval eval echo $drivedbdir`/drivedb.h" >&AS_MESSAGE_FD
-      echo "database update script: `eval eval eval echo $sbindir`/update-smart-drivedb" >&AS_MESSAGE_FD
-      echo "download tools:         `eval eval eval echo $os_dltools`" >&AS_MESSAGE_FD
-    else
-      echo "drive database file:    [[disabled]]" >&AS_MESSAGE_FD
-    fi
-    echo "local drive database:   `eval eval eval echo $sysconfdir`/smart_drivedb.h" >&AS_MESSAGE_FD
-    echo "smartd config file:     `eval eval eval echo $sysconfdir`/smartd.conf${smartd_suffix}" >&AS_MESSAGE_FD
-    echo "smartd warning script:  `eval eval eval echo $smartdscriptdir`/smartd_warning.sh" >&AS_MESSAGE_FD
-    if test -n "$smartdplugindir"; then
-      echo "smartd plugin path:     `eval eval eval echo $smartdplugindir`" >&AS_MESSAGE_FD
-    else
-      echo "smartd plugin path:     [[disabled]]" >&AS_MESSAGE_FD
-    fi
-    if test -n "$initddir"; then
-      echo "smartd initd script:    `eval eval eval echo $initddir`/smartd${smartd_suffix}" >&AS_MESSAGE_FD
-    elif test -z "$systemdsystemunitdir"; then
-      echo "smartd initd script:    [[disabled]]" >&AS_MESSAGE_FD
-    fi
-    if test -n "$systemdsystemunitdir"; then
-      echo "smartd systemd file:    `eval eval eval echo $systemdsystemunitdir`/smartd.service" >&AS_MESSAGE_FD
-      if test -n "$systemdenvfile"; then
-        echo "smartd environ file:    `eval eval eval echo $systemdenvfile`" >&AS_MESSAGE_FD
+      if test -n "$savestates"; then
+        echo "smartd save files:      \`eval eval eval echo $savestates\`MODEL-SERIAL.TYPE.state"
+      fi
+      if test -n "$attributelog"; then
+        echo "smartd attribute logs:  \`eval eval eval echo $attributelog\`MODEL-SERIAL.TYPE.csv"
+      fi
+      echo "NVMe DEVICESCAN:        ${with_nvme_devicescan-no}"
+      ;;
+
+    *)
+      echo "binary install path:    \`eval eval eval echo $sbindir\`"
+      echo "man page install path:  \`eval eval eval echo $mandir\`"
+      echo "doc file install path:  \`eval eval eval echo $docdir\`"
+      echo "examples install path:  \`eval eval eval echo $exampledir\`"
+      if test -n "$drivedbdir"; then
+        echo "drive database file:    \`eval eval eval echo $drivedbdir\`/drivedb.h"
+        if test "$with_update_smart_drivedb" = "yes"; then
+          echo "database update script: \`eval eval eval echo $sbindir\`/update-smart-drivedb"
+          echo "download tools:         \`eval eval eval echo $os_dltools\`"
+        else
+          echo "database update script: [[disabled]]"
+        fi
       else
-        echo "smartd environ file:    [[disabled]]" >&AS_MESSAGE_FD
+        echo "drive database file:    [[disabled]]"
       fi
-    fi
-    if test -n "$savestates"; then
-      echo "smartd save files:      `eval eval eval echo $savestates`MODEL-SERIAL.TYPE.state" >&AS_MESSAGE_FD
-    else
-      echo "smartd save files:      [[disabled]]" >&AS_MESSAGE_FD
-    fi
-    if test -n "$attributelog"; then
-      echo "smartd attribute logs:  `eval eval eval echo $attributelog`MODEL-SERIAL.TYPE.csv" >&AS_MESSAGE_FD
-    else
-      echo "smartd attribute logs:  [[disabled]]" >&AS_MESSAGE_FD
-    fi
-    echo "libcap-ng support:      $use_libcap_ng" >&AS_MESSAGE_FD
-    case "$host_os" in
-      linux*) echo "SELinux support:        ${with_selinux-no}" >&AS_MESSAGE_FD ;;
-    esac
+      echo "local drive database:   \`eval eval eval echo $sysconfdir\`/smart_drivedb.h"
+      echo "smartd config file:     \`eval eval eval echo $sysconfdir\`/smartd.conf${smartd_suffix}"
+      echo "smartd warning script:  \`eval eval eval echo $smartdscriptdir\`/smartd_warning.sh"
+      if test -n "$smartdplugindir"; then
+        echo "smartd plugin path:     \`eval eval eval echo $smartdplugindir\`"
+      else
+        echo "smartd plugin path:     [[disabled]]"
+      fi
+      if test -n "$initddir"; then
+        echo "smartd initd script:    \`eval eval eval echo $initddir\`/${initdfile}"
+      elif test -z "$systemdsystemunitdir"; then
+        echo "smartd initd script:    [[disabled]]"
+      fi
+      if test -n "$systemdsystemunitdir"; then
+        echo "smartd systemd file:    \`eval eval eval echo $systemdsystemunitdir\`/smartd.service"
+        if test -n "$systemdenvfile"; then
+          echo "smartd environ file:    \`eval eval eval echo $systemdenvfile\`"
+        else
+          echo "smartd environ file:    [[disabled]]"
+        fi
+      fi
+      if test -n "$savestates"; then
+        echo "smartd save files:      \`eval eval eval echo $savestates\`MODEL-SERIAL.TYPE.state"
+      else
+        echo "smartd save files:      [[disabled]]"
+      fi
+      if test -n "$attributelog"; then
+        echo "smartd attribute logs:  \`eval eval eval echo $attributelog\`MODEL-SERIAL.TYPE.csv"
+      else
+        echo "smartd attribute logs:  [[disabled]]"
+      fi
+      echo "libcap-ng support:      $use_libcap_ng"
+      case "$host_os" in
+        linux*) echo "SELinux support:        ${with_selinux-no}" ;;
+      esac
+      case "$host_os" in
+        linux*|cygwin*) echo "NVMe DEVICESCAN:        ${with_nvme_devicescan-no}" ;;
+      esac
+      ;;
+  esac
+  echo "-----------------------------------------------------------------------------"
+`
+
+AC_MSG_NOTICE([
+$info
+])
+
+# TODO: Remove when NVMe support is no longer EXPERIMENTAL
+case "$host_os:${with_nvme_devicescan+set}" in
+  linux*:|cygwin*:|mingw*:)
+    AC_MSG_WARN([
+This version of smartmontools provides NVMe support which is still
+EXPERIMENTAL.  NVMe devices are not yet included in smartd.conf
+'DEVICESCAN' and 'smartctl --scan' unless '-d nvme' is specified.
+Use option '--with-nvme-devicescan' to include NVMe devices.
+Use option '--without-nvme-devicescan' to suppress this warning.])
+    ;;
+esac
+
+# TODO: Remove after smartmontools 6.5
+case "$host:${with_solaris_sparc_ata+set}" in
+  sparc-*-solaris*:)
+    AC_MSG_WARN([
+Legacy ATA support is no longer enabled by default on Solaris SPARC.
+The required source file 'os_solaris_ata.s' is no longer included in
+the source tarball but still available in the SVN repository.
+Use option '--with-solaris-sparc-ata' to enable legacy ATA support.
+Use option '--without-solaris-sparc-ata' to suppress this warning.])
     ;;
 esac
-echo "-----------------------------------------------------------------------------" >&AS_MESSAGE_FD
index 941d90a2048d7e6542eb38c4343ace6baafc0c99..91edb03f1949356c4a16df7f71e8f0d731d82635 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * dev_areca.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
  *
@@ -21,7 +21,7 @@
 #include "dev_interface.h"
 #include "dev_areca.h"
 
-const char * dev_areca_cpp_cvsid = "$Id: dev_areca.cpp 3872 2014-02-03 21:07:51Z chrfranke $"
+const char * dev_areca_cpp_cvsid = "$Id: dev_areca.cpp 4209 2016-01-22 20:49:44Z chrfranke $"
   DEV_ARECA_H_CVSID;
 
 #include "atacmds.h"
@@ -297,14 +297,14 @@ int generic_areca_device::arcmsr_ui_handler(unsigned char *areca_packet, int are
   if (expected==-3) {
     return set_err(EIO);
   }
-  expected = arcmsr_command_handler(ARCMSR_CLEAR_WQBUFFER, NULL, 0);
+  arcmsr_command_handler(ARCMSR_CLEAR_WQBUFFER, NULL, 0);
   expected = arcmsr_command_handler(ARCMSR_WRITE_WQBUFFER, areca_packet, areca_packet_len);
   if ( expected > 0 )
   {
     expected = arcmsr_command_handler(ARCMSR_READ_RQBUFFER, return_buff, sizeof(return_buff));
   }
 
-  if ( expected < 0 )
+  if ( expected < 3 + 1 ) // Prefix + Checksum
   {
     return -1;
   }
index 612717111567ff143e90e967a4c584aa51b2f3a8..0297be97e4e410a75ecf04396a2ff47b5dfa5846 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * dev_areca.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
  *
@@ -18,7 +18,7 @@
 #ifndef DEV_ARECA_H
 #define DEV_ARECA_H
 
-#define DEV_ARECA_H_CVSID "$Id: dev_areca.h 3854 2013-09-12 05:36:20Z chrfranke $"
+#define DEV_ARECA_H_CVSID "$Id: dev_areca.h 4146 2015-10-17 12:12:49Z chrfranke $"
 
 /////////////////////////////////////////////////////////////////////////////
 /// Areca RAID support
@@ -118,9 +118,10 @@ public:
   virtual bool arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
 
 protected:
-  generic_areca_device() : smart_device(never_called)
-  {
-  }
+  generic_areca_device()
+    : smart_device(never_called),
+      m_disknum(-1), m_encnum(-1)
+      { }
 
   void set_disknum(int disknum)
   {m_disknum = disknum;}
index 41c6234d015381c9887762d8d86d577eb3a96350..93cb339c5209a71093907eed13944168554557f4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * dev_ata_cmd_set.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -22,7 +22,7 @@
 
 #include <errno.h>
 
-const char * dev_ata_cmd_set_cpp_cvsid = "$Id: dev_ata_cmd_set.cpp,v 1.4 2008/10/24 21:49:23 manfred99 Exp $"
+const char * dev_ata_cmd_set_cpp_cvsid = "$Id: dev_ata_cmd_set.cpp 4122 2015-08-27 19:08:07Z chrfranke $"
   DEV_ATA_CMD_SET_H_CVSID;
 
 
index ec1457bd43647053edee1372ade7a2890eb86223..7cdddc198f3ea96189115e65d9efba12708a5c38 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * dev_ata_cmd_set.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -18,7 +18,7 @@
 #ifndef DEV_ATA_CMD_SET_H
 #define DEV_ATA_CMD_SET_H
 
-#define DEV_ATA_CMD_SET_H_CVSID "$Id: dev_ata_cmd_set.h,v 1.3 2008/08/23 21:32:12 chrfranke Exp $\n"
+#define DEV_ATA_CMD_SET_H_CVSID "$Id: dev_ata_cmd_set.h 4122 2015-08-27 19:08:07Z chrfranke $"
 
 #include "atacmds.h" // smart_command_set
 #include "dev_interface.h"
index 71bf8bcd35cf2fe30e0a2a0947649c5785da0afa..dd428413a393b8b7e308e6267a3986bee24d1603 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * dev_interface.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-16 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <sys/timeb.h>
 #endif
 
-const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 3741 2013-01-02 17:06:54Z chrfranke $"
+const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 4283 2016-04-10 12:55:59Z chrfranke $"
   DEV_INTERFACE_H_CVSID;
 
 /////////////////////////////////////////////////////////////////////////////
 // smart_device
 
+int smart_device::s_num_objects = 0;
+
 smart_device::smart_device(smart_interface * intf, const char * dev_name,
     const char * dev_type, const char * req_type)
 : m_intf(intf), m_info(dev_name, dev_type, req_type),
-  m_ata_ptr(0), m_scsi_ptr(0)
+  m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
 {
+  s_num_objects++;
 }
 
 smart_device::smart_device(do_not_use_in_implementation_classes)
-: m_intf(0), m_ata_ptr(0), m_scsi_ptr(0)
+: m_intf(0), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
 {
   throw std::logic_error("smart_device: wrong constructor called in implementation class");
 }
 
 smart_device::~smart_device() throw()
 {
+  s_num_objects--;
 }
 
 bool smart_device::is_syscall_unsup() const
@@ -88,6 +92,11 @@ smart_device * smart_device::autodetect_open()
   return this;
 }
 
+bool smart_device::is_powered_down()
+{
+  return false;
+}
+
 bool smart_device::owns(const smart_device * /*dev*/) const
 {
   return false;
@@ -194,6 +203,20 @@ bool ata_device::ata_identify_is_cached() const
 }
 
 
+/////////////////////////////////////////////////////////////////////////////
+// nvme_device
+
+bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */)
+{
+  if (!status)
+    throw std::logic_error("nvme_device: set_nvme_err() called with status=0");
+
+  out.status = status;
+  out.status_valid = true;
+  return set_err(EIO, "%sNVMe Status 0x%02x", (msg ? msg : ""), status);
+}
+
+
 /////////////////////////////////////////////////////////////////////////////
 // tunnelled_device_base
 
@@ -258,7 +281,8 @@ std::string smart_interface::get_valid_dev_types_str()
 {
   // default
   std::string s =
-    "ata, scsi, sat[,auto][,N][+TYPE], usbcypress[,X], usbjmicron[,p][,x][,N], usbsunplus";
+    "ata, scsi, nvme[,NSID], sat[,auto][,N][+TYPE], "
+    "usbcypress[,X], usbjmicron[,p][,x][,N], usbprolific, usbsunplus";
   // append custom
   std::string s2 = get_valid_custom_dev_types_str();
   if (!s2.empty()) {
@@ -364,6 +388,17 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
   else if (!strcmp(type, "scsi"))
     dev = get_scsi_device(name, type);
 
+  else if (str_starts_with(type, "nvme")) {
+    int n1 = -1, n2 = -1, len = strlen(type);
+    unsigned nsid = 0; // invalid namespace id -> use default
+    sscanf(type, "nvme%n,0x%x%n", &n1, &nsid, &n2);
+    if (!(n1 == len || n2 == len)) {
+      set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type);
+      return 0;
+    }
+    dev = get_nvme_device(name, type, nsid);
+  }
+
   else if (  ((!strncmp(type, "sat", 3) && (!type[3] || strchr(",+", type[3])))
            || (!strncmp(type, "usb", 3)))) {
     // Split "sat...+base..." -> ("sat...", "base...")
@@ -384,11 +419,7 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
       return 0;
     }
     // Attach SAT tunnel
-    ata_device * satdev = get_sat_device(sattype.c_str(), basedev->to_scsi());
-    if (!satdev)
-      return 0;
-    basedev.release();
-    return satdev;
+    return get_sat_device(sattype.c_str(), basedev.release()->to_scsi());
   }
 
   else {
@@ -400,6 +431,31 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
   return dev;
 }
 
+bool smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const smart_devtype_list & types, const char * pattern /* = 0 */)
+{
+  unsigned n = types.size();
+  if (n == 0)
+    return scan_smart_devices(devlist, (const char *)0, pattern);
+  if (n == 1)
+    return scan_smart_devices(devlist, types.front().c_str(), pattern);
+
+  for (unsigned i = 0; i < n; i++) {
+    smart_device_list tmplist;
+    if (!scan_smart_devices(tmplist, types[i].c_str(), pattern))
+      return false;
+    devlist.append(tmplist);
+  }
+
+  return true;
+}
+
+nvme_device * smart_interface::get_nvme_device(const char * /*name*/, const char * /*type*/, unsigned /*nsid*/)
+{
+  set_err(ENOSYS, "NVMe devices are not supported in this version of smartmontools");
+  return 0;
+}
+
 smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
 {
   return 0;
index fe3bb2a38d4f7a81208337e635cd0b5bd373c68b..44368507135b5ae28d438bd56262783c0fc8c504 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * dev_interface.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-16 Christian Franke
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 #ifndef DEV_INTERFACE_H
 #define DEV_INTERFACE_H
 
-#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 3663 2012-10-24 20:35:55Z chrfranke $\n"
+#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 4283 2016-04-10 12:55:59Z chrfranke $\n"
 
 #include "utility.h"
 
@@ -33,6 +33,7 @@
 class smart_interface;
 class ata_device;
 class scsi_device;
+class nvme_device;
 
 /// Base class for all devices
 class smart_device
@@ -78,7 +79,7 @@ protected:
   enum do_not_use_in_implementation_classes { never_called };
   /// Dummy constructor for abstract classes.
   /// Must never be called in implementation classes.
-  smart_device(do_not_use_in_implementation_classes);
+  explicit smart_device(do_not_use_in_implementation_classes);
 
 public:
   virtual ~smart_device() throw();
@@ -94,6 +95,9 @@ public:
   /// Return true if SCSI device
   bool is_scsi() const
     { return !!m_scsi_ptr; }
+  /// Return true if NVMe device
+  bool is_nvme() const
+    { return !!m_nvme_ptr; }
 
   /// Downcast to ATA device.
   ata_device * to_ata()
@@ -107,6 +111,12 @@ public:
   /// Downcast to SCSI device (const).
   const scsi_device * to_scsi() const
     { return m_scsi_ptr; }
+  /// Downcast to NVMe device.
+  nvme_device * to_nvme()
+    { return m_nvme_ptr; }
+  /// Downcast to NVMe device (const).
+  const nvme_device * to_nvme() const
+    { return m_nvme_ptr; }
 
   ///////////////////////////////////////////////
   // Device information
@@ -169,6 +179,10 @@ public:
   /// Message is retrieved from interface's get_msg_for_errno(no).
   bool set_err(int no);
 
+  /// Get current number of allocated 'smart_device' objects.
+  static int get_num_objects()
+    { return s_num_objects; }
+
 // Operations
 public:
   ///////////////////////////////////////////////
@@ -190,6 +204,17 @@ public:
   /// Default implementation calls 'open()' and returns 'this'.
   virtual smart_device * autodetect_open();
 
+  ///////////////////////////////////////////////
+  // Support for checking power mode reported by operating system
+
+  /// Early test if device is powered up or down.
+  /// Can be used without calling 'open()' first!
+  /// Return true when device is powered down, false when
+  /// powered up. If this function is not implemented or
+  /// the mode cannot be determined, return false.
+  /// Default implementation returns false.
+  virtual bool is_powered_down();
+
   ///////////////////////////////////////////////
   // Support for tunnelled devices
 
@@ -215,12 +240,17 @@ private:
   device_info m_info;
   error_info m_err;
 
-  // Pointers for to_ata(), to_scsi(),
-  // set by ATA/SCSI interface classes.
+  // Pointers for to_ata(), to_scsi(), to_nvme()
+  // set by ATA/SCSI/NVMe interface classes.
   friend class ata_device;
   ata_device * m_ata_ptr;
   friend class scsi_device;
   scsi_device * m_scsi_ptr;
+  friend class nvme_device;
+  nvme_device * m_nvme_ptr;
+
+  // Number of objects.
+  static int s_num_objects;
 
   // Prevent copy/assigment
   smart_device(const smart_device &);
@@ -573,6 +603,94 @@ protected:
 };
 
 
+/////////////////////////////////////////////////////////////////////////////
+// NVMe specific interface
+
+/// NVMe pass through input parameters
+struct nvme_cmd_in
+{
+  unsigned char opcode; ///< Opcode (CDW0 07:00)
+  unsigned nsid; ///< Namespace ID
+  unsigned cdw10, cdw11, cdw12, cdw13, cdw14, cdw15; ///< Cmd specific
+
+  void * buffer; ///< Pointer to data buffer
+  unsigned size; ///< Size of buffer
+
+  enum {
+    no_data = 0x0, data_out = 0x1, data_in = 0x2, data_io = 0x3
+  };
+
+  /// Get I/O direction from opcode
+  unsigned char direction() const
+    { return (opcode & 0x3); }
+
+  // Prepare for DATA IN command
+  void set_data_in(unsigned char op, void * buf, unsigned sz)
+    {
+      opcode = op;
+      if (direction() != data_in)
+        throw std::logic_error("invalid opcode for DATA IN");
+      buffer = buf;
+      size = sz;
+    }
+
+  nvme_cmd_in()
+    : opcode(0), nsid(0),
+      cdw10(0), cdw11(0), cdw12(0), cdw13(0), cdw14(0), cdw15(0),
+      buffer(0), size(0)
+    { }
+};
+
+/// NVMe pass through output parameters
+struct nvme_cmd_out
+{
+   unsigned result; ///< Command specific result (DW0)
+   unsigned short status; ///< Status Field (DW3 31:17)
+   bool status_valid; ///< true if status is valid
+
+   nvme_cmd_out()
+     : result(0), status(0), status_valid(false)
+     { }
+};
+
+/// NVMe device access
+class nvme_device
+: virtual public /*extends*/ smart_device
+{
+public:
+  /// NVMe pass through.
+  /// Return false on error.
+  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) = 0;
+
+  /// Get namespace id.
+  unsigned get_nsid() const
+    { return m_nsid; }
+
+protected:
+  /// Hide/unhide NVMe interface.
+  void hide_nvme(bool hide = true)
+    { m_nvme_ptr = (!hide ? this : 0); }
+
+  /// Constructor requires namespace ID, registers device as NVMe.
+  explicit nvme_device(unsigned nsid)
+    : smart_device(never_called),
+      m_nsid(nsid)
+    { hide_nvme(false); }
+
+  /// Set namespace id.
+  /// Should be called in open() function if get_nsid() returns 0.
+  void set_nsid(unsigned nsid)
+    { m_nsid = nsid; }
+
+  /// Set last error number and message if pass-through returns NVMe error status.
+  /// Returns false always to allow use as a return expression.
+  bool set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg = 0);
+
+private:
+  unsigned m_nsid;
+};
+
+
 /////////////////////////////////////////////////////////////////////////////
 /// Smart pointer class for device pointers
 
@@ -660,6 +778,7 @@ private:
 typedef any_device_auto_ptr<smart_device> smart_device_auto_ptr;
 typedef any_device_auto_ptr<ata_device>   ata_device_auto_ptr;
 typedef any_device_auto_ptr<scsi_device>  scsi_device_auto_ptr;
+typedef any_device_auto_ptr<nvme_device>  nvme_device_auto_ptr;
 
 
 /////////////////////////////////////////////////////////////////////////////
@@ -714,6 +833,17 @@ public:
       return dev;
     }
 
+  void append(smart_device_list & devlist)
+    {
+      for (unsigned i = 0; i < devlist.size(); i++) {
+        smart_device * dev = devlist.at(i);
+        if (!dev)
+          continue;
+        push_back(dev);
+        devlist.m_list.at(i) = 0;
+      }
+    }
+
 // Implementation
 private:
   std::vector<smart_device *> m_list;
@@ -724,6 +854,10 @@ private:
 };
 
 
+/// List of types for DEVICESCAN
+typedef std::vector<std::string> smart_devtype_list;
+
+
 /////////////////////////////////////////////////////////////////////////////
 // smart_interface
 
@@ -820,10 +954,20 @@ public:
 
   /// Fill 'devlist' with devices of some 'type' with device names
   /// specified by some optional 'pattern'.
+  /// Use platform specific default if 'type' is empty or 0.
   /// Return false on error.
   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
     const char * pattern = 0) = 0;
 
+  /// Fill 'devlist' with devices of all 'types' with device names
+  /// specified by some optional 'pattern'.
+  /// Use platform specific default if 'types' is empty.
+  /// Return false on error.
+  /// Default implementation calls above function for all types
+  /// and concatenates the results.
+  virtual bool scan_smart_devices(smart_device_list & devlist,
+    const smart_devtype_list & types, const char * pattern = 0);
+
 protected:
   /// Return standard ATA device.
   virtual ata_device * get_ata_device(const char * name, const char * type) = 0;
@@ -831,6 +975,11 @@ protected:
   /// Return standard SCSI device.
   virtual scsi_device * get_scsi_device(const char * name, const char * type) = 0;
 
+  /// Return standard NVMe device.
+  /// Default implementation returns 0.
+  virtual nvme_device * get_nvme_device(const char * name, const char * type,
+    unsigned nsid);
+
   /// Autodetect device if no device type specified.
   virtual smart_device * autodetect_smart_device(const char * name) = 0;
 
@@ -843,7 +992,9 @@ protected:
   /// Default implementation returns empty string.
   virtual std::string get_valid_custom_dev_types_str();
 
-  /// Return ATA->SCSI filter for SAT or USB.
+  /// Return ATA->SCSI filter for a SAT or USB 'type'.
+  /// Device 'scsidev' is used for SCSI access.
+  /// Return 0 and delete 'scsidev' on error.
   /// Override only if platform needs special handling.
   virtual ata_device * get_sat_device(const char * type, scsi_device * scsidev);
   //{ implemented in scsiata.cpp }
index ca4575bcd5d287ccc01e94519633ac00c5f5fd24..4d4c0faa54dca0fe072fe1de15aadc1b09a744c6 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * dev_legacy.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008-11 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-16 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
 
 #include <errno.h>
 
-const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 3263 2011-02-20 18:32:56Z chrfranke $"
+const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 4251 2016-03-26 16:48:32Z chrfranke $"
   DEV_INTERFACE_H_CVSID;
 
 /////////////////////////////////////////////////////////////////////////////
@@ -275,10 +275,13 @@ smart_device * legacy_smart_interface::autodetect_smart_device(const char * name
 
 static void free_devnames(char * * devnames, int numdevs)
 {
-  static const char version[] = "$Id: dev_legacy.cpp 3263 2011-02-20 18:32:56Z chrfranke $";
-  for (int i = 0; i < numdevs; i++)
-    FreeNonZero(devnames[i], -1,__LINE__, version);
-  FreeNonZero(devnames, (sizeof (char*) * numdevs),__LINE__, version);
+  if (!devnames)
+    return;
+  for (int i = 0; i < numdevs; i++) {
+    if (devnames[i])
+      free(devnames[i]);
+  }
+  free(devnames);
 }
 
 bool legacy_smart_interface::scan_smart_devices(smart_device_list & devlist,
index 173c6f4401ff4038e6d601b90621cc94cdd564bd..aa4ff2b0679078d081fee727aa9f1f4a6b173f44 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * dev_tunnelled.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -18,7 +18,7 @@
 #ifndef DEV_TUNNELLED_H
 #define DEV_TUNNELLED_H
 
-#define DEV_TUNNELLED_H_CVSID "$Id: dev_tunnelled.h,v 1.1 2008/07/25 21:16:00 chrfranke Exp $\n"
+#define DEV_TUNNELLED_H_CVSID "$Id: dev_tunnelled.h 4122 2015-08-27 19:08:07Z chrfranke $"
 
 #include "dev_interface.h"
 
index f88d4a43a497c1cb096629e239e59454926b036f..64e21dc7b360203f852ab9cbb63848f5c2148a86 100755 (executable)
@@ -1,10 +1,9 @@
 #!/bin/bash
 #
 # do a smartmontools release
-# (C) 2003-11 Bruce Allen <ballen4705@users.sourceforge.net>,
-#             Guido Guenther <agx@sigxcpu.org>
-# (C) 2006-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
-# $Id: do_release 3765 2013-02-05 17:17:13Z chrfranke $
+# (C) 2003-11 Bruce Allen, Guido Guenther
+# (C) 2006-15 Christian Franke
+# $Id: do_release 4071 2015-04-26 18:25:12Z chrfranke $
 
 # Notes on generating releases:
 # (1) update NEWS
@@ -15,8 +14,8 @@
 
 set -e
 
-# Smartmontools Signing Key (through 2014)
-KEYID=0x8F6ED8AA
+# Smartmontools Signing Key (through 2016)
+KEYID=0xC4A4903A
 
 inc_release()
 {
index 0d6288b5d5ddb2f462a977ac84afe7611d922b0e..65c1c4527fa0b78cca0519148de2f3848e9870fa 100644 (file)
--- a/drivedb.h
+++ b/drivedb.h
@@ -1,10 +1,10 @@
 /*
  * drivedb.h - smartmontools drive database file
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-11 Philip Williams, Bruce Allen
- * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-16 Christian Franke
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 /*
 const drive_settings builtin_knowndrives[] = {
  */
-  { "$Id: drivedb.h 3990 2014-09-29 17:59:37Z samm2 $",
+  { "$Id: drivedb.h 4324 2016-05-31 20:45:50Z chrfranke $",
     "-", "-",
     "This is a dummy entry to hold the SVN-Id of drivedb.h",
     ""
-  /* Default settings:
+  },
+  { "DEFAULT",
+    "-", "-",
+    "Default settings",
     "-v 1,raw48,Raw_Read_Error_Rate "
     "-v 2,raw48,Throughput_Performance "
     "-v 3,raw16(avg16),Spin_Up_Time "
     "-v 4,raw48,Start_Stop_Count "
     "-v 5,raw16(raw16),Reallocated_Sector_Ct "
-    "-v 6,raw48,Read_Channel_Margin "             // HDD only
-    "-v 7,raw48,Seek_Error_Rate "                 // HDD only
-    "-v 8,raw48,Seek_Time_Performance "           // HDD only
+    "-v 6,raw48,Read_Channel_Margin,HDD "
+    "-v 7,raw48,Seek_Error_Rate,HDD "
+    "-v 8,raw48,Seek_Time_Performance,HDD "
     "-v 9,raw24(raw8),Power_On_Hours "
-    "-v 10,raw48,Spin_Retry_Count "               // HDD only
-    "-v 11,raw48,Calibration_Retry_Count "        // HDD only
+    "-v 10,raw48,Spin_Retry_Count,HDD "
+    "-v 11,raw48,Calibration_Retry_Count,HDD "
     "-v 12,raw48,Power_Cycle_Count "
     "-v 13,raw48,Read_Soft_Error_Rate "
     //  14-174 Unknown_Attribute
-    "-v 175,raw48,Program_Fail_Count_Chip "       // SSD only
-    "-v 176,raw48,Erase_Fail_Count_Chip "         // SSD only
-    "-v 177,raw48,Wear_Leveling_Count "           // SSD only
-    "-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip "        // SSD only
-    "-v 179,raw48,Used_Rsvd_Blk_Cnt_Tot "         // SSD only
-    "-v 180,raw48,Unused_Rsvd_Blk_Cnt_Tot "       // SSD only
+    "-v 175,raw48,Program_Fail_Count_Chip,SSD "
+    "-v 176,raw48,Erase_Fail_Count_Chip,SSD "
+    "-v 177,raw48,Wear_Leveling_Count,SSD "
+    "-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip,SSD "
+    "-v 179,raw48,Used_Rsvd_Blk_Cnt_Tot,SSD "
+    "-v 180,raw48,Unused_Rsvd_Blk_Cnt_Tot,SSD "
     "-v 181,raw48,Program_Fail_Cnt_Total "
-    "-v 182,raw48,Erase_Fail_Count_Total "        // SSD only
+    "-v 182,raw48,Erase_Fail_Count_Total,SSD "
     "-v 183,raw48,Runtime_Bad_Block "
     "-v 184,raw48,End-to-End_Error "
     //  185-186 Unknown_Attribute
     "-v 187,raw48,Reported_Uncorrect "
     "-v 188,raw48,Command_Timeout "
-    "-v 189,raw48,High_Fly_Writes "               // HDD only
+    "-v 189,raw48,High_Fly_Writes,HDD "
     "-v 190,tempminmax,Airflow_Temperature_Cel "
-    "-v 191,raw48,G-Sense_Error_Rate "            // HDD only
+    "-v 191,raw48,G-Sense_Error_Rate,HDD "
     "-v 192,raw48,Power-Off_Retract_Count "
-    "-v 193,raw48,Load_Cycle_Count "              // HDD only
+    "-v 193,raw48,Load_Cycle_Count,HDD "
     "-v 194,tempminmax,Temperature_Celsius "
     "-v 195,raw48,Hardware_ECC_Recovered "
     "-v 196,raw16(raw16),Reallocated_Event_Count "
     "-v 197,raw48,Current_Pending_Sector "
     "-v 198,raw48,Offline_Uncorrectable "
     "-v 199,raw48,UDMA_CRC_Error_Count "
-    "-v 200,raw48,Multi_Zone_Error_Rate "         // HDD only
-    "-v 201,raw48,Soft_Read_Error_Rate "          // HDD only
-    "-v 202,raw48,Data_Address_Mark_Errs "        // HDD only
+    "-v 200,raw48,Multi_Zone_Error_Rate,HDD "
+    "-v 201,raw48,Soft_Read_Error_Rate,HDD "
+    "-v 202,raw48,Data_Address_Mark_Errs,HDD "
     "-v 203,raw48,Run_Out_Cancel "
     "-v 204,raw48,Soft_ECC_Correction "
     "-v 205,raw48,Thermal_Asperity_Rate "
-    "-v 206,raw48,Flying_Height "                 // HDD only
-    "-v 207,raw48,Spin_High_Current "             // HDD only
-    "-v 208,raw48,Spin_Buzz "                     // HDD only
-    "-v 209,raw48,Offline_Seek_Performnce "       // HDD only
+    "-v 206,raw48,Flying_Height,HDD "
+    "-v 207,raw48,Spin_High_Current,HDD "
+    "-v 208,raw48,Spin_Buzz,HDD "
+    "-v 209,raw48,Offline_Seek_Performnce,HDD "
     //  210-219 Unknown_Attribute
-    "-v 220,raw48,Disk_Shift "                    // HDD only
-    "-v 221,raw48,G-Sense_Error_Rate "            // HDD only
-    "-v 222,raw48,Loaded_Hours "                  // HDD only
-    "-v 223,raw48,Load_Retry_Count "              // HDD only
-    "-v 224,raw48,Load_Friction "                 // HDD only
-    "-v 225,raw48,Load_Cycle_Count "              // HDD only
-    "-v 226,raw48,Load-in_Time "                  // HDD only
-    "-v 227,raw48,Torq-amp_Count "                // HDD only
+    "-v 220,raw48,Disk_Shift,HDD "
+    "-v 221,raw48,G-Sense_Error_Rate,HDD "
+    "-v 222,raw48,Loaded_Hours,HDD "
+    "-v 223,raw48,Load_Retry_Count,HDD "
+    "-v 224,raw48,Load_Friction,HDD "
+    "-v 225,raw48,Load_Cycle_Count,HDD "
+    "-v 226,raw48,Load-in_Time,HDD "
+    "-v 227,raw48,Torq-amp_Count,HDD "
     "-v 228,raw48,Power-off_Retract_Count "
     //  229 Unknown_Attribute
-    "-v 230,raw48,Head_Amplitude "                // HDD only
+    "-v 230,raw48,Head_Amplitude,HDD "
     "-v 231,raw48,Temperature_Celsius "
     "-v 232,raw48,Available_Reservd_Space "
-    "-v 233,raw48,Media_Wearout_Indicator "       // SSD only
+    "-v 233,raw48,Media_Wearout_Indicator,SSD "
     //  234-239 Unknown_Attribute
-    "-v 240,raw24(raw8),Head_Flying_Hours "       // HDD only, smartmontools <= r3966: raw48
+    "-v 240,raw24(raw8),Head_Flying_Hours,HDD "
     "-v 241,raw48,Total_LBAs_Written "
     "-v 242,raw48,Total_LBAs_Read "
     //  243-249 Unknown_Attribute
     "-v 250,raw48,Read_Error_Retry_Rate "
     //  251-253 Unknown_Attribute
-    "-v 254,raw48,Free_Fall_Sensor "              // HDD only
-  */
+    "-v 254,raw48,Free_Fall_Sensor,HDD"
   },
   { "Apacer SSD",
     "(2|4|8|16|32)GB SATA Flash Drive", // tested with APSDM002G15AN-CT/SFDDA01C and SFI2101D, APSDM004G13AN-AT/SFDE001A
@@ -166,9 +168,16 @@ const drive_settings builtin_knowndrives[] = {
     "-v 167,raw48,SSD_Protect_Mode "
     "-v 168,raw48,SATA_PHY_Err_Ct "
   },
+  { "Apple MacBook Air SSD", // probably Toshiba
+    "APPLE SSD TS(064|128)E", // tested with APPLE SSD TS064E/TQAABBF0
+    "", "",
+    "-v 173,raw48,Wear_Leveling_Count " //  ]
+    "-v 241,raw48,Host_Writes_GiB "     //  ]  guessed (ticket #655)
+    "-v 242,raw48,Host_Reades_GiB "     //  ]
+  },
   { "Apple SD/SM/TS...E/F SSDs", // SanDisk/Samsung/Toshiba?
-    "APPLE SSD (S[DM]|TS)0?(128|256|512|768)[EF]", // tested with APPLE SSD SD256E/1021AP, SD0128F/A223321
-     // APPLE SSD SM768E/CXM90A1Q, SM0512F/UXM2JA1Q, TS0256F/109L0704
+    "APPLE SSD (S[DM]|TS)0?(128|256|512|768)[EFG]", // tested with APPLE SSD SD256E/1021AP, SD0128F/A223321
+     // APPLE SSD SM768E/CXM90A1Q, SM0512F/UXM2JA1Q, TS0256F/109L0704, SM0512G/BXW1SA0Q
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
@@ -221,8 +230,8 @@ const drive_settings builtin_knowndrives[] = {
   { "Crucial/Micron RealSSD m4/C400/P400", // Marvell 9176, fixed firmware
     "C400-MTFDDA[ACK](064|128|256|512)MAM|"
     "M4-CT(064|128|256|512)M4SSD[123]|" // tested with M4-CT512M4SSD2/0309
-    "MTFDDAK(064|128|256|512|050|100|200|400)MA[RN]-1[JKS]1AA.*", // tested with
-                                             // MTFDDAK256MAR-1K1AA/MA52
+    "MTFDDAK(064|128|256|512|050|100|200|400)MA[MNR]-1[JKS]1.*", // tested with
+       // MTFDDAK256MAR-1K1AA/MA52, MTFDDAK256MAM-1K12/08TH
     "030[9-Z]|03[1-Z].|0[4-Z]..|[1-Z]....*", // >= "0309"
     "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
@@ -270,11 +279,18 @@ const drive_settings builtin_knowndrives[] = {
     "-v 202,raw48,Perc_Rated_Life_Used "
     "-v 206,raw48,Write_Error_Rate"
   },
-  { "Crucial/Micron MX100/M500/M510/M550 Client SSDs",
+  { "Crucial/Micron MX100/MX200/M5x0/M600 Client SSDs",
     "Crucial_CT(128|256|512)MX100SSD1|"// tested with Crucial_CT256MX100SSD1/MU01
+    "Crucial_CT(200|250|256|500|512|1000|1024)MX200SSD[1346]|" // tested with Crucial_CT500MX200SSD1/MU01,
+      // Crucial_CT1024MX200SSD1/MU01, Crucial_CT250MX200SSD3/MU01, Crucial_CT250MX200SSD1/MU03
+    "Crucial_CT(120|240|480|960)M500SSD[134]|" // tested with Crucial_CT960M500SSD1/MU03,
+      // Crucial_CT240M500SSD4/MU05
+    "Crucial_CT(128|256|512|1024)M550SSD[13]|" // tested with Crucial_CT512M550SSD3/MU01,
+      // Crucial_CT1024M550SSD1/MU01
     "Micron_M500_MTFDDA[KTV](120|240|480|960)MAV|"// tested with Micron_M500_MTFDDAK960MAV/MU05
     "(Micron_)?M510[_-]MTFDDA[KTV](128|256)MAZ|" // tested with M510-MTFDDAK256MAZ/MU01
-    "(Micron_)?M550[_-]MTFDDA[KTV](064|128|256|512|1T0)MAY", // tested with M550-MTFDDAK256MAY/MU01
+    "(Micron_)?M550[_-]MTFDDA[KTV](064|128|256|512|1T0)MAY|" // tested with M550-MTFDDAK256MAY/MU01
+    "Micron_M600_(EE|MT)FDDA[KTV](128|256|512|1T0)MBF[25Z]?", // tested with Micron_M600_MTFDDAK1T0MBF/MU01
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
     "-v 5,raw48,Reallocate_NAND_Blk_Cnt "
@@ -300,9 +316,11 @@ const drive_settings builtin_knowndrives[] = {
     "-v 247,raw48,Host_Program_Page_Count "
     "-v 248,raw48,Bckgnd_Program_Page_Cnt"
   },
-  { "Micron M500DC Enterprise SSDs",
-    "Micron_M500DC_(EE|MT)FDDA[AK](120|240|480|800)MBB", // tested with
+  { "Micron M500DC/M510DC Enterprise SSDs",
+    "Micron_M500DC_(EE|MT)FDDA[AK](120|240|480|800)MBB|" // tested with
       // Micron_M500DC_EEFDDAA120MBB/129, Micron_M500DC_MTFDDAK800MBB/0129
+    "MICRON_M510DC_(EE|MT)FDDAK(120|240|480|800|960)MBP", // tested with
+      // Micron_M510DC_MTFDDAK240MBP/0005
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
     "-v 5,raw48,Reallocated_Block_Count "
@@ -326,18 +344,48 @@ const drive_settings builtin_knowndrives[] = {
     "-v 247,raw48,Host_Program_Page_Count "
     "-v 248,raw48,Bckgnd_Program_Page_Cnt"
   },
+  { "SandForce Driven SSDs", // Corsair Force LS with buggy firmware only
+    "Corsair Force LS SSD", // tested with Corsair Force LS SSD/S9FM01.8
+    "S9FM01\\.8",
+    "A firmware update is available for this drive.\n"
+    "It is HIGHLY RECOMMENDED for drives with specific serial numbers.\n"
+    "See the following web pages for details:\n"
+    "http://www.corsair.com/en-us/force-series-ls-60gb-sata-3-6gb-s-ssd\n"
+    "https://www.smartmontools.org/ticket/628",
+    "-v 1,raw24/raw32,Raw_Read_Error_Rate "
+    "-v 5,raw48,Retired_Block_Count "
+    "-v 9,msec24hour32,Power_On_Hours_and_Msec "
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 162,raw48,Unknown_SandForce_Attr "
+    "-v 170,raw48,Reserve_Block_Count "
+    "-v 172,raw48,Erase_Fail_Count "
+    "-v 173,raw48,Unknown_SandForce_Attr "
+    "-v 174,raw48,Unexpect_Power_Loss_Ct "
+    "-v 181,raw48,Program_Fail_Count "
+  //"-v 187,raw48,Reported_Uncorrect "
+  //"-v 192,raw48,Power-Off_Retract_Count "
+  //"-v 194,tempminmax,Temperature_Celsius "
+  //"-v 196,raw16(raw16),Reallocated_Event_Count "
+    "-v 218,raw48,Unknown_SandForce_Attr "
+    "-v 231,raw48,SSD_Life_Left "
+    "-v 241,raw48,Lifetime_Writes_GiB "
+    "-v 242,raw48,Lifetime_Reads_GiB"
+  },
   { "SandForce Driven SSDs",
     "SandForce 1st Ed\\.|" // Demo Drive, tested with firmware 320A13F0
     "ADATA SSD S(396|510|599) .?..GB|" // tested with ADATA SSD S510 60GB/320ABBF0,
       // ADATA SSD S599 256GB/3.1.0, 64GB/3.4.6
     "ADATA SP[389]00|" // tested with ADATA SP300/5.0.2d, SP800/5.0.6c,
       // ADATA SP900/5.0.6 (Premier Pro, SF-2281)
-    "ADATA SSD SP900 (64|128|256)GB-DL2|" // tested with ADATA SSD SP900 256GB-DL2/5.0.6
+    "ADATA SSD S[PX]900 (64|128|256|512)GB-DL2|" // tested with ADATA SSD SP900 256GB-DL2/5.0.6,
+      // ADATA SSD SX900 512GB-DL2/5.8.2
     "ADATA XM11 (128|256)GB|" // tested with ADATA XM11 128GB/5.0.1
+    "ATP Velocity MIV (60|120|240|480)GB|" // tested with ATP Velocity MIV 480GB/110719
     "Corsair CSSD-F(40|60|80|115|120|160|240)GBP?2.*|" // Corsair Force, tested with
       // Corsair CSSD-F40GB2/1.1, Corsair CSSD-F115GB2-A/2.1a
     "Corsair Force ((3 |LS )?SSD|GS|GT)|" // SF-2281, tested with
-      // Corsair Force SSD/5.05, 3 SSD/1.3.2, GT/1.3.3, GS/5.03, LS SSD/S8FM06.5
+      // Corsair Force SSD/5.05, 3 SSD/1.3.2, GT/1.3.3, GS/5.03,
+      // Corsair Force LS SSD/S8FM06.5, S9FM01.8, S9FM02.0
     "FM-25S2S-(60|120|240)GBP2|" // G.SKILL Phoenix Pro, SF-1200, tested with
       // FM-25S2S-240GBP2/4.2
     "FTM(06|12|24|48)CT25H|" // Supertalent TeraDrive CT, tested with
@@ -345,15 +393,18 @@ const drive_settings builtin_knowndrives[] = {
     "KINGSTON SE50S3(100|240|480)G|" // tested with SE50S3100G/KE1ABBF0
     "KINGSTON SH10[03]S3(90|120|240|480)G|" // HyperX (3K), SF-2281, tested with
       // SH100S3240G/320ABBF0, SH103S3120G/505ABBF0
-    "KINGSTON SKC(300S37A|380S3)(60|120|240|480)G|" // SF-2281, tested with SKC300S37A120G/KC4ABBF0,
-      // SKC380S3120G/507ABBF0
+    "KINGSTON SKC(300S37A|380S3)(60|120|180|240|480)G|" // KC300, SF-2281, tested with
+      // SKC300S37A120G/KC4ABBF0, SKC380S3120G/507ABBF0
     "KINGSTON SVP200S3(7A)?(60|90|120|240|480)G|" // V+ 200, SF-2281, tested with
       // SVP200S37A480G/502ABBF0, SVP200S390G/332ABBF0
     "KINGSTON SMS200S3(30|60|120)G|" // mSATA, SF-2241, tested with SMS200S3120G/KC3ABBF0
     "KINGSTON SMS450S3(32|64|128)G|" // mSATA, SF-2281, tested with SMS450S3128G/503ABBF0
     "KINGSTON (SV300|SKC100|SE100)S3.*G|" // other SF-2281
-    "MKNSSDCR(45|60|90|120|180|240|480)GB(-DX)?|" // Mushkin Chronos (deluxe), SF-2281,
-      // tested with MKNSSDCR120GB
+    "MKNSSDCR(45|60|90|120|180|240|360|480)GB(-(7|DX7?|MX|G2))?|" // Mushkin Chronos (7mm/Deluxe/MX/G2),
+      // SF-2281, tested with MKNSSDCR120GB, MKNSSDCR120GB-MX/560ABBF0, MKNSSDCR480GB-DX7/603ABBF0
+    "MKNSSDEC(60|120|240|480|512)GB|" // Mushkin Enhanced ECO2, tested with MKNSSDEC120GB/604ABBF0
+    "MKNSSDAT(30|40|60|120|180|240|480)GB(-(DX|V))?|" // Mushkin Atlas (Deluxe/Value), mSATA, SF-2281,
+      // tested with MKNSSDAT120GB-V/540ABBF0
     "Mushkin MKNSSDCL(40|60|80|90|115|120|180|240|480)GB-DX2?|" // Mushkin Callisto deluxe,
       // SF-1200/1222, Mushkin MKNSSDCL60GB-DX/361A13F0
     "OCZ[ -](AGILITY2([ -]EX)?|COLOSSUS2|ONYX2|VERTEX(2|-LE))( [123]\\..*)?|" // SF-1200,
@@ -361,6 +412,7 @@ const drive_settings builtin_knowndrives[] = {
     "OCZ-NOCTI|" // mSATA, SF-2100, tested with OCZ-NOCTI/2.15
     "OCZ-REVODRIVE3?( X2)?|" // PCIe, SF-1200/2281, tested with
       // OCZ-REVODRIVE( X2)?/1.20, OCZ-REVODRIVE3 X2/2.11
+    "OCZ-REVODRIVE350|"
     "OCZ[ -](VELO|VERTEX2[ -](EX|PRO))( [123]\\..*)?|" // SF-1500, tested with
       // OCZ VERTEX2-PRO/1.10 (Bogus thresholds for attribute 232 and 235)
     "D2[CR]STK251...-....|" // OCZ Deneva 2 C/R, SF-22xx/25xx,
@@ -369,11 +421,13 @@ const drive_settings builtin_knowndrives[] = {
       // OCZ-AGILITY3/2.11, OCZ-SOLID3/2.15, OCZ-VERTEX3 MI/2.15
     "OCZ Z-DRIVE R4 [CR]M8[48]|" // PCIe, SF-2282/2582, tested with OCZ Z-DRIVE R4 CM84/2.13
       // (Bogus attributes under Linux)
+    "OCZ Z-DRIVE 4500|"
+    "OCZ-VELO DRIVE|" // VeloDrive R, PCIe, tested with OCZ-VELO DRIVE/1.33
     "TALOS2|" // OCZ Talos 2 C/R, SAS (works with -d sat), 2*SF-2282, tested with TALOS2/3.20E
     "(APOC|DENC|DENEVA|FTNC|GFGC|MANG|MMOC|NIMC|TMSC).*|" // other OCZ SF-1200,
       // tested with DENCSTE251M11-0120/1.33, DENEVA PCI-E/1.33
     "(DENR|DRSAK|EC188|NIMR|PSIR|TRSAK).*|" // other OCZ SF-1500
-    "OWC Aura Pro 6G SSD|" // tested with OWC Aura Pro 6G SSD/507ABBF0
+    "OWC Aura Pro( 6G SSD)?|" // tested with OWC Aura Pro 6G SSD/507ABBF0, OWC Aura Pro/603ABBF0
     "OWC Mercury Electra (Pro )?[36]G SSD|" // tested with
       // OWC Mercury Electra 6G SSD/502ABBF0, OWC Mercury Electra Pro 3G SSD/541ABBF0
     "OWC Mercury E(xtreme|XTREME) Pro (6G |RE )?SSD|" // tested with
@@ -381,6 +435,7 @@ const drive_settings builtin_knowndrives[] = {
     "Patriot Pyro|" // tested with Patriot Pyro/332ABBF0
     "SanDisk SDSSDX(60|120|240|480)GG25|" // SanDisk Extreme, SF-2281, tested with
       // SDSSDX240GG25/R201
+    "SanDisk SDSSDA(120|240|480)G|" // SanDisk SSD Plus, tested with SanDisk SDSSDA240G/U21010RL
     "SuperSSpeed S301 [0-9]*GB|" // SF-2281, tested with SuperSSpeed S301 128GB/503
     "SG9XCS2D(0?50|100|200|400)GESLT|" // Smart Storage Systems XceedIOPS2, tested with
       // SG9XCS2D200GESLT/SA03L370
@@ -392,6 +447,7 @@ const drive_settings builtin_knowndrives[] = {
       // TS128GSSD320, TS256GSSD720/5.2.0
     "UGB(88P|99S)GC...H[BF].|" // Unigen, tested with
       // UGB88PGC100HF2/MP Rev2, UGB99SGC100HB3/RC Rev3
+    "SG9XCS(1F|2D)(50|100|200|400)GE01|" // XceedIOPS, tested with SG9XCS2D50GE01/SA03F34V
     "VisionTek GoDrive (60|120|240|480)GB", // tested with VisionTek GoDrive 480GB/506ABBF0
     "", "",
     "-v 1,raw24/raw32,Raw_Read_Error_Rate "
@@ -400,9 +456,11 @@ const drive_settings builtin_knowndrives[] = {
   //"-v 12,raw48,Power_Cycle_Count "
     "-v 13,raw24/raw32,Soft_Read_Error_Rate "
     "-v 100,raw48,Gigabytes_Erased "
+    "-v 162,raw48,Unknown_SandForce_Attr " // Corsair Force LS SSD/S9FM01.8, *2.0
     "-v 170,raw48,Reserve_Block_Count "
     "-v 171,raw48,Program_Fail_Count "
     "-v 172,raw48,Erase_Fail_Count "
+    "-v 173,raw48,Unknown_SandForce_Attr " // Corsair Force LS SSD/S9FM01.8, *2.0
     "-v 174,raw48,Unexpect_Power_Loss_Ct "
     "-v 177,raw48,Wear_Range_Delta "
     "-v 181,raw48,Program_Fail_Count "
@@ -410,6 +468,7 @@ const drive_settings builtin_knowndrives[] = {
     "-v 184,raw48,IO_Error_Detect_Code_Ct "
   //"-v 187,raw48,Reported_Uncorrect "
     "-v 189,tempminmax,Airflow_Temperature_Cel "
+  //"-v 192,raw48,Power-Off_Retract_Count "
   //"-v 194,tempminmax,Temperature_Celsius "
     "-v 195,raw24/raw32,ECC_Uncorr_Error_Count "
   //"-v 196,raw16(raw16),Reallocated_Event_Count "
@@ -417,6 +476,7 @@ const drive_settings builtin_knowndrives[] = {
     "-v 199,raw48,SATA_CRC_Error_Count "
     "-v 201,raw24/raw32,Unc_Soft_Read_Err_Rate "
     "-v 204,raw24/raw32,Soft_ECC_Correct_Rate "
+    "-v 218,raw48,Unknown_SandForce_Attr " // Corsair Force LS SSD/S9FM01.8, *2.0
     "-v 230,raw48,Life_Curve_Status "
     "-v 231,raw48,SSD_Life_Left "
   //"-v 232,raw48,Available_Reservd_Space "
@@ -426,8 +486,48 @@ const drive_settings builtin_knowndrives[] = {
     "-v 241,raw48,Lifetime_Writes_GiB "
     "-v 242,raw48,Lifetime_Reads_GiB"
   },
+  { "Phison Driven SSDs", // see MKP_521_Phison_SMART_attribute.pdf
+    "KINGSTON SUV300S37A(120|240|480)G|" // UV300 SSD, tested with KINGSTON SUV300S37A120G/SAFM11.K
+    "KINGSTON SKC310S3B?7A960G|" // SSDNow KC310, KINGSTON SKC310S37A960G/SAFM00.r
+    "KINGSTON SKC400S37(128G|256G|512G|1T)|" // SSDNow KC400, KINGSTON SKC400S37128G
+    "KINGSTON SV310S3(7A|D7|N7A|B7A)960G|" // SSDNow V310
+    "KINGSTON SHSS3B?7A(120|240|480|960)G", // HyperX Savage
+    "", "",
+  //"-v 1,raw48,Raw_Read_Error_Rate "
+    "-v 2,raw48,Not_In_Use "
+    "-v 3,raw48,Not_In_Use "
+    "-v 5,raw48,Not_In_Use "
+    "-v 7,raw48,Not_In_Use "
+    "-v 8,raw48,Not_In_Use "
+  //"-v 9,raw24(raw8),Power_On_Hours "
+    "-v 5,raw48,Retired_Block_Count "
+  //"-v 9,raw24(raw8),Power_On_Hours "
+    "-v 10,raw48,Not_In_Use "
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 168,raw48,SATA_Phy_Error_Count "
+    "-v 170,raw24/raw24:z54z10,Bad_Blk_Ct_Erl/Lat " // Early bad block/Later bad block
+    "-v 173,raw16(avg16),MaxAvgErase_Ct "
+    "-v 175,raw48,Not_In_Use "
+    "-v 183,raw48,Unknown_Attribute "
+  //"-v 187,raw48,Reported_Uncorrect "
+    "-v 192,raw48,Unsafe_Shutdown_Count "
+  //"-v 194,tempminmax,Temperature_Celsius "
+    "-v 196,raw48,Not_In_Use "
+    "-v 197,raw48,Not_In_Use "
+    "-v 199,raw48,CRC_Error_Count "
+    "-v 218,raw48,CRC_Error_Count "
+    "-v 231,raw48,SSD_Life_Left "
+    "-v 233,raw48,Flash_Writes_GiB "
+    "-v 240,raw48,Not_In_Use "
+    "-v 241,raw48,Lifetime_Writes_GiB "
+    "-v 242,raw48,Lifetime_Reads_GiB "
+    "-v 244,raw48,Average_Erase_Count "
+    "-v 245,raw48,Max_Erase_Count "
+    "-v 246,raw48,Total_Erase_Count "
+  },
   { "Indilinx Barefoot based SSDs",
     "Corsair CSSD-V(32|60|64|128|256)GB2|" // Corsair Nova, tested with Corsair CSSD-V32GB2/2.2
+    "Corsair CMFSSD-(32|64|128|256)D1|" // Corsair Extreme, tested with Corsair CMFSSD-128D1/1.0
     "CRUCIAL_CT(64|128|256)M225|" // tested with CRUCIAL_CT64M225/1571
     "G.SKILL FALCON (64|128|256)GB SSD|" // tested with G.SKILL FALCON 128GB SSD/2030
     "OCZ[ -](AGILITY|ONYX|VERTEX( 1199|-TURBO| v1\\.10)?)|" // tested with
@@ -469,7 +569,7 @@ const drive_settings builtin_knowndrives[] = {
     "OCZ-PETROL|" // Everest 1, tested with OCZ-PETROL/3.12
     "OCZ-AGILITY4|" // Everest 2, tested with OCZ-AGILITY4/1.5.2
     "OCZ-VERTEX4", // Everest 2, tested with OCZ-VERTEX4/1.5
-    "", "", ""
+    "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
   //"-v 3,raw16(avg16),Spin_Up_Time "
   //"-v 4,raw48,Start_Stop_Count "
@@ -480,9 +580,12 @@ const drive_settings builtin_knowndrives[] = {
   //"-v 233,raw48,Media_Wearout_Indicator"
   },
   { "Indilinx Barefoot 3 based SSDs",
-    "OCZ-VECTOR|" // tested with OCZ-VECTOR/1.03
-    "OCZ-VERTEX450", // tested with OCZ-VERTEX450/1.0 (Barefoot 3 M10)
-    "", "", ""
+    "OCZ-VECTOR(1[58]0)?|" // tested with OCZ-VECTOR/1.03, OCZ-VECTOR150/1.2, OCZ-VECTOR180
+    "OCZ-VERTEX4[56]0A?|" // Barefoot 3 M10, tested with OCZ-VERTEX450/1.0, OCZ-VERTEX460/1.0, VERTEX460A
+    "OCZ-SABER1000|"
+    "OCZ-ARC100|"
+    "Radeon R7", // Barefoot 3 M00, tested with Radeon R7/1.00
+    "", "",
     "-v 5,raw48,Runtime_Bad_Block "
   //"-v 9,raw24(raw8),Power_On_Hours "
   //"-v 12,raw48,Power_Cycle_Count "
@@ -494,16 +597,28 @@ const drive_settings builtin_knowndrives[] = {
     "-v 197,raw48,Total_Unc_Read_Failures "
     "-v 198,raw48,Host_Reads_GiB "
     "-v 199,raw48,Host_Writes_GiB "
+    "-v 205,raw48,Max_Rated_PE_Count "
+    "-v 206,raw48,Min_Erase_Count "
+    "-v 207,raw48,Max_Erase_Count "
     "-v 208,raw48,Average_Erase_Count "
     "-v 210,raw48,SATA_CRC_Error_Count "
+    "-v 212,raw48,Pages_Requiring_Rd_Rtry "
+    "-v 213,raw48,Snmple_Retry_Attempts "
+    "-v 214,raw48,Adaptive_Retry_Attempts "
+    "-v 222,raw48,RAID_Recovery_Count "
+    "-v 224,raw48,In_Warranty "
+    "-v 225,raw48,DAS_Polarity "
+    "-v 226,raw48,Partial_Pfail "
+    "-v 230,raw48,Write_Throttling "
     "-v 233,raw48,Remaining_Lifetime_Perc "
-    "-v 241,raw48,Host_Writes_GiB " // M10
-    "-v 242,raw48,Host_Reads_GiB "  // M10
-    "-v 249,raw48,Total_NAND_Prog_Ct_GiB"
+    "-v 241,raw48,Host_Writes_GiB " // M00/M10
+    "-v 242,raw48,Host_Reads_GiB "  // M00/M10
+    "-v 249,raw48,Total_NAND_Prog_Ct_GiB "
+    "-v 251,raw48,Total_NAND_Read_Ct_GiB"
   },
-  { "OCZ Intrepid 3000 SSDs", // tested with OCZ INTREPID 3600/1.4.3.6, 3800/1.4.3.0
-    "OCZ INTREPID 3[68]00",
-    "", "", ""
+  { "OCZ Intrepid 3000 SSDs", // tested with OCZ INTREPID 3600/1.4.3.6, 3800/1.4.3.0, 3700/1.5.0.4
+    "OCZ INTREPID 3[678]00",
+    "", "",
     "-v 5,raw48,Runtime_Bad_Block "
   //"-v 9,raw24(raw8),Power_On_Hours "
   //"-v 12,raw48,Power_Cycle_Count "
@@ -535,6 +650,20 @@ const drive_settings builtin_knowndrives[] = {
     "-v 249,raw48,Total_NAND_Prog_Ct_GiB "
     "-v 251,raw48,Total_NAND_Read_Ct_GiB"
   },
+  {
+    "OCZ Trion SSDs",
+    "OCZ-TRION1[05]0", // tested with OCZ-TRION100/SAFM11.2A, TRION150/SAFZ72.2
+    "", "",
+  //"-v 9,raw24(raw8),Power_On_Hours "
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 167,raw48,SSD_Protect_Mode "
+    "-v 168,raw48,SATA_PHY_Error_Count "
+    "-v 169,raw48,Bad_Block_Count "
+    "-v 173,raw48,Erase_Count "
+    "-v 192,raw48,Unexpect_Power_Loss_Ct "
+  //"-v 194,tempminmax,Temperature_Celsius "
+    "-v 241,raw48,Host_Writes"
+  },
   { "InnoDisk InnoLite SATADOM D150QV-L SSDs", // tested with InnoLite SATADOM D150QV-L/120319
     "InnoLite SATADOM D150QV-L",
     "", "",
@@ -559,6 +688,140 @@ const drive_settings builtin_knowndrives[] = {
     "-v 236,raw48,Unstable_Power_Count "
     "-v 240,raw48,Write_Head"
   },
+  { "Innodisk 3ME SSDs", // tested with 2.5" SATA SSD 3ME/S140714
+    "((1\\.8|2\\.5)\"? SATA SSD|SATA Slim) 3ME",
+    "", "",
+  //"-v 1,raw48,Raw_Read_Error_Rate "
+  //"-v 2,raw48,Throughput_Performance "
+  //"-v 3,raw16(avg16),Spin_Up_Time "
+  //"-v 5,raw48,Reallocated_Sector_Count "
+    "-v 7,raw48,Seek_Error_Rate "       // ?
+    "-v 8,raw48,Seek_Time_Performance " // ?
+  //"-v 9,raw24(raw8),Power_On_Hours "
+    "-v 10,raw48,Spin_Retry_Count "     // ?
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 168,raw48,SATA_PHY_Error_Count "
+    "-v 169,raw48,Unknown_Innodisk_Attr "
+    "-v 170,raw16,Bad_Block_Count "
+    "-v 173,raw16,Erase_Count "
+    "-v 175,raw48,Bad_Cluster_Table_Count "
+    "-v 176,raw48,Uncorr_RECORD_Count "
+  //"-v 192,raw48,Power-Off_Retract_Count "
+  //"-v 194,tempminmax,Temperature_Celsius " // ] only in spec
+  //"-v 197,raw48,Current_Pending_Sector "
+    "-v 225,raw48,Unknown_Innodisk_Attr "
+    "-v 229,hex48,Flash_ID "
+    "-v 235,raw48,Later_Bad_Block "
+    "-v 236,raw48,Unstable_Power_Count "
+    "-v 240,raw48,Write_Head"
+  },
+  { "Innodisk 3IE2/3MG2/3SE2-P SSDs", // tested with 2.5" SATA SSD 3MG2-P/M140402,
+      // 1.8 SATA SSD 3IE2-P/M150821, 2.5" SATA SSD 3IE2-P/M150821,
+      // SATA Slim 3MG2-P/M141114, M.2 (S80) 3MG2-P/M141114, M.2 (S42) 3SE2-P/M150821
+    "((1\\.8|2\\.5)\"? SATA SSD|SATA Slim|M\\.2 \\(S(42|80)\\)) 3(IE|MG|SE)2-P",
+    "", "",
+  //"-v 1,raw48,Raw_Read_Error_Rate "
+  //"-v 2,raw48,Throughput_Performance "
+  //"-v 9,raw24(raw8),Power_On_Hours "
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 160,raw48,Uncorrectable_Error_Cnt "
+    "-v 161,raw48,Number_of_Pure_Spare "
+    "-v 163,raw48,Initial_Bad_Block_Count "
+    "-v 164,raw48,Total_Erase_Count "
+    "-v 165,raw48,Max_Erase_Count "
+    "-v 166,raw48,Min_Erase_Count "
+    "-v 167,raw48,Average_Erase_Count "
+    "-v 168,raw48,Max_Erase_Count_of_Spec "
+    "-v 169,raw48,Remaining_Lifetime_Perc "
+  //"-v 175,raw48,Program_Fail_Count_Chip "
+  //"-v 176,raw48,Erase_Fail_Count_Chip "
+  //"-v 177,raw48,Wear_Leveling_Count "
+    "-v 178,raw48,Runtime_Invalid_Blk_Cnt "
+  //"-v 181,raw48,Program_Fail_Cnt_Total "
+  //"-v 182,raw48,Erase_Fail_Count_Total "
+  //"-v 187,raw48,Reported_Uncorrect " // ] only in spec
+  //"-v 192,raw48,Power-Off_Retract_Count "
+  //"-v 194,tempminmax,Temperature_Celsius "
+  //"-v 195,raw48,Hardware_ECC_Recovered "
+  //"-v 196,raw16(raw16),Reallocated_Event_Count "
+  //"-v 197,raw48,Current_Pending_Sector "
+  //"-v 198,raw48,Offline_Uncorrectable "
+  //"-v 199,raw48,UDMA_CRC_Error_Count "
+    "-v 225,raw48,Host_Writes_32MiB "  // ]
+  //"-v 232,raw48,Available_Reservd_Space "
+    "-v 233,raw48,Flash_Writes_32MiB " // ]
+    "-v 234,raw48,Flash_Reads_32MiB "  // ]
+    "-v 241,raw48,Host_Writes_32MiB "
+    "-v 242,raw48,Host_Reads_32MiB "
+    "-v 245,raw48,Flash_Writes_32MiB"
+  },
+  { "Innodisk 3IE3/3ME3 SSDs", // tested with 2.5" SATA SSD 3ME3/S15A19, CFast 3ME3/S15A19
+      // InnoDisk Corp. - mSATA 3ME3/S15A19, mSATA mini 3ME3/S15A19, M.2 (S42) 3ME3,
+      // SATA Slim 3ME3/S15A19, SATADOM-MH 3ME3/S15A19, SATADOM-ML 3ME3/S15A19,
+      // SATADOM-MV 3ME3/S15A19, SATADOM-SL 3ME3/S15A19, SATADOM-SV 3ME3/S15A19,
+      // SATADOM-SL 3IE3/S151019N, 2.5" SATA SSD 3IE3/S15C14i, CFast 3IE3/S15C14i,
+      // InnoDisk Corp. - mSATA 3IE3/S15C14i, Mini PCIeDOM 1IE3/S15C14i,
+      // mSATA mini 3IE3/S15C14i, M.2 (S42) 3IE3/S15C14i, SATA Slim 3IE3/S15C14i,
+      // SATADOM-SH 3IE3 V2/S15C14i, SATADOM-SL 3IE3 V2/S15A19i, SATADOM-SV 3IE3 V2/S15C14i
+    "(2.5\" SATA SSD|CFast|InnoDisk Corp\\. - mSATA|Mini PCIeDOM|mSATA mini|"
+    "M\\.2 \\(S42\\)|SATA Slim|SATADOM-[MS][HLV]) 3[IM]E3( V2)?",
+    "", "",
+  //"-v 1,raw48,Raw_Read_Error_Rate "
+  //"-v 2,raw48,Throughput_Performance "
+  //"-v 3,raw16(avg16),Spin_Up_Time "
+    "-v 5,raw48,Later_Bad_Block "
+    "-v 7,raw48,Seek_Error_Rate "       // ?
+    "-v 8,raw48,Seek_Time_Performance " // ?
+  //"-v 9,raw24(raw8),Power_On_Hours "
+    "-v 10,raw48,Spin_Retry_Count "     // ?
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 163,raw48,Total_Bad_Block_Count "
+    "-v 165,raw48,Max_Erase_Count "
+    "-v 167,raw48,Average_Erase_Count "
+    "-v 168,raw48,SATA_PHY_Error_Count "
+    "-v 169,raw48,Remaining_Lifetime_Perc "
+    "-v 170,raw48,Spare_Block_Count "
+    "-v 171,raw48,Program_Fail_Count "
+    "-v 172,raw48,Erase_Fail_Count "
+    "-v 175,raw48,Bad_Cluster_Table_Count "
+    "-v 176,raw48,RANGE_RECORD_Count "
+  //"-v 187,raw48,Reported_Uncorrect "
+  //"-v 192,raw48,Power-Off_Retract_Count "
+  //"-v 194,tempminmax,Temperature_Celsius "
+  //"-v 197,raw48,Current_Pending_Sector "
+    "-v 225,raw48,Data_Log_Write_Count "
+    "-v 229,hex48,Flash_ID "
+    "-v 232,raw48,Spares_Remaining_Perc "
+    "-v 235,raw16,Later_Bad_Blk_Inf_R/W/E " // Read/Write/Erase
+    "-v 240,raw48,Write_Head "
+    "-v 241,raw48,Host_Writes_32MiB "
+    "-v 242,raw48,Host_Reads_32MiB"
+  },
+  { "InnoDisk iCF 9000 CompactFlash Cards", // tested with InnoDisk Corp. - iCF9000 1GB/140808,
+       // ..., InnoDisk Corp. - iCF9000 64GB/140808
+    "InnoDisk Corp\\. - iCF9000 (1|2|4|8|16|32|64)GB",
+    "", "",
+  //"-v 1,raw48,Raw_Read_Error_Rate "
+  //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 160,raw48,Uncorrectable_Error_Cnt "
+    "-v 161,raw48,Valid_Spare_Block_Cnt "
+    "-v 162,raw48,Child_Pair_Count "
+    "-v 163,raw48,Initial_Bad_Block_Count "
+    "-v 164,raw48,Total_Erase_Count "
+    "-v 165,raw48,Max_Erase_Count "
+    "-v 166,raw48,Min_Erase_Count "
+    "-v 167,raw48,Average_Erase_Count "
+  //"-v 192,raw48,Power-Off_Retract_Count "
+  //"-v 194,tempminmax,Temperature_Celsius "
+  //"-v 195,raw48,Hardware_ECC_Recovered "
+  //"-v 196,raw16(raw16),Reallocated_Event_Count "
+  //"-v 198,raw48,Offline_Uncorrectable "
+  //"-v 199,raw48,UDMA_CRC_Error_Count "
+  //"-v 229,raw48,Flash_ID " // only in spec
+    "-v 241,raw48,Host_Writes_32MiB "
+    "-v 242,raw48,Host_Reads_32MiB"
+  },
   { "Intel X25-E SSDs",
     "SSDSA2SH(032|064)G1.* INTEL",  // G1 = first generation
     "", "",
@@ -623,8 +886,10 @@ const drive_settings builtin_knowndrives[] = {
     "-v 227,raw48,Workld_Host_Reads_Perc "
     "-v 228,raw48,Workload_Minutes"
   },
-  { "Intel 313 Series SSDs", // tested with INTEL SSDSA2VP020G3/9CV10379
-    "INTEL SSDSA2VP(020|024)G3",
+  { "Intel 311/313 Series SSDs", // tested with INTEL SSDSA2VP020G2/2CV102M5,
+      // INTEL SSDSA2VP020G3/9CV10379, INTEL SSDMAEXC024G3H/9CV10379
+    "INTEL SSD(SA2VP|MAEXC)(020|024)G[23]H?",
+      // SA2VP = 2.5", MAEXC = mSATA, G2 = 311, G3 = 313
     "", "",
   //"-v 3,raw16(avg16),Spin_Up_Time "
   //"-v 4,raw48,Start_Stop_Count "
@@ -648,9 +913,10 @@ const drive_settings builtin_knowndrives[] = {
     "-v 242,raw48,Host_Reads_32MiB"
   },
   { "Intel 320 Series SSDs", // tested with INTEL SSDSA2CT040G3/4PC10362,
-      // INTEL SSDSA2CW160G3/4PC10362, INTEL SSDSA2BT040G3/4PC10362, INTEL SSDSA2BW120G3A/4PC10362,
-      // INTEL SSDSA2BW300G3D/4PC10362, INTEL SSDSA2BW160G3L/4PC1LE04
-    "INTEL SSDSA[12][BC][WT](040|080|120|160|300|600)G3[ADL]?",
+      // INTEL SSDSA2CW160G3/4PC10362, SSDSA2BT040G3/4PC10362, SSDSA2BW120G3A/4PC10362,
+      // INTEL SSDSA2BW300G3D/4PC10362, SSDSA2BW160G3L/4PC1LE04, SSDSA1NW160G3/4PC10362
+    "INTEL SSDSA[12][BCN][WT](040|080|120|160|300|600)G3[ADL]?",
+      // 2B = 2.5" 7mm, 2C = 2.5" 9.5mm, 1N = 1.8" microSATA
     "", "",
     "-F nologdir "
   //"-v 3,raw16(avg16),Spin_Up_Time "
@@ -715,8 +981,9 @@ const drive_settings builtin_knowndrives[] = {
   //"-v 232,raw48,Available_Reservd_Space "
   //"-v 233,raw48,Media_Wearout_Indicator"
   },
-  { "Intel 520 Series SSDs", // tested with INTEL SSDSC2CW120A3/400i, SSDSC2BW480A3F/400i
-    "INTEL SSDSC2[BC]W(060|120|180|240|480)A3F?",
+  { "Intel 520 Series SSDs", // tested with INTEL SSDSC2CW120A3/400i, SSDSC2BW480A3F/400i,
+      // INTEL SSDSC2BW180A3L/LB3i
+    "INTEL SSDSC2[BC]W(060|120|180|240|480)A3[FL]?",
     "", "",
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
     "-v 9,msec24hour32,Power_On_Hours_and_Msec "
@@ -764,8 +1031,11 @@ const drive_settings builtin_knowndrives[] = {
     "-v 242,raw48,Host_Reads_32MiB "
     "-v 249,raw48,NAND_Writes_1GiB"
   },
-  { "Intel 530 Series SSDs", // tested with INTEL SSDSC2BW180A4/DC12, SSDSC2BW240A4/DC12
-    "INTEL SSDSC2BW(080|120|180|240|360|480)A4",
+  { "Intel 53x and Pro 2500 Series SSDs", // SandForce SF-2281, tested with
+      // INTEL SSDSC2BW180A4/DC12, SSDSC2BW240A4/DC12, SSDMCEAW120A4/DC33
+      // INTEL SSDMCEAW240A4/DC33, SSDSC2BF480A5/TG26, SSDSC2BW240H6/RG21
+    "INTEL SSD(MCEA|SC2B|SCKJ)[WF](056|080|120|180|240|360|480)(A4|A5|H6)",
+      // SC2B = 2.5", MCEA = mSATA, SCKJ = M.2; A4 = 530, A5 = Pro 2500, H6 = 535
     "", "",
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
     "-v 9,msec24hour32,Power_On_Hours_and_Msec "
@@ -807,9 +1077,11 @@ const drive_settings builtin_knowndrives[] = {
     "-v 242,raw48,Host_Reads_32MiB "
     "-v 249,raw48,NAND_Writes_1GiB"
   },
-  { "Intel 730 and DC S3500/S3700 Series SSDs", // tested with INTEL SSDSC2BP480G4, SSDSC2BB120G4/D2010355,
-      // INTEL SSDSC2BB800G4T, SSDSC2BA200G3/5DV10250
-    "INTEL SSDSC(1N|2B)[ABP](080|100|120|160|200|240|300|400|480|600|800)G[34]T?", // A=S3700, B=S3500, P=730
+  { "Intel 730 and DC S35x0/3610/3700 Series SSDs", // tested with INTEL SSDSC2BP480G4, SSDSC2BB120G4/D2010355,
+      // INTEL SSDSC2BB800G4T, SSDSC2BA200G3/5DV10250, SSDSC2BB080G6/G2010130,  SSDSC2BX200G4/G2010110,
+      // INTEL SSDSC2BB016T6/G2010140, SSDSC2BX016T4/G2010140
+    "INTEL SSDSC(1N|2B)[ABPX]((080|100|120|160|200|240|300|400|480|600|800)G[346]T?|(012|016)T[46])",
+      // A = S3700, B*4 = S3500, B*6 = S3510, P = 730, X = S3610
     "", "",
   //"-v 3,raw16(avg16),Spin_Up_Time "
   //"-v 4,raw48,Start_Stop_Count "
@@ -838,6 +1110,7 @@ const drive_settings builtin_knowndrives[] = {
     "-v 234,raw24/raw32:04321,Thermal_Throttle "
     "-v 241,raw48,Host_Writes_32MiB "
     "-v 242,raw48,Host_Reads_32MiB "
+    "-v 243,raw48,NAND_Writes_32MiB " // S3510/3610
     "-F xerrorlba" // tested with SSDSC2BB600G4/D2010355
   },
   { "Kingston branded X25-V SSDs", // fixed firmware
@@ -878,77 +1151,114 @@ const drive_settings builtin_knowndrives[] = {
     "-v 234,raw24/raw24:w01234,Avg/Max_Erase_Count "
     "-v 235,raw24/raw24:w01z23,Good/Sys_Block_Count"
   },
-  { "JMicron based SSDs", // JMicron JMF61x, JMF661
+  { "JMicron based SSDs", // JMicron JMF61x, JMF66x, JMF670
     "ADATA S596 Turbo|"  // tested with ADATA S596 Turbo 256GB SATA SSD (JMicron JMF616)
     "ADATA SP600|"  // tested with ADATA SP600/2.4 (JMicron JMF661)
+    "ADATA SP310|"  // Premier Pro SP310 mSATA, JMF667, tested with ADATA SP310/3.04
     "APPLE SSD TS(064|128|256|512)C|"  // Toshiba?, tested with APPLE SSD TS064C/CJAA0201
     "KINGSTON SNV425S2(64|128)GB|"  // SSDNow V Series (2. Generation, JMF618),
                                     // tested with KINGSTON SNV425S264GB/C091126a
     "KINGSTON SSDNOW 30GB|" // tested with KINGSTON SSDNOW 30GB/AJXA0202
     "KINGSTON SS100S2(8|16)G|"  // SSDNow S100 Series, tested with KINGSTON SS100S28G/D100309a
+    "KINGSTON SNVP325S2(64|128|256|512)GB|" // SSDNow V+ Series, tested with KINGSTON SNVP325S2128GB/AGYA0201
     "KINGSTON SVP?100S2B?(64|96|128|256|512)G|"  // SSDNow V100/V+100 Series,
       // tested with KINGSTON SVP100S296G/CJR10202, KINGSTON SV100S2256G/D110225a
     "KINGSTON SV200S3(64|128|256)G|" // SSDNow V200 Series, tested with KINGSTON SV200S3128G/E120506a
     "TOSHIBA THNS128GG4BBAA|"  // Toshiba / Super Talent UltraDrive DX,
                                // tested with Toshiba 128GB 2.5" SSD (built in MacBooks)
     "TOSHIBA THNSNC128GMLJ|" // tested with THNSNC128GMLJ/CJTA0202 (built in Toshiba Protege/Dynabook)
-    "TS(8|16|32|64|128|192|256|512)GSSD25S?-(MD?|S)", // Transcend IDE and SATA (JMF612), tested with
+    "TS(8|16|32|64|128|192|256|512)GSSD25S?-(MD?|S)|" // Transcend IDE and SATA, JMF612, tested with
       // TS256GSSD25S-M/101028, TS32GSSD25-M/20101227
+    "TS(32|64|128|256)G(SSD|MSA)340", // Transcend SSD340 SATA/mSATA, JMF667/670, tested with
+      // TS256GSSD340/SVN263, TS256GSSD340/SVN423b, TS256GMSA340/SVN263
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
   //"-v 2,raw48,Throughput_Performance "
-    "-v 3,raw48,Unknown_Attribute "
+    "-v 3,raw48,Unknown_JMF_Attribute "
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
-    "-v 7,raw48,Unknown_Attribute "
-    "-v 8,raw48,Unknown_Attribute "
+    "-v 7,raw48,Unknown_JMF_Attribute "
+    "-v 8,raw48,Unknown_JMF_Attribute "
   //"-v 9,raw24(raw8),Power_On_Hours "
-    "-v 10,raw48,Unknown_Attribute "
+    "-v 10,raw48,Unknown_JMF_Attribute "
   //"-v 12,raw48,Power_Cycle_Count "
-  //"-v 167,raw48,Unknown_Attribute "
+    "-v 167,raw48,Unknown_JMF_Attribute "
     "-v 168,raw48,SATA_Phy_Error_Count "
-  //"-v 169,raw48,Unknown_Attribute "
+    "-v 169,raw48,Unknown_JMF_Attribute "
     "-v 170,raw16,Bad_Block_Count "
     "-v 173,raw16,Erase_Count " // JMF661: different?
     "-v 175,raw48,Bad_Cluster_Table_Count "
     "-v 192,raw48,Unexpect_Power_Loss_Ct "
   //"-v 194,tempminmax,Temperature_Celsius "
   //"-v 197,raw48,Current_Pending_Sector "
-    "-v 240,raw48,Unknown_Attribute"
-  },
-  { "Plextor M3/M5 (Pro) Series SSDs", // Marvell 88SS9174 (M3, M5S), 88SS9187 (M5Pro), tested with
-      // PLEXTOR PX-128M3/1.01, PX-128M3P/1.04, PX-256M3/1.05, PX-128M5S/1.02, PX-256M5S/1.03,
-      // PX-128M5M/1.05, PX-128M5S/1.05, PX-128M5Pro/1.05, PX-512M5Pro/1.06
+    "-v 233,raw48,Unknown_JMF_Attribute " // FW SVN423b
+    "-v 234,raw48,Unknown_JMF_Attribute " // FW SVN423b
+    "-v 240,raw48,Unknown_JMF_Attribute "
+  //"-v 241,raw48,Total_LBAs_Written "    // FW SVN423b
+  //"-v 242,raw48,Total_LBAs_Read "       // FW SVN423b
+  },
+  { "Plextor M3/M5/M6 Series SSDs", // Marvell 88SS9174 (M3, M5S), 88SS9187 (M5P, M5Pro), 88SS9188 (M6M/S),
+      // tested with PLEXTOR PX-128M3/1.01, PX-128M3P/1.04, PX-256M3/1.05, PX-128M5S/1.02, PX-256M5S/1.03,
+      // PX-128M5M/1.05, PX-128M5S/1.05, PX-128M5Pro/1.05, PX-512M5Pro/1.06, PX-256M5P/1.01, PX-128M6S/1.03
       // (1.04/5 Firmware self-test log lifetime unit is bogus, possibly 1/256 hours)
-    "PLEXTOR PX-(64|128|256|512)M(3P?|5[MS]|5Pro)",
+    "PLEXTOR PX-(64|128|256|512|768)M(3P?|5[MPS]|5Pro|6[MS])",
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
   //"-v 9,raw24(raw8),Power_On_Hours "
   //"-v 12,raw48,Power_Cycle_Count "
+    "-v 170,raw48,Unknown_Plextor_Attrib "  // M6S/1.03
+    "-v 171,raw48,Unknown_Plextor_Attrib "  // M6S/1.03
+    "-v 172,raw48,Unknown_Plextor_Attrib "  // M6S/1.03
+    "-v 173,raw48,Unknown_Plextor_Attrib "  // M6S/1.03
+    "-v 174,raw48,Unknown_Plextor_Attrib "  // M6S/1.03
+  //"-v 175,raw48,Program_Fail_Count_Chip " // M6S/1.03
+  //"-v 176,raw48,Erase_Fail_Count_Chip "   // M6S/1.03
   //"-v 177,raw48,Wear_Leveling_Count "
   //"-v 178,raw48,Used_Rsvd_Blk_Cnt_Chip "
+  //"-v 179,raw48,Used_Rsvd_Blk_Cnt_Tot "   // M6S/1.03
+  //"-v 180,raw48,Unused_Rsvd_Blk_Cnt_Tot " // M6S/1.03
   //"-v 181,raw48,Program_Fail_Cnt_Total "
   //"-v 182,raw48,Erase_Fail_Count_Total "
+  //"-v 183,raw48,Runtime_Bad_Block "       // M6S/1.03
+  //"-v 184,raw48,End-to-End_Error "        // M6S/1.03
   //"-v 187,raw48,Reported_Uncorrect "
+  //"-v 188,raw48,Command_Timeout "         // M6S/1.03
   //"-v 192,raw48,Power-Off_Retract_Count "
+  //"-v 195,raw48,Hardware_ECC_Recovered "  // MS6/1.03
   //"-v 196,raw16(raw16),Reallocated_Event_Count "
   //"-v 198,raw48,Offline_Uncorrectable "
   //"-v 199,raw48,UDMA_CRC_Error_Count "
   //"-v 232,raw48,Available_Reservd_Space "
+  //"-v 233,raw48,Media_Wearout_Indicator " // MS6/1.03
     "-v 241,raw48,Host_Writes_32MiB "
     "-v 242,raw48,Host_Reads_32MiB"
   },
   { "Samsung based SSDs",
     "SAMSUNG SSD PM800 .*GB|"  // SAMSUNG PM800 SSDs, tested with SAMSUNG SSD PM800 TH 64GB/VBM25D1Q
     "SAMSUNG SSD PM810 .*GB|"  // SAMSUNG PM810 (470 series) SSDs, tested with SAMSUNG SSD PM810 2.5" 128GB/AXM06D1Q
+    "SAMSUNG SSD PM851 (mSATA )?(128|256|512)GB|" // tested with SAMSUNG SSD PM851 mSATA 128GB
+    "SAMSUNG SSD SM841N (mSATA )?(128|256|512)GB|" // tested with SAMSUNG SSD SM841N mSATA 256GB
     "SAMSUNG 470 Series SSD|"  // tested with SAMSUNG 470 Series SSD 64GB/AXM09B1Q
     "SAMSUNG SSD 830 Series|"  // tested with SAMSUNG SSD 830 Series 64GB/CXM03B1Q
+    "MZ7PC(512|256|128|064)HA(GH|FU|DR)-000.*|" // probably PM830, tested with SAMSUNG MZ7PC128HAFU-000L1/CXM04L1Q
     "Samsung SSD 840 (PRO )?Series|" // tested with Samsung SSD 840 PRO Series 128GB/DXM04B0Q,
       // Samsung SSD 840 Series/DXT06B0Q
-    "Samsung SSD 840 EVO ([0-9]*G|1T)B( mSATA)?|"  // tested with Samsung SSD 840 EVO (120|250|500)GB/EXT0AB0Q,
-      // Samsung SSD 840 EVO (120|250)GB/EXT0BB6Q, 1TB/EXT0BB0Q, 120GB mSATA/EXT41B6Q
-    "SAMSUNG MZ7WD((120|240)HAFV|480HAGM|960HAGP)-00003", // SM843T Series, tested with
-      // SAMSUNG MZ7WD120HAFV-00003/DXM85W3Q
+    "Samsung SSD 8[45]0 EVO (mSATA |M\\.2 )?((120|250|500|750)G|1T)B( mSATA)?|" // tested with
+      // Samsung SSD 840 EVO (120|250|500|750)GB/EXT0AB0Q,
+      // Samsung SSD 840 EVO (120|250)GB/EXT0BB6Q, 1TB/EXT0BB0Q, 120GB mSATA/EXT41B6Q,
+      // Samsung SSD 850 EVO 250GB/EMT01B6Q
+      // Samsung SSD 850 EVO M.2 250GB/EMT21B6Q
+      // Samsung SSD 850 EVO mSATA 120GB/EMT41B6Q
+    "Samsung SSD 850 PRO ((128|256|512)G|1T)B|" // tested with Samsung SSD 850 PRO 128GB/EXM01B6Q,
+      // Samsung SSD 850 PRO 1TB/EXM01B6Q
+    "SAMSUNG MZ7WD((120|240)H[AC]FV|480HAGM|960HAGP)-00003|" // SM843T Series, tested with
+      // SAMSUNG MZ7WD120HAFV-00003/DXM85W3Q, SAMSUNG MZ7WD120HCFV-00003/DXM9203Q
+    "SAMSUNG MZ7GE(240HMGR|(480|960)HMHP)-00003|" // SM853T Series, tested with
+      // SAMSUNG MZ7GE240HMGR-00003/EXT0303Q
+    "SAMSUNG MZ7LM(120|240|480|960|1T9|3T8)HC(JM|HP|GR|FD)-.*|" // PM863 Series, tested with
+      // SAMSUNG MZ7LM960HCHP-0E003/GXT3003Q
+    "SAMSUNG MZ7KM(120|240|480|960|1T9)HA(JM|HP|GR|FD|JM)-.*|" // SM863, tested with MZ7KM480HAHP-0E005/GXM1003Q
+    "SAMSUNG MZ[7N]LN(128|256|512)HC(HP|GR|JH)-.*", // PM871 Series, tested with SAMSUNG MZ7LN128HCHP
     "", "",
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
   //"-v 9,raw24(raw8),Power_On_Hours "
@@ -971,57 +1281,128 @@ const drive_settings builtin_knowndrives[] = {
     "-v 199,raw48,CRC_Error_Count "
     "-v 201,raw48,Supercap_Status "
     "-v 202,raw48,Exception_Mode_Status "
-    "-v 235,raw48,POR_Recovery_Count " // 830/840 Series
-  //"-v 241,raw48,Total_LBAs_Written"
+    "-v 235,raw48,POR_Recovery_Count " // 830/840/850 Series
+  //"-v 241,raw48,Total_LBAs_Written "
+  //"-v 242,raw48,Total_LBAs_Read " // PM851, SM841N
+    "-v 243,raw48,SATA_Downshift_Ct " // PM863
+    "-v 244,raw48,Thermal_Throttle_St " // PM863
+    "-v 245,raw48,Timed_Workld_Media_Wear " // PM863
+    "-v 246,raw48,Timed_Workld_RdWr_Ratio " // PM863
+    "-v 247,raw48,Timed_Workld_Timer " // PM863
+    "-v 250,raw48,SATA_Iface_Downshift " // from the spec
+    "-v 251,raw48,NAND_Writes" // PM863
   },
   { "Marvell based SanDisk SSDs",
     "SanDisk SD5SG2[0-9]*G1052E|" // X100 (88SS9174), tested with SanDisk SD5SG2256G1052E/10.04.01
-    "SanDisk SD6SB[12]M[0-9]*G(1022I)?|" // X110/X210 (88SS9175), tested with SanDisk SD6SB1M064G1022I/X231600,
-      // SanDisk SD6SB1M256G1022I/X231600, SanDisk SD6SB2M512G1022I/X210400
+    "SanDisk SD6S[BF][12]M[0-9]*G(1022I?)?|" // X110/X210 (88SS9175/187?), tested with SanDisk SD6SB1M064G1022I/X231600,
+      // SanDisk SD6SB1M256G1022I/X231600, SanDisk SD6SF1M128G1022/X231200, SanDisk SD6SB2M512G1022I/X210400
+    "SanDisk SD7SB6S(128|256|512)G1122|" // X300 (88SS9189?), tested with SanDisk SD7SB6S128G1122/X3310000
     "SanDisk SDSSDHP[0-9]*G|" // Ultra Plus (88SS9175), tested with SanDisk SDSSDHP128G/X23[01]6RL
-    "SanDisk SDSSDXP[0-9]*G", // Extreme II (88SS9187), tested with SanDisk SDSSDXP480G/R1311
+    "SanDisk SDSSDHII[0-9]*G|" // Ultra II (88SS9190/88SS9189), tested with SanDisk SDSSDHII120G/X31200RL
+    "SanDisk SDSSDXPS?[0-9]*G", // Extreme II/Pro (88SS9187), tested with SanDisk SDSSDXP480G/R1311,
+      // SanDisk SDSSDXPS480G/X21200RL
     "", "",
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
   //"-v 9,raw24(raw8),Power_On_Hours "
   //"-v 12,raw48,Power_Cycle_Count "
+    "-v 165,raw48,Total_Write/Erase_Count "
     "-v 166,raw48,Min_W/E_Cycle "
     "-v 167,raw48,Min_Bad_Block/Die "
     "-v 168,raw48,Maximum_Erase_Cycle "
     "-v 169,raw48,Total_Bad_Block "
     "-v 171,raw48,Program_Fail_Count "
     "-v 172,raw48,Erase_Fail_Count "
-    "-v 173,raw48,Avg_Write_Erase_Ct "
+    "-v 173,raw48,Avg_Write/Erase_Count "
     "-v 174,raw48,Unexpect_Power_Loss_Ct "
+  //"-v 184,raw48,End-to-End_Error "
   //"-v 187,raw48,Reported_Uncorrect "
+  //"-v 188,raw48,Command_Timeout "
   //"-v 194,tempminmax,Temperature_Celsius "
+    "-v 199,raw48,SATA_CRC_Error "
     "-v 212,raw48,SATA_PHY_Error "
-    "-v 230,raw48,Perc_Write_Erase_Count "
+    "-v 230,raw48,Perc_Write/Erase_Count "
     "-v 232,raw48,Perc_Avail_Resrvd_Space "
     "-v 233,raw48,Total_NAND_Writes_GiB "
+    "-v 234,raw48,Perc_Write/Erase_Ct_BC "
     "-v 241,raw48,Total_Writes_GiB "
     "-v 242,raw48,Total_Reads_GiB "
   //"-v 243,raw48,Unknown_Attribute "
+    "-v 244,raw48,Thermal_Throttle "
   },
-  { "SanDisk based SSDs",
+  { "SanDisk based SSDs", // see also #463 for the vendor attribute description
     "SanDisk iSSD P4 [0-9]*GB|" // tested with SanDisk iSSD P4 16GB/SSD 9.14
+    "SanDisk pSSD|" // tested with SandDisk pSSD/3 (62.7 GB, SanDisk Extreme USB3.0 SDCZ80-064G-J57, 0x0781:0x5580)
     "SanDisk SDSSDP[0-9]*G|" // tested with SanDisk SDSSDP064G/1.0.0, SDSSDP128G/2.0.0
+    "SanDisk SDSSDRC032G|" // tested with SanDisk SanDisk SDSSDRC032G/3.1.0
     "SanDisk SSD i100 [0-9]*GB|" // tested with SanDisk SSD i100 8GB/11.56.04, 24GB/11.56.04
     "SanDisk SSD U100 ([0-9]*GB|SMG2)|" // tested with SanDisk SSD U100 8GB/10.56.00, 256GB/10.01.02, SMG2/10.56.04
-    "SanDisk SD7[SU]B3Q(064|128|256|512)G.*", // tested with SD7SB3Q064G1122/SD7UB3Q256G1122/SD7SB3Q128G
+    "SanDisk SSD U110 (8|16|24|32|64|128)GB|" // tested with SanDisk SSD U110 32GB/U221000
+    "SanDisk SD7[SU]B[23]Q(064|128|256|512)G.*", // tested with SD7SB3Q064G1122/SD7UB3Q256G1122/SD7SB3Q128G/SD7UB2Q512G1122
     "", "",
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
   //"-v 9,raw24(raw8),Power_On_Hours "
   //"-v 12,raw48,Power_Cycle_Count "
+    "-v 165,raw48,Total_Write/Erase_Count "
     "-v 171,raw48,Program_Fail_Count "
     "-v 172,raw48,Erase_Fail_Count "
-    "-v 173,raw48,Avg_Write_Erase_Ct "
+    "-v 173,raw48,Avg_Write/Erase_Count "
     "-v 174,raw48,Unexpect_Power_Loss_Ct "
   //"-v 187,raw48,Reported_Uncorrect "
-    "-v 230,raw48,Perc_Write_Erase_Count "
+    "-v 212,raw48,SATA_PHY_Error "
+    "-v 230,raw48,Perc_Write/Erase_Count "
     "-v 232,raw48,Perc_Avail_Resrvd_Space "
-    "-v 234,raw48,Perc_Write_Erase_Ct_BC "
+    "-v 234,raw48,Perc_Write/Erase_Ct_BC "
   //"-v 241,raw48,Total_LBAs_Written "
   //"-v 242,raw48,Total_LBAs_Read "
+    "-v 244,raw48,Thermal_Throttle "
+  },
+  { "SiliconMotion based SSDs", // SM2246EN (Transcend TS6500)
+    "CT(120|250|500|1000)BX100SSD1|" // Crucial BX100, tested with CT250BX100SSD1/MU02,
+      // CT500BX100SSD1/MU02, CT1000BX100SSD1/MU02
+    "CT(240|480|960)BX200SSD1|" // Crucial BX200 Solid State Drive, tested with CT480BX200SSD1/MU02.6
+    "TS((16|32|64|128|256|512)G|1T)(SSD|MSA)(370S?|420I?)|" // Transcend SSD370/420 SATA/mSATA, TS6500,
+      // tested with TS32GMSA370/20140402, TS16GMSA370/20140516, TS64GSSD370/20140516,
+      // TS256GSSD370/N0815B, TS256GSSD370S/N1114H, TS512GSSD370S/N1114H, TS32GSSD420I/N1114H
+    "ADATA SP550", // ADATA SP550/O0803B5a
+    "", "",
+  //"-v 1,raw48,Raw_Read_Error_Rate "
+  //"-v 2,raw48,Throughput_Performance "
+  //"-v 9,raw24(raw8),Power_On_Hours "
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 148,raw48,Total_SLC_Erase_Ct "
+    "-v 149,raw48,Max_SLC_Erase_Ct "
+    "-v 150,raw48,Min_SLC_Erase_Ct "
+    "-v 151,raw48,Average_SLC_Erase_Ct "
+    "-v 160,raw48,Uncorrectable_Error_Cnt "
+    "-v 161,raw48,Valid_Spare_Block_Cnt "
+    "-v 163,raw48,Initial_Bad_Block_Count "
+    "-v 164,raw48,Total_Erase_Count "
+    "-v 165,raw48,Max_Erase_Count "
+    "-v 166,raw48,Min_Erase_Count "
+    "-v 167,raw48,Average_Erase_Count "
+    "-v 168,raw48,Max_Erase_Count_of_Spec "
+    "-v 169,raw48,Remaining_Lifetime_Perc "
+  //"-v 175,raw48,Program_Fail_Count_Chip "
+  //"-v 176,raw48,Erase_Fail_Count_Chip "
+  //"-v 177,raw48,Wear_Leveling_Count "
+    "-v 178,raw48,Runtime_Invalid_Blk_Cnt "
+  //"-v 181,raw48,Program_Fail_Cnt_Total "
+  //"-v 182,raw48,Erase_Fail_Count_Total "
+  //"-v 187,raw48,Reported_Uncorrect "
+  //"-v 192,raw48,Power-Off_Retract_Count "
+  //"-v 194,tempminmax,Temperature_Celsius "
+  //"-v 195,raw48,Hardware_ECC_Recovered "
+  //"-v 196,raw16(raw16),Reallocated_Event_Count "
+  //"-v 197,raw48,Current_Pending_Sector "
+  //"-v 198,raw48,Offline_Uncorrectable "
+  //"-v 199,raw48,UDMA_CRC_Error_Count "
+    "-v 225,raw48,Host_Writes_32MiB " // FW 20140402
+  //"-v 232,raw48,Available_Reservd_Space "
+    "-v 241,raw48,Host_Writes_32MiB "
+    "-v 242,raw48,Host_Reads_32MiB "
+    "-v 245,raw48,TLC_Writes_32MiB " // FW N0815B, N1114H
+    "-v 246,raw48,SLC_Writes_32MiB "
+    "-v 247,raw48,Raid_Recoverty_Ct"
   },
   { "Smart Storage Systems Xcel-10 SSDs",  // based on http://www.smartm.com/files/salesLiterature/storage/xcel10.pdf
     "SMART A25FD-(32|64|128)GI32N", // tested with SMART A25FD-128GI32N/B9F23D4K
@@ -1395,8 +1776,8 @@ const drive_settings builtin_knowndrives[] = {
     "SAMSUNG HD(250KD|(30[01]|320|40[01])L[DJ])",
     "", "", ""
   },
-  { "SAMSUNG SpinPoint T166", // tested with HD501LJ/CR100-1[01]
-    "SAMSUNG HD(080G|160H|32[01]K|403L|50[01]L)J",
+  { "SAMSUNG SpinPoint T166", // tested with HD252KJ/CM100-11, HD501LJ/CR100-1[01]
+    "SAMSUNG HD(080G|160H|252K|32[01]K|403L|50[01]L)J",
     "", "",
     "-v 197,increasing" // at least HD501LJ/CR100-11
   },
@@ -1412,8 +1793,8 @@ const drive_settings builtin_knowndrives[] = {
     "May need -F samsung3 enabled; see manual for details.",
     ""
   },
-  { "SAMSUNG SpinPoint P80 SD", // tested with HD160JJ/ZM100-33
-    "SAMSUNG HD(080H|120I|160J)J",
+  { "SAMSUNG SpinPoint P80 SD", // tested with HD160JJ/ZM100-33, SAMSUNG HD080HJ/P/ZH100-34
+    "SAMSUNG HD(080H|120I|160J)J(/P)?",
     "", "", ""
   },
   { "SAMSUNG SpinPoint P80", // BH100-35 firmware, tested with SP0842N/BH100-35
@@ -1472,6 +1853,10 @@ const drive_settings builtin_knowndrives[] = {
     "SAMSUNG HM(161G|(251|321)H|501I|641J)I",
     "", "", ""
   },
+  { "Seagate Samsung SpinPoint M7E", // tested with ST640LM000 HM641JI/2AJ10001
+    "ST(160|250|320|500|640)LM00[01] HM[0-9]*[GHIJ]I",
+    "", "", ""
+  },
   { "SAMSUNG SpinPoint M7U (USB)", // tested with HM252HX/2AC101C4
     "SAMSUNG HM(162H|252H|322I|502J)X",
     "", "", ""
@@ -1480,9 +1865,11 @@ const drive_settings builtin_knowndrives[] = {
     "SAMSUNG HN-M(250|320|500|640|750|101)MBB",
     "", "", ""
   },
-  { "Seagate Momentus SpinPoint M8 (AF)", // tested with
-      // ST750LM022 HN-M750MBB/2AR10001, ST320LM001 HN-M320MBB/2AR10002
-    "ST(250|320|500|640|750|1000)LM0[012][124] HN-M[0-9]*MBB",
+  { "Seagate Samsung SpinPoint M8 (AF)", // tested with
+      // ST750LM022 HN-M750MBB/2AR10001, ST320LM001 HN-M320MBB/2AR10002,
+      // APPLE HDD ST500LM012/2BA30003
+    "ST(250|320|500|640|750|1000)LM0[012][124] HN-M[0-9]*MBB|"
+    "APPLE HDD ST500LM012",
     "", "", ""
   },
   { "SAMSUNG SpinPoint M8U (USB)", // tested with HN-M500XBB/2AR10001
@@ -1493,6 +1880,16 @@ const drive_settings builtin_knowndrives[] = {
     "ST(250|320|500|640|750|1000)LM0[012][3459] HN-M[0-9]*ABB",
     "", "", ""
   },
+  { "Seagate Samsung SpinPoint M9T", // tested with ST2000LM003 HN-M201RAD/2BC10003
+      // (Seagate Expansion Portable)
+    "ST(1500|2000)LM0(03|04|06|07|10) HN-M[0-9]*RAD",
+    "", "", ""
+  },
+  { "Seagate Samsung SpinPoint M9TU (USB)", // tested with ST1500LM008 HN-M151AAD/2BC10001
+       // (0x04e8:0x61b5), ST2000LM005 HN-M201AAD2BC10001 (0x04e8:0x61b4)
+    "ST(1500|2000)LM00[58] HN-M[0-9]*AAD",
+    "", "", ""
+  },
   { "SAMSUNG SpinPoint MP5", // tested with HM250HJ/2AK10001
     "SAMSUNG HM(250H|320H|500J|640J)J",
     "", "", ""
@@ -1897,12 +2294,18 @@ const drive_settings builtin_knowndrives[] = {
     "(Hitachi |HITACHI )?HT(S|E)5432(80|12|16|25|32)L9(A3(00)?|SA0[012])",
     "", "", ""
   },
-  { "Hitachi Travelstar 5K500.B",
-    "(Hitachi )?HT[ES]5450(12|16|25|32|40|50)B9A30[01]",
+  { "Hitachi/HGST Travelstar Z5K320", // tested with Hitachi HTS543232A7A384/ES2OA70K
+    "(Hitachi|HGST) HT[ES]5432(16|25|32)A7A38[145]",
+    "", "", ""
+  },
+  { "Hitachi Travelstar 5K500.B", // tested with Hitachi HTS545050B9SA00/PB4OC60X
+    "(Hitachi )?HT[ES]5450(12|16|25|32|40|50)B9(A30[01]|SA00)",
     "", "", ""
   },
-  { "Hitachi/HGST Travelstar Z5K500", // tested with HGST HTS545050A7E380/GG2OAC90
-    "HGST HT[ES]5450(25|32|50)A7E38[01]",
+  { "Hitachi/HGST Travelstar Z5K500", // tested with HGST HTS545050A7E380/GG2OAC90,
+      // Hitachi HTS545032A7E380/GGBOA7A0, HGST HTS545050A7E680/GR2OA230,
+      // APPLE HDD HTS545050A7E362/GG2AB990
+    "(Hitachi|HGST|APPLE HDD) HT[ES]5450(25|32|50)A7E(362|38[01]|680)",
     "", "", ""
   },
   { "Hitachi/HGST Travelstar 5K750", // tested with Hitachi HTS547575A9E384/JE4OA60A,
@@ -1910,6 +2313,19 @@ const drive_settings builtin_knowndrives[] = {
     "(Hitachi|APPLE HDD) HT[ES]5475(50|64|75)A9E38[14]",
     "", "", ""
   },
+  { "HGST Travelstar 5K1000", // tested with HGST HTS541010A9E680/JA0OA560,
+      // HGST HTS541075A9E680/JA2OA560
+    "HGST HT[ES]5410(64|75|10)A9E68[01]",
+    "", "", ""
+  },
+  { "HGST Travelstar Z5K1000", // tested with HGST HTS541010A7E630/SE0OA4A0
+    "HGST HTS5410(75|10)A7E63[015]",
+    "", "", ""
+  },
+  { "HGST Travelstar 5K1500", // tested with HGST HTS541515A9E630/KA0OA500
+    "HGST HT[ES]541515A9E63[015]",
+    "", "", ""
+  },
   { "Hitachi Travelstar 7K60",
     "(Hitachi )?HTS726060M9AT00",
     "", "", ""
@@ -1939,12 +2355,13 @@ const drive_settings builtin_knowndrives[] = {
     "(HITACHI )?HT[ES]7232(16|25|32)A7A36[145]",
     "", "", ""
   },
-  { "Hitachi Travelstar 7K500",
-    "(Hitachi )?HT[ES]7250(12|16|25|32|50)A9A36[2-5]",
+  { "Hitachi Travelstar 7K500", // tested with Hitachi HTS725050A9A360/PC4OC70D,
+    // HITACHI HTS725032A9A364/PC3ZC70F
+    "(Hitachi |HITACHI )?HT[ES]7250(12|16|25|32|50)A9A36[02-5]",
     "", "", ""
   },
   { "Hitachi/HGST Travelstar Z7K500", // tested with HITACHI HTS725050A7E630/GH2ZB390,
-      // HGST HTS725050A7E630/GH2OA420
+      // HGST HTS725050A7E630/GH2OA420, HGST HTS725050A7E630/GH2OA530
     "(HITACHI|HGST) HT[ES]7250(25|32|50)A7E63[015]",
     "", "", ""
   },
@@ -1977,6 +2394,10 @@ const drive_settings builtin_knowndrives[] = {
     "(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]",
     "", "", ""
   },
+  { "Hitachi CinemaStar 5K320", // tested with Hitachi HCS5C3225SLA380/STBOA37H
+    "Hitachi HCS5C32(25|32)SLA380",
+    "", "", ""
+  },
   { "Hitachi Deskstar 5K3000", // tested with HDS5C3030ALA630/MEAOA5C0,
        // Hitachi HDS5C3020BLE630/MZ4OAAB0 (OEM, Toshiba Canvio Desktop)
     "(Hitachi )?HDS5C30(15|20|30)(ALA|BLE)63[02].*",
@@ -2030,8 +2451,9 @@ const drive_settings builtin_knowndrives[] = {
     "(Hitachi )?HDT7210((16|25)SLA380|(32|50|64|75|10)SLA360)",
     "", "", ""
   },
-  { "Hitachi Deskstar 7K1000.C", // tested with Hitachi HDS721010CLA330/JP4OA3MA
-    "(Hitachi )?HDS7210((16|25)CLA382|(32|50)CLA362|(64|75|10)CLA33[02])",
+  { "Hitachi Deskstar 7K1000.C", // tested with Hitachi HDS721010CLA330/JP4OA3MA,
+      // Hitachi HDS721025CLA682/JP1OA41A
+    "(Hitachi )?HDS7210((16|25)CLA[36]82|(32|50)CLA[36]62|(64|75|10)CLA[36]3[02])",
     "", "", ""
   },
   { "Hitachi Deskstar 7K1000.D", // tested with HDS721010DLE630/MS2OA5Q0
@@ -2054,6 +2476,11 @@ const drive_settings builtin_knowndrives[] = {
     "Hitachi HDS724040ALE640",
     "", "", ""
   },
+  { "HGST Deskstar NAS", // tested with HGST HDN724040ALE640/MJAOA5E0,
+       // HGST HDN726050ALE610/APGNT517, HGST HDN726060ALE610/APGNT517
+    "HGST HDN72(4030|4040|6050|6060)ALE6[14]0",
+    "", "", ""
+  },
   { "Hitachi Ultrastar A7K1000", // tested with
     // HUA721010KLA330      44X2459 42C0424IBM/GKAOAB4A
     "(Hitachi )?HUA7210(50|75|10)KLA330.*",
@@ -2064,12 +2491,28 @@ const drive_settings builtin_knowndrives[] = {
     "(Hitachi )?HUA7220(50|10|20)[AC]LA33[01].*",
     "", "", ""
   },
-  { "Hitachi Ultrastar 7K3000", // tested with HUA723030ALA640/MKAOA580
-    "Hitachi HUA7230(20|30)ALA640",
+  { "Hitachi Ultrastar 7K3000", // tested with Hitachi HUA723030ALA640/MKAOA580,
+      // Hitachi HUA723020ALA641/MK7OA840
+    "Hitachi HUA7230(20|30)ALA64[01]",
     "", "", ""
   },
-  { "Hitachi Ultrastar 7K4000", // tested with Hitachi HUS724040ALE640/MJAOA3B0
-    "Hitachi HUS7240(20|30|40)ALE640",
+  { "Hitachi/HGST Ultrastar 7K4000", // tested with Hitachi HUS724040ALE640/MJAOA3B0,
+      // HGST HUS724040ALE640/MJAOA580, HGST HUS724020ALA640/MF6OAA70
+    "(Hitachi|HGST) HUS7240(20|30|40)AL[AE]64[01]",
+    "", "", ""
+  },
+  { "HGST Ultrastar He6", // tested with HGST HUS726060ALA640/AHGNT1E2
+    "HGST HUS726060ALA64[01]",
+    "", "",
+    "-v 22,raw48,Helium_Level"
+  },
+  { "HGST Ultrastar He8", // tested with HGST HUH728060ALE600/GR2OA230
+    "HGST HUH7280(60|80)AL[EN]60[014]",
+    "", "",
+    "-v 22,raw48,Helium_Level"
+  },
+  { "HGST MegaScale 4000", // tested with HGST HMS5C4040ALE640/MPAOA580
+    "HGST HMS5C4040[AB]LE64[01]", // B = DC 4000.B
     "", "", ""
   },
   { "Toshiba 2.5\" HDD (10-20 GB)",
@@ -2096,6 +2539,12 @@ const drive_settings builtin_knowndrives[] = {
     "TOSHIBA MK8050GACY",
     "", "", ""
   },
+  { "Toshiba 2.5\" HDD MK..51GSY", // tested with TOSHIBA MK1251GSY/LD101D
+    "TOSHIBA MK(80|12|16|25)51GSY",
+    "",
+    "",
+    "-v 9,minutes"
+  },
   { "Toshiba 2.5\" HDD MK..52GSX",
     "TOSHIBA MK(80|12|16|25|32)52GSX",
     "", "", ""
@@ -2118,8 +2567,15 @@ const drive_settings builtin_knowndrives[] = {
     "TOSHIBA MK(75|10)59GSM",
     "", "", ""
   },
-  { "Toshiba 2.5\" HDD MK..61GSY[N]", // tested with TOSHIBA MK5061GSY/MC102E, MK5061GSYN/MH000A
+  { "Toshiba 2.5\" HDD MK..61GSY[N]", // tested with TOSHIBA MK5061GSY/MC102E, MK5061GSYN/MH000A,
+      // TOSHIBA MK2561GSYN/MH000D
     "TOSHIBA MK(16|25|32|50|64)61GSYN?",
+    "",
+    "",
+    "-v 9,minutes" // TOSHIBA MK2561GSYN/MH000D
+  },
+  { "Toshiba 2.5\" HDD MK..61GSYB", // tested with TOSHIBA MK5061GSYB/ME0A
+    "TOSHIBA MK(16|25|32|50|64)61GSYB",
     "", "", ""
   },
   { "Toshiba 2.5\" HDD MK..65GSX", // tested with TOSHIBA MK5065GSX/GJ003A, MK3265GSXN/GH012H,
@@ -2127,17 +2583,39 @@ const drive_settings builtin_knowndrives[] = {
     "TOSHIBA MK(16|25|32|50|64)65GSX[FN]?( H)?", // "... H" = USB ?
     "", "", ""
   },
+  { "Toshiba 2.5\" HDD MK..75GSX", // tested with TOSHIBA MK7575GSX/GT001C
+    "TOSHIBA MK(32|50|64|75)75GSX",
+    "", "", ""
+  },
   { "Toshiba 2.5\" HDD MK..76GSX", // tested with TOSHIBA MK3276GSX/GS002D
     "TOSHIBA MK(16|25|32|50|64)76GSX",
     "",
     "",
     "-v 9,minutes"
   },
-  { "Toshiba 2.5\" HDD MQ01ABD...", // tested with TOSHIBA MQ01ABD100/AX001U
-    "TOSHIBA MQ01ABD(025|032|050|064|075|100)",
+  { "Toshiba 2.5\" HDD MQ01ABB...", // tested with TOSHIBA MQ01ABB200/AY000U
+    "TOSHIBA MQ01ABB(100|150|200)",
+    "", "", ""
+  },
+  { "Toshiba 2.5\" HDD MQ01ABC...", // tested with TOSHIBA MQ01ABC150/AQ001U
+    "TOSHIBA MQ01ABC(100|150|200)",
+    "", "", ""
+  },
+  { "Toshiba 2.5\" HDD MQ01ABD...", // tested with TOSHIBA MQ01ABD100/AX001U,
+      // TOSHIBA MQ01ABD100V/AX001Q
+    "TOSHIBA MQ01ABD(025|032|050|064|075|100)V?",
+    "", "", ""
+  },
+  { "Toshiba 2.5\" HDD MQ01ABF...", // tested with TOSHIBA MQ01ABF050/AM001J
+    "TOSHIBA MQ01ABF(050|075|100)",
+    "", "", ""
+  },
+  { "Toshiba 2.5\" HDD MQ01UBB... (USB 3.0)", // tested with TOSHIBA MQ01UBB200/AY000U (0x0480:0xa100)
+    "TOSHIBA MQ01UBB200",
     "", "", ""
   },
-  { "Toshiba 2.5\" HDD MQ01UBD... (USB 3.0)", // tested with TOSHIBA MQ01ABD100/AX001U
+  { "Toshiba 2.5\" HDD MQ01UBD... (USB 3.0)", // tested with TOSHIBA MQ01UBD050/AX001U (0x0480:0xa007),
+      // TOSHIBA MQ01UBD100/AX001U (0x0480:0x0201, 0x0480:0xa200)
     "TOSHIBA MQ01UBD(050|075|100)",
     "", "", ""
   },
@@ -2149,7 +2627,15 @@ const drive_settings builtin_knowndrives[] = {
     "TOSHIBA MG03ACA[1234]00Y?",
     "", "", ""
   },
-  { "Toshiba 3.5\" HDD DT01ACA...", // tested with TOSHIBA DT01ACA100/MS2OA750,
+  { "Toshiba 3.5\" MD04ACA... Enterprise HDD", // tested with TOSHIBA MD04ACA500/FP1A
+    "TOSHIBA MD04ACA[2345]00",
+    "", "", ""
+  },
+  { "Toshiba 3.5\" DT01ABA... Desktop HDD", // tested with TOSHIBA DT01ABA300/MZ6OABB0
+    "TOSHIBA DT01ABA(100|150|200|300)",
+    "", "", ""
+  },
+  { "Toshiba 3.5\" DT01ACA... Desktop HDD", // tested with TOSHIBA DT01ACA100/MS2OA750,
       // TOSHIBA DT01ACA200/MX4OABB0, TOSHIBA DT01ACA300/MX6OABB0
     "TOSHIBA DT01ACA(025|032|050|075|100|150|200|300)",
     "", "", ""
@@ -2281,8 +2767,9 @@ const drive_settings builtin_knowndrives[] = {
     "ST(160|250|320)LT0(07|09|11|14)-.*",
     "", "", ""
   },
-  { "Seagate Laptop Thin HDD", // tested with ST500LT012-9WS142/0001SDM1
-    "ST(250|320|500)LT0(12|15|25)-.*",
+  { "Seagate Laptop Thin HDD", // tested with ST500LT012-9WS142/0001SDM1,
+      // ST500LM021-1KJ152/0002LIM1
+    "ST((250|320|500)LT0(12|15|25)|(320|500)LM0(10|21))-.*",
     "", "", ""
   },
   { "Seagate Laptop SSHD", // tested with ST500LM000-1EJ162/SM11
@@ -2457,8 +2944,9 @@ const drive_settings builtin_knowndrives[] = {
   },
   { "Seagate Barracuda 7200.14 (AF)", // different part number, tested with
       // ST1000DM003-1CH162/CC47, ST1000DM003-1CH162/CC49, ST2000DM001-1CH164/CC24,
-      // ST1000DM000-9TS15E/CC92
-    "ST(1000|1500|2000|2500|3000)DM00[0-3]-.*",
+      // ST1000DM000-9TS15E/CC92, APPLE HDD ST3000DM001/AP15 (no attr 240)
+    "ST(1000|1500|2000|2500|3000)DM00[0-3]-.*|"
+    "APPLE HDD ST3000DM001",
     "", "",
     "-v 188,raw16 -v 240,msec24hour32"
   },
@@ -2467,13 +2955,13 @@ const drive_settings builtin_knowndrives[] = {
     "", "",
     "-v 188,raw16 -v 240,msec24hour32"
   },
-  { "Seagate Desktop HDD.15", // tested with ST4000DM000-1CD168/CC43
-    "ST4000DM000-.*",
+  { "Seagate Desktop HDD.15", // tested with ST4000DM000-1CD168/CC43, ST5000DM000-1FK178/CC44
+    "ST[45]000DM000-.*",
     "", "",
     "-v 188,raw16 -v 240,msec24hour32"
   },
   { "Seagate Desktop SSHD", // tested with ST2000DX001-1CM164/CC43
-    "ST(1000|2000|4000)DX001-.*",
+    "ST[124]000DX001-.*",
     "", "",
     "-v 188,raw16 -v 240,msec24hour32"
   },
@@ -2547,8 +3035,10 @@ const drive_settings builtin_knowndrives[] = {
     "ST(5|10|20)00NM0011",
     "", "", ""
   },
-  { "Seagate Constellation ES.2 (SATA 6Gb/s)", // tested with ST32000645NS/0004, ST33000650NS
-    "ST3(2000645|300065[012])NS",
+  { "Seagate Constellation ES.2 (SATA 6Gb/s)", // tested with ST32000645NS/0004, ST33000650NS,
+      // MB3000EBKAB/HPG6
+    "ST3(2000645|300065[012])NS|"
+    "MB3000EBKAB", // HP OEM
     "", "", ""
   },
   { "Seagate Constellation ES.3", // tested with ST1000NM0033-9ZM173/0001, ST4000NM0033-9ZM170/SN03
@@ -2559,14 +3049,24 @@ const drive_settings builtin_knowndrives[] = {
     "ST(1000|2000|3000)NC00[0-3](-.*)?",
     "", "", ""
   },
-  { "Seagate Constellation.2 (SATA)", // 2.5", tested with ST91000640NS/SN02
-    "ST9(25061|50062|100064)[012]NS", // *SS = SAS
+  { "Seagate Constellation.2 (SATA)", // 2.5", tested with ST91000640NS/SN02, MM1000GBKAL/HPGB
+    "ST9(25061|50062|100064)[012]NS|" // *SS = SAS
+    "MM1000GBKAL", // HP OEM
     "", "", ""
   },
+  { "Seagate Enterprise Capacity 3.5 HDD", // tested with ST6000NM0024-1HT17Z/SN02
+    "ST[2456]000NM0[01][248]4-.*", // *[069]4 = 4Kn
+    "", "", 
+    "-v 188,raw16"
+  },
   { "Seagate NAS HDD", // tested with ST2000VN000-1H3164/SC42, ST3000VN000-1H4167/SC43
     "ST[234]000VN000-.*",
     "", "", ""
   },
+  { "Seagate Archive HDD", // tested with ST8000AS0002-1NA17Z/AR13
+    "ST[568]000AS00[01][12]-.*",
+    "", "", ""
+  },
   { "Seagate Pipeline HD 5900.1",
     "ST3(160310|320[34]10|500(321|422))CS",
     "", "", ""
@@ -2595,16 +3095,20 @@ const drive_settings builtin_knowndrives[] = {
     "ST3(160815|250820|320620|500630|750640)[AS]V",
     "", "", ""
   },
+  { "Seagate SV35.3", // tested with ST3500320SV/SV16
+    "ST3(500320|750330|1000340)SV",
+    "", "", ""
+  },
   { "Seagate SV35.5", // tested with ST31000525SV/CV12
     "ST3(250311|500410|1000525)SV",
     "", "", ""
   },
-  { "Seagate SV35", // tested with ST2000VX000-9YW164/CV12
-    "ST([123]000VX00[20]|31000526SV|3500411SV)(-.*)?",
+  { "Seagate SV35", // tested with ST1000VX001-1HH162/CV11, ST2000VX000-9YW164/CV12
+    "ST([123]000VX00[012]|31000526SV|3500411SV)(-.*)?",
     "", "", ""
   },
-  { "Seagate DB35", // tested with ST3250823ACE/3.03
-    "ST3(200826|250823|300831|400832)ACE",
+  { "Seagate DB35", // tested with ST3250823ACE/3.03, ST3300831SCE/3.03
+    "ST3(200826|250823|300831|400832)[AS]CE",
     "", "", ""
   },
   { "Seagate DB35.2", // tested with ST3160212SCE/3.ACB
@@ -2721,8 +3225,9 @@ const drive_settings builtin_knowndrives[] = {
     "WDC WD((16|20|25|32|40|50|64|75)00AAKS|1602ABKS|10EALS)-.*",
     "", "", ""
   },
-  { "Western Digital Caviar Blue (SATA 6Gb/s)", // tested with WDC WD10EZEX-00RKKA0/80.00A80
-    "WDC WD((25|32|50)00AAKX|7500AALX|10EALX|10EZEX)-.*",
+  { "Western Digital Blue", // tested with WDC WD5000AZLX-00K4KA0/80.00A80,
+      // WDC WD10EZEX-00RKKA0/80.00A80, WDC WD10EZEX-75M2NA0/01.01A01, WDC WD40EZRZ-00WN9B0/80.00A80
+    "WDC WD((25|32|50)00AAKX|5000AZ(LX|RZ)|7500A(AL|ZE)X|10E(AL|ZE)X|[1-6]0EZRZ)-.*",
     "", "", ""
   },
   { "Western Digital RE Serial ATA",
@@ -2741,25 +3246,29 @@ const drive_settings builtin_knowndrives[] = {
     "WDC WD((25|32|50|75)02A|(75|10)02F)BYS-.*",
     "", "", ""
   },
-  { "Western Digital RE4", // tested with WDC WD2003FYYS-18W0B0/01.01D02
-    "WDC WD((((25|50)03A|1003F)BYX)|((15|20)03FYYS))-.*",
+  { "Western Digital RE4", // tested with WDC WD2003FYYS-18W0B0/01.01D02,
+      // WDC WD1003FBYZ-010FB0/01.01V03
+    "WDC WD((25|50)03ABYX|1003FBY[XZ]|(15|20)03FYYS)-.*",
     "", "", ""
   },
-  { "Western Digital RE4-GP", // tested with WDC WD2002FYPS-02W3B0/04.01G01
-    "WDC WD2002FYPS-.*",
+  { "Western Digital RE4-GP", // tested with WDC WD2002FYPS-02W3B0/04.01G01,
+      // WD2003FYPS-27W9B0/01.01D02
+    "(WDC )?WD200[23]FYPS-.*",
     "", "", ""
   },
-  { "Western Digital RE4 (SATA 6Gb/s)", // tested with WDC WD2000FYYZ-01UL1B0/01.01K01,
-      // WD2000FYYX/00.0D1K2
-    "WDC WD(20|30|40)00FYYZ-.*|WD2000FYYX",
+  { "Western Digital Re", // tested with WDC WD1004FBYZ-01YCBB0/RR02,
+      // WDC WD2000FYYZ-01UL1B0/01.01K01, WDC WD2000FYYZ-01UL1B1/01.01K02,
+      // WDC WD4000FYYZ-01UL1B2/01.01K03, WD2000FYYX/00.0D1K2
+    "WDC WD([12]004FBYZ|[1-6]00[01M]F[SXY]YZ)-.*|WD2000FYYX",
     "", "", ""
   },
   { "Western Digital Se", // tested with WDC WD2000F9YZ-09N20L0/01.01A01
     "WDC WD(1002|2000|3000|4000)F9YZ-.*",
     "", "", ""
   },
-  { "Western Digital Caviar Green",
-    "WDC WD((50|64|75)00AA(C|V)S|(50|64|75)00AADS|10EA(C|V)S|(10|15|20)EADS)-.*",
+  { "Western Digital Caviar Green", // tested with WDC WD7500AADS-00M2B0/01.00A01,
+       // WDC WD10EADX/77.04D77
+    "WDC WD((50|64|75)00AA[CV]S|(50|64|75)00AADS|10EA[CV]S|(10|15|20)EAD[SX])-.*",
     "",
     "",
     "-F xerrorlba" // tested with WDC WD7500AADS-00M2B0/01.00A01
@@ -2768,20 +3277,29 @@ const drive_settings builtin_knowndrives[] = {
     "WDC WD(((64|75|80)00AA|(10|15|20)EA|(25|30)EZ)R|20EAC)S-.*",
     "", "", ""
   },
-  { "Western Digital Caviar Green (AF, SATA 6Gb/s)", // tested with
+  { "Western Digital Green", // tested with
       // WDC WD10EZRX-00A8LB0/01.01A01, WDC WD20EZRX-00DC0B0/80.00A80,
-      // WDC WD30EZRX-00MMMB0/80.00A80, WDC WD40EZRX-00SPEB0/80.00A80
-    "WDC WD(7500AA|(10|15|20)EA|(10|20|25|30|40)EZ)RX-.*",
+      // WDC WD30EZRX-00MMMB0/80.00A80, WDC WD40EZRX-00SPEB0/80.00A80,
+      // WDC WD60EZRX-00MVLB1/80.00A80
+    "WDC WD(7500AA|(10|15|20)EA|(10|20|25|30|40|50|60)EZ)RX-.*",
     "", "", ""
   },
-  { "Western Digital Caviar Black",
-    "WDC WD((500|640|750)1AAL|1001FA[EL]|2001FAS)S-.*",
+  { "Western Digital Caviar Black", // tested with WDC WD7501AAES/06.01D06
+    "WDC WD((500|640)1AAL|7501AA[EL]|1001FA[EL]|2001FAS)S-.*|"
+    "WDC WD(2002|7502|1502|5003|1002|5002)(FAE|AAE|AZE|AAL)X-.*", // could be
+    // WD2002FAEX, WD7502AAEX, WD1502FAEX, WD5003AZEX, WD1002FAEX, WD5002AALX
     "", "", ""
   },
   { "Western Digital Black", // tested with
-      // WDC WD5003AZEX-00RKKA0/80.00A80, WDC WD1003FZEX-00MK2A0/01.01A01,
-      // WDC WD3001FAEX-00MJRA0/01.01L01, WDC WD4001FAEX-00MJRA0/01.01L01
-    "WDC WD(5002AAL|5003AZE|(64|75)02AAE|((10|15|20)0[23]|[34]001)F[AZ]E)X-.*",
+      // WDC WD1003FZEX-00MK2A0/01.01A01, WDC WD3001FAEX-00MJRA0/01.01L01,
+      // WDC WD3003FZEX-00Z4SA0/01.01A01, WDC WD4001FAEX-00MJRA0/01.01L01
+      // WDC WD4003FZEX-00Z4SA0/01.01A01, WDC WD5003AZEX-00RKKA0/80.00A80,
+    "WDC WD(6001|2003|5001|1003|4003|5003|3003|3001)(FZW|FZE|AZE)X-.*|" // could be
+    // new series  WD6001FZWX WD2003FZEX WD5001FZWX WD1003FZEX
+    //             WD4003FZEX WD5003AZEX WD3003FZEX
+    "WDC WD(4001|3001|2002|1002|5003|7500|5000|3200|2500|1600)(FAE|AZE|B[PE]K)[XT]-.*",
+    // old series: WD4001FAEX WD3001FAEX WD2002FAEX WD1002FAEX  WD5003AZEX
+    //             WD7500BPKT  WD5000BPKT WD3200BEKT WD2500BEKT WD1600BEKT
     "", "", ""
   },
   { "Western Digital AV ATA", // tested with WDC WD3200AVJB-63J5A0/01.03E01
@@ -2796,12 +3314,13 @@ const drive_settings builtin_knowndrives[] = {
     "WDC WD((16|25|32|50|64|75)00AV[CDV]S|(10|15|20)EV[CDV]S)-.*",
     "", "", ""
   },
-  { "Western Digital AV-GP (AF)", // tested with WDC WD10EURS-630AB1/80.00A80, WDC WD10EUCX-63YZ1Y0/51.0AB52
-    "WDC WD(7500AURS|10EU[CR]X|(10|15|20|25|30)EURS)-.*",
+  { "Western Digital AV-GP (AF)", // tested with WDC WD10EURS-630AB1/80.00A80,
+      // WDC WD10EUCX-63YZ1Y0/51.0AB52, WDC WD20EURX-64HYZY0/80.00A80
+    "WDC WD(5000AUDX|7500AURS|10EUCX|(10|15|20|25|30)EUR[SX])-.*",
     "", "", ""
   },
-  { "Western Digital AV-25",
-    "WDC WD((16|25|32|50)00BUD|5000BUC)T-.*",
+  { "Western Digital AV", // tested with DC WD10JUCT-63CYNY0/01.01A01
+    "WDC WD((16|25|32|50)00BU[CD]|5000LUC|10JUC)T-.*",
     "", "", ""
   },
   { "Western Digital Raptor",
@@ -2849,27 +3368,52 @@ const drive_settings builtin_knowndrives[] = {
     "WDC WD(50|75)00BPKT-.*",
     "", "", ""
   },
-  { "Western Digital Red (AF)", // tested with WDC WD10EFRX-68JCSN0/01.01A01,
-      // WDC WD10JFCX-68N6GN0/01.01A01, WDC WD40EFRX-68WT0N0/80.00A80
-    "WDC WD(10|20|30|40)[EJ]F[CR]X-.*",
+  { "Western Digital Red", // tested with WDC WD10EFRX-68JCSN0/01.01A01,
+      // WDC WD10JFCX-68N6GN0/01.01A01, WDC WD40EFRX-68WT0N0/80.00A80,
+      // WDC WD60EFRX-68MYMN1/82.00A82, WDC WD80EFZX-68UW8N0/83.H0A83
+    "WDC WD(7500BFC|10JFC|[1-6]0EFR|80EFZ)X-.*",
+    "", "",
+    "-v 22,raw48,Helium_Level" // WD80EFZX
+  },
+  { "Western Digital Red Pro", // tested with WDC WD2001FFSX-68JNUN0/81.00A81
+    "WDC WD[234]001FFSX-.*",
     "", "", ""
   },
-  { "Western Digital Blue Mobile", // tested with WDC WD5000LPVX-08V0TT2/03.01A03
-    "WDC WD((25|32|50|75)00[BLM]|10[JS])P[CV][TX]-.*",
+  { "Western Digital Purple", // tested with WDC WD40PURX-64GVNY0/80.00A80
+    "WDC WD[123456]0PURX-.*",
+    "", "", ""
+  },
+  { "Western Digital Blue Mobile", // tested with WDC WD5000LPVX-08V0TT2/03.01A03,
+      // WDC WD20NPVZ-00WFZT0/01.01A01
+    "WDC WD((25|32|50|75)00[BLM]|10[JS]|20N)P[CV][TXZ]-.*",
     "", "", ""
   },
   { "Western Digital Green Mobile", // tested with WDC WD20NPVX-00EA4T0/01.01A01
     "WDC WD(15|20)NPV[TX]-.*",
     "", "", ""
   },
+  { "Western Digital Black Mobile", // tested with WDC WD7500BPKX-22HPJT0/01.01A01
+    "WDC WD((16|25|32)00BE|(50|75)00BP)KX-.*",
+    "", "", ""
+  },
   { "Western Digital Elements / My Passport (USB)", // tested with WDC WD5000BMVW-11AMCS0/01.01A01
     "WDC WD(25|32|40|50)00BMV[UVW]-.*",  // *W-* = USB 3.0
     "", "", ""
   },
   { "Western Digital Elements / My Passport (USB, AF)", // tested with
-      // WDC WD5000KMVV-11TK7S1/01.01A01, WDC WD10TMVW-11ZSMS5/01.01A01,
-      // WDC WD10JMVW-11S5XS1/01.01A01, WDC WD20NMVW-11W68S0/01.01A01
-    "WDC WD(5000[LK]|7500K|10[JT]|20N)MV[VW]-.*", // *W-* = USB 3.0
+      // WDC WD5000KMVV-11TK7S1/01.01A01,
+      // WDC WD5000LMVW-11CKRS0/01.01A01 (0x1058:0x07ae),
+      // WDC WD5000LMVW-11VEDS0/01.01A01 (0x1058:0x0816),
+      // WDC WD7500BMVW-11AJGS2/01.01A01,
+      // WDC WD10JMVW-11AJGS2/01.01A01 (0x1058:0x10b8),
+      // WDC WD10JMVW-11AJGS4/01.01A01 (0x1058:0x25a2),
+      // WDC WD10JMVW-11S5XS1/01.01A01,
+      // WDC WD10TMVW-11ZSMS5/01.01A01,
+      // WDC WD20NMVW-11AV3S2/01.01A01 (0x1058:0x0822),
+      // WDC WD20NMVW-11AV3S3/01.01A01 (0x1058:0x0837),
+      // WDC WD20NMVW-11W68S0/01.01A01,
+      // WDC WD30NMVW-11C3NS4/01.01A01
+    "WDC WD(5000[LK]|7500[BK]|10[JT]|[23]0N)MV[VW]-.*", // *W-* = USB 3.0
     "", "", ""
   },
   { "Quantum Bigfoot", // tested with TS10.0A/A21.0G00, TS12.7A/A21.0F00
@@ -2925,6 +3469,13 @@ const drive_settings builtin_knowndrives[] = {
   // USB ID entries
   ////////////////////////////////////////////////////
 
+  // 0x0350 (?)
+  { "USB: ViPowER USB3.0 Storage; ",
+    "0x0350:0x0038",
+    "", // 0x1905
+    "",
+    "-d sat,12" // ATA output registers missing
+  },
   // Hewlett-Packard
   { "USB: HP Desktop HD BD07; ", // 2TB
     "0x03f0:0xbd07",
@@ -2953,6 +3504,18 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: Buffalo Drivestation Duo; ",
+    "0x0411:0x01ce",
+    "",
+    "",
+    "-d sat"
+  },
+  { "USB: Buffalo DriveStation HD-LBU2 ; Medialogic MLDU11",
+    "0x0411:0x01ea",
+    "",
+    "",
+    "-d sat"
+  },
   { "USB: Buffalo MiniStation Stealth HD-PCTU2; ",
     "0x0411:0x01d9",
     "", // 0x0108
@@ -2965,6 +3528,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: Buffalo MiniStation Safe HD-PNFU3; ", // 1TB
+    "0x0411:0x0251",
+    "",
+    "",
+    "-d sat"
+  },
   // LG Electronics
   { "USB: LG Mini HXD5; JMicron",
     "0x043e:0x70f1",
@@ -2972,6 +3541,13 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbjmicron"
   },
+  // Hitachi (?)
+  { "USB: ; Renesas uPD720231A", // USB2/3->SATA
+    "0x045b:0x022a", // DeLock 62652 converter SATA 6GB/s > USB 3.0
+    "",
+    "",
+    "-d sat"
+  },
   // Philips
   { "USB: Philips; ", // SDE3273FC/97 2.5" SATA HDD enclosure
     "0x0471:0x2021",
@@ -2980,12 +3556,18 @@ const drive_settings builtin_knowndrives[] = {
     "-d sat"
   },
   // Toshiba
-  { "USB: Toshiba Stor.E Slim USB 3.0; ", // 1TB, MQ01UBD100
+  { "USB: Toshiba Stor.E Slim USB 3.0; ", // TOSHIBA MQ01UBD100
     "0x0480:0x0100",
     "", // 0x0000
     "",
     "-d sat"
   },
+  { "USB: Toshiba Canvio; ", // 0x0210: TOSHIBA MQ03UBB300
+    "0x0480:0x02(01|10)",
+    "",
+    "",
+    "-d sat"
+  },
   { "USB: Toshiba Canvio 500GB; SunPlus",
     "0x0480:0xa004",
     "",
@@ -2993,32 +3575,32 @@ const drive_settings builtin_knowndrives[] = {
     "-d usbsunplus"
   },
   { "USB: Toshiba Canvio Basics; ",
-    "0x0480:0xa006",
-    "", // 0x0001
+    "0x0480:0xa00[6d]",
+    "", // 0x0001, 0x0407
     "",
     "-d sat"
   },
-  { "USB: Toshiba Canvio 3.0 Portable Hard Drive; ", // 1TB
+  { "USB: Toshiba Canvio 3.0 Portable Hard Drive; ", // TOSHIBA MQ01UBD050
     "0x0480:0xa007",
     "", // 0x0001
     "",
     "-d sat"
   },
-  { "USB: Toshiba Stor.E Basics; ", // 1TB
-    "0x0480:0xa009",
-    "",
+  { "USB: Toshiba Stor.E; ",
+    "0x0480:0xa00[9ace]",
+    "", // 0x0000 (0xa00e)
     "",
     "-d sat"
   },
-  { "USB: Toshiba Stor.E Plus", // 2TB
-    "0x0480:0xa00a",
-    "",
+  { "USB: Toshiba Canvio; ",
+    "0x0480:0xa(100|20[0c])", // 0xa100: TOSHIBA MQ01UBB200, 0xa200: TOSHIBA MQ01UBD100,
+    "",  // 0xa20c: TOSHIBA MQ01ABB200
     "",
     "-d sat"
   },
-  { "USB: Toshiba Canvio Desktop; ", // 2TB
-    "0x0480:0xd010",
-    "",
+  { "USB: Toshiba Canvio Desktop; ",
+    "0x0480:0xd0(00|10|11)",
+    "", // 0x0316 (0xd011)
     "",
     "-d sat"
   },
@@ -3042,8 +3624,7 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
-  // Fujitsu chip on DeLock 42475 
-  { "USB: Fujitsu  SATA-to-USB3.0 bridge chip", // USB 3.0
+  { "USB: ; Fujitsu", // DeLock 42475, USB 3.0
     "0x04c5:0x201d",
     "", // 0x0001
     "",
@@ -3058,10 +3639,10 @@ const drive_settings builtin_knowndrives[] = {
   },
   // Samsung
   { "USB: Samsung S2 Portable; JMicron",
-    "0x04e8:0x1f0[568]",
+    "0x04e8:0x1f0[568a]", // 0x1f0a: SAMSUNG HN-M101XBB
     "",
     "",
-    "-d usbjmicron"
+    "-d usbjmicron" // 0x1f0a: works also with "-d sat"
   },
   { "USB: Samsung S1 Portable; JMicron",
     "0x04e8:0x2f03",
@@ -3077,9 +3658,15 @@ const drive_settings builtin_knowndrives[] = {
   },
   { "USB: Samsung G2 Portable; JMicron",
     "0x04e8:0x6032",
+    "0x0000",
     "",
+    "-d usbjmicron" // ticket #132
+  },
+  { "USB: Samsung G2 Portable; ",
+    "0x04e8:0x6032",
+    "0x...[1-9]", // >= 0x0001
     "",
-    "-d usbjmicron"
+    "-d sat"
   },
   { "USB: Samsung Story Station 3.0; ",
     "0x04e8:0x6052",
@@ -3099,18 +3686,24 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
-  { "USB: Samsung D3 Station; ", // 3TB
-    "0x04e8:0x6124",
-    "", // 0x200
+  { "USB: Samsung D3 Station; ",
+    "0x04e8:0x612[45]", // 3TB, 4TB
+    "", // 0x200, 0x202
     "",
     "-d sat"
   },
-  { "USB: Samsung M3 Portable USB 3.0; ",
-    "0x04e8:0x61b[456]", // 4=2TB, 5=1.5TB, 6=1TB
+  { "USB: Samsung M3 Portable USB 3.0; ", // 1.5/2TB: SpinPoint M9TU
+    "0x04e8:0x61b[3456]", // 500MB, 2TB, 1.5TB, 1TB
     "", // 0x0e00
     "",
     "-d sat"
   },
+  { "USB: Samsung S3 Portable; ",
+    "0x04e8:0x61c8", // ST1000LM025 HN-M101ABB
+    "", // 0x1301
+    "",
+    "-d sat"
+  },
   // Sunplus
   { "USB: ; SunPlus",
     "0x04fc:0x0c05",
@@ -3149,9 +3742,9 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "" // unsupported
   },
-  { "USB: Iomega MDHD-UE; ",
-    "0x059b:0x0277",
-    "",
+  { "USB: Iomega; JMicron",
+    "0x059b:0x027[78]",  // 0x0277: MDHD-UE, 0x0278: LDHD-UPS
+    "", // 0x0000
     "",
     "-d usbjmicron"
   },
@@ -3161,9 +3754,9 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbsunplus"
   },
-  { "USB: Iomega GDHDU2; JMicron",
-    "0x059b:0x0475",
-    "", // 0x0100
+  { "USB: Iomega; JMicron",
+    "0x059b:0x0(47[05]|575)", // 0x0470: LPHD-UP, 0x0475: GDHDU2 (0x0100), 0x0575: LDHD-UP
+    "",
     "",
     "-d usbjmicron"
   },
@@ -3186,9 +3779,15 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbjmicron"
   },
-  { "USB: LaCie hard disk (Neil Poulton design);",
-    "0x059f:0x1018",
+  { "USB: LaCie Rugged Triple Interface; ",
+    "0x059f:0x100c",
+    "", // 0x0001
     "",
+    "-d sat"
+  },
+  { "USB: LaCie Desktop Hard Drive; ",
+    "0x059f:0x101[68]", // 0x1016: SAMSUNG HD103UJ
+    "", // 0x0001
     "",
     "-d sat"
   },
@@ -3240,6 +3839,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: LaCie; ",
+    "0x059f:0x106f",
+    "", // 0x0001
+    "",
+    "-d sat"
+  },
   // In-System Design
   { "USB: ; In-System/Cypress ISD-300A1",
     "0x05ab:0x0060",
@@ -3260,6 +3865,18 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: ; Genesys Logic GL3310",
+    "0x05e3:0x0731", // Chieftec USB 3.0 2.5" case
+    "",
+    "",
+    "-d sat"
+  },
+  { "USB: ; Genesys Logic",
+    "0x05e3:0x0735",
+    "", // 0x1003
+    "",
+    "-d sat"
+  },
   // Micron
   { "USB: Micron USB SSD; ",
     "0x0634:0x0655",
@@ -3274,7 +3891,13 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbjmicron,0" // Port number is required
   },
-  { "USB: ; Prolific PL3507", // USB+IEE1394->PATA
+  { "USB: ; Prolific PL2571/2771/2773/2775", // USB->SATA, USB3->SATA,
+    "0x067b:0x(2571|277[135])",              // USB3+eSATA->SATA, USB3->2xSATA
+    "",
+    "",
+    "-d usbprolific"
+  },
+  { "USB: ; Prolific PL3507", // USB+IEEE1394->PATA
     "0x067b:0x3507",
     "", // 0x0001
     "",
@@ -3287,7 +3910,20 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  // SanDisk
+  { "USB: SanDisk SDCZ80 Flash Drive; Fujitsu", // ATA ID: SanDisk pSSD
+    "0x0781:0x558[08]",
+    "",
+    "",
+    "-d sat"
+  },
   // Freecom
+  { "USB: ; Innostor IS631", // No Name USB3->SATA Enclosure
+    "0x07ab:0x0621",
+    "",
+    "",
+    "-d sat"
+  },
   { "USB: Freecom Mobile Drive XXS; JMicron",
     "0x07ab:0xfc88",
     "", // 0x0101
@@ -3312,8 +3948,8 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "" // unsupported
   },
-  { "USB: Freecom HD 500GB; JMicron",
-    "0x07ab:0xfcda",
+  { "USB: Freecom HD; JMicron", // 500GB
+    "0x07ab:0xfcd[6a]",
     "",
     "",
     "-d usbjmicron"
@@ -3369,6 +4005,13 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbsunplus"
   },
+  // Apricorn
+  { "USB: Apricorn SATA Wire; ",
+    "0x0984:0x0040",
+    "",
+    "",
+    "-d sat"
+  },
   // Seagate
   { "USB: Seagate External Drive; Cypress",
     "0x0bc2:0x0503",
@@ -3389,8 +4032,8 @@ const drive_settings builtin_knowndrives[] = {
     "-d sat"
   },
   { "USB: Seagate Expansion Portable; ",
-    "0x0bc2:0x23(00|12)",
-    "",
+    "0x0bc2:0x23(00|12|20|21|22)",
+    "", // 12=0x0219, 22=0x0000
     "",
     "-d sat"
   },
@@ -3412,8 +4055,14 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat,12"
   },
-  { "USB: Seagate Expansion External; ", // 2TB, 3TB
-    "0x0bc2:0x33(00|12|20|32)",
+  { "USB: Seagate FreeAgent XTreme; ",
+    "0x0bc2:0x3101",
+    "",
+    "",
+    "-d sat"
+  },
+  { "USB: Seagate Expansion External; ",
+    "0x0bc2:0x33(00|12|20|21|32)", // 0x3321: Expansion Desktop 4TB
     "",
     "",
     "-d sat"
@@ -3437,7 +4086,7 @@ const drive_settings builtin_knowndrives[] = {
     "-d sat"
   },
   { "USB: Seagate FreeAgent GoFlex USB 3.0; ", // 2TB
-    "0x0bc2:0x5071",
+    "0x0bc2:0x507[01]",
     "",
     "",
     "-d sat"
@@ -3448,30 +4097,49 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat,12" // "-d sat" does not work (ticket #151)
   },
-  { "USB: Seagate FreeAgent GoFlex Desk USB 3.0; ", // 4TB
-    "0x0bc2:0x50a5",
+  { "USB: Seagate FreeAgent GoFlex Desk USB 3.0; ", // 2TB, 4TB
+    "0x0bc2:0x50a[57]",
     "", // 0x0100
     "",
     "-d sat"
   },
+  { "USB: Seagate FreeAgent GoFlex Pro for Mac; ",
+    "0x0bc2:0x6121",
+    "", // 0x0148
+    "",
+    "-d sat"
+  },
   { "USB: Seagate Backup Plus USB 3.0; ", // 1TB
-    "0x0bc2:0xa013",
+    "0x0bc2:0xa0[01]3",
     "", // 0x0100
     "",
     "-d sat"
   },
-  { "USB: Seagate Backup Plus Desktop USB 3.0; ", // 4TB, 3TB (8 LBA/1 PBA offset)
-    "0x0bc2:0xa0a[14]",
+  { "USB: Seagate Backup Plus Desktop USB 3.0; ",
+    "0x0bc2:0xa(0a[14]|b31)", // 4TB, 3TB (8 LBA/1 PBA offset), 5TB
     "",
     "",
     "-d sat"
   },
-  { "USB: Seagate Backup Plus Slim USB 3.0; ", // (ticket #443)
-    "0x0bc2:0xab24",
+  { "USB: Seagate Slim Portable Drive; ", // SRD00F1
+    "0x0bc2:0xab00",
+    "",
+    "",
+    "-d sat"
+  },
+  { "USB: Seagate Backup Plus USB 3.0; ",
+    "0x0bc2:0xab2[0145]", // 0xab24: Slim (ticket #443), 0xab25: Mac
     "", // 0x0100
     "",
     "-d sat"
   },
+  // Addonics
+  { "USB: Addonics HDMU3; ", // (ticket #609)
+    "0x0bf6:0x1001",
+    "", // 0x0100
+    "",
+    ""
+  },
   // Dura Micro
   { "USB: Dura Micro; Cypress",
     "0x0c0b:0xb001",
@@ -3528,6 +4196,13 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  // Jess-Link International
+  { "USB: ; Cypress", // Medion HDDrive2Go
+    "0x0dbf:0x9001",
+    "", // 0x0240
+    "",
+    "-d usbcypress"
+  },
   // Oyen Digital
   { "USB: Oyen Digital MiniPro USB 3.0; ",
     "0x0dc4:0x020a",
@@ -3557,104 +4232,38 @@ const drive_settings builtin_knowndrives[] = {
     "-d usbcypress"
   },
   { "USB: WD My Passport; ",
-    "0x1058:0x07(0[245a]|30)",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: WD My Passport USB 3.0; ",
-    "0x1058:0x0(74[0128a]|7a8|820)",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: WD My Book ES; ",
-    "0x1058:0x0906",
-    "", // 0x0012
+    "0x1058:0x0(70[245a]|71a|730|74[0128a]|7a[8e]|81[06]|82[02]|83[37a])",
+    "", // 822=0x1007, 837=0x1072
     "",
     "-d sat"
   },
-  { "USB: WD My Book Essential; ",
-    "0x1058:0x0910",
-    "", // 0x0106
+  { "USB: WD My Book; ",
+    "0x1058:0x09(00|06|10)",
+    "", // 06=0x0012, 10=0x0106
     "",
     "-d sat"
   },
   { "USB: WD Elements Desktop; ",
-    "0x1058:0x1001",
-    "", // 0x0104
-    "",
-    "-d sat"
-  },
-  { "USB: WD Elements Desktop WDE1UBK...; ",
-    "0x1058:0x1003",
-    "", // 0x0175
-    "",
-    "-d sat"
-  },
-  { "USB: WD Elements; ",
-    "0x1058:0x10(10|48|a2)",
-    "", // 0x0105
-    "",
-    "-d sat"
-  },
-  { "USB: WD Elements Desktop; ", // 2TB
-    "0x1058:0x1021",
-    "", // 0x2002
-    "",
-    "-d sat"
-  },
-  { "USB: WD Elements SE; ", // 1TB
-    "0x1058:0x1023",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: WD Elements SE USB 3.0; ",
-    "0x1058:0x1042",
-    "",
+    "0x1058:0x10(01|03|21|7c)",
+    "", // 01=0x0104, 03=0x0175, 21=0x2002, 7c=0x1065
     "",
     "-d sat"
   },
   { "USB: WD Elements; ",
-    "0x1058:0x10[ab]8", // a=1TB, b=2TB
-    "", // a=0x1042, b=0x1007
-    "",
-    "-d sat"
-  },
-  { "USB: WD My Book Essential; ",
-    "0x1058:0x1100",
-    "", // 0x0165
-    "",
-    "-d sat"
-  },
-  { "USB: WD My Book Office Edition; ", // 1TB
-    "0x1058:0x1101",
-    "", // 0x0165
+    "0x1058:0x10(10|23|42|48|a2|a8|b8)",
+    "", // 10=0x0105, a8=0x1042, b8=0x1007
     "",
     "-d sat"
   },
   { "USB: WD My Book; ",
-    "0x1058:0x1102",
-    "", // 0x1028
+    "0x1058:0x11(0[01245]|1[0d]|30|40)",
+    "", // 00/01=0x0165, 02=0x1028, 10=0x1030, 1d=0x1020, 30=0x1012, 40=0x1003
     "",
     "-d sat"
   },
-  { "USB: WD My Book Studio II; ", // 2x1TB
-    "0x1058:0x1105",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: WD My Book Essential; ",
-    "0x1058:0x1110",
-    "", // 0x1030
-    "",
-    "-d sat"
-  },
-  { "USB: WD My Book Essential USB 3.0; ", // 3TB
-    "0x1058:0x11[34]0",
-    "", // 0x1012/0x1003
+  { "USB: WD Elements; ",
+    "0x1058:0x25a2",
+    "", // 0x1004
     "",
     "-d sat"
   },
@@ -3667,19 +4276,13 @@ const drive_settings builtin_knowndrives[] = {
   },
   // ADATA
   { "USB: ADATA; ",
-    "0x125f:0xa[13]1a", // 1=Classic CH11 1TB, 3=DashDrive HV620 2TB
-    "", // 0x0100
+    "0x125f:0xa(11|31|35|15)a", // 0xa11a: Classic CH11 1TB, 0xa31a: HV620 2TB (0x0100)
+    "", // 0xa35a: HD650 2TB (0x6503), 0xa15a: HD710 1TB
     "",
     "-d sat"
   },
-  { "USB: A-DATA SH93; Cypress",
-    "0x125f:0xa93a",
-    "", // 0x0150
-    "",
-    "-d usbcypress"
-  },
-  { "USB: A-DATA DashDrive; Cypress",
-    "0x125f:0xa94a",
+  { "USB: ADATA; Cypress",
+    "0x125f:0xa9[34]a", // 0xa93a: SH93 (0x0150)
     "",
     "",
     "-d usbcypress"
@@ -3722,8 +4325,8 @@ const drive_settings builtin_knowndrives[] = {
     "-d sat"
   },
   { "USB: ; Initio",
-    "0x13fd:0x1640",
-    "", // 0x0864
+    "0x13fd:0x16[45]0",
+    "", // 0x1640: 0x0864, 0x1650: 0x0436
     "",
     "-d sat,12" // some SMART commands fail, see ticket #295
   },
@@ -3739,39 +4342,63 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: ; Initio",
+    "0x13fd:0x39[124]0", // 0x3910: Seagate Expansion Portable SRD00F1 (0x0100)
+    "", // 0x3920: ezDISK EZ370 (0x0205)
+    "", // 0x3940: MS-TECH LU-275S (0x0306)
+    "-d sat"
+  },
   // Super Top
-  { "USB: Super Top generic enclosure; Cypress",
+  { "USB: Super Top generic enclosure; ",
     "0x14cd:0x6116",
+    "", // 0x0150, older report suggests -d usbcypress
     "", // 0x0160 also reported as unsupported
-    "",
-    "-d usbcypress"
+    "-d sat"
   },
   // JMicron
   { "USB: ; JMicron JMS539", // USB2/3->SATA (old firmware)
     "0x152d:0x0539",
-    "0x0100",  //  1.00
-    "",
-    "-d usbjmicron"
+    "0x0100",      // 1.00, various devices support -d usbjmicron
+    "",            // 1.00, SSI SI-1359RUS3 supports -d sat,
+    ""             //       -d usbjmicron may disconnect drive (ticket #552)
   },
   { "USB: ; JMicron JMS539", // USB2/3->SATA (new firmware)
     "0x152d:0x0539",
-    "0x0205|"  //  2.05, ticket #338
-    "0x2812",  // 28.12, Mediasonic ProBox H82-SU3S2 (port multiplier)
-    "",
+    "0x020[56]|"   //  2.05, ZTC USB 3.0 enclosure (ticket #338)
+    "0x28(03|12)", // 28.03, Mediasonic ProBox HF2-SU3S2 Rev 2 (port multiplier, ticket #504)
+    "",            // 28.12, Mediasonic ProBox H82-SU3S2 (port multiplier)
     "-d sat"
   },
   { "USB: ; JMicron ", // USB->SATA->4xSATA (port multiplier)
-    "0x152d:0x0551",
+    "0x152d:0x0551",   // JMS539? (old firmware may use 0x152d:0x0539, ticket #552)
     "", // 0x0100
     "",
     "-d usbjmicron,x"
   },
+  { "USB: ; JMicron", // USB2/3->2xSATA
+    "0x152d:0x0565",
+    "", // 0x9114, Akasa DuoDock X (ticket #607)
+    "",
+    "-d sat"
+  },
+  { "USB: ; JMicron JMS567", // USB2/3->SATA
+    "0x152d:0x0567",
+    "", // 0x0114
+    "", // 0x0205, 2.05, Mediasonic ProBox HF2-SU3S2 Rev 3 (port multiplier, ticket #504)
+    "-d sat"
+  },
   { "USB: OCZ THROTTLE OCZESATATHR8G; JMicron JMF601",
     "0x152d:0x0602",
     "",
     "",
     "" // unsupported
   },
+  { "USB: ; JMicron JMS561", // USB2/3->2xSATA
+    "0x152d:0x[19]561", // 0x1561(0x0106), Sabrent USB 3.0 Dual Bay SATA Dock
+    "",  // 0x9561(0x0105), Orico 6629US3-C USB 3.0 Dual Bay SATA Dock
+    "",
+    "-d sat"
+  },
   { "USB: ; JMicron JM20329", // USB->SATA
     "0x152d:0x2329",
     "", // 0x0100
@@ -3820,18 +4447,40 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbjmicron,x"
   },
+  { "USB: ; JMicron JMS566", // USB3->SATA
+    "0x152d:0x2566", // e.g. Chieftec CEB-7035S
+    "", // 0x0114
+    "",
+    "-d usbjmicron,x"
+  },
+  { "USB: ; JMicron",
+    "0x152d:0x2590",
+    "", // 0x0x8105 (ticket #550)
+    "",
+    "-d sat"
+  },
+  { "USB: ; JMicron JMS567", // USB2/3->SATA
+    "0x152d:0x3562",
+    "", // 0x0310, StarTech S358BU33ERM (port multiplier, ticket #508)
+    "",
+    "-d sat"
+  },
+  { "USB: ; JMicron", // USB3->SATA
+    "0x152d:0x3569",
+    "", // 0x0203
+    "",
+    "-d sat"
+  },
   // ASMedia
-  { "USB: ; ASMedia ASM1051",
-    "0x174c:0x5106", // 0x174c:0x55aa after firmware update
+  { "USB: ; ASMedia ASM1053/1153",
+    "0x174c:0x1[01]53",
     "",
     "",
     "-d sat"
   },
-  { "USB: ; ASMedia USB 3.0", // MEDION HDDrive-n-GO, LaCie Rikiki USB 3.0,
-      // Silicon Power Armor A80 (ticket #237)
-      // reported as unsupported: BYTECC T-200U3, Kingwin USB 3.0 docking station
-    "0x174c:0x55aa",
-    "", // 0x0100
+  { "USB: ; ASMedia ASM1051",
+    "0x174c:0x5106", // 0x174c:0x55aa after firmware update
+    "",
     "",
     "-d sat"
   },
@@ -3841,6 +4490,20 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: ; ASMedia", // Vantec NexStar USB 3.0 & SATA dual drive dock
+    "0x174c:0x5516",
+    "",
+    "",
+    "-d sat"
+  },
+  { "USB: ; ASMedia", // MEDION HDDrive-n-GO, LaCie Rikiki USB 3.0,
+      // Silicon Power Armor A80 (ticket #237)
+      // reported as unsupported: BYTECC T-200U3, Kingwin USB 3.0 docking station
+    "0x174c:0x55aa",
+    "", // 0x0100
+    "",
+    "-d sat"
+  },
   // LucidPort
   { "USB: ; LucidPORT USB300", // RaidSonic ICY BOX IB-110StU3-B, Sharkoon SATA QuickPort H3
     "0x1759:0x500[02]", // 0x5000: USB 2.0, 0x5002: USB 3.0
@@ -3861,7 +4524,7 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbsunplus"
   },
-  { "USB: Verbatim FW/USB160; Oxford OXUF934SSA-LQAG", // USB+IEE1394->SATA
+  { "USB: Verbatim FW/USB160; Oxford OXUF934SSA-LQAG", // USB+IEEE1394->SATA
     "0x18a5:0x0215",
     "", // 0x0001
     "",
@@ -3891,6 +4554,18 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbjmicron"
   },
+  { "USB: Verbatim Pocket Hard Drive; ", // 1TB USB 3.0
+    "0x18a5:0x0237",
+    "",
+    "",
+    "-d sat,12"
+  },
+  { "USB: Verbatim External Hard Drive; ", // 3TB USB 3.0
+    "0x18a5:0x0400",
+    "",
+    "",
+    "-d sat"
+  },
   // Silicon Image
   { "USB: Vantec NST-400MX-SR; Silicon Image 5744",
     "0x1a4a:0x1670",
@@ -3913,12 +4588,44 @@ const drive_settings builtin_knowndrives[] = {
     "-d sat"
   },
   // Innostor
-  { "USB: ; Innostor IS888", // Sharkoon SATA QuickDeck Pro USB 3.0
-    "0x1f75:0x0888",
+  { "USB: ; Innostor IS611", // USB3->SATA+PATA
+    "0x1f75:0x0611", // SMART access via PATA does not work
+    "",
+    "",
+    "-d sat"
+  },
+  { "USB: ; Innostor IS621", // USB3->SATA
+    "0x1f75:0x0621", // Dynex 2.5" USB 3.0 Exclosure DX-HD302513
+    "",
+    "",
+    "-d sat"
+  },
+  { "USB: ; Innostor IS888", // USB3->SATA
+    "0x1f75:0x0888", // Sharkoon SATA QuickDeck Pro USB 3.0
     "", // 0x0034
     "",
     "" // unsupported
   },
+  // VIA Labs
+  { "USB: ; VIA VL701", // USB2/3->SATA
+    "0x2109:0x0701", // Intenso 2,5" 1TB USB3
+    "", // 0x0107
+    "",
+    "-d sat" // ATA output registers missing
+  },
+  { "USB: ; VIA VL711", // USB2/3->SATA
+    "0x2109:0x0711",
+    "", // 0x0114, Mediasonic ProBox K32-SU3 (ticket #594)
+    "", // 0x0507, Intenso 2,5" Memory Case 2TB USB3
+    "-d sat"
+  },
+  // 0x2537 (?)
+  { "USB: ; ", // USB 3.0
+    "0x2537:0x106[68]", // 0x1066: Orico 2599US3, 0x1068: Fantec ER-35U3
+    "", // 0x0100
+    "",
+    "-d sat"
+  },
   // Power Quotient International
   { "USB: PQI H560; ",
     "0x3538:0x0902",
@@ -3926,6 +4633,13 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  // Sharkoon
+  { "USB: Sharkoon QuickPort XT USB 3.0; ",
+     "0x357d:0x7788",
+     "",
+     "",
+     "-d sat"
+  },
   // Hitachi/SimpleTech
   { "USB: Hitachi Touro Desk; JMicron", // 3TB
     "0x4971:0x1011",
@@ -3933,12 +4647,24 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbjmicron"
   },
-  { "USB: Hitachi Touro Desk 3.0; ", // 2TB
-    "0x4971:0x1015",
+  { "USB: Hitachi Touro; ",
+    "0x4971:0x101[45]", // 14=1TB, 15=2TB
     "", // 0x0000
     "",
     "-d sat" // ATA output registers missing
   },
+  { "USB: Hitachi Touro Mobile; ", // 1TB
+    "0x4971:0x102[04]",
+    "", // 0x0100
+    "",
+    "-d sat"
+  },
+  { "USB: SimpleTech;", // USB 3.0 HDD BOX Agestar,  Rock External HDD 3,5" UASP
+    "0x4971:0x8017",
+    "",
+    "",
+    "-d sat"
+  },
   { "USB: Hitachi/SimpleTech; JMicron", // 1TB
     "0x4971:0xce17",
     "",
@@ -3959,6 +4685,13 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  // 0xabcd (?)
+  { "USB: ; ",
+    "0xabcd:0x6103", // LogiLink AU0028A V1.0 USB 3.0 to IDE & SATA Adapter
+    "",
+    "",
+    "-d sat"
+  },
 /*
 }; // builtin_knowndrives[]
  */
index 43a351be0432d8215511947ad3d5357eac08d19c..baa211a46612e9d9de0af67fa9795b64b0e02262 100644 (file)
@@ -1,6 +1,6 @@
-# Home page: http://smartmontools.sourceforge.net
+# Home page: http://www.smartmontools.org
 #
-# $Id: README 3958 2014-07-18 19:13:32Z chrfranke $
+# $Id: README 4120 2015-08-27 16:12:21Z samm2 $
 #
 # Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 # Copyright (C) 2009-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
diff --git a/freebsd_nvme_ioctl.h b/freebsd_nvme_ioctl.h
new file mode 100644 (file)
index 0000000..e950fd5
--- /dev/null
@@ -0,0 +1,151 @@
+/*-
+ * Copyright (C) 2012-2013 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+
+#define        NVME_PASSTHROUGH_CMD    _IOWR('n', 0, struct nvme_pt_command)
+
+struct nvme_command
+{
+       /* dword 0 */
+       uint16_t opc    :  8;   /* opcode */
+       uint16_t fuse   :  2;   /* fused operation */
+       uint16_t rsvd1  :  6;
+       uint16_t cid;           /* command identifier */
+
+       /* dword 1 */
+       uint32_t nsid;          /* namespace identifier */
+
+       /* dword 2-3 */
+       uint32_t rsvd2;
+       uint32_t rsvd3;
+
+       /* dword 4-5 */
+       uint64_t mptr;          /* metadata pointer */
+
+       /* dword 6-7 */
+       uint64_t prp1;          /* prp entry 1 */
+
+       /* dword 8-9 */
+       uint64_t prp2;          /* prp entry 2 */
+
+       /* dword 10-15 */
+       uint32_t cdw10;         /* command-specific */
+       uint32_t cdw11;         /* command-specific */
+       uint32_t cdw12;         /* command-specific */
+       uint32_t cdw13;         /* command-specific */
+       uint32_t cdw14;         /* command-specific */
+       uint32_t cdw15;         /* command-specific */
+} __packed;
+
+struct nvme_status {
+
+       uint16_t p      :  1;   /* phase tag */
+       uint16_t sc     :  8;   /* status code */
+       uint16_t sct    :  3;   /* status code type */
+       uint16_t rsvd2  :  2;
+       uint16_t m      :  1;   /* more */
+       uint16_t dnr    :  1;   /* do not retry */
+} __packed;
+
+struct nvme_completion {
+
+       /* dword 0 */
+       uint32_t                cdw0;   /* command-specific */
+
+       /* dword 1 */
+       uint32_t                rsvd1;
+
+       /* dword 2 */
+       uint16_t                sqhd;   /* submission queue head pointer */
+       uint16_t                sqid;   /* submission queue identifier */
+
+       /* dword 3 */
+       uint16_t                cid;    /* command identifier */
+       struct nvme_status      status;
+} __packed;
+
+struct nvme_pt_command {
+
+       /*
+        * cmd is used to specify a passthrough command to a controller or
+        *  namespace.
+        *
+        * The following fields from cmd may be specified by the caller:
+        *      * opc  (opcode)
+        *      * nsid (namespace id) - for admin commands only
+        *      * cdw10-cdw15
+        *
+        * Remaining fields must be set to 0 by the caller.
+        */
+       struct nvme_command     cmd;
+
+       /*
+        * cpl returns completion status for the passthrough command
+        *  specified by cmd.
+        *
+        * The following fields will be filled out by the driver, for
+        *  consumption by the caller:
+        *      * cdw0
+        *      * status (except for phase)
+        *
+        * Remaining fields will be set to 0 by the driver.
+        */
+       struct nvme_completion  cpl;
+
+       /* buf is the data buffer associated with this passthrough command. */
+       void *                  buf;
+
+       /*
+        * len is the length of the data buffer associated with this
+        *  passthrough command.
+        */
+       uint32_t                len;
+
+       /*
+        * is_read = 1 if the passthrough command will read data into the
+        *  supplied buffer from the controller.
+        *
+        * is_read = 0 if the passthrough command will write data from the
+        *  supplied buffer to the controller.
+        */
+       uint32_t                is_read;
+
+       /*
+        * driver_lock is used by the driver only.  It must be set to 0
+        *  by the caller.
+        */
+       struct mtx *            driver_lock;
+};
+
+#define nvme_completion_is_error(cpl)                                  \
+       ((cpl)->status.sc != 0 || (cpl)->status.sct != 0)
+
+#define NVME_CTRLR_PREFIX      "/dev/nvme"
+#define NVME_NS_PREFIX         "ns"
diff --git a/int64.h b/int64.h
index bc328ab850120a8113f3f4191c9d2d929f3a7724..c71353861333841cc8053cef465a612654cff218 100644 (file)
--- a/int64.h
+++ b/int64.h
@@ -1,7 +1,7 @@
 /*
  * int64.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2004-11 Christian Franke
@@ -20,7 +20,7 @@
 #ifndef INT64_H_
 #define INT64_H_
 
-#define INT64_H_CVSID "$Id: int64.h 3727 2012-12-13 17:23:06Z samm2 $"
+#define INT64_H_CVSID "$Id: int64.h 4120 2015-08-27 16:12:21Z samm2 $"
 
 // 64 bit integer typedefs and format strings
 
index 25d3babcb7d0a321ea1161245ebc50c97bf4ee18..050733e31b8223ea98b0c97ac192ac9ccc165640 100644 (file)
@@ -1,11 +1,10 @@
 /*
  * knowndrives.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
- * Address of support mailing list: smartmontools-support@lists.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-11 Philip Williams, Bruce Allen
- * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-16 Christian Franke
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,7 +32,7 @@
 
 #include <stdexcept>
 
-const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 3719 2012-12-03 21:19:33Z chrfranke $"
+const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 4208 2016-01-22 19:45:35Z chrfranke $"
                                      KNOWNDRIVES_H_CVSID;
 
 #define MODEL_STRING_LENGTH                         40
@@ -51,6 +50,8 @@ const drive_settings builtin_knowndrives[] = {
 #include "drivedb.h"
 };
 
+const unsigned builtin_knowndrives_size =
+  sizeof(builtin_knowndrives) / sizeof(builtin_knowndrives[0]);
 
 /// Drive database class. Stores custom entries read from file.
 /// Provides transparent access to concatenation of custom and
@@ -140,16 +141,26 @@ const char * drive_database::copy_string(const char * src)
 static drive_database knowndrives;
 
 
-// Return true if modelfamily string describes entry for USB ID
-static bool is_usb_modelfamily(const char * modelfamily)
+enum dbentry_type {
+  DBENTRY_ATA_DEFAULT,
+  DBENTRY_ATA,
+  DBENTRY_USB
+};
+
+// Return type of entry
+static dbentry_type get_modelfamily_type(const char * modelfamily)
 {
-  return !strncmp(modelfamily, "USB:", 4);
+  if (modelfamily[0] == 'D' && !strcmp(modelfamily, "DEFAULT"))
+    return DBENTRY_ATA_DEFAULT;
+  else if(modelfamily[0] == 'U' && str_starts_with(modelfamily, "USB:"))
+    return DBENTRY_USB;
+  else
+    return DBENTRY_ATA;
 }
 
-// Return true if entry for USB ID
-static inline bool is_usb_entry(const drive_settings * dbentry)
+static inline dbentry_type get_dbentry_type(const drive_settings * dbentry)
 {
-  return is_usb_modelfamily(dbentry->modelfamily);
+  return get_modelfamily_type(dbentry->modelfamily);
 }
 
 // Compile regular expression, print message on failure.
@@ -185,8 +196,8 @@ static const drive_settings * lookup_drive(const char * model, const char * firm
     firmware = "";
 
   for (unsigned i = 0; i < knowndrives.size(); i++) {
-    // Skip USB entries
-    if (is_usb_entry(&knowndrives[i]))
+    // Skip DEFAULT and USB entries
+    if (get_dbentry_type(&knowndrives[i]) != DBENTRY_ATA)
       continue;
 
     // Check whether model matches the regular expression in knowndrives[i].
@@ -219,8 +230,8 @@ static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs,
     if (!(sscanf(presets+i, "-%c %80[^ ]%n", &opt, arg, &len) >= 2 && len > 0))
       return false;
     if (opt == 'v' && defs) {
-      // Parse "-v N,format[,name]"
-      if (!parse_attribute_def(arg, *defs, PRIOR_DATABASE))
+      // Parse "-v N,format[,name[,HDD|SSD]]"
+      if (!parse_attribute_def(arg, *defs, (firmwarebugs ? PRIOR_DATABASE : PRIOR_DEFAULT)))
         return false;
     }
     else if (opt == 'F' && firmwarebugs) {
@@ -243,6 +254,13 @@ static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs,
   return true;
 }
 
+// Parse '-v' options in default preset string, return false on error.
+static inline bool parse_default_presets(const char * presets,
+                                         ata_vendor_attr_defs & defs)
+{
+  return parse_db_presets(presets, &defs, 0, 0);
+}
+
 // Parse '-v' and '-F' options in preset string, return false on error.
 static inline bool parse_presets(const char * presets,
                                  ata_vendor_attr_defs & defs,
@@ -287,7 +305,7 @@ int lookup_usb_device(int vendor_id, int product_id, int bcd_device,
     const drive_settings & dbentry = knowndrives[i];
 
     // Skip drive entries
-    if (!is_usb_entry(&dbentry))
+    if (get_dbentry_type(&dbentry) != DBENTRY_USB)
       continue;
 
     // Check whether USB vendor:product ID matches
@@ -341,7 +359,8 @@ static int showonepreset(const drive_settings * dbentry)
     return 1;
   }
 
-  bool usb = is_usb_entry(dbentry);
+  dbentry_type type = get_dbentry_type(dbentry);
+  bool usb = (type == DBENTRY_USB);
 
   // print and check model and firmware regular expressions
   int errcnt = 0;
@@ -364,12 +383,21 @@ static int showonepreset(const drive_settings * dbentry)
     bool first_preset = true;
     if (*dbentry->presets) {
       ata_vendor_attr_defs defs;
-      if (!parse_presets(dbentry->presets, defs, firmwarebugs)) {
-        pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
-        errcnt++;
+      if (type == DBENTRY_ATA_DEFAULT) {
+        if (!parse_default_presets(dbentry->presets, defs)) {
+          pout("Syntax error in DEFAULT option string \"%s\"\n", dbentry->presets);
+          errcnt++;
+        }
       }
+      else {
+        if (!parse_presets(dbentry->presets, defs, firmwarebugs)) {
+          pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
+          errcnt++;
+        }
+      }
+
       for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) {
-        if (defs[i].priority != PRIOR_DEFAULT) {
+        if (defs[i].priority != PRIOR_DEFAULT || !defs[i].name.empty()) {
           std::string name = ata_get_smart_attr_name(i, defs);
           // Use leading zeros instead of spaces so that everything lines up.
           pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "",
@@ -563,7 +591,7 @@ class stdin_iterator
 {
 public:
   explicit stdin_iterator(FILE * f)
-    : m_f(f) { get(); get(); }
+    : m_f(f), m_next(0) { get(); get(); }
 
   stdin_iterator & operator++()
     { get(); return *this; }
@@ -767,19 +795,29 @@ static bool parse_drive_database(parse_ptr src, drive_database & db, const char
             break;
           case 4:
             if (!token.value.empty()) {
-              if (!is_usb_modelfamily(values[0].c_str())) {
-                ata_vendor_attr_defs defs; firmwarebug_defs fix;
-                if (!parse_presets(token.value.c_str(), defs, fix)) {
-                  pout("%s(%d): Syntax error in preset option string\n", path, token.line);
-                  ok = false;
-                }
-              }
-              else {
-                std::string type;
-                if (!parse_usb_type(token.value.c_str(), type)) {
-                  pout("%s(%d): Syntax error in USB type string\n", path, token.line);
-                  ok = false;
-                }
+              // Syntax check
+              switch (get_modelfamily_type(values[0].c_str())) {
+                case DBENTRY_ATA_DEFAULT: {
+                  ata_vendor_attr_defs defs;
+                  if (!parse_default_presets(token.value.c_str(), defs)) {
+                    pout("%s(%d): Syntax error in DEFAULT option string\n", path, token.line);
+                    ok = false;
+                  }
+                } break;
+                default: { // DBENTRY_ATA
+                  ata_vendor_attr_defs defs; firmwarebug_defs fix;
+                  if (!parse_presets(token.value.c_str(), defs, fix)) {
+                    pout("%s(%d): Syntax error in preset option string\n", path, token.line);
+                    ok = false;
+                  }
+                } break;
+                case DBENTRY_USB: {
+                  std::string type;
+                  if (!parse_usb_type(token.value.c_str(), type)) {
+                    pout("%s(%d): Syntax error in USB type string\n", path, token.line);
+                    ok = false;
+                  }
+                } break;
               }
             }
             break;
@@ -857,7 +895,7 @@ const char * get_drivedb_path_default()
 #endif
 
 // Read drive databases from standard places.
-bool read_default_drive_databases()
+static bool read_default_drive_databases()
 {
   // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h
   const char * db1 = get_drivedb_path_add();
@@ -877,9 +915,60 @@ bool read_default_drive_databases()
 #endif
   {
     // Append builtin table.
-    knowndrives.append(builtin_knowndrives,
-      sizeof(builtin_knowndrives)/sizeof(builtin_knowndrives[0]));
+    knowndrives.append(builtin_knowndrives, builtin_knowndrives_size);
+  }
+
+  return true;
+}
+
+static ata_vendor_attr_defs default_attr_defs;
+
+// Initialize default_attr_defs.
+static bool init_default_attr_defs()
+{
+  // Lookup default entry
+  const drive_settings * entry = 0;
+  for (unsigned i = 0; i < knowndrives.size(); i++) {
+    if (get_dbentry_type(&knowndrives[i]) != DBENTRY_ATA_DEFAULT)
+      continue;
+    entry = &knowndrives[i];
+    break;
+  }
+
+  if (!entry) {
+    // Fall back to builtin database
+    for (unsigned i = 0; i < builtin_knowndrives_size; i++) {
+      if (get_dbentry_type(&builtin_knowndrives[i]) != DBENTRY_ATA_DEFAULT)
+        continue;
+      entry = &builtin_knowndrives[i];
+      break;
+    }
+
+    if (!entry)
+      throw std::logic_error("DEFAULT entry missing in builtin drive database");
+
+    pout("Warning: DEFAULT entry missing in drive database file(s)\n");
+  }
+
+  if (!parse_default_presets(entry->presets, default_attr_defs)) {
+    pout("Syntax error in DEFAULT drive database entry\n");
+    return false;
   }
 
   return true;
 }
+
+// Init default db entry and optionally read drive databases from standard places.
+bool init_drive_database(bool use_default_db)
+{
+  if (use_default_db && !read_default_drive_databases())
+    return false;
+
+  return init_default_attr_defs();
+}
+
+// Get vendor attribute options from default db entry.
+const ata_vendor_attr_defs & get_default_attr_defs()
+{
+  return default_attr_defs;
+}
index 7d2a8cc18a1ba0f866431293f03989ff58295d34..3c754e7d96888e0cd6a858fdcbf641ab5424fe1e 100644 (file)
@@ -1,11 +1,10 @@
 /*
  * knowndrives.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
- * Address of support mailing list: smartmontools-support@lists.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-11 Philip Williams, Bruce Allen
- * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-15 Christian Franke
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,7 +19,7 @@
 #ifndef KNOWNDRIVES_H_
 #define KNOWNDRIVES_H_
 
-#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h 3597 2012-09-04 21:10:37Z chrfranke $\n"
+#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h 4162 2015-10-31 16:36:16Z chrfranke $\n"
 
 // Structure to store drive database entries, see drivedb.h for a description.
 struct drive_settings {
@@ -73,7 +72,10 @@ const char * get_drivedb_path_default();
 // Read drive database from file.
 bool read_drive_database(const char * path);
 
-// Read drive databases from standard places.
-bool read_default_drive_databases();
+// Init default db entry and optionally read drive databases from standard places.
+bool init_drive_database(bool use_default_db);
+
+// Get vendor attribute options from default db entry.
+const ata_vendor_attr_defs & get_default_attr_defs();
 
 #endif
diff --git a/linux_nvme_ioctl.h b/linux_nvme_ioctl.h
new file mode 100644 (file)
index 0000000..c4b2a3f
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Definitions for the NVM Express ioctl interface
+ * Copyright (c) 2011-2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _UAPI_LINUX_NVME_IOCTL_H
+#define _UAPI_LINUX_NVME_IOCTL_H
+
+#include <linux/types.h>
+
+struct nvme_user_io {
+       __u8    opcode;
+       __u8    flags;
+       __u16   control;
+       __u16   nblocks;
+       __u16   rsvd;
+       __u64   metadata;
+       __u64   addr;
+       __u64   slba;
+       __u32   dsmgmt;
+       __u32   reftag;
+       __u16   apptag;
+       __u16   appmask;
+};
+
+struct nvme_passthru_cmd {
+       __u8    opcode;
+       __u8    flags;
+       __u16   rsvd1;
+       __u32   nsid;
+       __u32   cdw2;
+       __u32   cdw3;
+       __u64   metadata;
+       __u64   addr;
+       __u32   metadata_len;
+       __u32   data_len;
+       __u32   cdw10;
+       __u32   cdw11;
+       __u32   cdw12;
+       __u32   cdw13;
+       __u32   cdw14;
+       __u32   cdw15;
+       __u32   timeout_ms;
+       __u32   result;
+};
+
+#define nvme_admin_cmd nvme_passthru_cmd
+
+#define NVME_IOCTL_ID          _IO('N', 0x40)
+#define NVME_IOCTL_ADMIN_CMD   _IOWR('N', 0x41, struct nvme_admin_cmd)
+#define NVME_IOCTL_SUBMIT_IO   _IOW('N', 0x42, struct nvme_user_io)
+#define NVME_IOCTL_IO_CMD      _IOWR('N', 0x43, struct nvme_passthru_cmd)
+#define NVME_IOCTL_RESET       _IO('N', 0x44)
+#define NVME_IOCTL_SUBSYS_RESET        _IO('N', 0x45)
+
+#endif /* _UAPI_LINUX_NVME_IOCTL_H */
diff --git a/nvmecmds.cpp b/nvmecmds.cpp
new file mode 100644 (file)
index 0000000..b3f6a0f
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * nvmecmds.cpp
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2016 Christian Franke
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "nvmecmds.h"
+
+const char * nvmecmds_cvsid = "$Id: nvmecmds.cpp 4313 2016-05-01 16:17:53Z chrfranke $"
+  NVMECMDS_H_CVSID;
+
+#include "dev_interface.h"
+#include "atacmds.h" // swapx(), ASSERT_*(), dont_print_serial_number
+#include "scsicmds.h" // dStrHex()
+#include "utility.h"
+
+using namespace smartmontools;
+
+// Check nvme_* struct sizes
+ASSERT_SIZEOF_STRUCT(nvme_id_ctrl, 4096);
+ASSERT_SIZEOF_STRUCT(nvme_id_ns, 4096);
+ASSERT_SIZEOF_STRUCT(nvme_error_log_page, 64);
+ASSERT_SIZEOF_STRUCT(nvme_smart_log, 512);
+
+
+// Print NVMe debug messages?
+unsigned char nvme_debugmode = 0;
+
+// Dump up to 4096 bytes, do not dump trailing zero bytes.
+// TODO: Handle this by new unified function in utility.cpp
+static void debug_hex_dump(const void * data, unsigned size)
+{
+  const unsigned char * p = (const unsigned char *)data;
+  const unsigned limit = 4096; // sizeof(nvme_id_ctrl)
+  unsigned sz = (size <= limit ? size : limit);
+
+  while (sz > 0x10 && !p[sz-1])
+    sz--;
+  if (sz < size) {
+    if (sz & 0x0f)
+      sz = (sz & ~0x0f) + 0x10;
+    sz += 0x10;
+    if (sz > size)
+      sz = size;
+  }
+
+  dStrHex(p, sz, 0);
+  if (sz < size)
+    pout(" ...\n");
+}
+
+// Call NVMe pass-through and print debug info if requested.
+static bool nvme_pass_through(nvme_device * device, const nvme_cmd_in & in,
+  nvme_cmd_out & out)
+{
+  int64_t start_usec = -1;
+
+  if (nvme_debugmode) {
+    pout(" [NVMe call: opcode=0x%02x, size=0x%04x, nsid=0x%08x, cdw10=0x%08x",
+      in.opcode, in.size, in.nsid, in.cdw10);
+    if (in.cdw11 || in.cdw12 || in.cdw13 || in.cdw14 || in.cdw15)
+      pout(",\n  cdw1x=0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
+       in.cdw11, in.cdw12, in.cdw13, in.cdw14, in.cdw15);
+    pout("]\n");
+
+    start_usec = smi()->get_timer_usec();
+  }
+
+  bool ok = device->nvme_pass_through(in, out);
+
+  if (   dont_print_serial_number && ok
+      && in.opcode == nvme_admin_identify && in.cdw10 == 0x01) {
+        // Invalidate serial number
+        nvme_id_ctrl & id_ctrl = *reinterpret_cast<nvme_id_ctrl *>(in.buffer);
+        memset(id_ctrl.sn, 'X', sizeof(id_ctrl.sn));
+  }
+
+  if (nvme_debugmode) {
+    if (start_usec >= 0) {
+      int64_t duration_usec = smi()->get_timer_usec() - start_usec;
+      if (duration_usec >= 500)
+        pout("  [Duration: %.3fs]\n", duration_usec / 1000000.0);
+    }
+
+    if (!ok) {
+      pout(" [NVMe call failed: ");
+      if (out.status_valid)
+        pout("NVMe Status=0x%04x", out.status);
+      else
+        pout("%s", device->get_errmsg());
+    }
+    else {
+      pout(" [NVMe call succeeded: result=0x%08x", out.result);
+      if (nvme_debugmode > 1 && in.direction() == nvme_cmd_in::data_in) {
+        pout("\n");
+        debug_hex_dump(in.buffer, in.size);
+        pout(" ");
+      }
+    }
+    pout("]\n");
+  }
+
+  return ok;
+}
+
+// Call NVMe pass-through and print debug info if requested.
+// Version without output parameters.
+static bool nvme_pass_through(nvme_device * device, const nvme_cmd_in & in)
+{
+  nvme_cmd_out out;
+  return nvme_pass_through(device, in, out);
+}
+
+// Read NVMe identify info with controller/namespace field CNS.
+static bool nvme_read_identify(nvme_device * device, unsigned nsid,
+  unsigned char cns, void * data, unsigned size)
+{
+  memset(data, 0, size);
+  nvme_cmd_in in;
+  in.set_data_in(nvme_admin_identify, data, size);
+  in.nsid = nsid;
+  in.cdw10 = cns;
+
+  return nvme_pass_through(device, in);
+}
+
+// Read NVMe Identify Controller data structure.
+bool nvme_read_id_ctrl(nvme_device * device, nvme_id_ctrl & id_ctrl)
+{
+  if (!nvme_read_identify(device, 0, 0x01, &id_ctrl, sizeof(id_ctrl)))
+    return false;
+
+  if (isbigendian()) {
+    swapx(&id_ctrl.vid);
+    swapx(&id_ctrl.ssvid);
+    swapx(&id_ctrl.cntlid);
+    swapx(&id_ctrl.oacs);
+    swapx(&id_ctrl.wctemp);
+    swapx(&id_ctrl.cctemp);
+    swapx(&id_ctrl.mtfa);
+    swapx(&id_ctrl.hmpre);
+    swapx(&id_ctrl.hmmin);
+    swapx(&id_ctrl.rpmbs);
+    swapx(&id_ctrl.nn);
+    swapx(&id_ctrl.oncs);
+    swapx(&id_ctrl.fuses);
+    swapx(&id_ctrl.awun);
+    swapx(&id_ctrl.awupf);
+    swapx(&id_ctrl.acwu);
+    swapx(&id_ctrl.sgls);
+    for (int i = 0; i < 32; i++) {
+      swapx(&id_ctrl.psd[i].max_power);
+      swapx(&id_ctrl.psd[i].entry_lat);
+      swapx(&id_ctrl.psd[i].exit_lat);
+      swapx(&id_ctrl.psd[i].idle_power);
+      swapx(&id_ctrl.psd[i].active_power);
+    }
+  }
+
+  return true;
+}
+
+// Read NVMe Identify Namespace data structure for namespace NSID.
+bool nvme_read_id_ns(nvme_device * device, unsigned nsid, nvme_id_ns & id_ns)
+{
+  if (!nvme_read_identify(device, nsid, 0x00, &id_ns, sizeof(id_ns)))
+    return false;
+
+  if (isbigendian()) {
+    swapx(&id_ns.nsze);
+    swapx(&id_ns.ncap);
+    swapx(&id_ns.nuse);
+    swapx(&id_ns.nawun);
+    swapx(&id_ns.nawupf);
+    swapx(&id_ns.nacwu);
+    swapx(&id_ns.nabsn);
+    swapx(&id_ns.nabo);
+    swapx(&id_ns.nabspf);
+    for (int i = 0; i < 16; i++)
+      swapx(&id_ns.lbaf[i].ms);
+  }
+
+  return true;
+}
+
+// Read NVMe log page with identifier LID.
+bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data, unsigned size)
+{
+  if (!(4 <= size && size <= 0x4000 && (size % 4) == 0))
+    throw std::logic_error("nvme_read_log_page(): invalid size");
+
+  memset(data, 0, size);
+  nvme_cmd_in in;
+  in.set_data_in(nvme_admin_get_log_page, data, size);
+  in.nsid = device->get_nsid();
+  in.cdw10 = lid | (((size / 4) - 1) << 16);
+
+  return nvme_pass_through(device, in);
+}
+
+// Read NVMe Error Information Log.
+bool nvme_read_error_log(nvme_device * device, nvme_error_log_page * error_log, unsigned num_entries)
+{
+  if (!nvme_read_log_page(device, 0x01, error_log, num_entries * sizeof(*error_log)))
+    return false;
+
+  if (isbigendian()) {
+    for (unsigned i = 0; i < num_entries; i++) {
+      swapx(&error_log[i].error_count);
+      swapx(&error_log[i].sqid);
+      swapx(&error_log[i].cmdid);
+      swapx(&error_log[i].status_field);
+      swapx(&error_log[i].parm_error_location);
+      swapx(&error_log[i].lba);
+      swapx(&error_log[i].nsid);
+    }
+  }
+
+  return true;
+}
+
+// Read NVMe SMART/Health Information log.
+bool nvme_read_smart_log(nvme_device * device, nvme_smart_log & smart_log)
+{
+  if (!nvme_read_log_page(device, 0x02, &smart_log, sizeof(smart_log)))
+    return false;
+
+  if (isbigendian()) {
+    swapx(&smart_log.warning_temp_time);
+    swapx(&smart_log.critical_comp_time);
+    for (int i = 0; i < 8; i++)
+      swapx(&smart_log.temp_sensor[i]);
+  }
+
+  return true;
+}
diff --git a/nvmecmds.h b/nvmecmds.h
new file mode 100644 (file)
index 0000000..eff9ee0
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * nvmecmds.h
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2016 Christian Franke
+ *
+ * Original code from <linux/nvme.h>:
+ *   Copyright (C) 2011-2014 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef NVMECMDS_H
+#define NVMECMDS_H
+
+#define NVMECMDS_H_CVSID "$Id: nvmecmds.h 4297 2016-04-16 16:48:01Z chrfranke $"
+
+#include "int64.h"
+
+// The code below was orginally imported from <linux/nvme.h> include file from
+// Linux kernel sources.  Types from <linux/types.h> were replaced.
+// Symbol names are unchanged but placed in a namespace to allow inclusion
+// of the original <linux/nvme.h>.
+namespace smartmontools {
+
+////////////////////////////////////////////////////////////////////////////
+// BEGIN: From <linux/nvme.h>
+/*
+ * Definitions for the NVM Express interface
+ * Copyright (c) 2011-2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+struct nvme_error_log_page {
+  uint64_t        error_count;
+  unsigned short  sqid;
+  unsigned short  cmdid;
+  unsigned short  status_field;
+  unsigned short  parm_error_location;
+  uint64_t        lba;
+  unsigned int    nsid;
+  unsigned char   vs;
+  unsigned char   resv[35];
+};
+
+struct nvme_id_power_state {
+  unsigned short  max_power; // centiwatts
+  unsigned char   rsvd2;
+  unsigned char   flags;
+  unsigned int    entry_lat; // microseconds
+  unsigned int    exit_lat;  // microseconds
+  unsigned char   read_tput;
+  unsigned char   read_lat;
+  unsigned char   write_tput;
+  unsigned char   write_lat;
+  unsigned short  idle_power;
+  unsigned char   idle_scale;
+  unsigned char   rsvd19;
+  unsigned short  active_power;
+  unsigned char   active_work_scale;
+  unsigned char   rsvd23[9];
+};
+
+struct nvme_id_ctrl {
+  unsigned short  vid;
+  unsigned short  ssvid;
+  char            sn[20];
+  char            mn[40];
+  char            fr[8];
+  unsigned char   rab;
+  unsigned char   ieee[3];
+  unsigned char   cmic;
+  unsigned char   mdts;
+  unsigned short  cntlid;
+  unsigned int    ver;
+  unsigned int    rtd3r;
+  unsigned int    rtd3e;
+  unsigned int    oaes;
+  unsigned char   rsvd96[160];
+  unsigned short  oacs;
+  unsigned char   acl;
+  unsigned char   aerl;
+  unsigned char   frmw;
+  unsigned char   lpa;
+  unsigned char   elpe;
+  unsigned char   npss;
+  unsigned char   avscc;
+  unsigned char   apsta;
+  unsigned short  wctemp;
+  unsigned short  cctemp;
+  unsigned short  mtfa;
+  unsigned int    hmpre;
+  unsigned int    hmmin;
+  unsigned char   tnvmcap[16];
+  unsigned char   unvmcap[16];
+  unsigned int    rpmbs;
+  unsigned char   rsvd316[196];
+  unsigned char   sqes;
+  unsigned char   cqes;
+  unsigned char   rsvd514[2];
+  unsigned int    nn;
+  unsigned short  oncs;
+  unsigned short  fuses;
+  unsigned char   fna;
+  unsigned char   vwc;
+  unsigned short  awun;
+  unsigned short  awupf;
+  unsigned char   nvscc;
+  unsigned char   rsvd531;
+  unsigned short  acwu;
+  unsigned char   rsvd534[2];
+  unsigned int    sgls;
+  unsigned char   rsvd540[1508];
+  struct nvme_id_power_state  psd[32];
+  unsigned char   vs[1024];
+};
+
+struct nvme_lbaf {
+  unsigned short  ms;
+  unsigned char   ds;
+  unsigned char   rp;
+};
+
+struct nvme_id_ns {
+  uint64_t        nsze;
+  uint64_t        ncap;
+  uint64_t        nuse;
+  unsigned char   nsfeat;
+  unsigned char   nlbaf;
+  unsigned char   flbas;
+  unsigned char   mc;
+  unsigned char   dpc;
+  unsigned char   dps;
+  unsigned char   nmic;
+  unsigned char   rescap;
+  unsigned char   fpi;
+  unsigned char   rsvd33;
+  unsigned short  nawun;
+  unsigned short  nawupf;
+  unsigned short  nacwu;
+  unsigned short  nabsn;
+  unsigned short  nabo;
+  unsigned short  nabspf;
+  unsigned char   rsvd46[2];
+  unsigned char   nvmcap[16];
+  unsigned char   rsvd64[40];
+  unsigned char   nguid[16];
+  unsigned char   eui64[8];
+  struct nvme_lbaf  lbaf[16];
+  unsigned char   rsvd192[192];
+  unsigned char   vs[3712];
+};
+
+struct nvme_smart_log {
+  unsigned char  critical_warning;
+  unsigned char  temperature[2];
+  unsigned char  avail_spare;
+  unsigned char  spare_thresh;
+  unsigned char  percent_used;
+  unsigned char  rsvd6[26];
+  unsigned char  data_units_read[16];
+  unsigned char  data_units_written[16];
+  unsigned char  host_reads[16];
+  unsigned char  host_writes[16];
+  unsigned char  ctrl_busy_time[16];
+  unsigned char  power_cycles[16];
+  unsigned char  power_on_hours[16];
+  unsigned char  unsafe_shutdowns[16];
+  unsigned char  media_errors[16];
+  unsigned char  num_err_log_entries[16];
+  unsigned int   warning_temp_time;
+  unsigned int   critical_comp_time;
+  unsigned short temp_sensor[8];
+  unsigned char  rsvd216[296];
+};
+
+enum nvme_admin_opcode {
+//nvme_admin_delete_sq     = 0x00,
+//nvme_admin_create_sq     = 0x01,
+  nvme_admin_get_log_page  = 0x02,
+//nvme_admin_delete_cq     = 0x04,
+//nvme_admin_create_cq     = 0x05,
+  nvme_admin_identify      = 0x06,
+//nvme_admin_abort_cmd     = 0x08,
+//nvme_admin_set_features  = 0x09,
+//nvme_admin_get_features  = 0x0a,
+//nvme_admin_async_event   = 0x0c,
+//nvme_admin_ns_mgmt       = 0x0d,
+//nvme_admin_activate_fw   = 0x10,
+//nvme_admin_download_fw   = 0x11,
+//nvme_admin_ns_attach     = 0x15,
+//nvme_admin_format_nvm    = 0x80,
+//nvme_admin_security_send = 0x81,
+//nvme_admin_security_recv = 0x82,
+};
+
+// END: From <linux/nvme.h>
+////////////////////////////////////////////////////////////////////////////
+
+} // namespace smartmontools
+
+class nvme_device;
+
+// Print NVMe debug messages?
+extern unsigned char nvme_debugmode;
+
+// Read NVMe Identify Controller data structure.
+bool nvme_read_id_ctrl(nvme_device * device, smartmontools::nvme_id_ctrl & id_ctrl);
+
+// Read NVMe Identify Namespace data structure for namespace NSID.
+bool nvme_read_id_ns(nvme_device * device, unsigned nsid, smartmontools::nvme_id_ns & id_ns);
+
+// Read NVMe log page with identifier LID.
+bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data, unsigned size);
+
+// Read NVMe Error Information Log.
+bool nvme_read_error_log(nvme_device * device, smartmontools::nvme_error_log_page * error_log,
+  unsigned num_entries);
+
+// Read NVMe SMART/Health Information log.
+bool nvme_read_smart_log(nvme_device * device, smartmontools::nvme_smart_log & smart_log);
+
+#endif // NVMECMDS_H
diff --git a/nvmeprint.cpp b/nvmeprint.cpp
new file mode 100644 (file)
index 0000000..2714fe6
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * nvmeprint.cpp
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2016 Christian Franke
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "nvmeprint.h"
+
+const char * nvmeprint_cvsid = "$Id: nvmeprint.cpp 4311 2016-04-27 21:03:01Z chrfranke $"
+  NVMEPRINT_H_CVSID;
+
+#include "int64.h"
+#include "utility.h"
+#include "dev_interface.h"
+#include "nvmecmds.h"
+#include "atacmds.h" // dont_print_serial_number
+#include "scsicmds.h" // dStrHex()
+#include "smartctl.h"
+
+using namespace smartmontools;
+
+// Return true if 128 bit LE integer is != 0.
+static bool le128_is_non_zero(const unsigned char (& val)[16])
+{
+  for (int i = 0; i < 16; i++) {
+    if (val[i])
+      return true;
+  }
+  return false;
+}
+
+// Format 128 bit integer for printing.
+// Add value with SI prefixes if BYTES_PER_UNIT is specified.
+static const char * le128_to_str(char (& str)[64], uint64_t hi, uint64_t lo, unsigned bytes_per_unit)
+{
+  if (!hi) {
+    // Up to 64-bit, print exact value
+    format_with_thousands_sep(str, sizeof(str)-16, lo);
+
+    if (lo && bytes_per_unit && lo < 0xffffffffffffffffULL / bytes_per_unit) {
+      int i = strlen(str);
+      str[i++] = ' '; str[i++] = '[';
+      format_capacity(str+i, (int)sizeof(str)-i-1, lo * bytes_per_unit);
+      i = strlen(str);
+      str[i++] = ']'; str[i] = 0;
+    }
+  }
+  else {
+    // More than 64-bit, print approximate value, prepend ~ flag
+    snprintf(str, sizeof(str), "~%.0f",
+             hi * (0xffffffffffffffffULL + 1.0) + lo);
+  }
+
+  return str;
+}
+
+// Format 128 bit LE integer for printing.
+// Add value with SI prefixes if BYTES_PER_UNIT is specified.
+static const char * le128_to_str(char (& str)[64], const unsigned char (& val)[16],
+  unsigned bytes_per_unit = 0)
+{
+  uint64_t hi = val[15];
+  for (int i = 15-1; i >= 8; i--) {
+    hi <<= 8; hi += val[i];
+  }
+  uint64_t lo = val[7];
+  for (int i =  7-1; i >= 0; i--) {
+    lo <<= 8; lo += val[i];
+  }
+  return le128_to_str(str, hi, lo, bytes_per_unit);
+}
+
+// Format capacity specified as 64bit LBA count for printing.
+static const char * lbacap_to_str(char (& str)[64], uint64_t lba_cnt, int lba_bits)
+{
+  return le128_to_str(str, (lba_cnt >> (64 - lba_bits)), (lba_cnt << lba_bits), 1);
+}
+
+// Format a Kelvin temperature value in Celsius.
+static const char * kelvin_to_str(char (& str)[64], int k)
+{
+  if (!k) // unsupported?
+    str[0] = '-', str[1] = 0;
+  else
+    snprintf(str, sizeof(str), "%d Celsius", k - 273);
+  return str;
+}
+
+static inline unsigned le16_to_uint(const unsigned char (& val)[2])
+{
+  return ((val[1] << 8) | val[0]);
+}
+
+static void print_drive_info(const nvme_id_ctrl & id_ctrl, const nvme_id_ns & id_ns,
+  unsigned nsid, bool show_all)
+{
+  char buf[64];
+  pout("Model Number:                       %s\n", format_char_array(buf, id_ctrl.mn));
+  if (!dont_print_serial_number)
+    pout("Serial Number:                      %s\n", format_char_array(buf, id_ctrl.sn));
+  pout("Firmware Version:                   %s\n", format_char_array(buf, id_ctrl.fr));
+
+  // Vendor and Subsystem IDs are usually equal
+  if (show_all || id_ctrl.vid != id_ctrl.ssvid) {
+    pout("PCI Vendor ID:                      0x%04x\n", id_ctrl.vid);
+    pout("PCI Vendor Subsystem ID:            0x%04x\n", id_ctrl.ssvid);
+  }
+  else {
+    pout("PCI Vendor/Subsystem ID:            0x%04x\n", id_ctrl.vid);
+  }
+
+  pout("IEEE OUI Identifier:                0x%02x%02x%02x\n",
+       id_ctrl.ieee[2], id_ctrl.ieee[1], id_ctrl.ieee[0]);
+
+  // Capacity info is optional for devices without namespace management
+  if (show_all || le128_is_non_zero(id_ctrl.tnvmcap) || le128_is_non_zero(id_ctrl.unvmcap)) {
+    pout("Total NVM Capacity:                 %s\n", le128_to_str(buf, id_ctrl.tnvmcap, 1));
+    pout("Unallocated NVM Capacity:           %s\n", le128_to_str(buf, id_ctrl.unvmcap, 1));
+  }
+
+  pout("Controller ID:                      %d\n", id_ctrl.cntlid);
+
+  // Print namespace info if available
+  pout("Number of Namespaces:               %u\n", id_ctrl.nn);
+
+  if (nsid && id_ns.nsze) {
+    const char * align = &("  "[nsid < 10 ? 0 : (nsid < 100 ? 1 : 2)]);
+    int fmt_lba_bits = id_ns.lbaf[id_ns.flbas & 0xf].ds;
+
+    // Size and Capacity are equal if thin provisioning is not supported
+    if (show_all || id_ns.ncap != id_ns.nsze || (id_ns.nsfeat & 0x01)) {
+      pout("Namespace %u Size:                 %s%s\n", nsid, align,
+           lbacap_to_str(buf, id_ns.nsze, fmt_lba_bits));
+      pout("Namespace %u Capacity:             %s%s\n", nsid, align,
+           lbacap_to_str(buf, id_ns.ncap, fmt_lba_bits));
+    }
+    else {
+      pout("Namespace %u Size/Capacity:        %s%s\n", nsid, align,
+           lbacap_to_str(buf, id_ns.nsze, fmt_lba_bits));
+    }
+    // Utilization may be always equal to Capacity if thin provisioning is not supported
+    if (show_all || id_ns.nuse != id_ns.ncap || (id_ns.nsfeat & 0x01))
+      pout("Namespace %u Utilization:          %s%s\n", nsid, align,
+           lbacap_to_str(buf, id_ns.nuse, fmt_lba_bits));
+
+    pout("Namespace %u Formatted LBA Size:   %s%u\n", nsid, align, (1U << fmt_lba_bits));
+  }
+
+  char td[DATEANDEPOCHLEN]; dateandtimezone(td);
+  pout("Local Time is:                      %s\n", td);
+}
+
+// Format scaled power value.
+static const char * format_power(char (& str)[16], unsigned power, unsigned scale)
+{
+  switch (scale & 0x3) {
+    case 0: // not reported
+      str[0] = '-'; str[1] = ' '; str[2] = 0; break;
+    case 1: // 0.0001W
+      snprintf(str, sizeof(str), "%u.%04uW", power / 10000, power % 10000); break;
+    case 2: // 0.01W
+      snprintf(str, sizeof(str), "%u.%02uW", power / 100, power % 100); break;
+    default: // reserved
+      str[0] = '?'; str[1] = 0; break;
+  }
+  return str;
+}
+
+static void print_drive_capabilities(const nvme_id_ctrl & id_ctrl, const nvme_id_ns & id_ns,
+  unsigned nsid, bool show_all)
+{
+  pout("Firmware Updates (0x%02x):            %d Slot%s%s%s\n", id_ctrl.frmw,
+       ((id_ctrl.frmw >> 1) & 0x7), (((id_ctrl.frmw >> 1) & 0x7) != 1 ? "s" : ""),
+       ((id_ctrl.frmw & 0x01) ? ", Slot 1 R/O" : ""),
+       ((id_ctrl.frmw & 0x10) ? ", no Reset required" : ""));
+
+  if (show_all || id_ctrl.oacs)
+    pout("Optional Admin Commands (0x%04x):  %s%s%s%s%s%s\n", id_ctrl.oacs,
+         (!id_ctrl.oacs ? " -" : ""),
+         ((id_ctrl.oacs & 0x0001) ? " Security" : ""),
+         ((id_ctrl.oacs & 0x0002) ? " Format" : ""),
+         ((id_ctrl.oacs & 0x0004) ? " Frmw_DL" : ""),
+         ((id_ctrl.oacs & 0x0008) ? " NS_Mngmt" : ""),
+         ((id_ctrl.oacs & ~0x000f) ? " *Other*" : ""));
+
+  if (show_all || id_ctrl.oncs)
+    pout("Optional NVM Commands (0x%04x):    %s%s%s%s%s%s%s%s\n", id_ctrl.oncs,
+         (!id_ctrl.oncs ? " -" : ""),
+         ((id_ctrl.oncs & 0x0001) ? " Comp" : ""),
+         ((id_ctrl.oncs & 0x0002) ? " Wr_Unc" : ""),
+         ((id_ctrl.oncs & 0x0004) ? " DS_Mngmt" : ""),
+         ((id_ctrl.oncs & 0x0008) ? " Wr_Zero" : ""),
+         ((id_ctrl.oncs & 0x0010) ? " Sav/Sel_Feat" : ""),
+         ((id_ctrl.oncs & 0x0020) ? " Resv" : ""),
+         ((id_ctrl.oncs & ~0x003f) ? " *Other*" : ""));
+
+  if (id_ctrl.mdts)
+    pout("Maximum Data Transfer Size:         %u Pages\n", (1U << id_ctrl.mdts));
+  else if (show_all)
+    pout("Maximum Data Transfer Size:         -\n");
+
+  // Temperature thresholds are optional
+  char buf[64];
+  if (show_all || id_ctrl.wctemp)
+    pout("Warning  Comp. Temp. Threshold:     %s\n", kelvin_to_str(buf, id_ctrl.wctemp));
+  if (show_all || id_ctrl.cctemp)
+    pout("Critical Comp. Temp. Threshold:     %s\n", kelvin_to_str(buf, id_ctrl.cctemp));
+
+  if (nsid && (show_all || id_ns.nsfeat)) {
+    const char * align = &("  "[nsid < 10 ? 0 : (nsid < 100 ? 1 : 2)]);
+    pout("Namespace %u Features (0x%02x):     %s%s%s%s%s%s\n", nsid, id_ns.nsfeat, align,
+         (!id_ns.nsfeat ? " -" : ""),
+         ((id_ns.nsfeat & 0x01) ? " Thin_Prov" : ""),
+         ((id_ns.nsfeat & 0x02) ? " NA_Fields" : ""),
+         ((id_ns.nsfeat & 0x04) ? " Dea/Unw_Error" : ""),
+         ((id_ns.nsfeat & ~0x07) ? " *Other*" : ""));
+  }
+
+  // Print Power States
+  pout("\nSupported Power States\n");
+  pout("St Op     Max   Active     Idle   RL RT WL WT  Ent_Lat  Ex_Lat\n");
+  for (int i = 0; i <= id_ctrl.npss /* 1-based */ && i < 32; i++) {
+    char p1[16], p2[16], p3[16];
+    const nvme_id_power_state & ps = id_ctrl.psd[i];
+    pout("%2d %c %9s %8s %8s %3d %2d %2d %2d %8u %7u\n", i,
+         ((ps.flags & 0x02) ? '-' : '+'),
+         format_power(p1, ps.max_power, ((ps.flags & 0x01) ? 1 : 2)),
+         format_power(p2, ps.active_power, ps.active_work_scale),
+         format_power(p3, ps.idle_power, ps.idle_scale),
+         ps.read_lat & 0x1f, ps.read_tput & 0x1f,
+         ps.write_lat & 0x1f, ps.write_tput & 0x1f,
+         ps.entry_lat, ps.exit_lat);
+  }
+
+  // Print LBA sizes
+  if (nsid && id_ns.lbaf[0].ds) {
+    pout("\nSupported LBA Sizes (NSID 0x%x)\n", nsid);
+    pout("Id Fmt  Data  Metadt  Rel_Perf\n");
+    for (int i = 0; i <= id_ns.nlbaf /* 1-based */ && i < 16; i++) {
+      const nvme_lbaf & lba = id_ns.lbaf[i];
+      pout("%2d %c %7u %7d %9d\n", i, (i == id_ns.flbas ? '+' : '-'),
+           (1U << lba.ds), lba.ms, lba.rp);
+    }
+  }
+}
+
+static void print_critical_warning(unsigned char w)
+{
+  pout("SMART overall-health self-assessment test result: %s\n",
+       (!w ? "PASSED" : "FAILED!"));
+
+  if (w) {
+   if (w & 0x01)
+     pout("- available spare has fallen below threshold\n");
+   if (w & 0x02)
+     pout("- temperature is above or below threshold\n");
+   if (w & 0x04)
+     pout("- NVM subsystem reliability has been degraded\n");
+   if (w & 0x08)
+     pout("- media has been placed in read only mode\n");
+   if (w & 0x10)
+     pout("- volatile memory backup device has failed\n");
+   if (w & ~0x1f)
+     pout("- unknown critical warning(s) (0x%02x)\n", w & ~0x1f);
+  }
+
+  pout("\n");
+}
+
+static void print_smart_log(const nvme_smart_log & smart_log, unsigned nsid,
+  const nvme_id_ctrl & id_ctrl, bool show_all)
+{
+  char buf[64];
+  pout("SMART/Health Information (NVMe Log 0x02, NSID 0x%x)\n", nsid);
+  pout("Critical Warning:                   0x%02x\n", smart_log.critical_warning);
+  pout("Temperature:                        %s\n",
+       kelvin_to_str(buf, le16_to_uint(smart_log.temperature)));
+  pout("Available Spare:                    %u%%\n", smart_log.avail_spare);
+  pout("Available Spare Threshold:          %u%%\n", smart_log.spare_thresh);
+  pout("Percentage Used:                    %u%%\n", smart_log.percent_used);
+  pout("Data Units Read:                    %s\n", le128_to_str(buf, smart_log.data_units_read, 1000*512));
+  pout("Data Units Written:                 %s\n", le128_to_str(buf, smart_log.data_units_written, 1000*512));
+  pout("Host Read Commands:                 %s\n", le128_to_str(buf, smart_log.host_reads));
+  pout("Host Write Commands:                %s\n", le128_to_str(buf, smart_log.host_writes));
+  pout("Controller Busy Time:               %s\n", le128_to_str(buf, smart_log.ctrl_busy_time));
+  pout("Power Cycles:                       %s\n", le128_to_str(buf, smart_log.power_cycles));
+  pout("Power On Hours:                     %s\n", le128_to_str(buf, smart_log.power_on_hours));
+  pout("Unsafe Shutdowns:                   %s\n", le128_to_str(buf, smart_log.unsafe_shutdowns));
+  pout("Media and Data Integrity Errors:    %s\n", le128_to_str(buf, smart_log.media_errors));
+  pout("Error Information Log Entries:      %s\n", le128_to_str(buf, smart_log.num_err_log_entries));
+
+  // Temperature thresholds are optional
+  if (show_all || id_ctrl.wctemp || smart_log.warning_temp_time)
+    pout("Warning  Comp. Temperature Time:    %d\n", smart_log.warning_temp_time);
+  if (show_all || id_ctrl.cctemp || smart_log.critical_comp_time)
+    pout("Critical Comp. Temperature Time:    %d\n", smart_log.critical_comp_time);
+
+  // Temperature sensors are optional
+  for (int i = 0; i < 8; i++) {
+    if (show_all || smart_log.temp_sensor[i])
+      pout("Temperature Sensor %d:               %s\n", i + 1,
+           kelvin_to_str(buf, smart_log.temp_sensor[i]));
+  }
+  pout("\n");
+}
+
+static void print_error_log(const nvme_error_log_page * error_log,
+  unsigned num_entries, unsigned print_entries)
+{
+  pout("Error Information (NVMe Log 0x01, max %u entries)\n", num_entries);
+
+  unsigned cnt = 0;
+  for (unsigned i = 0; i < num_entries; i++) {
+    const nvme_error_log_page & e = error_log[i];
+    if (!e.error_count)
+      continue; // unused or invalid entry
+    if (++cnt > print_entries)
+      continue;
+
+    if (cnt == 1)
+      pout("Num   ErrCount  SQId   CmdId  Status  PELoc          LBA  NSID    VS\n");
+
+    char sq[16] = "-", cm[16] = "-", st[16] = "-", pe[16] = "-";
+    char lb[32] = "-", ns[16] = "-", vs[8] = "-";
+    if (e.sqid != 0xffff)
+      snprintf(sq, sizeof(sq), "%d", e.sqid);
+    if (e.cmdid != 0xffff)
+      snprintf(cm, sizeof(cm), "0x%04x", e.cmdid);
+    if (e.status_field != 0xffff)
+      snprintf(st, sizeof(st), "0x%04x", e.status_field);
+    if (e.parm_error_location != 0xffff)
+      snprintf(pe, sizeof(pe), "0x%03x", e.parm_error_location);
+    if (e.lba != 0xffffffffffffffffULL)
+      snprintf(lb, sizeof(lb), "%" PRIu64, e.lba);
+    if (e.nsid != 0xffffffffU)
+      snprintf(ns, sizeof(ns), "%u", e.nsid);
+    if (e.vs != 0x00)
+      snprintf(vs, sizeof(vs), "0x%02x", e.vs);
+
+    pout("%3u %10" PRIu64 " %5s %7s %7s %6s %12s %5s %5s\n",
+         i, e.error_count, sq, cm, st, pe, lb, ns, vs);
+  }
+
+  if (!cnt)
+    pout("No Errors Logged\n");
+  else if (cnt > print_entries)
+    pout("... (%u entries not shown)\n", cnt - print_entries);
+  pout("\n");
+}
+
+int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
+{
+  if (!(   options.drive_info || options.drive_capabilities
+        || options.smart_check_status || options.smart_vendor_attrib
+        || options.error_log_entries || options.log_page_size       )) {
+    pout("NVMe device successfully opened\n\n"
+         "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n");
+    return 0;
+  }
+
+  // Show unset optional values only if debugging is enabled
+  bool show_all = (nvme_debugmode > 0);
+
+  // Read Identify Controller always
+  nvme_id_ctrl id_ctrl;
+  if (!nvme_read_id_ctrl(device, id_ctrl)) {
+    pout("Read NVMe Identify Controller failed: %s\n", device->get_errmsg());
+    return FAILID;
+  }
+
+  // Print Identify Controller/Namespace info
+  if (options.drive_info || options.drive_capabilities) {
+    pout("=== START OF INFORMATION SECTION ===\n");
+    nvme_id_ns id_ns; memset(&id_ns, 0, sizeof(id_ns));
+
+    unsigned nsid = device->get_nsid();
+    if (nsid == 0xffffffffU) {
+      // Broadcast namespace
+      if (id_ctrl.nn == 1) {
+        // No namespace management, get size from single namespace
+        nsid = 1;
+        if (!nvme_read_id_ns(device, nsid, id_ns))
+          nsid = 0;
+      }
+    }
+    else {
+        // Identify current namespace
+        if (!nvme_read_id_ns(device, nsid, id_ns)) {
+          pout("Read NVMe Identify Namespace 0x%x failed: %s\n", nsid, device->get_errmsg());
+          return FAILID;
+        }
+    }
+
+    if (options.drive_info)
+      print_drive_info(id_ctrl, id_ns, nsid, show_all);
+    if (options.drive_capabilities)
+      print_drive_capabilities(id_ctrl, id_ns, nsid, show_all);
+    pout("\n");
+  }
+
+  if (   options.smart_check_status || options.smart_vendor_attrib
+      || options.error_log_entries)
+    pout("=== START OF SMART DATA SECTION ===\n");
+
+  // Print SMART Status and SMART/Health Information
+  int retval = 0;
+  if (options.smart_check_status || options.smart_vendor_attrib) {
+    nvme_smart_log smart_log;
+    if (!nvme_read_smart_log(device, smart_log)) {
+      pout("Read NVMe SMART/Health Information failed: %s\n\n", device->get_errmsg());
+      return FAILSMART;
+    }
+
+    if (options.smart_check_status) {
+      print_critical_warning(smart_log.critical_warning);
+      if (smart_log.critical_warning)
+        retval |= FAILSTATUS;
+    }
+
+    if (options.smart_vendor_attrib) {
+      print_smart_log(smart_log, device->get_nsid(), id_ctrl, show_all);
+    }
+  }
+
+  // Print Error Information Log
+  if (options.error_log_entries) {
+    unsigned num_entries = id_ctrl.elpe + 1; // 0-based value
+    raw_buffer error_log_buf(num_entries * sizeof(nvme_error_log_page));
+    nvme_error_log_page * error_log =
+      reinterpret_cast<nvme_error_log_page *>(error_log_buf.data());
+
+    if (!nvme_read_error_log(device, error_log, num_entries)) {
+      pout("Read Error Information Log failed: %s\n\n", device->get_errmsg());
+      return retval | FAILSMART;
+    }
+
+    print_error_log(error_log, num_entries, options.error_log_entries);
+  }
+
+  // Dump log page
+  if (options.log_page_size) {
+    // Align size to dword boundary
+    unsigned size = ((options.log_page_size + 4-1) / 4) * 4;
+    raw_buffer log_buf(size);
+
+    if (!nvme_read_log_page(device, options.log_page, log_buf.data(), size)) {
+      pout("Read NVMe Log 0x%02x failed: %s\n\n", options.log_page, device->get_errmsg());
+      return retval | FAILSMART;
+    }
+
+    pout("NVMe Log 0x%02x (0x%04x bytes)\n", options.log_page, size);
+    dStrHex(log_buf.data(), size, 0);
+    pout("\n");
+  }
+
+  return retval;
+}
diff --git a/nvmeprint.h b/nvmeprint.h
new file mode 100644 (file)
index 0000000..667396a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * nvmeprint.h
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2016 Christian Franke
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef NVMEPRINT_H
+#define NVMEPRINT_H
+
+#define NVMEPRINT_H_CVSID "$Id: nvmeprint.h 4311 2016-04-27 21:03:01Z chrfranke $"
+
+#include "nvmecmds.h"
+
+// options for nvmePrintMain
+struct nvme_print_options
+{
+  bool drive_info;
+  bool drive_capabilities;
+  bool smart_check_status;
+  bool smart_vendor_attrib;
+  unsigned error_log_entries;
+  unsigned char log_page;
+  unsigned log_page_size;
+
+  nvme_print_options()
+    : drive_info(false),
+      drive_capabilities(false),
+      smart_check_status(false),
+      smart_vendor_attrib(false),
+      error_log_entries(0),
+      log_page(0),
+      log_page_size(0)
+    { }
+};
+
+int nvmePrintMain(nvme_device * device, const nvme_print_options & options);
+
+#endif // NVMEPRINT_H
index 8d05d1cd858c60cfd4f0b1869441cbac3c3b6366..53a0f4609c60c5dfdc01ac8d92317e8c1acd22fe 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_darwin.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org>
  * Copyright (C) 2014 Alex Samorukov <samm@os2.kiev.ua>
@@ -22,6 +22,7 @@
 #include <mach/mach.h>
 #include <mach/mach_error.h>
 #include <mach/mach_init.h>
+#include <sys/utsname.h>
 #include <IOKit/IOCFPlugIn.h>
 #include <IOKit/IOKitLib.h>
 #include <IOKit/IOReturn.h>
@@ -45,7 +46,7 @@
 #include "dev_interface.h"
 
 // Needed by '-V' option (CVS versioning) of smartd/smartctl
-const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 3982 2014-08-16 21:07:19Z samm2 $" \
+const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 4214 2016-01-24 22:53:37Z samm2 $" \
 ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 // examples for smartctl
@@ -77,7 +78,7 @@ static struct {
   IOATASMARTInterface **smartIf;
 } devices[20];
 
-const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 3982 2014-08-16 21:07:19Z samm2 $"
+const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 4214 2016-01-24 22:53:37Z samm2 $"
   DEV_INTERFACE_H_CVSID;
 
 /////////////////////////////////////////////////////////////////////////////
@@ -181,8 +182,8 @@ bool darwin_smart_device::open()
   
   if (strcmp (type, "ATA") != 0)
     {
-      errno = EINVAL;
-      return -1;
+      set_err (EINVAL);
+      return false;
     }
   
   // Find a free device number.
@@ -191,8 +192,8 @@ bool darwin_smart_device::open()
       break;
   if (devnum == sizeof (devices) / sizeof (devices[0]))
     {
-      errno = EMFILE;
-      return -1;
+      set_err (EMFILE);
+      return false;
     }
   
   devname = NULL;
@@ -218,8 +219,8 @@ bool darwin_smart_device::open()
 
   if (! disk)
     {
-      errno = ENOENT;
-      return -1;
+      set_err(ENOENT);
+      return false;
     }
   
   // Find a SMART-capable driver which is a parent of this device.
@@ -232,9 +233,9 @@ bool darwin_smart_device::open()
       err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk);
       if (err != kIOReturnSuccess || ! disk)
       {
-        errno = ENODEV;
+        set_err(ENODEV);
         IOObjectRelease (prevdisk);
-        return -1;
+        return false;
       }
     }
   
@@ -257,8 +258,8 @@ bool darwin_smart_device::open()
       CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID),
       (void **)&devices[devnum].smartIf);
   }
-  
-  
+
+
   m_fd = devnum;
   if (m_fd < 0) {
     set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
@@ -311,9 +312,9 @@ static int make_device_names (char*** devlist, const char* name) {
 
   // Create an array of service names.
   IOIteratorReset (i);
-  *devlist = (char**)calloc (result, sizeof (char *)); 
-  if (! *devlist)
+  if (! result)
     goto error;
+  *devlist = (char**)calloc (result, sizeof (char *)); 
   index = 0;
   while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
     if (is_smart_capable (device))
@@ -342,6 +343,9 @@ static int make_device_names (char*** devlist, const char* name) {
       free ((*devlist)[index]);
       free (*devlist);
     }
+  if(!result) // no devs found
+    return 0;
+
   return -1;
 }
 
@@ -454,7 +458,7 @@ bool darwin_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
         if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST)
         {
           errno = EINVAL;
-          err = -1;
+          return set_err(ENOSYS, "Unsupported SMART self-test mode");
         }
         err = smartIf->SMARTExecuteOffLineImmediate (ifp, 
           select == EXTEND_SELF_TEST);
@@ -488,6 +492,8 @@ class darwin_smart_interface
 : public /*implements*/ smart_interface
 {
 public:
+  virtual std::string get_os_version_str();
+
   virtual std::string get_app_examples(const char * appname);
 
   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
@@ -505,6 +511,15 @@ protected:
 
 //////////////////////////////////////////////////////////////////////
 
+std::string darwin_smart_interface::get_os_version_str()
+{
+  // now we are just getting darwin runtime version, to get OSX version more things needs to be done, see
+  // http://stackoverflow.com/questions/11072804/how-do-i-determine-the-os-version-at-runtime-in-os-x-or-ios-without-using-gesta
+  struct utsname osname;
+  uname(&osname);
+  return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine);
+}
+
 std::string darwin_smart_interface::get_app_examples(const char * appname)
 {
   if (!strcmp(appname, "smartctl"))
index 59781874470e82990eef018a6809ba84f422e464..8031b73251da0ea57e26d6fc0e83149c66f91cd3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_generic.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-8 Geoff Keating <geoffk@geoffk.org>
  *
@@ -24,7 +24,7 @@
 #ifndef OS_DARWIN_H_
 #define OS_DARWIN_H_
 
-#define OS_DARWIN_H_CVSID "$Id: os_darwin.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define OS_DARWIN_H_CVSID "$Id: os_darwin.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 // Isn't in 10.3.9?
 
diff --git a/os_darwin/English_Localizable.strings b/os_darwin/English_Localizable.strings
deleted file mode 100644 (file)
index 4f5d3d6..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
-<plist version="0.9">
-<dict>
-        <key>SMART disk monitoring</key>
-        <string>SMART disk monitoring</string>
-        <key>Starting SMART disk monitoring</key>
-        <string>Starting SMART disk monitoring</string>
-        <key>Stopping SMART disk monitoring</key>
-        <string>Stopping SMART disk monitoring</string>
-</dict>
-</plist>
diff --git a/os_darwin/SMART.in b/os_darwin/SMART.in
deleted file mode 100644 (file)
index df2cb5c..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-# Darwin init file for smartd
-#
-# Home page of code is: http://smartmontools.sourceforge.net
-#
-# Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org>
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; either version 2, or (at your option) any later
-# version.
-#
-# You should have received a copy of the GNU General Public License (for
-# example COPYING); if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-# $Id: SMART.in 3728 2012-12-13 17:57:50Z chrfranke $
-
-##
-# SMART monitoring
-##
-
-. /etc/rc.common
-
-StartService ()
-{
-    if [ "${SMARTd:=-YES-}" = "-YES-" ] &&
-       ! GetPID smartd > /dev/null; then
-
-        ConsoleMessage "Starting SMART disk monitoring"
-
-        /usr/sbin/smartd -p /var/run/smartd.pid
-    fi
-}
-
-StopService ()
-{
-    if pid=$(GetPID smartd); then
-        ConsoleMessage "Stopping SMART disk monitoring"
-        kill -TERM "${pid}"
-    else
-        echo "smartd is not running."
-    fi
-}
-
-RestartService ()
-{
-    if pid=$(GetPID smartd); then
-        kill -HUP "${pid}"
-    else
-        StartService
-    fi
-}
-
-RunService "$1"
diff --git a/os_darwin/StartupParameters.plist b/os_darwin/StartupParameters.plist
deleted file mode 100644 (file)
index 174d831..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  Description     = "SMART disk monitoring";
-  Provides        = ("SMART");
-  Requires        = ("System Log");
-}
diff --git a/os_darwin/com.smartmontools.smartd.plist.in b/os_darwin/com.smartmontools.smartd.plist.in
new file mode 100644 (file)
index 0000000..c025e45
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+    <key>KeepAlive</key>
+    <dict>
+      <key>SuccessfulExit</key>
+      <false/>
+    </dict>
+    <key>Label</key>
+    <string>com.smartmontools.smartd</string>
+    <key>ProgramArguments</key>
+    <array>
+      <string>/usr/local/sbin/smartd</string>
+      <string>-n</string>
+    </array>
+    <key>RunAtLoad</key>
+    <true/>
+  </dict>
+</plist>
diff --git a/os_darwin/pkg/Distribution.in b/os_darwin/pkg/Distribution.in
new file mode 100644 (file)
index 0000000..df3ec9c
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<installer-gui-script minSpecVersion="2">
+  <allowed-os-versions>
+    <os-version min="10.5"/>
+  </allowed-os-versions>
+  <license file="license.txt" mime-type="text/plain"/>
+  <!-- <welcome file="welcome.rtf" mime-type="text/rtf"/> -->
+  <title>S.M.A.R.T. disk monitoring tools</title>
+  <pkg-ref id="com.smartmontools.pkg"/>
+  <choices-outline>
+    <line choice="default">
+      <line choice="com.smartmontools.pkg"/>
+    </line>
+  </choices-outline>
+  <choice id="default"/>
+  <choice id="com.smartmontools.pkg" visible="false">
+    <pkg-ref version="@version@" installKBytes="@size@" id="com.smartmontools.pkg">@pkgname@</pkg-ref>
+  </choice>
+  <domains enable_localSystem="true"/>
+  <options customize="never" rootVolumeOnly="true"/>
+</installer-gui-script>
diff --git a/os_darwin/pkg/PackageInfo.in b/os_darwin/pkg/PackageInfo.in
new file mode 100644 (file)
index 0000000..92ea7a2
--- /dev/null
@@ -0,0 +1,6 @@
+<pkg-info format-version="2" identifier="com.smartmontools.pkg" version="@version@" install-location="/" auth="root">
+<payload installKBytes="@size@" numberOfFiles="@files@"/>
+<!-- <scripts>
+    <postinstall file="./postinstall"/>
+</scripts> -->
+</pkg-info>
diff --git a/os_darwin/pkg/installer/README.html b/os_darwin/pkg/installer/README.html
new file mode 100644 (file)
index 0000000..6abdc80
--- /dev/null
@@ -0,0 +1,26 @@
+<html>
+<body>
+<h2>About this package</h2>
+The smartmontools package contains two utility programs (smartctl and smartd) to control 
+and monitor storage systems using the Self-Monitoring, Analysis and Reporting
+Technology System (SMART) built into most modern ATA and SCSI harddisks.
+In many cases, these utilities will provide advanced warning of disk degradation and failure.
+<h2>Installing</h2>
+To install package click on the smartmontools.pkg icon and follow installation process. Files will be installed to the <b>/usr/local/</b> directory.
+<h2>Usage</h2>
+ If you are having trouble understanding the output of smartctl or smartd, please first read the manual pages installed on your system:
+<pre>
+  man 8 smartctl
+  man 8 smartd
+  man 8 update-smart-drivedb
+  man 5 smartd.conf
+</pre>
+To use smartmontools with USB drives please download and install 
+<a href="https://github.com/kasbert/OS-X-SAT-SMART-Driver">Max OS X kernel driver for providing access to external drive SMART data</a>. SAT SMART Driver is a free open source project (published under Apple Public Source License) by Jarkko Sonninen.
+If you are using OS X El Capitan 10.11+ it is recommended to use signed version available from <a href="http://binaryfruit.com/drivedx/usb-drive-support/">DriveDx web site</a>.
+<p>
+More information could be found on the <a href="https://www.smartmontools.org">www.smartmontools.org</a> website.
+<h2>Uninstalling</h2>
+If you want to uninstall already installed package run <tt>'sudo smart-pkg-uninstall'</tt> in the terminal.
+</body>
+</html>
diff --git a/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall b/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall
new file mode 100755 (executable)
index 0000000..72bb3fa
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+echo "Smartmontools package uninstaller:"
+
+# check if we are running with root uid
+if [[ $EUID -ne 0 ]]; then
+   echo "   Error: this script must be run as root"
+   exit 1
+fi
+
+# check if package is installed
+pkgutil --info com.smartmontools.pkg > /dev/null 2>/dev/null
+if [ $? -ne 0 ]; then
+  echo "   Error: smartmontools package is not installed"
+  exit 1
+fi
+
+# smartmontools pkg could be installed only on system volume, so this should be safe
+cd /
+
+echo "  - removing files"
+for str in `pkgutil --files com.smartmontools.pkg`
+do
+   if [ -f "$str" ]
+   then
+      rm -f "$str"
+   fi
+done
+echo "  - removing empty directories"
+for str in `pkgutil --files com.smartmontools.pkg`
+do
+   if [ -d "$str" ]
+   then
+      rmdir -p "$str" 2>/dev/null
+   fi
+done
+
+echo "  - removing package system entry"
+pkgutil --forget com.smartmontools.pkg
+echo "Done, smartmontolls package removed"
index 9c5db2bbf7eb8f8cd2ba6eee52635bd8a98a3a41..05da8d1b1f10628a58926915620e8ffca9d6d14b 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * os_freebsd.c
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2003-10 Eduard Martinescu <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-10 Eduard Martinescu
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -38,6 +38,8 @@
 
 #include "config.h"
 #include "int64.h"
+// set by /usr/include/sys/ata.h, suppress warning
+#undef ATA_READ_LOG_EXT
 #include "atacmds.h"
 #include "scsicmds.h"
 #include "cciss.h"
@@ -68,6 +70,9 @@
 #include <dev/usb/usbhid.h>
 #endif
 
+// based on "/sys/dev/nvme/nvme.h" from FreeBSD kernel sources
+#include "freebsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error
+
 #define CONTROLLER_3WARE_9000_CHAR      0x01
 #define CONTROLLER_3WARE_678K_CHAR      0x02
 
@@ -75,7 +80,7 @@
 #define PATHINQ_SETTINGS_SIZE   128
 #endif
 
-const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 3902 2014-05-23 19:14:15Z samm2 $" \
+const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 4257 2016-03-27 23:32:54Z samm2 $" \
 ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 #define NO_RETURN 0
@@ -86,19 +91,21 @@ ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SC
 
 // Utility function for printing warnings
 void printwarning(int msgNo, const char* extra) {
-  static int printed[] = {0,0,0,0};
-  static const char* message[]={
-    "The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n can not be retrieved with this version of ATAng, please do not rely on this value\nYou should update to at least 5.2\n",
 
-    "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
+  if (msgNo >= 0 && msgNo <= MAX_MSG) {
+    static int printed[] = {0,0,0,0};
+    if (!printed[msgNo]) {
 
-    "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n",
+      static const char* message[]={
+        "The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n can not be retrieved with this version of ATAng, please do not rely on this value\nYou should update to at least 5.2\n",
 
-    "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n"
-  };
+        "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
+
+        "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n",
+
+        "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n"
+      };
 
-  if (msgNo >= 0 && msgNo <= MAX_MSG) {
-    if (!printed[msgNo]) {
       printed[msgNo] = 1;
       pout("%s", message[msgNo]);
       if (extra)
@@ -120,8 +127,6 @@ void printwarning(int msgNo, const char* extra) {
 
 #define ARGUSED(x) ((void)(x))
 
-// global variable holding byte count of allocated memory
-long long bytes;
 extern unsigned char failuretest_permissive;
 
 /////////////////////////////////////////////////////////////////////////////
@@ -466,6 +471,112 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bi
 
 #endif
 
+/////////////////////////////////////////////////////////////////////////////
+/// NVMe support
+
+class freebsd_nvme_device
+: public /*implements*/ nvme_device,
+  public /*extends*/ freebsd_smart_device
+{
+public:
+  freebsd_nvme_device(smart_interface * intf, const char * dev_name,
+    const char * req_type, unsigned nsid);
+
+  virtual bool open();
+
+  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
+};
+
+freebsd_nvme_device::freebsd_nvme_device(smart_interface * intf, const char * dev_name,
+  const char * req_type, unsigned nsid)
+: smart_device(intf, dev_name, "nvme", req_type),
+  nvme_device(nsid),
+  freebsd_smart_device()
+{
+}
+
+bool freebsd_nvme_device::open()
+{
+  const char *dev = get_dev_name();
+  if (!strnstr(dev, NVME_CTRLR_PREFIX, strlen(NVME_CTRLR_PREFIX))) {
+       set_err(EINVAL, "NVMe controller controller/namespace ids must begin with '%s'", 
+               NVME_CTRLR_PREFIX);
+       return false;
+  }
+  
+  int nsid = -1, ctrlid = -1;
+  char tmp;
+  
+  if(sscanf(dev, NVME_CTRLR_PREFIX"%d%c", &ctrlid, &tmp) == 1)
+  {
+       if(ctrlid < 0) {
+               set_err(EINVAL, "Invalid NVMe controller number");
+               return false;
+       }
+       nsid = 0xFFFFFFFF; // broadcast id
+  }
+  else if (sscanf(dev, NVME_CTRLR_PREFIX"%d"NVME_NS_PREFIX"%d%c", 
+       &ctrlid, &nsid, &tmp) == 2) 
+  {
+       if(ctrlid < 0 || nsid < 0) {
+               set_err(EINVAL, "Invalid NVMe controller/namespace number");
+               return false;
+       }
+  }
+  else {
+       set_err(EINVAL, "Invalid NVMe controller/namespace syntax");
+       return false;
+  }
+  
+  // we should always open controller, not namespace device
+  char full_path[64];
+  snprintf(full_path, sizeof(full_path), NVME_CTRLR_PREFIX"%d", ctrlid);
+  
+  int fd; 
+  if ((fd = ::open(full_path, O_RDWR))<0) {
+    set_err(errno);
+    return false;
+  }
+  set_fd(fd);
+  
+  if (!get_nsid()) {
+    set_nsid(nsid);
+  }
+  
+  return true;
+}
+
+bool freebsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
+{
+  // nvme_passthru_cmd pt;
+  struct nvme_pt_command pt;
+  memset(&pt, 0, sizeof(pt));
+
+  pt.cmd.opc = in.opcode;
+  pt.cmd.nsid = in.nsid;
+  pt.buf = in.buffer;
+  pt.len = in.size;
+  pt.cmd.cdw10 = in.cdw10;
+  pt.cmd.cdw11 = in.cdw11;
+  pt.cmd.cdw12 = in.cdw12;
+  pt.cmd.cdw13 = in.cdw13;
+  pt.cmd.cdw14 = in.cdw14;
+  pt.cmd.cdw15 = in.cdw15;
+  pt.is_read = 1; // should we use in.direction()?
+  
+  int status = ioctl(get_fd(), NVME_PASSTHROUGH_CMD, &pt);
+
+  if (status < 0)
+    return set_err(errno, "NVME_PASSTHROUGH_CMD: %s", strerror(errno));
+
+  out.result=pt.cpl.cdw0; // Command specific result (DW0)
+
+  if (nvme_completion_is_error(&pt.cpl))
+    return set_nvme_err(out, nvme_completion_is_error(&pt.cpl));
+
+  return true;
+}
+
 /////////////////////////////////////////////////////////////////////////////
 /// Implement AMCC/3ware RAID support
 
@@ -917,7 +1028,8 @@ bool freebsd_scsi_device::close(){
 freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf,
   const char * dev_name, const char * req_type)
 : smart_device(intf, dev_name, "scsi", req_type),
-  freebsd_smart_device()
+  freebsd_smart_device(),
+  m_camdev(0)
 {
 }
 
@@ -1122,10 +1234,8 @@ freebsd_areca_ata_device::freebsd_areca_ata_device(smart_interface * intf, const
 
 smart_device * freebsd_areca_ata_device::autodetect_open()
 {
-  int is_ata = 1;
-
   // autodetect device type
-  is_ata = arcmsr_get_dev_type();
+  int is_ata = arcmsr_get_dev_type();
   if(is_ata < 0)
   {
     set_err(EIO);
@@ -1359,6 +1469,9 @@ protected:
 #endif
 
   virtual scsi_device * get_scsi_device(const char * name, const char * type);
+  virtual nvme_device * get_nvme_device(const char * name, const char * type,
+    unsigned nsid);
 
   virtual smart_device * autodetect_smart_device(const char * name);
 
@@ -1401,6 +1514,12 @@ scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const
   return new freebsd_scsi_device(this, name, type);
 }
 
+nvme_device * freebsd_smart_interface::get_nvme_device(const char * name, const char * type,
+  unsigned nsid)
+{
+  return new freebsd_nvme_device(this, name, type, nsid);
+}
+
 // we are using CAM subsystem XPT enumerator to found all CAM (scsi/usb/ada/...)
 // devices on system despite of it's names
 //
@@ -1476,11 +1595,12 @@ bool get_dev_names_cam(std::vector<std::string> & names, bool show_all)
     }
 
     for (unsigned i = 0; i < ccb.cdm.num_matches; i++) {
-      struct bus_match_result *bus_result;
       struct device_match_result *dev_result;
       struct periph_match_result *periph_result;
 
       if (ccb.cdm.matches[i].type == DEV_MATCH_BUS) {
+        struct bus_match_result *bus_result;
+
         bus_result = &ccb.cdm.matches[i].result.bus_result;
 
         if (strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */
@@ -1588,19 +1708,19 @@ int get_dev_names_ata(char*** names) {
           n = -1;
           goto end;
         };
-        bytes+=1+strlen(mp[n]);
         n++;
       };
     };
-  };  
+  };
+  if (n <= 0)
+    goto end;
   mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size
-  if (mp == NULL && n > 0 ) { // reallocf never fail for size=0, but may return NULL
+  if (mp == NULL) {
     serrno=errno;
     pout("Out of memory constructing scan device list (on line %d)\n", __LINE__);
     n = -1;
     goto end;
   };
-  bytes += (n)*(sizeof(char*)); // and set allocated byte count
 
 end:
   if (fd>=0)
@@ -1779,13 +1899,13 @@ static int usbdevlist(int busno,unsigned short & vendor_id,
   return false;
 #else // freebsd < 8.0 USB stack, ioctl interface
 
-  int  i, f, a, rc;
+  int  i, a, rc;
   char buf[50];
   int ncont;
 
   for (ncont = 0, i = 0; i < 10; i++) {
     snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
-    f = open(buf, O_RDONLY);
+    int f = open(buf, O_RDONLY);
     if (f >= 0) {
       memset(done, 0, sizeof done);
       for (a = 1; a < USB_MAX_DEVICES; a++) {
@@ -1813,12 +1933,11 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam
   struct cam_device *cam_dev;
   union ccb ccb;
   int bus=-1;
-  int i,c;
-  int len;
+  int i;
   const char * test_name = name;
 
   // if dev_name null, or string length zero
-  if (!name || !(len = strlen(name)))
+  if (!name || !*name)
     return 0;
 
   // Dereference symlinks
@@ -1840,7 +1959,7 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam
     // check ATA/ATAPI devices
     for (i = 0; i < numata; i++) {
       if(!strcmp(atanames[i],test_name)) {
-        for (c = i; c < numata; c++) free(atanames[c]);
+        for (int c = i; c < numata; c++) free(atanames[c]);
         free(atanames);
         return new freebsd_ata_device(this, test_name, "");
       }
@@ -1910,6 +2029,11 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam
   // device is LSI raid supported by mfi driver
   if(!strncmp("/dev/mfid", test_name, strlen("/dev/mfid")))
     set_err(EINVAL, "To monitor disks on LSI RAID load mfip.ko module and run 'smartctl -a /dev/passX' to show SMART information");
+
+  // form /dev/nvme* or nvme*
+  if(!strncmp("/dev/nvme", test_name, strlen("/dev/nvme")))
+    return new freebsd_nvme_device(this, name, "", 0 /* use default nsid */);
+
   // device type unknown
   return 0;
 }
@@ -1917,13 +2041,15 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam
 
 smart_device * freebsd_smart_interface::get_custom_smart_device(const char * name, const char * type)
 {
-  // 3Ware ?
-  static const char * fbsd_dev_twe_ctrl = "/dev/twe";
-  static const char * fbsd_dev_twa_ctrl = "/dev/twa";
-  static const char * fbsd_dev_tws_ctrl = "/dev/tws";
-  int disknum = -1, n1 = -1, n2 = -1, contr = -1;
+  int disknum = -1, n1 = -1, n2 = -1;
 
   if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    // 3Ware ?
+    static const char * fbsd_dev_twe_ctrl = "/dev/twe";
+    static const char * fbsd_dev_twa_ctrl = "/dev/twa";
+    static const char * fbsd_dev_tws_ctrl = "/dev/tws";
+    int contr = -1;
+
     if (n2 != (int)strlen(type)) {
       set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer");
       return 0;
index a22442f49f56f7fb8f588e1c1883aa5cf3e3d6bd..4832154399dad36f6f76a47e2cf74c7974bdd693 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_freebsd.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-8 Eduard Martinescu <smartmontools-support@lists.sourceforge.net>
  *
@@ -82,7 +82,7 @@
 #ifndef OS_FREEBSD_H_
 #define OS_FREEBSD_H_
 
-#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h 3727 2012-12-13 17:23:06Z samm2 $"
+#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h 4120 2015-08-27 16:12:21Z samm2 $"
 
 #define MAX_NUM_DEV 26
 
index 09998335c75aca7f83f4b7d5fcbbbd6800c33269..de39f551bc20a3075e3bebcb52facc838cadbf60 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_generic.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) YEAR YOUR_NAME <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
@@ -83,7 +83,7 @@
 // should have one *_H_CVSID macro appearing below for each file
 // appearing with #include "*.h" above.  Please list these (below) in
 // alphabetic/dictionary order.
-const char * os_XXXX_cpp_cvsid="$Id: os_generic.cpp 3579 2012-07-20 17:50:12Z chrfranke $"
+const char * os_XXXX_cpp_cvsid="$Id: os_generic.cpp 4120 2015-08-27 16:12:21Z samm2 $"
   ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_GENERIC_H_CVSID UTILITY_H_CVSID;
 
 // This is here to prevent compiler warnings for unused arguments of
index 4c0ba6682231b974643e104562dc05f0c675aa63..211ce1ea4bbb0e1fb96462e74e94507060e0030c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_generic.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) YEAR YOUR_NAME <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
@@ -25,7 +25,7 @@
 // In the three following lines, change 'GENERIC' to your OS name
 #ifndef OS_GENERIC_H_
 #define OS_GENERIC_H_
-#define OS_GENERIC_H_CVSID "$Id: os_generic.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define OS_GENERIC_H_CVSID "$Id: os_generic.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
index 4a96a821f02393f153f586e0be3a2aa7c2109bd0..629792f52b272b52aed4940ff9cbbd97151d61d8 100644 (file)
@@ -1,11 +1,11 @@
 /*
  *  os_linux.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2003-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-11 Bruce Allen
  * Copyright (C) 2003-11 Doug Gilbert <dgilbert@interlog.com>
- * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-16 Christian Franke
  *
  * Original AACRaid code:
  *  Copyright (C) 2014    Raghava Aditya <raghava.aditya@pmcs.com>
 #include "dev_ata_cmd_set.h"
 #include "dev_areca.h"
 
+// "include/uapi/linux/nvme_ioctl.h" from Linux kernel sources
+#include "linux_nvme_ioctl.h" // nvme_passthru_cmd, NVME_IOCTL_ADMIN_CMD
+
 #ifndef ENOTSUP
 #define ENOTSUP ENOSYS
 #endif
 
 #define ARGUSED(x) ((void)(x))
 
-const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 3900 2014-05-01 17:08:59Z chrfranke $"
+const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 4295 2016-04-15 20:01:32Z chrfranke $"
   OS_LINUX_H_CVSID;
 extern unsigned char failuretest_permissive;
 
@@ -194,11 +197,11 @@ bool linux_smart_device::close()
 // examples for smartctl
 static const char  smartctl_examples[] =
                  "=================================================== SMARTCTL EXAMPLES =====\n\n"
-                 "  smartctl --all /dev/hda                    (Prints all SMART information)\n\n"
-                 "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
+                 "  smartctl --all /dev/sda                    (Prints all SMART information)\n\n"
+                 "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
                  "                                              (Enables SMART on first disk)\n\n"
-                 "  smartctl --test=long /dev/hda          (Executes extended disk self-test)\n\n"
-                 "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
+                 "  smartctl --test=long /dev/sda          (Executes extended disk self-test)\n\n"
+                 "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
                  "                                      (Prints Self-Test & Attribute errors)\n"
                  "  smartctl --all --device=3ware,2 /dev/sda\n"
                  "  smartctl --all --device=3ware,2 /dev/twe0\n"
@@ -356,7 +359,6 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec
     unsigned char task[sizeof(ide_task_request_t)+512];
     ide_task_request_t *reqtask=(ide_task_request_t *) task;
     task_struct_t      *taskfile=(task_struct_t *) reqtask->io_ports;
-    int retval;
 
     memset(task,      0, sizeof(task));
 
@@ -377,9 +379,9 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec
     // copy user data into the task request structure
     memcpy(task+sizeof(ide_task_request_t), data, 512);
 
-    if ((retval=ioctl(get_fd(), HDIO_DRIVE_TASKFILE, task))) {
-      if (retval==-EINVAL)
-        pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n");
+    if (ioctl(get_fd(), HDIO_DRIVE_TASKFILE, task)) {
+      if (errno==EINVAL)
+        pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASK_IOCTL set\n");
       return -1;
     }
     return 0;
@@ -388,8 +390,6 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec
   // There are two different types of ioctls().  The HDIO_DRIVE_TASK
   // one is this:
   if (command==STATUS_CHECK || command==AUTOSAVE || command==AUTO_OFFLINE){
-    int retval;
-
     // NOT DOCUMENTED in /usr/src/linux/include/linux/hdreg.h. You
     // have to read the IDE driver source code.  Sigh.
     // buff[0]: ATA COMMAND CODE REGISTER
@@ -405,8 +405,8 @@ int linux_ata_device::ata_command_interface(smart_command_set command, int selec
     buff[4]=normal_lo;
     buff[5]=normal_hi;
 
-    if ((retval=ioctl(get_fd(), HDIO_DRIVE_TASK, buff))) {
-      if (retval==-EINVAL) {
+    if (ioctl(get_fd(), HDIO_DRIVE_TASK, buff)) {
+      if (errno==EINVAL) {
         pout("Error SMART Status command via HDIO_DRIVE_TASK failed");
         pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n");
       }
@@ -548,14 +548,14 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
             (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
             int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-                          "data, len=%d%s:\n", (int)iop->dxfer_len,
-                          (trunc ? " [only first 256 bytes shown]" : ""));
+            snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                     "data, len=%d%s:\n", (int)iop->dxfer_len,
+                     (trunc ? " [only first 256 bytes shown]" : ""));
             dStrHex((const char *)iop->dxferp,
                     (trunc ? 256 : iop->dxfer_len) , 1);
         }
         else
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+            snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
         pout("%s", buff);
     }
     memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
@@ -648,7 +648,7 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
             }
         }
         if (report) {
-            if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) {
+            if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status && iop->sensep) {
                 if ((iop->sensep[0] & 0x7f) > 0x71)
                     pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
                          iop->scsi_status, iop->sensep[1] & 0xf,
@@ -696,18 +696,17 @@ static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
         j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
         for (k = 0; k < (int)iop->cmnd_len; ++k)
             j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
-        if ((report > 1) &&
-            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+        if ((report > 1) && (DXFER_TO_DEVICE == iop->dxfer_dir)) {
             int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-                          "data, len=%d%s:\n", (int)iop->dxfer_len,
-                          (trunc ? " [only first 256 bytes shown]" : ""));
+            snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                     "data, len=%d%s:\n", (int)iop->dxfer_len,
+                     (trunc ? " [only first 256 bytes shown]" : ""));
             dStrHex((const char *)iop->dxferp,
                     (trunc ? 256 : iop->dxfer_len) , 1);
         }
         else
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+            snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
         pout("%s", buff);
     }
     switch (iop->dxfer_dir) {
@@ -977,14 +976,14 @@ bool linux_aacraid_device::scsi_pass_through(scsi_cmnd_io *iop)
         (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
         int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 
-        j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-                      "data, len=%d%s:\n", (int)iop->dxfer_len,
-                      (trunc ? " [only first 256 bytes shown]" : ""));
+        snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                 "data, len=%d%s:\n", (int)iop->dxfer_len,
+                 (trunc ? " [only first 256 bytes shown]" : ""));
         dStrHex((const char *)iop->dxferp,
                (trunc ? 256 : iop->dxfer_len) , 1);
     }
     else
-      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+      snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
 
     pout("%s", buff);
   }
@@ -1002,7 +1001,7 @@ bool linux_aacraid_device::scsi_pass_through(scsi_cmnd_io *iop)
     uint8_t aBuff[sizeof(user_aac_srb64) + sizeof(user_aac_reply)] = {0,};
 
     pSrb    = (user_aac_srb64*)aBuff;
-    pReply  = (user_aac_reply*)(aBuff+sizeof(user_aac_srb64));
+    pSrb->count = sizeof(user_aac_srb64) - sizeof(user_sgentry64);
 
  #elif defined(ENVIRONMENT32)
     //Create user 32 bit request
@@ -1010,8 +1009,7 @@ bool linux_aacraid_device::scsi_pass_through(scsi_cmnd_io *iop)
     uint8_t aBuff[sizeof(user_aac_srb32) + sizeof(user_aac_reply)] = {0,};
 
     pSrb    = (user_aac_srb32*)aBuff;
-    pReply  = (user_aac_reply*)(aBuff+sizeof(user_aac_srb32));
-
+    pSrb->count = sizeof(user_aac_srb32) - sizeof(user_sgentry32);
  #endif
 
   pSrb->function = SRB_FUNCTION_EXECUTE_SCSI;
@@ -1048,33 +1046,60 @@ bool linux_aacraid_device::scsi_pass_through(scsi_cmnd_io *iop)
       pSrb->sg64.sg64[0].addr64.hi32 = ((intptr_t)iop->dxferp) >> 32;
 
       pSrb->sg64.sg64[0].length = (uint32_t)iop->dxfer_len;
-      pSrb->count = sizeof(user_aac_srb64) +
-                          (sizeof(user_sgentry64)*(pSrb->sg64.count-1));
+      pSrb->count += pSrb->sg64.count * sizeof(user_sgentry64);
     #elif defined(ENVIRONMENT32)
       pSrb->sg32.count = 1;
       pSrb->sg32.sg32[0].addr32 = (intptr_t)iop->dxferp;
 
       pSrb->sg32.sg32[0].length = (uint32_t)iop->dxfer_len;
-      pSrb->count = sizeof(user_aac_srb32) +
-                          (sizeof(user_sgentry32)*(pSrb->sg32.count-1));
+      pSrb->count += pSrb->sg32.count * sizeof(user_sgentry32);
     #endif
 
   }
 
+  pReply  = (user_aac_reply*)(aBuff+pSrb->count);
+
   memcpy(pSrb->cdb,iop->cmnd,iop->cmnd_len);
 
   int rc = 0;
   errno = 0;
   rc = ioctl(get_fd(),FSACTL_SEND_RAW_SRB,pSrb);
-  if(rc!= 0 || pReply->srb_status != 0x01) {
-    if(pReply->srb_status == 0x08) {
-      return set_err(EIO, "aacraid: Device %d %d does not exist\n" ,aLun,aId );
-    }
-  return set_err((errno ? errno : EIO), "aacraid result: %d.%d = %d/%d",
-                            aLun, aId, errno,
-                            pReply->srb_status);
+
+  if (rc != 0)
+    return set_err(errno, "aacraid send_raw_srb: %d.%d = %s",
+                  aLun, aId, strerror(errno));
+
+/* see kernel aacraid.h and MSDN SCSI_REQUEST_BLOCK documentation */
+#define SRB_STATUS_SUCCESS            0x1
+#define SRB_STATUS_ERROR              0x4
+#define SRB_STATUS_NO_DEVICE         0x08
+#define SRB_STATUS_SELECTION_TIMEOUT 0x0a
+#define SRB_STATUS_AUTOSENSE_VALID   0x80
+
+  iop->scsi_status = pReply->scsi_status;
+
+  if (pReply->srb_status == (SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR)
+      && iop->scsi_status == SCSI_STATUS_CHECK_CONDITION) {
+    memcpy(iop->sensep, pReply->sense_data, pReply->sense_data_size);
+    iop->resp_sense_len = pReply->sense_data_size;
+    return true; /* request completed with sense data */
+  }
+
+  switch (pReply->srb_status & 0x3f) {
+
+    case SRB_STATUS_SUCCESS:
+      return true; /* request completed successfully */
+
+    case SRB_STATUS_NO_DEVICE:
+      return set_err(EIO, "aacraid: Device %d %d does not exist", aLun, aId);
+
+    case SRB_STATUS_SELECTION_TIMEOUT:
+      return set_err(EIO, "aacraid: Device %d %d not responding", aLun, aId);
+
+    default:
+      return set_err(EIO, "aacraid result: %d.%d = 0x%x",
+                    aLun, aId, pReply->srb_status);
   }
-  return true;
 }
 
 
@@ -1087,7 +1112,7 @@ class linux_megaraid_device
 {
 public:
   linux_megaraid_device(smart_interface *intf, const char *name, 
-    unsigned int bus, unsigned int tgt);
+    unsigned int tgt);
 
   virtual ~linux_megaraid_device() throw();
 
@@ -1100,7 +1125,6 @@ public:
 
 private:
   unsigned int m_disknum;
-  unsigned int m_busnum;
   unsigned int m_hba;
   int m_fd;
 
@@ -1113,10 +1137,10 @@ private:
 };
 
 linux_megaraid_device::linux_megaraid_device(smart_interface *intf,
-  const char *dev_name, unsigned int bus, unsigned int tgt)
+  const char *dev_name, unsigned int tgt)
  : smart_device(intf, dev_name, "megaraid", "megaraid"),
    linux_smart_device(O_RDWR | O_NONBLOCK),
-   m_disknum(tgt), m_busnum(bus), m_hba(0),
+   m_disknum(tgt), m_hba(0),
    m_fd(-1), pt_cmd(0)
 {
   set_info().info_name = strprintf("%s [megaraid_disk_%02d]", dev_name, m_disknum);
@@ -1176,7 +1200,7 @@ bool linux_megaraid_device::open()
   int   mjr;
   int report = scsi_debugmode;
 
-  if(sscanf(get_dev_name(),"/dev/bus/%d", &m_hba) == 0) {
+  if (sscanf(get_dev_name(), "/dev/bus/%u", &m_hba) == 0) {
     if (!linux_smart_device::open())
       return false;
     /* Get device HBA */
@@ -1256,14 +1280,14 @@ bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop)
             (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
             int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-                          "data, len=%d%s:\n", (int)iop->dxfer_len,
-                          (trunc ? " [only first 256 bytes shown]" : ""));
+            snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                     "data, len=%d%s:\n", (int)iop->dxfer_len,
+                     (trunc ? " [only first 256 bytes shown]" : ""));
             dStrHex((const char *)iop->dxferp,
                     (trunc ? 256 : iop->dxfer_len) , 1);
         }
         else
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+            snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
         pout("%s", buff);
   }
 
@@ -1299,7 +1323,6 @@ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb,
 {
   struct megasas_pthru_frame   *pthru;
   struct megasas_iocpacket     uio;
-  int rc;
 
   memset(&uio, 0, sizeof(uio));
   pthru = &uio.frame.pthru;
@@ -1341,9 +1364,8 @@ bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb,
     uio.sgl[0].iov_len = dataLen;
   }
 
-  rc = 0;
   errno = 0;
-  rc = ioctl(m_fd, MEGASAS_IOC_FIRMWARE, &uio);
+  int rc = ioctl(m_fd, MEGASAS_IOC_FIRMWARE, &uio);
   if (pthru->cmd_status || rc != 0) {
     if (pthru->cmd_status == 12) {
       return set_err(EIO, "megasas_cmd: Device %d does not exist\n", m_disknum);
@@ -1965,10 +1987,8 @@ linux_areca_ata_device::linux_areca_ata_device(smart_interface * intf, const cha
 
 smart_device * linux_areca_ata_device::autodetect_open()
 {
-  int is_ata = 1;
-
   // autodetect device type
-  is_ata = arcmsr_get_dev_type();
+  int is_ata = arcmsr_get_dev_type();
   if(is_ata < 0)
   {
     set_err(EIO);
@@ -2345,7 +2365,6 @@ int linux_highpoint_device::ata_command_interface(smart_command_set command, int
     unsigned int *hpt_tf = (unsigned int *)task;
     ide_task_request_t *reqtask = (ide_task_request_t *)(&task[4*sizeof(int)]);
     task_struct_t *taskfile = (task_struct_t *)reqtask->io_ports;
-    int retval;
 
     memset(task, 0, sizeof(task));
 
@@ -2370,16 +2389,13 @@ int linux_highpoint_device::ata_command_interface(smart_command_set command, int
 
     memcpy(task+sizeof(ide_task_request_t)+4*sizeof(int), data, 512);
 
-    if ((retval=ioctl(get_fd(), HPTIO_CTL, task))) {
-      if (retval==-EINVAL)
-        pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n");
+    if (ioctl(get_fd(), HPTIO_CTL, task))
       return -1;
-    }
+
     return 0;
   }
 
   if (command==STATUS_CHECK){
-    int retval;
     unsigned const char normal_lo=0x4f, normal_hi=0xc2;
     unsigned const char failed_lo=0xf4, failed_hi=0x2c;
     buff[4]=normal_lo;
@@ -2387,15 +2403,8 @@ int linux_highpoint_device::ata_command_interface(smart_command_set command, int
 
     hpt[2] = HDIO_DRIVE_TASK;
 
-    if ((retval=ioctl(get_fd(), HPTIO_CTL, hpt_buff))) {
-      if (retval==-EINVAL) {
-        pout("Error SMART Status command via HDIO_DRIVE_TASK failed");
-        pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n");
-      }
-      else
-        syserror("Error SMART Status command failed");
+    if (ioctl(get_fd(), HPTIO_CTL, hpt_buff))
       return -1;
-    }
 
     if (buff[4]==normal_lo && buff[5]==normal_hi)
       return 0;
@@ -2570,6 +2579,76 @@ smart_device * linux_scsi_device::autodetect_open()
   return this;
 }
 
+/////////////////////////////////////////////////////////////////////////////
+/// NVMe support
+
+class linux_nvme_device
+: public /*implements*/ nvme_device,
+  public /*extends*/ linux_smart_device
+{
+public:
+  linux_nvme_device(smart_interface * intf, const char * dev_name,
+    const char * req_type, unsigned nsid);
+
+  virtual bool open();
+
+  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
+};
+
+linux_nvme_device::linux_nvme_device(smart_interface * intf, const char * dev_name,
+  const char * req_type, unsigned nsid)
+: smart_device(intf, dev_name, "nvme", req_type),
+  nvme_device(nsid),
+  linux_smart_device(O_RDONLY | O_NONBLOCK)
+{
+}
+
+bool linux_nvme_device::open()
+{
+  if (!linux_smart_device::open())
+    return false;
+
+  if (!get_nsid()) {
+    // Use actual NSID (/dev/nvmeXnN) if available,
+    // else use broadcast namespace (/dev/nvmeX)
+    int nsid = ioctl(get_fd(), NVME_IOCTL_ID, (void*)0);
+    set_nsid(nsid);
+  }
+
+  return true;
+}
+
+bool linux_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
+{
+  nvme_passthru_cmd pt;
+  memset(&pt, 0, sizeof(pt));
+
+  pt.opcode = in.opcode;
+  pt.nsid = in.nsid;
+  pt.addr = (uint64_t)in.buffer;
+  pt.data_len = in.size;
+  pt.cdw10 = in.cdw10;
+  pt.cdw11 = in.cdw11;
+  pt.cdw12 = in.cdw12;
+  pt.cdw13 = in.cdw13;
+  pt.cdw14 = in.cdw14;
+  pt.cdw15 = in.cdw15;
+  // Kernel default for NVMe admin commands is 60 seconds
+  // pt.timeout_ms = 60 * 1000;
+
+  int status = ioctl(get_fd(), NVME_IOCTL_ADMIN_CMD, &pt);
+
+  if (status < 0)
+    return set_err(errno, "NVME_IOCTL_ADMIN_CMD: %s", strerror(errno));
+
+  if (status > 0)
+    return set_nvme_err(out, status);
+
+  out.result = pt.result;
+  return true;
+}
+
+
 //////////////////////////////////////////////////////////////////////
 // USB bridge ID detection
 
@@ -2642,6 +2721,9 @@ protected:
 
   virtual scsi_device * get_scsi_device(const char * name, const char * type);
 
+  virtual nvme_device * get_nvme_device(const char * name, const char * type,
+    unsigned nsid);
+
   virtual smart_device * autodetect_smart_device(const char * name);
 
   virtual smart_device * get_custom_smart_device(const char * name, const char * type);
@@ -2650,7 +2732,9 @@ protected:
 
 private:
   bool get_dev_list(smart_device_list & devlist, const char * pattern,
-    bool scan_ata, bool scan_scsi, const char * req_type, bool autodetect);
+    bool scan_ata, bool scan_scsi, bool scan_nvme,
+    const char * req_type, bool autodetect);
+
   bool get_dev_megasas(smart_device_list & devlist);
   smart_device * missing_option(const char * opt);
   int megasas_dcmd_cmd(int bus_no, uint32_t opcode, void *buf,
@@ -2675,10 +2759,9 @@ std::string linux_smart_interface::get_app_examples(const char * appname)
 }
 
 // we are going to take advantage of the fact that Linux's devfs will only
-// have device entries for devices that exist.  So if we get the equivalent of
-// ls /dev/hd[a-t], we have all the ATA devices on the system
+// have device entries for devices that exist.
 bool linux_smart_interface::get_dev_list(smart_device_list & devlist,
-  const char * pattern, bool scan_ata, bool scan_scsi,
+  const char * pattern, bool scan_ata, bool scan_scsi, bool scan_nvme,
   const char * req_type, bool autodetect)
 {
   // Use glob to look for any directory entries matching the pattern
@@ -2761,6 +2844,8 @@ bool linux_smart_interface::get_dev_list(smart_device_list & devlist,
         dev = autodetect_smart_device(name);
       else if (is_scsi)
         dev = new linux_scsi_device(this, name, req_type, true /*scanning*/);
+      else if (scan_nvme)
+        dev = new linux_nvme_device(this, name, req_type, 0 /* use default nsid */);
       else
         dev = new linux_ata_device(this, name, req_type);
       if (dev) // autodetect_smart_device() may return nullptr.
@@ -2799,20 +2884,19 @@ bool linux_smart_interface::get_dev_megasas(smart_device_list & devlist)
     return false;
 
   // getting bus numbers with megasas devices
-  struct dirent *ep;
-  unsigned int host_no = 0;
-  char sysfsdir[256];
-
-  /* we are using sysfs to get list of all scsi hosts */
+  // we are using sysfs to get list of all scsi hosts
   DIR * dp = opendir ("/sys/class/scsi_host/");
   if (dp != NULL)
   {
+    struct dirent *ep;
     while ((ep = readdir (dp)) != NULL) {
-      if (!sscanf(ep->d_name, "host%d", &host_no)) 
+      unsigned int host_no = 0;
+      if (!sscanf(ep->d_name, "host%u", &host_no))
         continue;
       /* proc_name should be megaraid_sas */
+      char sysfsdir[256];
       snprintf(sysfsdir, sizeof(sysfsdir) - 1,
-        "/sys/class/scsi_host/host%d/proc_name", host_no);
+        "/sys/class/scsi_host/host%u/proc_name", host_no);
       if((fp = fopen(sysfsdir, "r")) == NULL)
         continue;
       if(fgets(line, sizeof(line), fp) != NULL && !strncmp(line,"megaraid_sas",12)) {
@@ -2842,19 +2926,32 @@ bool linux_smart_interface::scan_smart_devices(smart_device_list & devlist,
   bool scan_ata  = (!*type || !strcmp(type, "ata" ));
   // "sat" detection will be later handled in linux_scsi_device::autodetect_open()
   bool scan_scsi = (!*type || !strcmp(type, "scsi") || !strcmp(type, "sat"));
-  if (!(scan_ata || scan_scsi))
-    return true;
+
+#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
+  bool scan_nvme = (!*type || !strcmp(type, "nvme"));
+#else
+  bool scan_nvme = (          !strcmp(type, "nvme"));
+#endif
+
+  if (!(scan_ata || scan_scsi || scan_nvme)) {
+    set_err(EINVAL, "Invalid type '%s', valid arguments are: ata, scsi, sat, nvme", type);
+    return false;
+  }
 
   if (scan_ata)
-    get_dev_list(devlist, "/dev/hd[a-t]", true, false, type, false);
+    get_dev_list(devlist, "/dev/hd[a-t]", true, false, false, type, false);
   if (scan_scsi) {
     bool autodetect = !*type; // Try USB autodetection if no type specifed
-    get_dev_list(devlist, "/dev/sd[a-z]", false, true, type, autodetect);
+    get_dev_list(devlist, "/dev/sd[a-z]", false, true, false, type, autodetect);
     // Support up to 104 devices
-    get_dev_list(devlist, "/dev/sd[a-c][a-z]", false, true, type, autodetect);
+    get_dev_list(devlist, "/dev/sd[a-c][a-z]", false, true, false, type, autodetect);
     // get device list from the megaraid device
     get_dev_megasas(devlist);
   }
+  if (scan_nvme) {
+    get_dev_list(devlist, "/dev/nvme[0-9]", false, false, true, type, false);
+    get_dev_list(devlist, "/dev/nvme[1-9][0-9]", false, false, true, type, false);
+  }
 
   // if we found traditional links, we are done
   if (devlist.size() > 0)
@@ -2862,7 +2959,7 @@ bool linux_smart_interface::scan_smart_devices(smart_device_list & devlist,
 
   // else look for devfs entries without traditional links
   // TODO: Add udev support
-  return get_dev_list(devlist, "/dev/discs/disc*", scan_ata, scan_scsi, type, false);
+  return get_dev_list(devlist, "/dev/discs/disc*", scan_ata, scan_scsi, false, type, false);
 }
 
 ata_device * linux_smart_interface::get_ata_device(const char * name, const char * type)
@@ -2875,6 +2972,12 @@ scsi_device * linux_smart_interface::get_scsi_device(const char * name, const ch
   return new linux_scsi_device(this, name, type);
 }
 
+nvme_device * linux_smart_interface::get_nvme_device(const char * name, const char * type,
+  unsigned nsid)
+{
+  return new linux_nvme_device(this, name, type, nsid);
+}
+
 smart_device * linux_smart_interface::missing_option(const char * opt)
 {
   set_err(EINVAL, "requires option '%s'", opt);
@@ -2922,6 +3025,7 @@ linux_smart_interface::megasas_dcmd_cmd(int bus_no, uint32_t opcode, void *buf,
   }
 
   int r = ioctl(fd, MEGASAS_IOC_FIRMWARE, &ioc);
+  ::close(fd);
   if (r < 0) {
     return (r);
   }
@@ -2946,7 +3050,7 @@ linux_smart_interface::megasas_pd_add_list(int bus_no, smart_device_list & devli
   */
   megasas_pd_list * list = 0;
   for (unsigned list_size = 1024; ; ) {
-    list = (megasas_pd_list *)realloc(list, list_size);
+    list = reinterpret_cast<megasas_pd_list *>(realloc(list, list_size));
     if (!list)
       throw std::bad_alloc();
     bzero(list, list_size);
@@ -2967,7 +3071,7 @@ linux_smart_interface::megasas_pd_add_list(int bus_no, smart_device_list & devli
       continue; /* non disk device found */
     char line[128];
     snprintf(line, sizeof(line) - 1, "/dev/bus/%d", bus_no);
-    smart_device * dev = new linux_megaraid_device(this, line, 0, list->addr[i].device_id);
+    smart_device * dev = new linux_megaraid_device(this, line, list->addr[i].device_id);
     devlist.push_back(dev);
   }
   free(list);
@@ -3059,6 +3163,10 @@ smart_device * linux_smart_interface::autodetect_smart_device(const char * name)
   if (str_starts_with(test_name, "nos"))
     return new linux_scsi_device(this, name, "");
 
+  // form /dev/nvme* or nvme*
+  if (str_starts_with(test_name, "nvme"))
+    return new linux_nvme_device(this, name, "", 0 /* use default nsid */);
+
   // form /dev/tw[ael]* or tw[ael]*
   if (str_starts_with(test_name, "tw") && strchr("ael", test_name[2]))
     return missing_option("-d 3ware,N");
@@ -3156,16 +3264,15 @@ smart_device * linux_smart_interface::get_custom_smart_device(const char * name,
 
   // MegaRAID ?
   if (sscanf(type, "megaraid,%d", &disknum) == 1) {
-    return new linux_megaraid_device(this, name, 0, disknum);
+    return new linux_megaraid_device(this, name, disknum);
   }
 
   //aacraid?
-  unsigned int device;
-  unsigned int host;
-  if(sscanf(type, "aacraid,%d,%d,%d", &host, &channel, &device)==3) {
+  unsigned host, chan, device;
+  if (sscanf(type, "aacraid,%u,%u,%u", &host, &chan, &device) == 3) {
     //return new linux_aacraid_device(this,name,channel,device);
     return get_sat_device("sat,auto",
-      new linux_aacraid_device(this, name, host, channel, device));
+      new linux_aacraid_device(this, name, host, chan, device));
 
   }
 
index 3cb2f3362e7bed80a64f35cd963faa3a0361b834..104d17619570f349122e93ec88112162a646f762 100644 (file)
@@ -1,7 +1,7 @@
 /* 
  *  os_linux.h
  * 
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  *
@@ -38,7 +38,7 @@
 #ifndef OS_LINUX_H_
 #define OS_LINUX_H_
 
-#define OS_LINUX_H_CVSID "$Id: os_linux.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define OS_LINUX_H_CVSID "$Id: os_linux.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 /* 
    The following definitions/macros/prototypes are used for three
index dce2d9d5a6a6a69f70ed478e9abbc74da4feb2e4..ebcd013ee2739b882489aa0088ff64180c86031f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_netbsd.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-8 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
  *
 #include <errno.h>
 #include <unistd.h>
 
-const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 3806 2013-03-29 20:17:03Z chrfranke $"
+const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 4320 2016-05-10 13:39:19Z chrfranke $"
   OS_NETBSD_H_CVSID;
 
-/* global variable holding byte count of allocated memory */
-extern long long bytes;
-
 enum warnings {
-  BAD_SMART, NO_3WARE, NO_ARECA, MAX_MSG
+  BAD_SMART, MAX_MSG
 };
 
 /* Utility function for printing warnings */
@@ -57,7 +54,7 @@ printwarning(int msgNo, const char *extra)
   return;
 }
 
-static const char *net_dev_prefix = "/dev/";
+static const char *net_dev_prefix = "/dev/r";
 static const char *net_dev_ata_disk = "wd";
 static const char *net_dev_scsi_disk = "sd";
 static const char *net_dev_scsi_tape = "enrst";
@@ -128,12 +125,17 @@ get_dev_names(char ***names, const char *prefix)
       return -1;
     }
     sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
-    bytes += strlen(mp[n]) + 1;
     n++;
   }
 
-  mp = (char **)realloc(mp, n * (sizeof(char *)));
-  bytes += (n) * (sizeof(char *));
+  char ** tmp = (char **)realloc(mp, n * (sizeof(char *)));
+  if (NULL == tmp) {
+    pout("Out of memory constructing scan device list\n");
+    free(mp);
+    return -1;
+  }
+  else
+    mp = tmp;
   *names = mp;
   return n;
 }
@@ -320,7 +322,8 @@ ata_command_interface(int fd, smart_command_set command, int select, char *data)
     return 0;
   }
 
-  if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+  retval = ioctl(fd, ATAIOCCOMMAND, &req);
+  if (retval < 0) {
     perror("Failed command");
     return -1;
   }
index 6b8c758a231f2021a3607a53ff2756b012d75e3b..7bf7b762843c857a033181ad16759581b18b335d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_netbsd.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-8 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
  *
@@ -24,7 +24,7 @@
 #ifndef OS_NETBSD_H_
 #define OS_NETBSD_H_
 
-#define OS_NETBSD_H_CVSID "$Id: os_netbsd.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define OS_NETBSD_H_CVSID "$Id: os_netbsd.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 #include <sys/device.h>
 #include <sys/param.h>
index 896b88d11be42afde167dbe1c3f795ad113e415e..1ddef187571ae817d3b9986de4e4a722dee5026b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_openbsd.c
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-10 David Snyder <smartmontools-support@lists.sourceforge.net>
  *
 
 #include <errno.h>
 
-const char * os_openbsd_cpp_cvsid = "$Id: os_openbsd.cpp 3727 2012-12-13 17:23:06Z samm2 $"
+const char * os_openbsd_cpp_cvsid = "$Id: os_openbsd.cpp 4321 2016-05-10 13:43:10Z chrfranke $"
   OS_OPENBSD_H_CVSID;
 
-/* global variable holding byte count of allocated memory */
-extern long long bytes;
-
 enum warnings {
-  BAD_SMART, NO_3WARE, NO_ARECA, MAX_MSG
+  BAD_SMART, MAX_MSG
 };
 
 /* Utility function for printing warnings */
@@ -132,12 +129,17 @@ get_dev_names(char ***names, const char *prefix)
       return -1;
     }
     sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
-    bytes += strlen(mp[n]) + 1;
     n++;
   }
 
-  mp = (char **)realloc(mp, n * (sizeof(char *)));
-  bytes += (n) * (sizeof(char *));
+  char ** tmp = (char **)realloc(mp, n * (sizeof(char *)));
+  if (NULL == tmp) {
+    pout("Out of memory constructing scan device list\n");
+    free(mp);
+    return -1;
+  }
+  else
+    mp = tmp;
   *names = mp;
   return n;
 }
@@ -316,7 +318,8 @@ ata_command_interface(int fd, smart_command_set command, int select, char *data)
 
     unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
 
-    if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+    retval = ioctl(fd, ATAIOCCOMMAND, &req);
+    if (retval < 0) {
       perror("Failed command");
       return -1;
     }
index b686c781188f3a7a1faf360df6fc3e351723ee05..358627ffaa0be2c9ef66a52f82b3d72eab856ccf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_openbsd.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-8 David Snyder <smartmontools-support@lists.sourceforge.net>
  *
@@ -26,7 +26,7 @@
 #ifndef OS_OPENBSD_H_
 #define OS_OPENBSD_H_
 
-#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 /* from NetBSD: atareg.h,v 1.17, by Manuel Bouyer */
 /* Actually fits _perfectly_ into OBSDs wdcreg.h, but... */
index 7ad7f190e086cc942177ce19ea0f16329b494b18..2846f4a054f850fa940f17da07930cfb239f7d36 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_os2.c
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-8 Yuri Dario <smartmontools-support@lists.sourceforge.net>
  *
@@ -31,7 +31,7 @@
 #include "os_os2.h"
 
 // Needed by '-V' option (CVS versioning) of smartd/smartctl
-const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 3806 2013-03-29 20:17:03Z chrfranke $" \
+const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 4120 2015-08-27 16:12:21Z samm2 $" \
 ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 // global handle to device driver
index 3d8cb860c82db66b44f7e2f09b3fd524d5daf2d3..6a7b1ff78ea7e8d10da1d002319eaa547aa9a19f 100644 (file)
--- a/os_os2.h
+++ b/os_os2.h
@@ -1,7 +1,7 @@
 /*
  * os_os2.c
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-8 Yuri Dario <smartmontools-support@lists.sourceforge.net>
  *
@@ -18,7 +18,7 @@
 #ifndef OS_OS2_H_
 #define OS_OS2_H_
 
-#define OS_XXXX_H_CVSID "$Id: os_os2.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define OS_XXXX_H_CVSID "$Id: os_os2.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
index 3b12cd42a5b4b0524e553dd0664a6a8bad427f5a..f6d4697f2089e9c0631d654356231851a781e8a4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_generic.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) Joerg Hering       <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
@@ -23,7 +23,7 @@
  */
 #ifndef OS_QNXNTO_H_
 #define OS_QNXNTO_H_
-#define OS_QNXNTO_H_CVSID "$Id: os_qnxnto.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define OS_QNXNTO_H_CVSID "$Id: os_qnxnto.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
index 793aec7fe52f7706e34880f97d49259030e6de75..e70afe784ba337baaf62c363e93d7643ac57a3a0 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * os_solaris.c
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2003-8 SAWADA Keiji <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2003-8 Casper Dik <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-08 SAWADA Keiji
+ * Copyright (C) 2003-15 Casper Dik
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
 #define ARGUSED(x) ((void)(x))
 
-extern long long bytes;
-
-static const char *filenameandversion="$Id: os_solaris.cpp 3806 2013-03-29 20:17:03Z chrfranke $";
-
-const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp 3806 2013-03-29 20:17:03Z chrfranke $" \
+const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp 4253 2016-03-26 19:47:47Z chrfranke $" \
 ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_SOLARIS_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 // The printwarning() function warns about unimplemented functions
@@ -99,6 +95,7 @@ void print_smartctl_examples(){
 static const char *uscsidrvrs[] = {
         "sd",
         "ssd",
+        "disk",       // SATA devices
         "st"
 };
 
@@ -166,9 +163,8 @@ addpath(const char *path, struct pathlist *res)
                 res->names = static_cast<char**>(realloc(res->names, res->maxnames * sizeof (char *)));
                 if (res->names == NULL)
                         return -1;
-                bytes += 16*sizeof(char *);
         }
-        if (!(res->names[res->nnames-1] = CustomStrDup((char *)path, 1, __LINE__, filenameandversion)))
+        if (!(res->names[res->nnames-1] = strdup(path)))
                 return -1;
         return 0;
 }
@@ -248,7 +244,6 @@ int make_device_names (char*** devlist, const char* name) {
 
        // shrink array to min possible size
        res.names = static_cast<char**>(realloc(res.names, res.nnames * sizeof (char *)));
-       bytes -= sizeof(char *)*(res.maxnames-res.nnames);
 
        // pass list back
        *devlist = res.names;
@@ -271,7 +266,7 @@ int deviceclose(int fd){
     return close(fd);
 }
 
-#if defined(__sparc)
+#if defined(WITH_SOLARIS_SPARC_ATA)
 // swap each 2-byte pairs in a sector
 static void swap_sector(void *p)
 {
@@ -286,7 +281,7 @@ static void swap_sector(void *p)
 
 // Interface to ATA devices.  See os_linux.c
 int ata_command_interface(int fd, smart_command_set command, int select, char *data){
-#if defined(__sparc)
+#if defined(WITH_SOLARIS_SPARC_ATA)
     int err;
  
     switch (command){
@@ -328,7 +323,7 @@ int ata_command_interface(int fd, smart_command_set command, int select, char *d
        EXIT(1);
        break;
     }
-#else /* __sparc */
+#else /* WITH_SOLARIS_SPARC_ATA */
     ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
 
     /* Above smart_* routines uses undocumented ioctls of "dada"
@@ -395,7 +390,7 @@ int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
   default:
     return -EINVAL;
   }
-  uscsi.uscsi_flags |= (USCSI_ISOLATE | USCSI_RQENABLE);
+  uscsi.uscsi_flags |= (USCSI_ISOLATE | USCSI_RQENABLE | USCSI_SILENT);
 
   if (ioctl(fd, USCSICMD, &uscsi)) {
     int err = errno;
index 70dc3ee87471a10ab6054452c780b45e2bd0dbb3..3d8d1bbe5e82abc973e1a69237d3df88ae0612ee 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_solaris.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-8 SAWADA Keiji <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2003-8 Casper Dik <smartmontools-support@lists.sourceforge.net>
@@ -25,7 +25,7 @@
 #ifndef OS_SOLARIS_H_
 #define OS_SOLARIS_H_
 
-#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
diff --git a/os_solaris_ata.s b/os_solaris_ata.s
deleted file mode 100644 (file)
index fc502b9..0000000
+++ /dev/null
@@ -1,697 +0,0 @@
-! 
-!   os_solaris_ata.s
-! 
-!   Home page of code is: http://smartmontools.sourceforge.net
-! 
-!   Copyright (C) 2003-8 SAWADA Keiji <smartmontools-support@lists.sourceforge.net>
-! 
-!   This program is free software; you can redistribute it and/or modify
-!   it under the terms of the GNU General Public License as published by
-!   the Free Software Foundation; either version 2 of the License, or
-!   (at your option) any later version.
-! 
-!   This program is distributed in the hope that it will be useful, but
-!   WITHOUT ANY WARRANTY; without even the implied warranty of
-!   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-!   General Public License for more details.
-! 
-!   You should have received a copy of the GNU General Public License
-!   along with this program; if not, write to the Free Software Foundation,
-!   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-! 
-! 
-!        --------------------------------------------------------
-!        direct access routines to ATA device under Solaris/SPARC
-!        --------------------------------------------------------
-! 
-! Information
-! -----------
-! 
-! In Solaris, programmer can pass SCSI command to target device directly
-! by using USCSI ioctl or using "scg" generic SCSI driver.  But, such
-! method does not exist for ATA devices.
-! 
-! However, I can access Solaris kernel source because I am subscriber of
-! Source Foundation Program of Solaris.  So, I can find method of
-! accessing ATA device directly.  The method is to pack command in
-! undocumented structure and issue ioctl that appears only in kernel
-! source.  Yes, that is the same way in using USCSI interface.
-! 
-! But, I met difficulty in disclosing this technique.  I have signed NDA
-! with Sun that inhibits me not to violate their intellectual property.
-! 
-! Fortunately, Sun allows licensees to publish "Interfaces" if:
-! 
-! (1) he/she treats Solaris code as confidential
-! 
-! (2) and he/she doesn't incorporate Sun's code into his/her code
-! 
-! (3) and disclose enough information to use "Interface" to everyone.
-! 
-! So, I publish that technique in assembly code or object code because:
-! 
-! (1) I believe Sun's intellectural property is not invaded because I
-!     didn't reveal any struct member and ioctl to non-licensee.
-! 
-! (2) no piece of kernel source is included in this code.
-! 
-! (3) And finally, I publish enough information below in order to use
-!     this code.
-! 
-! For last reason, please don't remove "Calling Interface" section from
-! distribution.
-! 
-! 
-! Calling Interface
-! -----------------
-! 
-! Name of function/macro presents corresponding S.M.A.R.T. command.
-! 
-! Parameters are described below.
-! 
-! int fd
-! 
-!     File descriptor of ATA device.  Device would be
-!     /dev/rdsk/cXtXdXsX.
-! 
-!     Device should be raw device serviced by "dada" driver.  ATAPI
-!     CD-ROM/R/RW, DVD-ROM, and so on are not allowed because they are
-!     serviced by "sd" driver.  On x86 Solaris, "cmdk" driver services
-!     them, this routines doesn't work.
-! 
-! int s
-!     Select sector for service.  For example, this indicates log sector
-!     number for smart_read_log() function.  Probably you need to read
-!     ATA specification for this parameter.
-! 
-! void *data
-!     Data going to be read/written.  It don't have to be word aligned,
-!     But data shall points valid user memory space.
-! 
-! This is very tiny routines, but if you feel this insufficient, please
-! let me know.
-! 
-!                                      ksw / SAWADA Keiji
-!                                      <card_captor@users.sourceforge.net>
-       .file   "solaris-ata-in.c"
-       .section        ".rodata"
-       .align 8
-.LLC0:
-       .asciz  "$Id: os_solaris_ata.s 3728 2012-12-13 17:57:50Z chrfranke $"
-       .global os_solaris_ata_s_cvsid
-       .section        ".data"
-       .align 4
-       .type   os_solaris_ata_s_cvsid, #object
-       .size   os_solaris_ata_s_cvsid, 4
-os_solaris_ata_s_cvsid:
-       .long   .LLC0
-       .section        ".text"
-       .align 4
-       .type   ata_cmd, #function
-       .proc   04
-ata_cmd:
-       !#PROLOGUE# 0
-       save    %sp, -184, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       st      %i2, [%fp+76]
-       st      %i3, [%fp+80]
-       st      %i4, [%fp+84]
-       st      %i5, [%fp+88]
-       ld      [%fp+92], %g1
-       st      %g1, [%fp-76]
-       ld      [%fp-76], %g1
-       and     %g1, 3, %g1
-       cmp     %g1, 0
-       be      .LL2
-       nop
-       mov     -2, %g1
-       st      %g1, [%fp-80]
-       b       .LL1
-        nop
-.LL2:
-       add     %fp, -56, %g1
-       mov     %g1, %o0
-       mov     0, %o1
-       mov     36, %o2
-       call    memset, 0
-        nop
-       add     %fp, -72, %g1
-       mov     %g1, %o0
-       mov     0, %o1
-       mov     16, %o2
-       call    memset, 0
-        nop
-       ld      [%fp+72], %g1
-       stb     %g1, [%fp-72]
-       mov     1, %g1
-       stb     %g1, [%fp-71]
-       mov     1, %g1
-       stb     %g1, [%fp-70]
-       ld      [%fp+76], %g1
-       stb     %g1, [%fp-69]
-       ld      [%fp+84], %g1
-       sll     %g1, 9, %g1
-       st      %g1, [%fp-68]
-       ld      [%fp+80], %g1
-       st      %g1, [%fp-60]
-       mov     10, %g1
-       sth     %g1, [%fp-52]
-       ld      [%fp+88], %g1
-       cmp     %g1, 0
-       be      .LL3
-       nop
-       mov     14, %g1
-       st      %g1, [%fp-84]
-       b       .LL4
-        nop
-.LL3:
-       mov     6, %g1
-       st      %g1, [%fp-84]
-.LL4:
-       ld      [%fp-84], %g1
-       st      %g1, [%fp-48]
-       ld      [%fp+88], %g1
-       sll     %g1, 9, %g1
-       st      %g1, [%fp-44]
-       ld      [%fp+88], %g1
-       sll     %g1, 9, %g1
-       st      %g1, [%fp-40]
-       ld      [%fp+88], %g1
-       cmp     %g1, 0
-       be      .LL5
-       nop
-       ld      [%fp+92], %g1
-       st      %g1, [%fp-88]
-       b       .LL6
-        nop
-.LL5:
-       st      %g0, [%fp-88]
-.LL6:
-       ld      [%fp-88], %g1
-       st      %g1, [%fp-36]
-       add     %fp, -72, %g1
-       st      %g1, [%fp-32]
-       add     %fp, -56, %g1
-       ld      [%fp+68], %o0
-       mov     1481, %o1
-       mov     %g1, %o2
-       call    ioctl, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-80]
-.LL1:
-       ld      [%fp-80], %i0
-       ret
-       restore
-       .size   ata_cmd, .-ata_cmd
-       .align 4
-       .global ata_identify
-       .type   ata_identify, #function
-       .proc   04
-ata_identify:
-       !#PROLOGUE# 0
-       save    %sp, -648, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       add     %fp, -536, %g1
-       st      %g1, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     236, %o1
-       mov     0, %o2
-       mov     0, %o3
-       mov     1, %o4
-       mov     1, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       add     %fp, -536, %g1
-       ld      [%fp+72], %o0
-       mov     %g1, %o1
-       mov     512, %o2
-       call    memcpy, 0
-        nop
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL8
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-540]
-       b       .LL9
-        nop
-.LL8:
-       st      %g0, [%fp-540]
-.LL9:
-       ld      [%fp-540], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   ata_identify, .-ata_identify
-       .align 4
-       .global ata_pidentify
-       .type   ata_pidentify, #function
-       .proc   04
-ata_pidentify:
-       !#PROLOGUE# 0
-       save    %sp, -648, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       add     %fp, -536, %g1
-       st      %g1, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     161, %o1
-       mov     0, %o2
-       mov     0, %o3
-       mov     1, %o4
-       mov     1, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       add     %fp, -536, %g1
-       ld      [%fp+72], %o0
-       mov     %g1, %o1
-       mov     512, %o2
-       call    memcpy, 0
-        nop
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL11
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-540]
-       b       .LL12
-        nop
-.LL11:
-       st      %g0, [%fp-540]
-.LL12:
-       ld      [%fp-540], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   ata_pidentify, .-ata_pidentify
-       .align 4
-       .global smart_read_data
-       .type   smart_read_data, #function
-       .proc   04
-smart_read_data:
-       !#PROLOGUE# 0
-       save    %sp, -648, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       add     %fp, -536, %g1
-       st      %g1, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     208, %o2
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %o3
-       mov     0, %o4
-       mov     1, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       add     %fp, -536, %g1
-       ld      [%fp+72], %o0
-       mov     %g1, %o1
-       mov     512, %o2
-       call    memcpy, 0
-        nop
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL14
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-540]
-       b       .LL15
-        nop
-.LL14:
-       st      %g0, [%fp-540]
-.LL15:
-       ld      [%fp-540], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_read_data, .-smart_read_data
-       .align 4
-       .global smart_read_thresholds
-       .type   smart_read_thresholds, #function
-       .proc   04
-smart_read_thresholds:
-       !#PROLOGUE# 0
-       save    %sp, -648, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       add     %fp, -536, %g1
-       st      %g1, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     209, %o2
-       sethi   %hi(12733440), %g1
-       or      %g1, 769, %o3
-       mov     1, %o4
-       mov     1, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       add     %fp, -536, %g1
-       ld      [%fp+72], %o0
-       mov     %g1, %o1
-       mov     512, %o2
-       call    memcpy, 0
-        nop
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL17
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-540]
-       b       .LL18
-        nop
-.LL17:
-       st      %g0, [%fp-540]
-.LL18:
-       ld      [%fp-540], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_read_thresholds, .-smart_read_thresholds
-       .align 4
-       .global smart_auto_save
-       .type   smart_auto_save, #function
-       .proc   04
-smart_auto_save:
-       !#PROLOGUE# 0
-       save    %sp, -128, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       st      %g0, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     210, %o2
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %o3
-       ld      [%fp+72], %o4
-       mov     0, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL20
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-24]
-       b       .LL21
-        nop
-.LL20:
-       st      %g0, [%fp-24]
-.LL21:
-       ld      [%fp-24], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_auto_save, .-smart_auto_save
-       .align 4
-       .global smart_immediate_offline
-       .type   smart_immediate_offline, #function
-       .proc   04
-smart_immediate_offline:
-       !#PROLOGUE# 0
-       save    %sp, -128, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       ld      [%fp+72], %g1
-       and     %g1, 255, %o5
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %g1
-       or      %o5, %g1, %g1
-       st      %g0, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     212, %o2
-       mov     %g1, %o3
-       mov     0, %o4
-       mov     0, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL23
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-24]
-       b       .LL24
-        nop
-.LL23:
-       st      %g0, [%fp-24]
-.LL24:
-       ld      [%fp-24], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_immediate_offline, .-smart_immediate_offline
-       .align 4
-       .global smart_read_log
-       .type   smart_read_log, #function
-       .proc   04
-smart_read_log:
-       !#PROLOGUE# 0
-       save    %sp, -128, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       st      %i2, [%fp+76]
-       st      %i3, [%fp+80]
-       ld      [%fp+72], %g1
-       and     %g1, 255, %o5
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %g1
-       or      %o5, %g1, %o5
-       ld      [%fp+80], %g1
-       st      %g1, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     213, %o2
-       mov     %o5, %o3
-       ld      [%fp+76], %o4
-       ld      [%fp+76], %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL26
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-24]
-       b       .LL27
-        nop
-.LL26:
-       st      %g0, [%fp-24]
-.LL27:
-       ld      [%fp-24], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_read_log, .-smart_read_log
-       .align 4
-       .global smart_enable
-       .type   smart_enable, #function
-       .proc   04
-smart_enable:
-       !#PROLOGUE# 0
-       save    %sp, -128, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %g0, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     216, %o2
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %o3
-       mov     0, %o4
-       mov     0, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL29
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-24]
-       b       .LL30
-        nop
-.LL29:
-       st      %g0, [%fp-24]
-.LL30:
-       ld      [%fp-24], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_enable, .-smart_enable
-       .align 4
-       .global smart_disable
-       .type   smart_disable, #function
-       .proc   04
-smart_disable:
-       !#PROLOGUE# 0
-       save    %sp, -128, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %g0, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     217, %o2
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %o3
-       mov     0, %o4
-       mov     0, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL32
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-24]
-       b       .LL33
-        nop
-.LL32:
-       st      %g0, [%fp-24]
-.LL33:
-       ld      [%fp-24], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_disable, .-smart_disable
-       .align 4
-       .global smart_status
-       .type   smart_status, #function
-       .proc   04
-smart_status:
-       !#PROLOGUE# 0
-       save    %sp, -128, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %g0, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     218, %o2
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %o3
-       mov     0, %o4
-       mov     0, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL35
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-24]
-       b       .LL36
-        nop
-.LL35:
-       st      %g0, [%fp-24]
-.LL36:
-       ld      [%fp-24], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_status, .-smart_status
-       .align 4
-       .global smart_status_check
-       .type   smart_status_check, #function
-       .proc   04
-smart_status_check:
-       !#PROLOGUE# 0
-       save    %sp, -128, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %g0, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     218, %o2
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %o3
-       mov     0, %o4
-       mov     0, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL38
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-24]
-       b       .LL37
-        nop
-.LL38:
-       st      %g0, [%fp-24]
-.LL37:
-       ld      [%fp-24], %i0
-       ret
-       restore
-       .size   smart_status_check, .-smart_status_check
-       .align 4
-       .global smart_auto_offline
-       .type   smart_auto_offline, #function
-       .proc   04
-smart_auto_offline:
-       !#PROLOGUE# 0
-       save    %sp, -128, %sp
-       !#PROLOGUE# 1
-       st      %i0, [%fp+68]
-       st      %i1, [%fp+72]
-       st      %g0, [%sp+92]
-       ld      [%fp+68], %o0
-       mov     176, %o1
-       mov     219, %o2
-       sethi   %hi(12733440), %g1
-       or      %g1, 768, %o3
-       ld      [%fp+72], %o4
-       mov     0, %o5
-       call    ata_cmd, 0
-        nop
-       mov     %o0, %g1
-       st      %g1, [%fp-20]
-       ld      [%fp-20], %g1
-       cmp     %g1, 0
-       be      .LL40
-       nop
-       mov     -1, %g1
-       st      %g1, [%fp-24]
-       b       .LL41
-        nop
-.LL40:
-       st      %g0, [%fp-24]
-.LL41:
-       ld      [%fp-24], %g1
-       mov     %g1, %i0
-       ret
-       restore
-       .size   smart_auto_offline, .-smart_auto_offline
-       .ident  "GCC: (GNU) 3.4.2"
index 2414d131be55dc0e39e6f4485c069961ef531fc7..b10e9215081d4c774787d5e29e08a1fd1a5b3aa4 100644 (file)
@@ -1,10 +1,15 @@
 /*
  * os_win32.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2004-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2012    Hank Wu <hank@areca.com.tw>
+ * Copyright (C) 2004-16 Christian Franke
+ *
+ * Original AACRaid code:
+ *  Copyright (C) 2015    Nidhi Malhotra <nidhi.malhotra@pmcs.com>
+ *
+ * Original Areca code:
+ *  Copyright (C) 2012    Hank Wu <hank@areca.com.tw>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,6 +28,7 @@
 #include "int64.h"
 #include "atacmds.h"
 #include "scsicmds.h"
+#include "nvmecmds.h"
 #include "utility.h"
 #include "smartctl.h" // TODO: Do not use smartctl only variables here
 
 #endif
 
 #ifndef _WIN32
-// csmisas.h requires _WIN32 but w32api-headers no longer define it on Cygwin
+// csmisas.h and aacraid.h require _WIN32 but w32api-headers no longer define it on Cygwin
+// (aacraid.h also checks for _WIN64 which is also set on Cygwin x64)
 #define _WIN32
 #endif
 
 // CSMI support
 #include "csmisas.h"
 
+// aacraid support
+#include "aacraid.h"
+
 // Silence -Wunused-local-typedefs warning from g++ >= 4.8
 #if __GNUC__ >= 4
 #define ATTR_UNUSED __attribute__((unused))
 #define SELECT_WIN_32_64(x32, x64) (x64)
 #endif
 
-const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3923 2014-06-25 17:10:46Z chrfranke $";
+// Cygwin does no longer provide strn?icmp() compatibility macros
+// MSVCRT does not provide strn?casecmp()
+#if defined(__CYGWIN__) && !defined(stricmp)
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#endif
+
+const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 4293 2016-04-14 19:33:05Z chrfranke $";
 
 /////////////////////////////////////////////////////////////////////////////
 // Windows I/O-controls, some declarations are missing in the include files
@@ -299,6 +316,39 @@ ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONINPARAMS));
 ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
 
 
+// NVME_PASS_THROUGH
+
+#ifndef NVME_PASS_THROUGH_SRB_IO_CODE
+
+#define NVME_SIG_STR "NvmeMini"
+#define NVME_STORPORT_DRIVER 0xe000
+
+#define NVME_PASS_THROUGH_SRB_IO_CODE \
+  CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#pragma pack(1)
+typedef struct _NVME_PASS_THROUGH_IOCTL
+{
+  SRB_IO_CONTROL SrbIoCtrl;
+  ULONG VendorSpecific[6];
+  ULONG NVMeCmd[16]; // Command DW[0...15]
+  ULONG CplEntry[4]; // Completion DW[0...3]
+  ULONG Direction; // 0=No, 1=Out, 2=In, 3=I/O
+  ULONG QueueId; // 0=AdminQ
+  ULONG DataBufferLen; // sizeof(DataBuffer) if Data In
+  ULONG MetaDataLen;
+  ULONG ReturnBufferLen; // offsetof(DataBuffer), plus sizeof(DataBuffer) if Data Out
+  UCHAR DataBuffer[1];
+} NVME_PASS_THROUGH_IOCTL;
+#pragma pack()
+
+#endif // NVME_PASS_THROUGH_SRB_IO_CODE
+
+ASSERT_CONST(NVME_PASS_THROUGH_SRB_IO_CODE, (int)0xe0002000);
+ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL, 152+1);
+ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL, offsetof(NVME_PASS_THROUGH_IOCTL, DataBuffer)+1);
+
+
 // CSMI structs
 
 ASSERT_SIZEOF(IOCTL_HEADER, sizeof(SRB_IO_CONTROL));
@@ -306,6 +356,10 @@ ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER, 204);
 ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER, 2080);
 ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER, 168);
 
+// aacraid struct
+
+ASSERT_SIZEOF(SCSI_REQUEST_BLOCK, SELECT_WIN_32_64(64, 88));
+
 } // extern "C"
 
 /////////////////////////////////////////////////////////////////////////////
@@ -316,6 +370,58 @@ namespace os_win32 { // no need to publish anything, name provided for Doxygen
 #pragma warning(disable:4250)
 #endif
 
+static int is_permissive()
+{
+  if (!failuretest_permissive) {
+    pout("To continue, add one or more '-T permissive' options.\n");
+    return 0;
+  }
+  failuretest_permissive--;
+  return 1;
+}
+
+// return number for drive letter, -1 on error
+// "[A-Za-z]:([/\\][.]?)?" => 0-25
+// Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
+static int drive_letter(const char * s)
+{
+  return (   (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
+          && s[1] == ':'
+          && (!s[2] || (   strchr("/\\\"", s[2])
+                        && (!s[3] || (s[3] == '.' && !s[4])))              ) ?
+          (s[0] & 0x1f) - 1 : -1);
+}
+
+// Skip trailing "/dev/", do not allow "/dev/X:"
+static const char * skipdev(const char * s)
+{
+  return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
+}
+
+// "sd[a-z]" -> 0-25, "sd[a-z][a-z]" -> 26-701
+static int sdxy_to_phydrive(const char (& xy)[2+1])
+{
+  int phydrive = xy[0] - 'a';
+  if (xy[1])
+    phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a');
+  return phydrive;
+}
+
+static void copy_swapped(unsigned char * dest, const char * src, int destsize)
+{
+  int srclen = strcspn(src, "\r\n");
+  int i;
+  for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
+    dest[i] = src[i+1]; dest[i+1] = src[i];
+  }
+  if (i < destsize-1 && i < srclen)
+    dest[i+1] = src[i];
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// win_smart_device
+
 class win_smart_device
 : virtual public /*implements*/ smart_device
 {
@@ -345,2811 +451,3123 @@ private:
 };
 
 
-/////////////////////////////////////////////////////////////////////////////
+// Common routines for devices with HANDLEs
 
-class win_ata_device
-: public /*implements*/ ata_device,
-  public /*extends*/ win_smart_device
+win_smart_device::~win_smart_device() throw()
 {
-public:
-  win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
-
-  virtual ~win_ata_device() throw();
-
-  virtual bool open();
-
-  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
-
-  virtual bool ata_identify_is_cached() const;
+  if (m_fh != INVALID_HANDLE_VALUE)
+    ::CloseHandle(m_fh);
+}
 
-private:
-  bool open(int phydrive, int logdrive, const char * options, int port);
+bool win_smart_device::is_open() const
+{
+  return (m_fh != INVALID_HANDLE_VALUE);
+}
 
-  std::string m_options;
-  bool m_usr_options; // options set by user?
-  bool m_admin; // open with admin access?
-  int m_phydrive; // PhysicalDriveN or -1
-  bool m_id_is_cached; // ata_identify_is_cached() return value.
-  bool m_is_3ware; // LSI/3ware controller detected?
-  int m_port; // LSI/3ware port
-  int m_smartver_state;
-};
+bool win_smart_device::close()
+{
+  if (m_fh == INVALID_HANDLE_VALUE)
+    return true;
+  BOOL rc = ::CloseHandle(m_fh);
+  m_fh = INVALID_HANDLE_VALUE;
+  return !!rc;
+}
 
 
 /////////////////////////////////////////////////////////////////////////////
 
-class win_scsi_device
-: public /*implements*/ scsi_device,
-  virtual public /*extends*/ win_smart_device
-{
-public:
-  win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
-
-  virtual bool open();
-
-  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+#define SMART_CYL_LOW  0x4F
+#define SMART_CYL_HI   0xC2
 
-private:
-  bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
-};
+static void print_ide_regs(const IDEREGS * r, int out)
+{
+  pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
+    (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
+    r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
+}
 
+static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
+{
+  pout("    Input : "); print_ide_regs(ri, 0);
+  if (ro) {
+    pout("    Output: "); print_ide_regs(ro, 1);
+  }
+}
 
 /////////////////////////////////////////////////////////////////////////////
 
-class csmi_device
-: virtual public /*extends*/ smart_device
-{
-public:
-  /// Get bitmask of used ports
-  unsigned get_ports_used();
+// call SMART_GET_VERSION, return device map or -1 on error
 
-protected:
-  csmi_device()
-    : smart_device(never_called)
-    { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); }
+static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
+{
+  GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers));
+  const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
+  DWORD num_out;
 
-  /// Get phy info
-  bool get_phy_info(CSMI_SAS_PHY_INFO & phy_info);
+  if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
+    NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
+    if (ata_debugmode)
+      pout("  SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError());
+    errno = ENOSYS;
+    return -1;
+  }
+  assert(num_out == sizeof(GETVERSIONINPARAMS));
 
-  /// Select physical drive
-  bool select_port(int port);
+  if (ata_debugmode > 1) {
+    pout("  SMART_GET_VERSION suceeded, bytes returned: %u\n"
+         "    Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
+      (unsigned)num_out, vers.bVersion, vers.bRevision,
+      (unsigned)vers.fCapabilities, vers.bIDEDeviceMap);
+    if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
+      pout("    Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n",
+      vers_ex.wIdentifier, vers_ex.wControllerId, (unsigned)vers_ex.dwDeviceMapEx);
+  }
 
-  /// Get info for selected physical drive
-  const CSMI_SAS_PHY_ENTITY & get_phy_ent() const
-    { return m_phy_ent; }
+  if (ata_version_ex)
+    *ata_version_ex = vers_ex;
 
-  /// Call platform-specific CSMI ioctl
-  virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
-    unsigned csmi_bufsiz) = 0;
+  // TODO: Check vers.fCapabilities here?
+  return vers.bIDEDeviceMap;
+}
 
-private:
-  CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy
-};
 
+// call SMART_* ioctl
 
-class csmi_ata_device
-: virtual public /*extends*/ csmi_device,
-  virtual public /*implements*/ ata_device
+static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port)
 {
-public:
-  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
-
-protected:
-  csmi_ata_device()
-    : smart_device(never_called) { }
-};
-
-
-//////////////////////////////////////////////////////////////////////
+  SENDCMDINPARAMS inpar;
+  SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
 
-class win_csmi_device
-: public /*implements*/ csmi_ata_device
-{
-public:
-  win_csmi_device(smart_interface * intf, const char * dev_name,
-    const char * req_type);
+  unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
+  const SENDCMDOUTPARAMS * outpar;
+  DWORD code, num_out;
+  unsigned int size_out;
+  const char * name;
 
-  virtual ~win_csmi_device() throw();
+  memset(&inpar, 0, sizeof(inpar));
+  inpar.irDriveRegs = *regs;
 
-  virtual bool open();
+  // Older drivers may require bits 5 and 7 set
+  // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete
+  inpar.irDriveRegs.bDriveHeadReg |= 0xa0;
 
-  virtual bool close();
+  // Drive number 0-3 was required on Win9x/ME only
+  //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
+  //inpar.bDriveNumber = drive;
 
-  virtual bool is_open() const;
+  if (port >= 0) {
+    // Set RAID port
+    inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
+    inpar_ex.bPortNumber = port;
+  }
 
-  bool open_scsi();
+  if (datasize == 512) {
+    code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
+    inpar.cBufferSize = size_out = 512;
+  }
+  else if (datasize == 0) {
+    code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
+    if (regs->bFeaturesReg == ATA_SMART_STATUS)
+      size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
+      // Note: cBufferSize must be 0 on Win9x
+    else
+      size_out = 0;
+  }
+  else {
+    errno = EINVAL;
+    return -1;
+  }
 
-protected:
-  virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
-    unsigned csmi_bufsiz);
+  memset(&outbuf, 0, sizeof(outbuf));
 
-private:
-  HANDLE m_fh; ///< Controller device handle
-  int m_port; ///< Port number
-};
-
-
-//////////////////////////////////////////////////////////////////////
-
-class win_tw_cli_device
-: public /*implements*/ ata_device_with_command_set
-{
-public:
-  win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type);
+  if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
+    outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
+    // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
+    long err = GetLastError();
+    if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
+      pout("  %s failed, Error=%ld\n", name, err);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (   err == ERROR_INVALID_FUNCTION/*9x*/
+             || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
+             || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+    return -1;
+  }
+  // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
 
-  virtual bool is_open() const;
+  outpar = (const SENDCMDOUTPARAMS *)outbuf;
 
-  virtual bool open();
+  if (outpar->DriverStatus.bDriverError) {
+    if (ata_debugmode) {
+      pout("  %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
+        outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
+    return -1;
+  }
 
-  virtual bool close();
+  if (ata_debugmode > 1) {
+    pout("  %s suceeded, bytes returned: %u (buffer %u)\n", name,
+      (unsigned)num_out, (unsigned)outpar->cBufferSize);
+    print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
+      (const IDEREGS *)(outpar->bBuffer) : NULL));
+  }
 
-protected:
-  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+  if (datasize)
+    memcpy(data, outpar->bBuffer, 512);
+  else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
+    if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
+      memcpy(regs, outpar->bBuffer, sizeof(IDEREGS));
+    else {  // Workaround for driver not returning regs
+      if (ata_debugmode)
+        pout("  WARNING: driver does not return ATA registers in output buffer!\n");
+      *regs = inpar.irDriveRegs;
+    }
+  }
 
-private:
-  bool m_ident_valid, m_smart_valid;
-  ata_identify_device m_ident_buf;
-  ata_smart_values m_smart_buf;
-};
+  return 0;
+}
 
 
 /////////////////////////////////////////////////////////////////////////////
-/// Areca RAID support
+// IDE PASS THROUGH (2000, XP, undocumented)
+//
+// Based on WinATA.cpp, 2002 c't/Matthias Withopf
+// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
 
-///////////////////////////////////////////////////////////////////
-// SATA(ATA) device behind Areca RAID Controller
-class win_areca_ata_device
-: public /*implements*/ areca_ata_device,
-  public /*extends*/ win_smart_device
+static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
 {
-public:
-  win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
-  virtual bool open();
-  virtual smart_device * autodetect_open();
-  virtual bool arcmsr_lock();
-  virtual bool arcmsr_unlock();
-  virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
+  if (datasize > 512) {
+    errno = EINVAL;
+    return -1;
+  }
+  unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
+  ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
+  DWORD num_out;
+  const unsigned char magic = 0xcf;
 
-private:
-  HANDLE m_mutex;
-};
+  if (!buf) {
+    errno = ENOMEM;
+    return -1;
+  }
 
-///////////////////////////////////////////////////////////////////
-// SAS(SCSI) device behind Areca RAID Controller
-class win_areca_scsi_device
-: public /*implements*/ areca_scsi_device,
-  public /*extends*/ win_smart_device
-{
-public:
-  win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
-  virtual bool open();
-  virtual smart_device * autodetect_open();
-  virtual bool arcmsr_lock();
-  virtual bool arcmsr_unlock();
-  virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
+  buf->IdeReg = *regs;
+  buf->DataBufferSize = datasize;
+  if (datasize)
+    buf->DataBuffer[0] = magic;
 
-private:
-  HANDLE m_mutex;
-};
+  if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
+    buf, size, buf, size, &num_out, NULL)) {
+    long err = GetLastError();
+    if (ata_debugmode) {
+      pout("  IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
+      print_ide_regs_io(regs, NULL);
+    }
+    VirtualFree(buf, 0, MEM_RELEASE);
+    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+    return -1;
+  }
 
+  // Check ATA status
+  if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
+    if (ata_debugmode) {
+      pout("  IOCTL_IDE_PASS_THROUGH command failed:\n");
+      print_ide_regs_io(regs, &buf->IdeReg);
+    }
+    VirtualFree(buf, 0, MEM_RELEASE);
+    errno = EIO;
+    return -1;
+  }
 
-//////////////////////////////////////////////////////////////////////
-// Platform specific interface
+  // Check and copy data
+  if (datasize) {
+    if (   num_out != size
+        || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
+      if (ata_debugmode) {
+        pout("  IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n",
+          (unsigned)num_out, (unsigned)buf->DataBufferSize);
+        print_ide_regs_io(regs, &buf->IdeReg);
+      }
+      VirtualFree(buf, 0, MEM_RELEASE);
+      errno = EIO;
+      return -1;
+    }
+    memcpy(data, buf->DataBuffer, datasize);
+  }
 
-class win_smart_interface
-: public /*implements part of*/ smart_interface
-{
-public:
-  virtual std::string get_os_version_str();
+  if (ata_debugmode > 1) {
+    pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n",
+      (unsigned)num_out, (unsigned)buf->DataBufferSize);
+    print_ide_regs_io(regs, &buf->IdeReg);
+  }
+  *regs = buf->IdeReg;
 
-  virtual std::string get_app_examples(const char * appname);
+  // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
+  VirtualFree(buf, 0, MEM_RELEASE);
+  return 0;
+}
 
-#ifndef __CYGWIN__
-  virtual int64_t get_timer_usec();
-#endif
 
-  virtual bool disable_system_auto_standby(bool disable);
+/////////////////////////////////////////////////////////////////////////////
+// ATA PASS THROUGH (Win2003, XP SP2)
 
-  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
-    const char * pattern = 0);
+// Warning:
+// IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
+// transfer per command. Therefore, multi-sector transfers are only supported
+// for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
+// or READ/WRITE LOG EXT work only with single sector transfers.
+// The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
+// See:
+// http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
 
-protected:
-  virtual ata_device * get_ata_device(const char * name, const char * type);
+static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
+{
+  const int max_sectors = 32; // TODO: Allocate dynamic buffer
 
-  virtual scsi_device * get_scsi_device(const char * name, const char * type);
+  typedef struct {
+    ATA_PASS_THROUGH_EX apt;
+    ULONG Filler;
+    UCHAR ucDataBuf[max_sectors * 512];
+  } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
 
-  virtual smart_device * autodetect_smart_device(const char * name);
+  const unsigned char magic = 0xcf;
 
-  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+  ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
+  ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
+  //ab.apt.PathId = 0;
+  //ab.apt.TargetId = 0;
+  //ab.apt.Lun = 0;
+  ab.apt.TimeOutValue = 10;
+  unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
+  ab.apt.DataBufferOffset = size;
 
-  virtual std::string get_valid_custom_dev_types_str();
-};
+  if (datasize > 0) {
+    if (datasize > (int)sizeof(ab.ucDataBuf)) {
+      errno = EINVAL;
+      return -1;
+    }
+    ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
+    ab.apt.DataTransferLength = datasize;
+    size += datasize;
+    ab.ucDataBuf[0] = magic;
+  }
+  else if (datasize < 0) {
+    if (-datasize > (int)sizeof(ab.ucDataBuf)) {
+      errno = EINVAL;
+      return -1;
+    }
+    ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
+    ab.apt.DataTransferLength = -datasize;
+    size += -datasize;
+    memcpy(ab.ucDataBuf, data, -datasize);
+  }
+  else {
+    assert(ab.apt.AtaFlags == 0);
+    assert(ab.apt.DataTransferLength == 0);
+  }
 
+  assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
+  IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
+  IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
+  *ctfregs = *regs;
 
-//////////////////////////////////////////////////////////////////////
+  if (prev_regs) {
+    *ptfregs = *prev_regs;
+    ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
+  }
 
-#ifndef _WIN64
-// Running on 64-bit Windows as 32-bit app ?
-static bool is_wow64()
-{
-  BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
-    (BOOL (WINAPI *)(HANDLE, PBOOL))
-    GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
-  if (!IsWow64Process_p)
-    return false;
-  BOOL w64 = FALSE;
-  if (!IsWow64Process_p(GetCurrentProcess(), &w64))
-    return false;
-  return !!w64;
-}
-#endif // _WIN64
-
-// Return info string about build host and OS version
-std::string win_smart_interface::get_os_version_str()
-{
-  char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
-    = SMARTMONTOOLS_BUILD_HOST;
-  if (vstr[1] < '6')
-    vstr[1] = '6';
-  char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1;
-  const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
-  assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
-
-  OSVERSIONINFOEXA vi; memset(&vi, 0, sizeof(vi));
-  vi.dwOSVersionInfoSize = sizeof(vi);
-  if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
-    memset(&vi, 0, sizeof(vi));
-    vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
-    if (!GetVersionExA((OSVERSIONINFOA *)&vi))
-      return vstr;
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
+    &ab, size, &ab, size, &num_out, NULL)) {
+    long err = GetLastError();
+    if (ata_debugmode) {
+      pout("  IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+    return -1;
   }
 
-  const char * w = 0;
-  if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
-
-    if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) {
-      // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the
-      // actual OS version, see:
-      // http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
-
-      ULONGLONG major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
-      for (unsigned major = vi.dwMajorVersion; major <= 9; major++) {
-        OSVERSIONINFOEXA vi2; memset(&vi2, 0, sizeof(vi2));
-        vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major;
-        if (!VerifyVersionInfo(&vi2, VER_MAJORVERSION, major_equal))
-          continue;
-        if (vi.dwMajorVersion < major) {
-          vi.dwMajorVersion = major; vi.dwMinorVersion = 0;
-        }
-
-        ULONGLONG minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
-        for (unsigned minor = vi.dwMinorVersion; minor <= 9; minor++) {
-          memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2);
-          vi2.dwMinorVersion = minor;
-          if (!VerifyVersionInfo(&vi2, VER_MINORVERSION, minor_equal))
-            continue;
-          vi.dwMinorVersion = minor;
-          break;
-        }
-
-        break;
-      }
+  // Check ATA status
+  if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
+    if (ata_debugmode) {
+      pout("  IOCTL_ATA_PASS_THROUGH command failed:\n");
+      print_ide_regs_io(regs, ctfregs);
     }
+    errno = EIO;
+    return -1;
+  }
 
-    if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
-      bool ws = (vi.wProductType <= VER_NT_WORKSTATION);
-      switch (vi.dwMajorVersion << 4 | vi.dwMinorVersion) {
-        case 0x50: w =       "2000";              break;
-        case 0x51: w =       "xp";                break;
-        case 0x52: w = (!GetSystemMetrics(89/*SM_SERVERR2*/)
-                           ? "2003"  : "2003r2"); break;
-        case 0x60: w = (ws ? "vista" : "2008"  ); break;
-        case 0x61: w = (ws ? "win7"  : "2008r2"); break;
-        case 0x62: w = (ws ? "win8"  : "2012"  ); break;
-        case 0x63: w = (ws ? "win8.1": "2012r2"); break;
+  // Check and copy data
+  if (datasize > 0) {
+    if (   num_out != size
+        || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
+      if (ata_debugmode) {
+        pout("  IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out);
+        print_ide_regs_io(regs, ctfregs);
       }
+      errno = EIO;
+      return -1;
     }
+    memcpy(data, ab.ucDataBuf, datasize);
   }
 
-  const char * w64 = "";
-#ifndef _WIN64
-  if (is_wow64())
-    w64 = "(64)";
-#endif
+  if (ata_debugmode > 1) {
+    pout("  IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out);
+    print_ide_regs_io(regs, ctfregs);
+  }
+  *regs = *ctfregs;
+  if (prev_regs)
+    *prev_regs = *ptfregs;
 
-  if (!w)
-    snprintf(vptr, vlen, "-%s%u.%u%s",
-      (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "??"),
-      (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
-  else if (vi.wServicePackMinor)
-    snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
-  else if (vi.wServicePackMajor)
-    snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
-  else
-    snprintf(vptr, vlen, "-%s%s", w, w64);
-  return vstr;
+  return 0;
 }
 
-#ifndef __CYGWIN__
-// MSVCRT only provides ftime() which uses GetSystemTime()
-// This provides only ~15ms resolution by default.
-// Use QueryPerformanceCounter instead (~300ns).
-// (Cygwin provides CLOCK_MONOTONIC which has the same effect)
-int64_t win_smart_interface::get_timer_usec()
-{
-  static int64_t freq = 0;
 
-  LARGE_INTEGER t;
-  if (freq == 0)
-    freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1);
-  if (freq <= 0)
-    return smart_interface::get_timer_usec();
+/////////////////////////////////////////////////////////////////////////////
+// SMART IOCTL via SCSI MINIPORT ioctl
 
-  if (!QueryPerformanceCounter(&t))
-    return -1;
-  if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000))
-    return -1;
+// This function is handled by ATAPI port driver (atapi.sys) or by SCSI
+// miniport driver (via SCSI port driver scsiport.sys).
+// It can be used to skip the missing or broken handling of some SMART
+// command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
 
-  return (t.QuadPart * 1000000LL) / freq;
-}
-#endif // __CYGWIN__
+static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
+{
+  // Select code
+  DWORD code = 0; const char * name = 0;
+  if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
+    code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
+  }
+  else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
+    case ATA_SMART_READ_VALUES:
+      code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
+    case ATA_SMART_READ_THRESHOLDS:
+      code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
+    case ATA_SMART_ENABLE:
+      code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
+    case ATA_SMART_DISABLE:
+      code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
+    case ATA_SMART_STATUS:
+      code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
+    case ATA_SMART_AUTOSAVE:
+      code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
+  //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
+  //  code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
+    case ATA_SMART_IMMEDIATE_OFFLINE:
+      code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
+    case ATA_SMART_AUTO_OFFLINE:
+      code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
+    case ATA_SMART_READ_LOG_SECTOR:
+      code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
+    case ATA_SMART_WRITE_LOG_SECTOR:
+      code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
+  }
+  if (!code) {
+    errno = ENOSYS;
+    return -1;
+  }
 
+  // Set SRB
+  struct {
+    SRB_IO_CONTROL srbc;
+    union {
+      SENDCMDINPARAMS in;
+      SENDCMDOUTPARAMS out;
+    } params;
+    char space[512-1];
+  } sb;
+  ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
+  memset(&sb, 0, sizeof(sb));
 
-// Return value for device detection functions
-enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB };
+  unsigned size;
+  if (datasize > 0) {
+    if (datasize > (int)sizeof(sb.space)+1) {
+      errno = EINVAL;
+      return -1;
+    }
+    size = datasize;
+  }
+  else if (datasize < 0) {
+    if (-datasize > (int)sizeof(sb.space)+1) {
+      errno = EINVAL;
+      return -1;
+    }
+    size = -datasize;
+    memcpy(sb.params.in.bBuffer, data, size);
+  }
+  else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
+    size = sizeof(IDEREGS);
+  else
+    size = 0;
+  sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+  memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
+  sb.srbc.Timeout = 60; // seconds
+  sb.srbc.ControlCode = code;
+  //sb.srbc.ReturnCode = 0;
+  sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
+  sb.params.in.irDriveRegs = *regs;
+  sb.params.in.cBufferSize = size;
 
-static win_dev_type get_phy_drive_type(int drive);
-static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex);
-static win_dev_type get_log_drive_type(int drive);
-static bool get_usb_id(int drive, unsigned short & vendor_id,
-                       unsigned short & product_id);
+  // Call miniport ioctl
+  size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+    &sb, size, &sb, size, &num_out, NULL)) {
+    long err = GetLastError();
+    if (ata_debugmode) {
+      pout("  IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+    return -1;
+  }
 
-static const char * ata_get_def_options(void);
+  // Check result
+  if (sb.srbc.ReturnCode) {
+    if (ata_debugmode) {
+      pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name, (unsigned)sb.srbc.ReturnCode);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = EIO;
+    return -1;
+  }
 
+  if (sb.params.out.DriverStatus.bDriverError) {
+    if (ata_debugmode) {
+      pout("  IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
+        sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
+    return -1;
+  }
 
-static int is_permissive()
-{
-  if (!failuretest_permissive) {
-    pout("To continue, add one or more '-T permissive' options.\n");
-    return 0;
+  if (ata_debugmode > 1) {
+    pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %u (buffer %u)\n", name,
+      (unsigned)num_out, (unsigned)sb.params.out.cBufferSize);
+    print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
+                             (const IDEREGS *)(sb.params.out.bBuffer) : 0));
   }
-  failuretest_permissive--;
-  return 1;
-}
 
-// return number for drive letter, -1 on error
-// "[A-Za-z]:([/\\][.]?)?" => 0-25
-// Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
-static int drive_letter(const char * s)
-{
-  return (   (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
-          && s[1] == ':'
-          && (!s[2] || (   strchr("/\\\"", s[2])
-                        && (!s[3] || (s[3] == '.' && !s[4])))              ) ?
-          (s[0] & 0x1f) - 1 : -1);
-}
+  if (datasize > 0)
+    memcpy(data, sb.params.out.bBuffer, datasize);
+  else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
+    memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS));
 
-// Skip trailing "/dev/", do not allow "/dev/X:"
-static const char * skipdev(const char * s)
-{
-  return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
+  return 0;
 }
 
-ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
-{
-  const char * testname = skipdev(name);
-  if (!strncmp(testname, "csmi", 4))
-    return new win_csmi_device(this, name, type);
-  if (!strncmp(testname, "tw_cli", 6))
-    return new win_tw_cli_device(this, name, type);
-  return new win_ata_device(this, name, type);
-}
 
-scsi_device * win_smart_interface::get_scsi_device(const char * name, const char * type)
+/////////////////////////////////////////////////////////////////////////////
+// ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
+
+static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
 {
-  return new win_scsi_device(this, name, type);
-}
-
-static int sdxy_to_phydrive(const char (& xy)[2+1])
-{
-  int phydrive = xy[0] - 'a';
-  if (xy[1])
-    phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a');
-  return phydrive;
-}
-
-static win_dev_type get_dev_type(const char * name, int & phydrive)
-{
-  phydrive = -1;
-  name = skipdev(name);
-  if (!strncmp(name, "st", 2))
-    return DEV_SCSI;
-  if (!strncmp(name, "nst", 3))
-    return DEV_SCSI;
-  if (!strncmp(name, "tape", 4))
-    return DEV_SCSI;
+  struct {
+    SRB_IO_CONTROL srbc;
+    IDEREGS regs;
+    UCHAR buffer[512];
+  } sb;
+  ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
 
-  int logdrive = drive_letter(name);
-  if (logdrive >= 0) {
-    win_dev_type type = get_log_drive_type(logdrive);
-    return (type != DEV_UNKNOWN ? type : DEV_SCSI);
+  if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
+    errno = EINVAL;
+    return -1;
   }
+  memset(&sb, 0, sizeof(sb));
+  strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature));
+  sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+  sb.srbc.Timeout = 60; // seconds
+  sb.srbc.ControlCode = 0xA0000000;
+  sb.srbc.ReturnCode = 0;
+  sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
+  sb.regs = *regs;
+  sb.regs.bReserved = port;
 
-  char drive[2+1] = "";
-  if (sscanf(name, "sd%2[a-z]", drive) == 1) {
-    phydrive = sdxy_to_phydrive(drive);
-    return get_phy_drive_type(phydrive);
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+    &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
+    long err = GetLastError();
+    if (ata_debugmode) {
+      pout("  ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+    return -1;
   }
 
-  phydrive = -1;
-  if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
-    return get_phy_drive_type(phydrive);
-  return DEV_UNKNOWN;
-}
-
-smart_device * win_smart_interface::get_custom_smart_device(const char * name, const char * type)
-{
-  // Areca?
-  int disknum = -1, n1 = -1, n2 = -1;
-  int encnum = 1;
-  char devpath[32];
-
-  if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
-    if (!(1 <= disknum && disknum <= 128)) {
-      set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum);
-      return 0;
-    }
-    if (!(1 <= encnum && encnum <= 8)) {
-      set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum);
-      return 0;
+  if (sb.srbc.ReturnCode) {
+    if (ata_debugmode) {
+      pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb.srbc.ReturnCode);
+      print_ide_regs_io(regs, NULL);
     }
+    errno = EIO;
+    return -1;
+  }
 
-    name = skipdev(name);
-#define ARECA_MAX_CTLR_NUM  16
-    n1 = -1;
-    int ctlrindex = 0;
-    if (sscanf(name, "arcmsr%d%n", &ctlrindex, &n1) >= 1 && n1 == (int)strlen(name)) {
-      /*
-       1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
-       2. map arcmsrX into "\\\\.\\scsiX"
-      */
-     for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) {
-        memset(devpath, 0, sizeof(devpath));
-        snprintf(devpath, sizeof(devpath), "\\\\.\\scsi%d:", idx);
-        win_areca_ata_device *arcdev = new win_areca_ata_device(this, devpath, disknum, encnum);
-        if(arcdev->arcmsr_probe()) {
-          if(ctlrindex-- == 0) {
-            return arcdev;
-          }
-        }
-        delete arcdev;
-      }
-      set_err(ENOENT, "No Areca controller found");
-    }
-    else
-      set_err(EINVAL, "Option -d areca,N/E requires device name /dev/arcmsrX");
+  // Copy data
+  if (datasize > 0)
+    memcpy(data, sb.buffer, datasize);
+
+  if (ata_debugmode > 1) {
+    pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out);
+    print_ide_regs_io(regs, &sb.regs);
   }
+  *regs = sb.regs;
 
   return 0;
 }
 
-std::string win_smart_interface::get_valid_custom_dev_types_str()
-{
-  return "areca,N[/E]";
-}
 
+/////////////////////////////////////////////////////////////////////////////
 
-smart_device * win_smart_interface::autodetect_smart_device(const char * name)
+// 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
+// 3DM/CLI "Rescan Controller" function does not to always update it.
+
+static int update_3ware_devicemap_ioctl(HANDLE hdevice)
 {
-  const char * testname = skipdev(name);
-  if (str_starts_with(testname, "hd"))
-    return new win_ata_device(this, name, "");
+  SRB_IO_CONTROL srbc;
+  memset(&srbc, 0, sizeof(srbc));
+  strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature));
+  srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+  srbc.Timeout = 60; // seconds
+  srbc.ControlCode = 0xCC010014;
+  srbc.ReturnCode = 0;
+  srbc.Length = 0;
 
-  if (str_starts_with(testname, "tw_cli"))
-    return new win_tw_cli_device(this, name, "");
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+    &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
+    long err = GetLastError();
+    if (ata_debugmode)
+      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
+    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+    return -1;
+  }
+  if (srbc.ReturnCode) {
+    if (ata_debugmode)
+      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc.ReturnCode);
+    errno = EIO;
+    return -1;
+  }
+  if (ata_debugmode > 1)
+    pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
+  return 0;
+}
 
-  if (str_starts_with(testname, "csmi"))
-    return new win_csmi_device(this, name, "");
 
-  int phydrive = -1;
-  win_dev_type type = get_dev_type(name, phydrive);
+/////////////////////////////////////////////////////////////////////////////
+// IOCTL_STORAGE_QUERY_PROPERTY
 
-  if (type == DEV_ATA)
-    return new win_ata_device(this, name, "");
-  if (type == DEV_SCSI)
-    return new win_scsi_device(this, name, "");
+union STORAGE_DEVICE_DESCRIPTOR_DATA {
+  STORAGE_DEVICE_DESCRIPTOR desc;
+  char raw[256];
+};
 
-  if (type == DEV_USB) {
-    // Get USB bridge ID
-    unsigned short vendor_id = 0, product_id = 0;
-    if (!(phydrive >= 0 && get_usb_id(phydrive, vendor_id, product_id))) {
-      set_err(EINVAL, "Unable to read USB device ID");
-      return 0;
-    }
-    // Get type name for this ID
-    const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
-    if (!usbtype)
-      return 0;
-    // Return SAT/USB device for this type
-    return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
+// Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
+// (This works without admin rights)
+
+static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
+{
+  STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
+  memset(data, 0, sizeof(*data));
+
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
+    &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
+    if (ata_debugmode > 1 || scsi_debugmode > 1)
+      pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError());
+    errno = ENOSYS;
+    return -1;
   }
 
+  if (ata_debugmode > 1 || scsi_debugmode > 1) {
+    pout("  IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
+         "    Vendor:   \"%s\"\n"
+         "    Product:  \"%s\"\n"
+         "    Revision: \"%s\"\n"
+         "    Removable: %s\n"
+         "    BusType:   0x%02x\n",
+         (data->desc.VendorIdOffset        ? data->raw+data->desc.VendorIdOffset : "(null)"),
+         (data->desc.ProductIdOffset       ? data->raw+data->desc.ProductIdOffset : "(null)"),
+         (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : "(null)"),
+         (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
+    );
+  }
   return 0;
 }
 
 
-// Scan for devices
+/////////////////////////////////////////////////////////////////////////////
+// IOCTL_STORAGE_PREDICT_FAILURE
 
-bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
-  const char * type, const char * pattern /* = 0*/)
+// Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
+// or -1 on error, opionally return VendorSpecific data.
+// (This works without admin rights)
+
+static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
 {
-  if (pattern) {
-    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
-    return false;
-  }
+  STORAGE_PREDICT_FAILURE pred;
+  memset(&pred, 0, sizeof(pred));
 
-  // Check for "[*,]pd" type
-  bool pd = false;
-  char type2[16+1] = "";
-  if (type) {
-    int nc = -1;
-    if (!strcmp(type, "pd")) {
-      pd = true;
-      type = 0;
-    }
-    else if (sscanf(type, "%16[^,],pd%n", type2, &nc) == 1 &&
-             nc == (int)strlen(type)) {
-      pd = true;
-      type = type2;
-    }
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
+    0, 0, &pred, sizeof(pred), &num_out, NULL)) {
+    if (ata_debugmode > 1)
+      pout("  IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError());
+    errno = ENOSYS;
+    return -1;
   }
 
-  // Set valid types
-  bool ata, scsi, usb, csmi;
-  if (!type) {
-    ata = scsi = usb = csmi = true;
+  if (ata_debugmode > 1) {
+    pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
+         "    PredictFailure: 0x%08x\n"
+         "    VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
+         (unsigned)pred.PredictFailure,
+         pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
+         pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
+    );
   }
-  else {
-    ata = scsi = usb = csmi = false;
-    if (!strcmp(type, "ata"))
-      ata = true;
-    else if (!strcmp(type, "scsi"))
-      scsi = true;
-    else if (!strcmp(type, "usb"))
-      usb = true;
-    else if (!strcmp(type, "csmi"))
-      csmi = true;
-    else {
-      set_err(EINVAL, "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], usb[,pd], csmi, pd", type);
-      return false;
-    }
-  }
-
-  char name[20];
-
-  if (ata || scsi || usb) {
-    // Scan up to 128 drives and 2 3ware controllers
-    const int max_raid = 2;
-    bool raid_seen[max_raid] = {false, false};
-
-    for (int i = 0; i < 128; i++) {
-      if (pd)
-        snprintf(name, sizeof(name), "/dev/pd%d", i);
-      else if (i + 'a' <= 'z')
-        snprintf(name, sizeof(name), "/dev/sd%c", i + 'a');
-      else
-        snprintf(name, sizeof(name), "/dev/sd%c%c",
-                 i / ('z'-'a'+1) - 1 + 'a',
-                 i % ('z'-'a'+1)     + 'a');
+  if (data)
+    memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
+  return (!pred.PredictFailure ? 0 : 1);
+}
 
-      GETVERSIONINPARAMS_EX vers_ex;
 
-      switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) {
-        case DEV_ATA:
-          // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
-          if (!ata)
-            continue;
+// Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
+static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
+{
+  STORAGE_DEVICE_DESCRIPTOR_DATA data;
+  if (storage_query_property_ioctl(hdevice, &data))
+    return -1;
 
-          // Interpret RAID drive map if present
-          if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
-            // Skip if too many controllers or logical drive from this controller already seen
-            if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId]))
-              continue;
-            raid_seen[vers_ex.wControllerId] = true;
-            // Add physical drives
-            int len = strlen(name);
-            for (int pi = 0; pi < 32; pi++) {
-              if (vers_ex.dwDeviceMapEx & (1L << pi)) {
-                snprintf(name+len, sizeof(name)-1-len, ",%u", pi);
-                devlist.push_back( new win_ata_device(this, name, "ata") );
-              }
-            }
-          }
-          else {
-            devlist.push_back( new win_ata_device(this, name, "ata") );
-          }
-          break;
+  memset(id, 0, sizeof(*id));
 
-        case DEV_SCSI:
-          // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
-          if (!scsi)
-            continue;
-          devlist.push_back( new win_scsi_device(this, name, "scsi") );
-          break;
+  // Some drivers split ATA model string into VendorId and ProductId,
+  // others return it as ProductId only.
+  char model[sizeof(id->model) + 1] = "";
 
-        case DEV_USB:
-          // STORAGE_QUERY_PROPERTY returned USB
-          if (!usb)
-            continue;
-          {
-            // TODO: Use common function for this and autodetect_smart_device()
-            // Get USB bridge ID
-            unsigned short vendor_id = 0, product_id = 0;
-            if (!get_usb_id(i, vendor_id, product_id))
-              continue;
-            // Get type name for this ID
-            const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
-            if (!usbtype)
-              continue;
-            // Return SAT/USB device for this type
-            ata_device * dev = get_sat_device(usbtype, new win_scsi_device(this, name, ""));
-            if (!dev)
-              continue;
-            devlist.push_back(dev);
-          }
-          break;
+  unsigned i = 0;
+  if (data.desc.VendorIdOffset) {
+    for ( ;i < sizeof(model)-1 && data.raw[data.desc.VendorIdOffset+i]; i++)
+      model[i] = data.raw[data.desc.VendorIdOffset+i];
+  }
 
-        default:
-          // Unknown type
-          break;
-      }
-    }
+  if (data.desc.ProductIdOffset) {
+    while (i > 1 && model[i-2] == ' ') // Keep last blank from VendorId
+      i--;
+    // Ignore VendorId "ATA"
+    if (i <= 4 && !strncmp(model, "ATA", 3) && (i == 3 || model[3] == ' '))
+      i = 0;
+    for (unsigned j = 0; i < sizeof(model)-1 && data.raw[data.desc.ProductIdOffset+j]; i++, j++)
+      model[i] = data.raw[data.desc.ProductIdOffset+j];
   }
 
-  if (csmi) {
-    // Scan CSMI devices
-    for (int i = 0; i <= 9; i++) {
-      snprintf(name, sizeof(name)-1, "/dev/csmi%d,0", i);
-      win_csmi_device test_dev(this, name, "");
-      if (!test_dev.open_scsi())
-        continue;
+  while (i > 0 && model[i-1] == ' ')
+    i--;
+  model[i] = 0;
+  copy_swapped(id->model, model, sizeof(id->model));
 
-      unsigned ports_used = test_dev.get_ports_used();
-      if (!ports_used)
-        continue;
+  if (data.desc.ProductRevisionOffset)
+    copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
 
-      for (int pi = 0; pi < 32; pi++) {
-        if (!(ports_used & (1 << pi)))
-          continue;
-        snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi);
-        devlist.push_back( new win_csmi_device(this, name, "ata") );
-      }
-    }
-  }
-  return true;
+  id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
+  id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
+  return 0;
 }
 
-
-// get examples for smartctl
-std::string win_smart_interface::get_app_examples(const char * appname)
+// Get Serial Number in IDENTIFY from WMI
+static bool get_serial_from_wmi(int drive, ata_identify_device * id)
 {
-  if (strcmp(appname, "smartctl"))
-    return "";
-  return "=================================================== SMARTCTL EXAMPLES =====\n\n"
-         "  smartctl -a /dev/sda                       (Prints all SMART information)\n\n"
-         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
-         "                                              (Enables SMART on first disk)\n\n"
-         "  smartctl -t long /dev/sda              (Executes extended disk self-test)\n\n"
-         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         "  smartctl -a /dev/sda\n"
-         "             (Prints all information for disk on PhysicalDrive 0)\n"
-         "  smartctl -a /dev/pd3\n"
-         "             (Prints all information for disk on PhysicalDrive 3)\n"
-         "  smartctl -a /dev/tape1\n"
-         "             (Prints all information for SCSI tape on Tape 1)\n"
-         "  smartctl -A /dev/hdb,3\n"
-         "                (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
-         "  smartctl -A /dev/tw_cli/c0/p1\n"
-         "            (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
-         "  smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
-         "           (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
-         "            on 1st Areca RAID controller)\n"
-         "\n"
-         "  ATA SMART access methods and ordering may be specified by modifiers\n"
-         "  following the device name: /dev/hdX:[saicm], where\n"
-         "  's': SMART_* IOCTLs,         'a': IOCTL_ATA_PASS_THROUGH,\n"
-         "  'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n"
-         "  'm': IOCTL_SCSI_MINIPORT_*.\n"
-      + strprintf(
-         "  The default on this system is /dev/sdX:%s\n", ata_get_def_options()
-        );
-}
-
+  bool debug = (ata_debugmode > 1);
 
-bool win_smart_interface::disable_system_auto_standby(bool disable)
-{
-  if (disable) {
-    SYSTEM_POWER_STATUS ps;
-    if (!GetSystemPowerStatus(&ps))
-      return set_err(ENOSYS, "Unknown power status");
-    if (ps.ACLineStatus != 1) {
-      SetThreadExecutionState(ES_CONTINUOUS);
-      if (ps.ACLineStatus == 0)
-        set_err(EIO, "AC offline");
-      else
-        set_err(EIO, "Unknown AC line status");
-      return false;
-    }
+  wbem_services ws;
+  if (!ws.connect()) {
+    if (debug)
+      pout("WMI connect failed\n");
+    return false;
   }
 
-  if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0)))
-    return set_err(ENOSYS);
+  wbem_object wo;
+  if (!ws.query1(wo, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE "
+                     "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
+    return false;
+
+  std::string serial = wo.get_str("SerialNumber");
+  if (debug)
+    pout("  WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive, wo.get_str("Model").c_str(), serial.c_str());
+
+  copy_swapped(id->serial_no, serial.c_str(), sizeof(id->serial_no));
   return true;
 }
 
 
 /////////////////////////////////////////////////////////////////////////////
-// ATA Interface
-/////////////////////////////////////////////////////////////////////////////
-
-#define SMART_CYL_LOW  0x4F
-#define SMART_CYL_HI   0xC2
+// USB ID detection using WMI
 
-static void print_ide_regs(const IDEREGS * r, int out)
+// Get USB ID for a physical or logical drive number
+static bool get_usb_id(int phydrive, int logdrive,
+                       unsigned short & vendor_id,
+                       unsigned short & product_id)
 {
-  pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
-    (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
-    r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
-}
+  bool debug = (scsi_debugmode > 1);
 
-static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
-{
-  pout("    Input : "); print_ide_regs(ri, 0);
-  if (ro) {
-    pout("    Output: "); print_ide_regs(ro, 1);
+  wbem_services ws;
+  if (!ws.connect()) {
+    if (debug)
+      pout("WMI connect failed\n");
+    return false;
   }
-}
 
-/////////////////////////////////////////////////////////////////////////////
+  // Get device name
+  std::string name;
 
-// call SMART_GET_VERSION, return device map or -1 on error
+  wbem_object wo;
+  if (0 <= logdrive && logdrive <= 'Z'-'A') {
+    // Drive letter -> Partition info
+    if (!ws.query1(wo, "ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"%c:\"} WHERE ResultClass = Win32_DiskPartition",
+                   'A'+logdrive))
+      return false;
 
-static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
-{
-  GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers));
-  const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
-  DWORD num_out;
+    std::string partid = wo.get_str("DeviceID");
+    if (debug)
+      pout("%c: --> \"%s\" -->\n", 'A'+logdrive, partid.c_str());
 
-  if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
-    NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
-    if (ata_debugmode)
-      pout("  SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError());
-    errno = ENOSYS;
-    return -1;
+    // Partition ID -> Physical drive info
+    if (!ws.query1(wo, "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"%s\"} WHERE ResultClass = Win32_DiskDrive",
+                   partid.c_str()))
+      return false;
+
+    name = wo.get_str("Model");
+    if (debug)
+      pout("%s --> \"%s\":\n", wo.get_str("DeviceID").c_str(), name.c_str());
   }
-  assert(num_out == sizeof(GETVERSIONINPARAMS));
 
-  if (ata_debugmode > 1) {
-    pout("  SMART_GET_VERSION suceeded, bytes returned: %u\n"
-         "    Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
-      (unsigned)num_out, vers.bVersion, vers.bRevision,
-      (unsigned)vers.fCapabilities, vers.bIDEDeviceMap);
-    if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
-      pout("    Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n",
-      vers_ex.wIdentifier, vers_ex.wControllerId, (unsigned)vers_ex.dwDeviceMapEx);
+  else if (phydrive >= 0) {
+    // Physical drive number -> Physical drive info
+    if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", phydrive))
+      return false;
+
+    name = wo.get_str("Model");
+    if (debug)
+      pout("\\.\\\\PHYSICALDRIVE%d --> \"%s\":\n", phydrive, name.c_str());
   }
+  else
+    return false;
 
-  if (ata_version_ex)
-    *ata_version_ex = vers_ex;
 
-  // TODO: Check vers.fCapabilities here?
-  return vers.bIDEDeviceMap;
-}
+  // Get USB_CONTROLLER -> DEVICE associations
+  wbem_enumerator we;
+  if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
+    return false;
 
+  unsigned short usb_venid = 0, prev_usb_venid = 0;
+  unsigned short usb_proid = 0, prev_usb_proid = 0;
+  std::string prev_usb_ant;
+  std::string prev_ant, ant, dep;
 
-// call SMART_* ioctl
+  const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED);
 
-static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port)
-{
-  SENDCMDINPARAMS inpar;
-  SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
+  while (we.next(wo)) {
+    prev_ant = ant;
+    // Find next 'USB_CONTROLLER, DEVICE' pair
+    ant = wo.get_str("Antecedent");
+    dep = wo.get_str("Dependent");
 
-  unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
-  const SENDCMDOUTPARAMS * outpar;
-  DWORD code, num_out;
-  unsigned int size_out;
-  const char * name;
+    if (debug && ant != prev_ant)
+      pout(" %s:\n", ant.c_str());
 
-  memset(&inpar, 0, sizeof(inpar));
-  inpar.irDriveRegs = *regs;
+    // Extract DeviceID
+    regmatch_t match[2];
+    if (!(regex.execute(dep.c_str(), 2, match) && match[1].rm_so >= 0)) {
+      if (debug)
+        pout("  | (\"%s\")\n", dep.c_str());
+      continue;
+    }
 
-  // Older drivers may require bits 5 and 7 set
-  // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete
-  inpar.irDriveRegs.bDriveHeadReg |= 0xa0;
+    std::string devid(dep.c_str()+match[1].rm_so, match[1].rm_eo-match[1].rm_so);
 
-  // Drive number 0-3 was required on Win9x/ME only
-  //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
-  //inpar.bDriveNumber = drive;
+    if (str_starts_with(devid, "USB\\\\VID_")) {
+      // USB bridge entry, save CONTROLLER, ID
+      int nc = -1;
+      if (!(sscanf(devid.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
+            &prev_usb_venid, &prev_usb_proid, &nc) == 2 && nc == 9+4+5+4)) {
+        prev_usb_venid = prev_usb_proid = 0;
+      }
+      prev_usb_ant = ant;
+      if (debug)
+        pout("  +-> \"%s\" [0x%04x:0x%04x]\n", devid.c_str(), prev_usb_venid, prev_usb_proid);
+    }
+    else if (str_starts_with(devid, "USBSTOR\\\\") || str_starts_with(devid, "SCSI\\\\")) {
+      // USBSTORage or SCSI device found
+      if (debug)
+        pout("  +--> \"%s\"\n", devid.c_str());
 
-  if (port >= 0) {
-    // Set RAID port
-    inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
-    inpar_ex.bPortNumber = port;
-  }
+      // Retrieve name
+      wbem_object wo2;
+      if (!ws.query1(wo2, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid.c_str()))
+        continue;
+      std::string name2 = wo2.get_str("Name");
 
-  if (datasize == 512) {
-    code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
-    inpar.cBufferSize = size_out = 512;
-  }
-  else if (datasize == 0) {
-    code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
-    if (regs->bFeaturesReg == ATA_SMART_STATUS)
-      size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
-      // Note: cBufferSize must be 0 on Win9x
-    else
-      size_out = 0;
-  }
-  else {
-    errno = EINVAL;
-    return -1;
-  }
+      // Continue if not name of physical disk drive
+      if (name2 != name) {
+        if (debug)
+          pout("  +---> (\"%s\")\n", name2.c_str());
+        continue;
+      }
 
-  memset(&outbuf, 0, sizeof(outbuf));
+      // Fail if previous USB bridge is associated to other controller or ID is unknown
+      if (!(ant == prev_usb_ant && prev_usb_venid)) {
+        if (debug)
+          pout("  +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str());
+        return false;
+      }
 
-  if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
-    outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
-    // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
-    long err = GetLastError();
-    if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
-      pout("  %s failed, Error=%ld\n", name, err);
-      print_ide_regs_io(regs, NULL);
-    }
-    errno = (   err == ERROR_INVALID_FUNCTION/*9x*/
-             || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
-             || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
-    return -1;
-  }
-  // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
+      // Handle multiple devices with same name
+      if (usb_venid) {
+        // Fail if multiple devices with same name have different USB bridge types
+        if (!(usb_venid == prev_usb_venid && usb_proid == prev_usb_proid)) {
+          if (debug)
+            pout("  +---> \"%s\" (Error: More than one USB ID found)\n", name2.c_str());
+          return false;
+        }
+      }
 
-  outpar = (const SENDCMDOUTPARAMS *)outbuf;
+      // Found
+      usb_venid = prev_usb_venid;
+      usb_proid = prev_usb_proid;
+      if (debug)
+        pout("  +===> \"%s\" [0x%04x:0x%04x]\n", name2.c_str(), usb_venid, usb_proid);
 
-  if (outpar->DriverStatus.bDriverError) {
-    if (ata_debugmode) {
-      pout("  %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
-        outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
-      print_ide_regs_io(regs, NULL);
+      // Continue to check for duplicate names ...
+    }
+    else {
+      if (debug)
+        pout("  |   \"%s\"\n", devid.c_str());
     }
-    errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
-    return -1;
   }
 
-  if (ata_debugmode > 1) {
-    pout("  %s suceeded, bytes returned: %u (buffer %u)\n", name,
-      (unsigned)num_out, (unsigned)outpar->cBufferSize);
-    print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
-      (const IDEREGS *)(outpar->bBuffer) : NULL));
-  }
+  if (!usb_venid)
+    return false;
 
-  if (datasize)
-    memcpy(data, outpar->bBuffer, 512);
-  else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
-    if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
-      memcpy(regs, outpar->bBuffer, sizeof(IDEREGS));
-    else {  // Workaround for driver not returning regs
-      if (ata_debugmode)
-        pout("  WARNING: driver does not return ATA registers in output buffer!\n");
-      *regs = inpar.irDriveRegs;
-    }
-  }
+  vendor_id = usb_venid;
+  product_id = usb_proid;
 
-  return 0;
+  return true;
 }
 
 
 /////////////////////////////////////////////////////////////////////////////
-// IDE PASS THROUGH (2000, XP, undocumented)
-//
-// Based on WinATA.cpp, 2002 c't/Matthias Withopf
-// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
 
-static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
+// Call GetDevicePowerState()
+// returns: 1=active, 0=standby, -1=error
+// (This would also work for SCSI drives)
+
+static int get_device_power_state(HANDLE hdevice)
 {
-  if (datasize > 512) {
-    errno = EINVAL;
+  BOOL state = TRUE;
+  if (!GetDevicePowerState(hdevice, &state)) {
+    long err = GetLastError();
+    if (ata_debugmode)
+      pout("  GetDevicePowerState() failed, Error=%ld\n", err);
+    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+    // TODO: This may not work as expected on transient errors,
+    // because smartd interprets -1 as SLEEP mode regardless of errno.
     return -1;
   }
-  unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
-  ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
-  DWORD num_out;
-  const unsigned char magic = 0xcf;
 
-  if (!buf) {
-    errno = ENOMEM;
-    return -1;
-  }
+  if (ata_debugmode > 1)
+    pout("  GetDevicePowerState() succeeded, state=%d\n", state);
+  return state;
+}
 
-  buf->IdeReg = *regs;
-  buf->DataBufferSize = datasize;
-  if (datasize)
-    buf->DataBuffer[0] = magic;
 
-  if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
-    buf, size, buf, size, &num_out, NULL)) {
-    long err = GetLastError();
-    if (ata_debugmode) {
-      pout("  IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
-      print_ide_regs_io(regs, NULL);
-    }
-    VirtualFree(buf, 0, MEM_RELEASE);
-    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
-    return -1;
-  }
+/////////////////////////////////////////////////////////////////////////////
+// win_ata_device
 
-  // Check ATA status
-  if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
-    if (ata_debugmode) {
-      pout("  IOCTL_IDE_PASS_THROUGH command failed:\n");
-      print_ide_regs_io(regs, &buf->IdeReg);
-    }
-    VirtualFree(buf, 0, MEM_RELEASE);
-    errno = EIO;
-    return -1;
-  }
+class win_ata_device
+: public /*implements*/ ata_device,
+  public /*extends*/ win_smart_device
+{
+public:
+  win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
 
-  // Check and copy data
-  if (datasize) {
-    if (   num_out != size
-        || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
-      if (ata_debugmode) {
-        pout("  IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n",
-          (unsigned)num_out, (unsigned)buf->DataBufferSize);
-        print_ide_regs_io(regs, &buf->IdeReg);
-      }
-      VirtualFree(buf, 0, MEM_RELEASE);
-      errno = EIO;
-      return -1;
-    }
-    memcpy(data, buf->DataBuffer, datasize);
-  }
+  virtual ~win_ata_device() throw();
 
-  if (ata_debugmode > 1) {
-    pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n",
-      (unsigned)num_out, (unsigned)buf->DataBufferSize);
-    print_ide_regs_io(regs, &buf->IdeReg);
-  }
-  *regs = buf->IdeReg;
+  virtual bool open();
 
-  // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
-  VirtualFree(buf, 0, MEM_RELEASE);
-  return 0;
-}
+  virtual bool is_powered_down();
 
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
 
-/////////////////////////////////////////////////////////////////////////////
-// ATA PASS THROUGH (Win2003, XP SP2)
+  virtual bool ata_identify_is_cached() const;
 
-// Warning:
-// IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
-// transfer per command. Therefore, multi-sector transfers are only supported
-// for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
-// or READ/WRITE LOG EXT work only with single sector transfers.
-// The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
-// See:
-// http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
+private:
+  bool open(bool query_device);
 
-static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
-{
-  const int max_sectors = 32; // TODO: Allocate dynamic buffer
+  bool open(int phydrive, int logdrive, const char * options, int port, bool query_device);
 
-  typedef struct {
-    ATA_PASS_THROUGH_EX apt;
-    ULONG Filler;
-    UCHAR ucDataBuf[max_sectors * 512];
-  } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
+  std::string m_options;
+  bool m_usr_options; // options set by user?
+  bool m_admin; // open with admin access?
+  int m_phydrive; // PhysicalDriveN or -1
+  bool m_id_is_cached; // ata_identify_is_cached() return value.
+  bool m_is_3ware; // LSI/3ware controller detected?
+  int m_port; // LSI/3ware port
+  int m_smartver_state;
+};
 
-  const unsigned char magic = 0xcf;
 
-  ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
-  ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
-  //ab.apt.PathId = 0;
-  //ab.apt.TargetId = 0;
-  //ab.apt.Lun = 0;
-  ab.apt.TimeOutValue = 10;
-  unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
-  ab.apt.DataBufferOffset = size;
+win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "ata", req_type),
+  m_usr_options(false),
+  m_admin(false),
+  m_phydrive(-1),
+  m_id_is_cached(false),
+  m_is_3ware(false),
+  m_port(-1),
+  m_smartver_state(0)
+{
+}
 
-  if (datasize > 0) {
-    if (datasize > (int)sizeof(ab.ucDataBuf)) {
-      errno = EINVAL;
-      return -1;
-    }
-    ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
-    ab.apt.DataTransferLength = datasize;
-    size += datasize;
-    ab.ucDataBuf[0] = magic;
+win_ata_device::~win_ata_device() throw()
+{
+}
+
+// Get default ATA device options
+
+static const char * ata_get_def_options()
+{
+  return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
+                   // STORAGE_*, SCSI_MINIPORT_*
+}
+
+// Open ATA device
+
+bool win_ata_device::open()
+{
+  // Open device for r/w operations
+  return open(false);
+}
+
+bool win_ata_device::open(bool query_device)
+{
+  const char * name = skipdev(get_dev_name()); int len = strlen(name);
+  // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options
+  char drive[2+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;
+  if (   sscanf(name, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive, &n1, options, &n2) >= 1
+      && ((n1 == len && !options[0]) || n2 == len)                                   ) {
+    return open(sdxy_to_phydrive(drive), -1, options, -1, query_device);
   }
-  else if (datasize < 0) {
-    if (-datasize > (int)sizeof(ab.ucDataBuf)) {
-      errno = EINVAL;
-      return -1;
-    }
-    ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
-    ab.apt.DataTransferLength = -datasize;
-    size += -datasize;
-    memcpy(ab.ucDataBuf, data, -datasize);
+  // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options
+  drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
+  unsigned port = ~0;
+  if (   sscanf(name, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive, &port, &n1, options, &n2) >= 2
+      && port < 32 && ((n1 == len && !options[0]) || n2 == len)                                  ) {
+    return open(sdxy_to_phydrive(drive), -1, options, port, query_device);
   }
-  else {
-    assert(ab.apt.AtaFlags == 0);
-    assert(ab.apt.DataTransferLength == 0);
+  // pd<m>,N => Physical drive <m>, RAID port N
+  int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
+  if (   sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
+      && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
+    return open(phydrive, -1, "", (int)port, query_device);
+  }
+  // [a-zA-Z]: => Physical drive behind logical drive 0-25
+  int logdrive = drive_letter(name);
+  if (logdrive >= 0) {
+    return open(-1, logdrive, "", -1, query_device);
   }
 
-  assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
-  IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
-  IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
-  *ctfregs = *regs;
+  return set_err(EINVAL);
+}
 
-  if (prev_regs) {
-    *ptfregs = *prev_regs;
-    ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
-  }
 
-  DWORD num_out;
-  if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
-    &ab, size, &ab, size, &num_out, NULL)) {
+bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port, bool query_device)
+{
+  m_phydrive = -1;
+  char devpath[30];
+  if (0 <= phydrive && phydrive <= 255)
+    snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive = phydrive));
+  else if (0 <= logdrive && logdrive <= 'Z'-'A')
+    snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
+  else
+    return set_err(ENOENT);
+
+  // Open device
+  HANDLE h = INVALID_HANDLE_VALUE;
+  if (!(*options && !options[strspn(options, "fp")]) && !query_device) {
+    // Open with admin rights
+    m_admin = true;
+    h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
+      FILE_SHARE_READ|FILE_SHARE_WRITE,
+      NULL, OPEN_EXISTING, 0, 0);
+  }
+  if (h == INVALID_HANDLE_VALUE) {
+    // Open without admin rights
+    m_admin = false;
+    h = CreateFileA(devpath, 0,
+      FILE_SHARE_READ|FILE_SHARE_WRITE,
+      NULL, OPEN_EXISTING, 0, 0);
+  }
+  if (h == INVALID_HANDLE_VALUE) {
     long err = GetLastError();
-    if (ata_debugmode) {
-      pout("  IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
-      print_ide_regs_io(regs, NULL);
-    }
-    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
-    return -1;
+    if (err == ERROR_FILE_NOT_FOUND)
+      set_err(ENOENT, "%s: not found", devpath);
+    else if (err == ERROR_ACCESS_DENIED)
+      set_err(EACCES, "%s: access denied", devpath);
+    else
+      set_err(EIO, "%s: Error=%ld", devpath, err);
+    return false;
   }
+  set_fh(h);
 
-  // Check ATA status
-  if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
-    if (ata_debugmode) {
-      pout("  IOCTL_ATA_PASS_THROUGH command failed:\n");
-      print_ide_regs_io(regs, ctfregs);
+  // Warn once if admin rights are missing
+  if (!m_admin && !query_device) {
+    static bool noadmin_warning = false;
+    if (!noadmin_warning) {
+      pout("Warning: Limited functionality due to missing admin rights\n");
+      noadmin_warning = true;
     }
-    errno = EIO;
-    return -1;
   }
 
-  // Check and copy data
-  if (datasize > 0) {
-    if (   num_out != size
-        || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
-      if (ata_debugmode) {
-        pout("  IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out);
-        print_ide_regs_io(regs, ctfregs);
-      }
-      errno = EIO;
-      return -1;
+  if (ata_debugmode > 1)
+    pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :""));
+
+  m_usr_options = false;
+  if (*options) {
+    // Save user options
+    m_options = options; m_usr_options = true;
+  }
+  else if (port >= 0)
+    // RAID: SMART_* and SCSI_MINIPORT
+    m_options = "s3";
+  else {
+    // Set default options according to Windows version
+    static const char * def_options = ata_get_def_options();
+    m_options = def_options;
+  }
+
+  // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
+  m_port = port;
+  if (port < 0)
+    return true;
+
+  // 3ware RAID: Get port map
+  GETVERSIONINPARAMS_EX vers_ex;
+  int devmap = smart_get_version(h, &vers_ex);
+
+  // 3ware RAID if vendor id present
+  m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
+
+  unsigned long portmap = 0;
+  if (port >= 0 && devmap >= 0) {
+    // 3ware RAID: check vendor id
+    if (!m_is_3ware) {
+      pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
+           "This is no 3ware 9000 controller or driver has no SMART support.\n",
+           vers_ex.wIdentifier);
+      devmap = -1;
+    }
+    else
+      portmap = vers_ex.dwDeviceMapEx;
+  }
+  if (devmap < 0) {
+    pout("%s: ATA driver has no SMART support\n", devpath);
+    if (!is_permissive()) {
+      close();
+      return set_err(ENOSYS);
     }
-    memcpy(data, ab.ucDataBuf, datasize);
   }
+  m_smartver_state = 1;
 
-  if (ata_debugmode > 1) {
-    pout("  IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out);
-    print_ide_regs_io(regs, ctfregs);
+  {
+    // 3ware RAID: update devicemap first
+
+    if (!update_3ware_devicemap_ioctl(h)) {
+      if (   smart_get_version(h, &vers_ex) >= 0
+          && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
+        portmap = vers_ex.dwDeviceMapEx;
+    }
+    // Check port existence
+    if (!(portmap & (1L << port))) {
+      if (!is_permissive()) {
+        close();
+        return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
+      }
+    }
   }
-  *regs = *ctfregs;
-  if (prev_regs)
-    *prev_regs = *ptfregs;
 
-  return 0;
+  return true;
 }
 
 
 /////////////////////////////////////////////////////////////////////////////
-// SMART IOCTL via SCSI MINIPORT ioctl
 
-// This function is handled by ATAPI port driver (atapi.sys) or by SCSI
-// miniport driver (via SCSI port driver scsiport.sys).
-// It can be used to skip the missing or broken handling of some SMART
-// command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
+// Query OS if device is powered up or down.
+bool win_ata_device::is_powered_down()
+{
+  // To check power mode, we open device for query operations only.
+  // Opening for SMART r/w operations can already spin up the disk.
+  bool self_open = !is_open();
+  if (self_open)
+    if (!open(true))
+      return false;
+  int rc = get_device_power_state(get_fh());
+  if (self_open)
+    close();
+  return !rc;
+}
 
-static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
+/////////////////////////////////////////////////////////////////////////////
+
+// Interface to ATA devices
+bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 {
-  // Select code
-  DWORD code = 0; const char * name = 0;
-  if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
-    code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
-  }
-  else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
-    case ATA_SMART_READ_VALUES:
-      code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
-    case ATA_SMART_READ_THRESHOLDS:
-      code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
-    case ATA_SMART_ENABLE:
-      code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
-    case ATA_SMART_DISABLE:
-      code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
-    case ATA_SMART_STATUS:
-      code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
-    case ATA_SMART_AUTOSAVE:
-      code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
-  //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
-  //  code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
-    case ATA_SMART_IMMEDIATE_OFFLINE:
-      code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
-    case ATA_SMART_AUTO_OFFLINE:
-      code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
-    case ATA_SMART_READ_LOG_SECTOR:
-      code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
-    case ATA_SMART_WRITE_LOG_SECTOR:
-      code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
-  }
-  if (!code) {
-    errno = ENOSYS;
-    return -1;
-  }
-
-  // Set SRB
-  struct {
-    SRB_IO_CONTROL srbc;
-    union {
-      SENDCMDINPARAMS in;
-      SENDCMDOUTPARAMS out;
-    } params;
-    char space[512-1];
-  } sb;
-  ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
-  memset(&sb, 0, sizeof(sb));
-
-  unsigned size;
-  if (datasize > 0) {
-    if (datasize > (int)sizeof(sb.space)+1) {
-      errno = EINVAL;
-      return -1;
-    }
-    size = datasize;
-  }
-  else if (datasize < 0) {
-    if (-datasize > (int)sizeof(sb.space)+1) {
-      errno = EINVAL;
-      return -1;
-    }
-    size = -datasize;
-    memcpy(sb.params.in.bBuffer, data, size);
-  }
-  else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
-    size = sizeof(IDEREGS);
-  else
-    size = 0;
-  sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
-  memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
-  sb.srbc.Timeout = 60; // seconds
-  sb.srbc.ControlCode = code;
-  //sb.srbc.ReturnCode = 0;
-  sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
-  sb.params.in.irDriveRegs = *regs;
-  sb.params.in.cBufferSize = size;
+  // No multi-sector support for now, see above
+  // warning about IOCTL_ATA_PASS_THROUGH
+  if (!ata_cmd_is_supported(in,
+    ata_device::supports_data_out |
+    ata_device::supports_output_regs |
+    ata_device::supports_48bit)
+  )
+    return false;
 
-  // Call miniport ioctl
-  size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
-  DWORD num_out;
-  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
-    &sb, size, &sb, size, &num_out, NULL)) {
-    long err = GetLastError();
-    if (ata_debugmode) {
-      pout("  IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
-      print_ide_regs_io(regs, NULL);
-    }
-    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
-    return -1;
-  }
+  // 3ware RAID: SMART DISABLE without port number disables SMART functions
+  if (   m_is_3ware && m_port < 0
+      && in.in_regs.command == ATA_SMART_CMD
+      && in.in_regs.features == ATA_SMART_DISABLE)
+    return set_err(ENOSYS, "SMART DISABLE requires 3ware port number");
 
-  // Check result
-  if (sb.srbc.ReturnCode) {
-    if (ata_debugmode) {
-      pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name, (unsigned)sb.srbc.ReturnCode);
-      print_ide_regs_io(regs, NULL);
-    }
-    errno = EIO;
-    return -1;
-  }
+  // Determine ioctl functions valid for this ATA cmd
+  const char * valid_options = 0;
 
-  if (sb.params.out.DriverStatus.bDriverError) {
-    if (ata_debugmode) {
-      pout("  IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
-        sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
-      print_ide_regs_io(regs, NULL);
-    }
-    errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
-    return -1;
-  }
+  switch (in.in_regs.command) {
+    case ATA_IDENTIFY_DEVICE:
+    case ATA_IDENTIFY_PACKET_DEVICE:
+      // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
+      // and SCSI_MINIPORT_* if requested by user
+      valid_options = (m_usr_options ? "saimf" : "saif");
+      break;
 
-  if (ata_debugmode > 1) {
-    pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %u (buffer %u)\n", name,
-      (unsigned)num_out, (unsigned)sb.params.out.cBufferSize);
-    print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
-                             (const IDEREGS *)(sb.params.out.bBuffer) : 0));
-  }
+    case ATA_CHECK_POWER_MODE:
+      // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
+      valid_options = "pai3";
+      break;
 
-  if (datasize > 0)
-    memcpy(data, sb.params.out.bBuffer, datasize);
-  else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
-    memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS));
+    case ATA_SMART_CMD:
+      switch (in.in_regs.features) {
+        case ATA_SMART_READ_VALUES:
+        case ATA_SMART_READ_THRESHOLDS:
+        case ATA_SMART_AUTOSAVE:
+        case ATA_SMART_ENABLE:
+        case ATA_SMART_DISABLE:
+        case ATA_SMART_AUTO_OFFLINE:
+          // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
+          // and SCSI_MINIPORT_* if requested by user
+          valid_options = (m_usr_options ? "saimf" : "saif");
+          break;
 
-  return 0;
-}
+        case ATA_SMART_IMMEDIATE_OFFLINE:
+          // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST
+          valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ ?
+                           "saim3" : "aim3");
+          break;
 
+        case ATA_SMART_READ_LOG_SECTOR:
+          // SMART_RCV_DRIVE_DATA does not support READ_LOG
+          // Try SCSI_MINIPORT also to skip buggy class driver
+          // SMART functions do not support multi sector I/O.
+          if (in.size == 512)
+            valid_options = (m_usr_options ? "saim3" : "aim3");
+          else
+            valid_options = "a";
+          break;
 
-/////////////////////////////////////////////////////////////////////////////
+        case ATA_SMART_WRITE_LOG_SECTOR:
+          // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
+          // but SCSI_MINIPORT_* only if requested by user and single sector.
+          valid_options = (in.size == 512 && m_usr_options ? "am" : "a");
+          break;
 
-// ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
+        case ATA_SMART_STATUS:
+          valid_options = (m_usr_options ? "saimf" : "saif");
+          break;
 
-static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
-{
-  struct {
-    SRB_IO_CONTROL srbc;
-    IDEREGS regs;
-    UCHAR buffer[512];
-  } sb;
-  ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
+        default:
+          // Unknown SMART command, handle below
+          break;
+      }
+      break;
 
-  if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
-    errno = EINVAL;
-    return -1;
+    default:
+      // Other ATA command, handle below
+      break;
   }
-  memset(&sb, 0, sizeof(sb));
-  strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature));
-  sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
-  sb.srbc.Timeout = 60; // seconds
-  sb.srbc.ControlCode = 0xA0000000;
-  sb.srbc.ReturnCode = 0;
-  sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
-  sb.regs = *regs;
-  sb.regs.bReserved = port;
 
-  DWORD num_out;
-  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
-    &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
-    long err = GetLastError();
-    if (ata_debugmode) {
-      pout("  ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
-      print_ide_regs_io(regs, NULL);
-    }
-    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
-    return -1;
+  if (!valid_options) {
+    // No special ATA command found above, select a generic pass through ioctl.
+    if (!(   in.direction == ata_cmd_in::no_data
+          || (in.direction == ata_cmd_in::data_in && in.size == 512))
+         ||  in.in_regs.is_48bit_cmd()                               )
+      // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
+      valid_options = "a";
+    else
+      // ATA/IDE_PASS_THROUGH
+      valid_options = "ai";
   }
 
-  if (sb.srbc.ReturnCode) {
-    if (ata_debugmode) {
-      pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb.srbc.ReturnCode);
-      print_ide_regs_io(regs, NULL);
-    }
-    errno = EIO;
-    return -1;
+  if (!m_admin) {
+    // Restrict to IOCTL_STORAGE_*
+    if (strchr(valid_options, 'f'))
+      valid_options = "f";
+    else if (strchr(valid_options, 'p'))
+      valid_options = "p";
+    else
+      return set_err(ENOSYS, "Function requires admin rights");
   }
 
-  // Copy data
-  if (datasize > 0)
-    memcpy(data, sb.buffer, datasize);
-
-  if (ata_debugmode > 1) {
-    pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out);
-    print_ide_regs_io(regs, &sb.regs);
+  // Set IDEREGS
+  IDEREGS regs, prev_regs;
+  {
+    const ata_in_regs & lo = in.in_regs;
+    regs.bFeaturesReg     = lo.features;
+    regs.bSectorCountReg  = lo.sector_count;
+    regs.bSectorNumberReg = lo.lba_low;
+    regs.bCylLowReg       = lo.lba_mid;
+    regs.bCylHighReg      = lo.lba_high;
+    regs.bDriveHeadReg    = lo.device;
+    regs.bCommandReg      = lo.command;
+    regs.bReserved        = 0;
   }
-  *regs = sb.regs;
-
-  return 0;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-// 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
-// 3DM/CLI "Rescan Controller" function does not to always update it.
-
-static int update_3ware_devicemap_ioctl(HANDLE hdevice)
-{
-  SRB_IO_CONTROL srbc;
-  memset(&srbc, 0, sizeof(srbc));
-  strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature));
-  srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
-  srbc.Timeout = 60; // seconds
-  srbc.ControlCode = 0xCC010014;
-  srbc.ReturnCode = 0;
-  srbc.Length = 0;
-
-  DWORD num_out;
-  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
-    &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
-    long err = GetLastError();
-    if (ata_debugmode)
-      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
-    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
-    return -1;
-  }
-  if (srbc.ReturnCode) {
-    if (ata_debugmode)
-      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc.ReturnCode);
-    errno = EIO;
-    return -1;
+  if (in.in_regs.is_48bit_cmd()) {
+    const ata_in_regs & hi = in.in_regs.prev;
+    prev_regs.bFeaturesReg     = hi.features;
+    prev_regs.bSectorCountReg  = hi.sector_count;
+    prev_regs.bSectorNumberReg = hi.lba_low;
+    prev_regs.bCylLowReg       = hi.lba_mid;
+    prev_regs.bCylHighReg      = hi.lba_high;
+    prev_regs.bDriveHeadReg    = hi.device;
+    prev_regs.bCommandReg      = hi.command;
+    prev_regs.bReserved        = 0;
   }
-  if (ata_debugmode > 1)
-    pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
-  return 0;
-}
-
 
+  // Set data direction
+  int datasize = 0;
+  char * data = 0;
+  switch (in.direction) {
+    case ata_cmd_in::no_data:
+      break;
+    case ata_cmd_in::data_in:
+      datasize = (int)in.size;
+      data = (char *)in.buffer;
+      break;
+    case ata_cmd_in::data_out:
+      datasize = -(int)in.size;
+      data = (char *)in.buffer;
+      break;
+    default:
+      return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d",
+          (int)in.direction);
+  }
 
-/////////////////////////////////////////////////////////////////////////////
 
-// Routines for pseudo device /dev/tw_cli/*
-// Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
+  // Try all valid ioctls in the order specified in m_options
+  bool powered_up = false;
+  bool out_regs_set = false;
+  bool id_is_cached = false;
+  const char * options = m_options.c_str();
 
+  for (int i = 0; ; i++) {
+    char opt = options[i];
 
-// Get clipboard data
+    if (!opt) {
+      if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) {
+        // Power up reported by GetDevicePowerState() and no ioctl available
+        // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
+        regs.bSectorCountReg = 0xff;
+        out_regs_set = true;
+        break;
+      }
+      // No IOCTL found
+      return set_err(ENOSYS);
+    }
+    if (!strchr(valid_options, opt))
+      // Invalid for this command
+      continue;
 
-static int get_clipboard(char * data, int datasize)
-{
-  if (!OpenClipboard(NULL))
-    return -1;
-  HANDLE h = GetClipboardData(CF_TEXT);
-  if (!h) {
-    CloseClipboard();
-    return 0;
-  }
-  const void * p = GlobalLock(h);
-  int n = GlobalSize(h);
-  if (n > datasize)
-    n = datasize;
-  memcpy(data, p, n);
-  GlobalFree(h);
-  CloseClipboard();
-  return n;
-}
+    errno = 0;
+    assert(   datasize == 0 || datasize == 512
+           || (datasize == -512 && strchr("am", opt))
+           || (datasize > 512 && opt == 'a'));
+    int rc;
+    switch (opt) {
+      default: assert(0);
+      case 's':
+        // call SMART_GET_VERSION once for each drive
+        if (m_smartver_state > 1) {
+          rc = -1; errno = ENOSYS;
+          break;
+        }
+        if (!m_smartver_state) {
+          assert(m_port == -1);
+          GETVERSIONINPARAMS_EX vers_ex;
+          if (smart_get_version(get_fh(), &vers_ex) < 0) {
+            if (!failuretest_permissive) {
+              m_smartver_state = 2;
+              rc = -1; errno = ENOSYS;
+              break;
+            }
+            failuretest_permissive--;
+          }
+          else  {
+            // 3ware RAID if vendor id present
+            m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
+          }
 
+          m_smartver_state = 1;
+        }
+        rc = smart_ioctl(get_fh(), &regs, data, datasize, m_port);
+        out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
+        id_is_cached = (m_port < 0); // Not cached by 3ware driver
+        break;
+      case 'm':
+        rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);
+        id_is_cached = (m_port < 0);
+        break;
+      case 'a':
+        rc = ata_pass_through_ioctl(get_fh(), &regs,
+          (in.in_regs.is_48bit_cmd() ? &prev_regs : 0),
+          data, datasize);
+        out_regs_set = true;
+        break;
+      case 'i':
+        rc = ide_pass_through_ioctl(get_fh(), &regs, data, datasize);
+        out_regs_set = true;
+        break;
+      case 'f':
+        if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
+            ata_identify_device * id = reinterpret_cast<ata_identify_device *>(data);
+            rc = get_identify_from_device_property(get_fh(), id);
+            if (rc == 0 && m_phydrive >= 0)
+              get_serial_from_wmi(m_phydrive, id);
+            id_is_cached = true;
+        }
+        else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
+          case ATA_SMART_READ_VALUES:
+            rc = storage_predict_failure_ioctl(get_fh(), data);
+            if (rc > 0)
+              rc = 0;
+            break;
+          case ATA_SMART_ENABLE:
+            rc = 0;
+            break;
+          case ATA_SMART_STATUS:
+            rc = storage_predict_failure_ioctl(get_fh());
+            if (rc == 0) {
+              // Good SMART status
+              out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
+            }
+            else if (rc > 0) {
+              // Bad SMART status
+              out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
+              rc = 0;
+            }
+            break;
+          default:
+            errno = ENOSYS; rc = -1;
+        }
+        else {
+            errno = ENOSYS; rc = -1;
+        }
+        break;
+      case '3':
+        rc = ata_via_3ware_miniport_ioctl(get_fh(), &regs, data, datasize, m_port);
+        out_regs_set = true;
+        break;
+      case 'p':
+        assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0);
+        rc = get_device_power_state(get_fh());
+        if (rc == 0) {
+          // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
+          // spin up the drive => simulate ATA result STANDBY.
+          regs.bSectorCountReg = 0x00;
+          out_regs_set = true;
+        }
+        else if (rc > 0) {
+          // Power up reported by GetDevicePowerState(), but this reflects the actual mode
+          // only if it is selected by the device driver => try a passthrough ioctl to get the
+          // actual mode, if none available simulate ACTIVE/IDLE.
+          powered_up = true;
+          rc = -1; errno = ENOSYS;
+        }
+        break;
+    }
 
-// Run a command, write stdout to dataout
-// TODO: Combine with daemon_win32.cpp:daemon_spawn()
+    if (!rc)
+      // Working ioctl found
+      break;
 
-static int run_cmd(const char * cmd, char * dataout, int outsize)
-{
-  // Create stdout pipe
-  SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
-  HANDLE pipe_out_w, h;
-  if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
-    return -1;
-  HANDLE self = GetCurrentProcess();
-  HANDLE pipe_out_r;
-  if (!DuplicateHandle(self, h, self, &pipe_out_r,
-    GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
-    CloseHandle(pipe_out_w);
-    return -1;
-  }
-  HANDLE pipe_err_w;
-  if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
-    0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
-    CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-    return -1;
-  }
+    if (errno != ENOSYS)
+      // Abort on I/O error
+      return set_err(errno);
 
-  // Create process
-  STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
-  si.hStdInput  = INVALID_HANDLE_VALUE;
-  si.hStdOutput = pipe_out_w; si.hStdError  = pipe_err_w;
-  si.dwFlags = STARTF_USESTDHANDLES;
-  PROCESS_INFORMATION pi;
-  if (!CreateProcess(
-    NULL, const_cast<char *>(cmd),
-    NULL, NULL, TRUE/*inherit*/,
-    CREATE_NO_WINDOW/*do not create a new console window*/,
-    NULL, NULL, &si, &pi)) {
-    CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-    return -1;
+    out_regs_set = false;
+    // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
   }
-  CloseHandle(pi.hThread);
-  CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
 
-  // Copy stdout to output buffer
-  int i = 0;
-  while (i < outsize) {
-    DWORD num_read;
-    if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
-      break;
-    i += num_read;
+  // Return IDEREGS if set
+  if (out_regs_set) {
+    ata_out_regs & lo = out.out_regs;
+    lo.error        = regs.bFeaturesReg;
+    lo.sector_count = regs.bSectorCountReg;
+    lo.lba_low      = regs.bSectorNumberReg;
+    lo.lba_mid      = regs.bCylLowReg;
+    lo.lba_high     = regs.bCylHighReg;
+    lo.device       = regs.bDriveHeadReg;
+    lo.status       = regs.bCommandReg;
+    if (in.in_regs.is_48bit_cmd()) {
+      ata_out_regs & hi = out.out_regs.prev;
+      hi.sector_count = prev_regs.bSectorCountReg;
+      hi.lba_low      = prev_regs.bSectorNumberReg;
+      hi.lba_mid      = prev_regs.bCylLowReg;
+      hi.lba_high     = prev_regs.bCylHighReg;
+    }
   }
-  CloseHandle(pipe_out_r);
-  // Wait for process
-  WaitForSingleObject(pi.hProcess, INFINITE);
-  CloseHandle(pi.hProcess);
-  return i;
-}
 
+  if (   in.in_regs.command == ATA_IDENTIFY_DEVICE
+      || in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE)
+    // Update ata_identify_is_cached() result according to ioctl used.
+    m_id_is_cached = id_is_cached;
 
-static const char * findstr(const char * str, const char * sub)
-{
-  const char * s = strstr(str, sub);
-  return (s ? s+strlen(sub) : "");
+  return true;
 }
 
-
-static void copy_swapped(unsigned char * dest, const char * src, int destsize)
+// Return true if OS caches the ATA identify sector
+bool win_ata_device::ata_identify_is_cached() const
 {
-  int srclen = strcspn(src, "\r\n");
-  int i;
-  for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
-    dest[i] = src[i+1]; dest[i+1] = src[i];
-  }
-  if (i < destsize-1 && i < srclen)
-    dest[i+1] = src[i];
+  return m_id_is_cached;
 }
 
 
-// TODO: This is OS independent
+//////////////////////////////////////////////////////////////////////
+// csmi_device
 
-win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type)
-: smart_device(intf, dev_name, "tw_cli", req_type),
-  m_ident_valid(false), m_smart_valid(false)
+class csmi_device
+: virtual public /*extends*/ smart_device
 {
-  memset(&m_ident_buf, 0, sizeof(m_ident_buf));
-  memset(&m_smart_buf, 0, sizeof(m_smart_buf));
-}
+public:
+  /// Get bitmask of used ports
+  unsigned get_ports_used();
 
+protected:
+  csmi_device()
+    : smart_device(never_called)
+    { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); }
 
-bool win_tw_cli_device::is_open() const
-{
-  return (m_ident_valid || m_smart_valid);
-}
+  /// Get phy info
+  bool get_phy_info(CSMI_SAS_PHY_INFO & phy_info);
 
+  /// Select physical drive
+  bool select_port(int port);
 
-bool win_tw_cli_device::open()
+  /// Get info for selected physical drive
+  const CSMI_SAS_PHY_ENTITY & get_phy_ent() const
+    { return m_phy_ent; }
+
+  /// Call platform-specific CSMI ioctl
+  virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
+    unsigned csmi_bufsiz) = 0;
+
+private:
+  CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
 {
-  m_ident_valid = m_smart_valid = false;
-  const char * name = skipdev(get_dev_name());
-  // Read tw_cli or 3DM browser output into buffer
-  char buffer[4096];
-  int size = -1, n1 = -1, n2 = -1;
-  if (!strcmp(name, "tw_cli/clip")) { // read clipboard
-    size = get_clipboard(buffer, sizeof(buffer));
-  }
-  else if (!strcmp(name, "tw_cli/stdin")) {  // read stdin
-    size = fread(buffer, 1, sizeof(buffer), stdin);
-  }
-  else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) {
-    // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
-    char cmd[100];
-    snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
-    if (ata_debugmode > 1)
-      pout("%s: Run: \"%s\"\n", name, cmd);
-    size = run_cmd(cmd, buffer, sizeof(buffer));
-  }
-  else {
-    return set_err(EINVAL);
+  // Get driver info to check CSMI support
+  CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf;
+  memset(&driver_info_buf, 0, sizeof(driver_info_buf));
+  if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO, &driver_info_buf.IoctlHeader, sizeof(driver_info_buf)))
+    return false;
+
+  if (scsi_debugmode > 1) {
+    const CSMI_SAS_DRIVER_INFO & driver_info = driver_info_buf.Information;
+    pout("CSMI_SAS_DRIVER_INFO:\n");
+    pout("  Name:        \"%.81s\"\n", driver_info.szName);
+    pout("  Description: \"%.81s\"\n", driver_info.szDescription);
+    pout("  Revision:    %d.%d\n", driver_info.usMajorRevision, driver_info.usMinorRevision);
   }
 
-  if (ata_debugmode > 1)
-    pout("%s: Read %d bytes\n", name, size);
-  if (size <= 0)
-    return set_err(ENOENT);
-  if (size >= (int)sizeof(buffer))
-    return set_err(EIO);
+  // Get Phy info
+  CSMI_SAS_PHY_INFO_BUFFER phy_info_buf;
+  memset(&phy_info_buf, 0, sizeof(phy_info_buf));
+  if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO, &phy_info_buf.IoctlHeader, sizeof(phy_info_buf)))
+    return false;
 
-  buffer[size] = 0;
-  if (ata_debugmode > 1)
-    pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
+  phy_info = phy_info_buf.Information;
 
-  // Fake identify sector
-  ASSERT_SIZEOF(ata_identify_device, 512);
-  ata_identify_device * id = &m_ident_buf;
-  memset(id, 0, sizeof(*id));
-  copy_swapped(id->model    , findstr(buffer, " Model = "   ), sizeof(id->model));
-  copy_swapped(id->fw_rev   , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
-  copy_swapped(id->serial_no, findstr(buffer, " Serial = "  ), sizeof(id->serial_no));
-  unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
-  sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
-  if (nblocks) {
-    id->words047_079[49-47] = 0x0200; // size valid
-    id->words047_079[60-47] = (unsigned short)(nblocks    ); // secs_16
-    id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
-  }
-  id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
-  id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
+  const int max_number_of_phys = sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]);
+  if (phy_info.bNumberOfPhys > max_number_of_phys)
+    return set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys);
 
-  // Parse smart data hex dump
-  const char * s = findstr(buffer, "Drive Smart Data:");
-  if (!*s)
-    s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x
-  if (!*s) {
-    s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
-    if (*s) {
-      const char * s1 = findstr(s, "<td class"); // html version
-      if (*s1)
-        s = s1;
-      s += strcspn(s, "\r\n");
+  if (scsi_debugmode > 1) {
+    pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info.bNumberOfPhys);
+    for (int i = 0; i < max_number_of_phys; i++) {
+      const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
+      const CSMI_SAS_IDENTIFY & id = pe.Identify, & at = pe.Attached;
+      if (id.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+        continue;
+
+      pout("Phy[%d] Port:   0x%02x\n", i, pe.bPortIdentifier);
+      pout("  Type:        0x%02x, 0x%02x\n", id.bDeviceType, at.bDeviceType);
+      pout("  InitProto:   0x%02x, 0x%02x\n", id.bInitiatorPortProtocol, at.bInitiatorPortProtocol);
+      pout("  TargetProto: 0x%02x, 0x%02x\n", id.bTargetPortProtocol, at.bTargetPortProtocol);
+      pout("  PhyIdent:    0x%02x, 0x%02x\n", id.bPhyIdentifier, at.bPhyIdentifier);
+      const unsigned char * b = id.bSASAddress;
+      pout("  SASAddress:  %02x %02x %02x %02x %02x %02x %02x %02x, ",
+        b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+      b = at.bSASAddress;
+      pout(               "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+        b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
     }
-    else
-      s = buffer; // try raw hex dump without header
-  }
-  unsigned char * sd = (unsigned char *)&m_smart_buf;
-  int i = 0;
-  for (;;) {
-    unsigned x = ~0; int n = -1;
-    if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
-      break;
-    sd[i] = (unsigned char)x;
-    if (!(++i < 512 && n > 0))
-      break;
-    s += n;
-    if (*s == '<') // "<br>"
-      s += strcspn(s, "\r\n");
   }
-  if (i < 512) {
-    if (!id->model[1]) {
-      // No useful data found
-      char * err = strstr(buffer, "Error:");
-      if (!err)
-        err = strstr(buffer, "error :");
-      if (err && (err = strchr(err, ':'))) {
-        // Show tw_cli error message
-        err++;
-        err[strcspn(err, "\r\n")] = 0;
-        return set_err(EIO, "%s", err);
-      }
-      return set_err(EIO);
+
+  return true;
+}
+
+unsigned csmi_device::get_ports_used()
+{
+  CSMI_SAS_PHY_INFO phy_info;
+  if (!get_phy_info(phy_info))
+    return 0;
+
+  unsigned ports_used = 0;
+  for (unsigned i = 0; i < sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); i++) {
+    const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
+    if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+      continue;
+    if (pe.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+      continue;
+    switch (pe.Attached.bTargetPortProtocol) {
+      case CSMI_SAS_PROTOCOL_SATA:
+      case CSMI_SAS_PROTOCOL_STP:
+        break;
+      default:
+        continue;
     }
-    sd = 0;
+
+    if (pe.bPortIdentifier == 0xff)
+      // Older (<= 9.*) Intel RST driver
+      ports_used |= (1 << i);
+    else
+      ports_used |= (1 << pe.bPortIdentifier);
   }
 
-  m_ident_valid = true;
-  m_smart_valid = !!sd;
-  return true;
+  return ports_used;
 }
 
 
-bool win_tw_cli_device::close()
-{
-  m_ident_valid = m_smart_valid = false;
-  return true;
-}
+bool csmi_device::select_port(int port)
+{
+  CSMI_SAS_PHY_INFO phy_info;
+  if (!get_phy_info(phy_info))
+    return false;
+
+  // Find port
+  int max_port = -1, port_index = -1;
+  for (unsigned i = 0; i < sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); i++) {
+    const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
+    if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+      continue;
+
+    if (pe.bPortIdentifier == 0xff) {
+      // Older (<= 9.*) Intel RST driver
+      max_port = phy_info.bNumberOfPhys - 1;
+      if (i >= phy_info.bNumberOfPhys)
+        break;
+      if ((int)i != port)
+        continue;
+    }
+    else {
+      if (pe.bPortIdentifier > max_port)
+        max_port = pe.bPortIdentifier;
+      if (pe.bPortIdentifier != port)
+        continue;
+    }
+
+    port_index = i;
+    break;
+  }
+
+  if (port_index < 0) {
+    if (port <= max_port)
+      return set_err(ENOENT, "Port %d is disabled", port);
+    else
+      return set_err(ENOENT, "Port %d does not exist (#ports: %d)", port,
+        max_port + 1);
+  }
 
+  const CSMI_SAS_PHY_ENTITY & phy_ent = phy_info.Phy[port_index];
+  if (phy_ent.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+    return set_err(ENOENT, "No device on port %d", port);
 
-int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data)
-{
-  switch (command) {
-    case IDENTIFY:
-      if (!m_ident_valid)
-        break;
-      memcpy(data, &m_ident_buf, 512);
-      return 0;
-    case READ_VALUES:
-      if (!m_smart_valid)
-        break;
-      memcpy(data, &m_smart_buf, 512);
-      return 0;
-    case ENABLE:
-    case STATUS:
-    case STATUS_CHECK: // Fake "good" SMART status
-      return 0;
-    default:
+  switch (phy_ent.Attached.bTargetPortProtocol) {
+    case CSMI_SAS_PROTOCOL_SATA:
+    case CSMI_SAS_PROTOCOL_STP:
       break;
+    default:
+      return set_err(ENOENT, "No SATA device on port %d (protocol: %d)",
+        port, phy_ent.Attached.bTargetPortProtocol);
   }
-  // Arrive here for all unsupported commands
-  set_err(ENOSYS);
-  return -1;
-}
-
 
-/////////////////////////////////////////////////////////////////////////////
-// IOCTL_STORAGE_QUERY_PROPERTY
+  m_phy_ent = phy_ent;
+  return true;
+}
 
-union STORAGE_DEVICE_DESCRIPTOR_DATA {
-  STORAGE_DEVICE_DESCRIPTOR desc;
-  char raw[256];
-};
 
-// Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
-// (This works without admin rights)
+//////////////////////////////////////////////////////////////////////
+// csmi_ata_device
 
-static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
+class csmi_ata_device
+: virtual public /*extends*/ csmi_device,
+  virtual public /*implements*/ ata_device
 {
-  STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
-  memset(data, 0, sizeof(*data));
+public:
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
 
-  DWORD num_out;
-  if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
-    &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
-    if (ata_debugmode > 1 || scsi_debugmode > 1)
-      pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError());
-    errno = ENOSYS;
-    return -1;
-  }
+protected:
+  csmi_ata_device()
+    : smart_device(never_called) { }
+};
 
-  if (ata_debugmode > 1 || scsi_debugmode > 1) {
-    pout("  IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
-         "    Vendor:   \"%s\"\n"
-         "    Product:  \"%s\"\n"
-         "    Revision: \"%s\"\n"
-         "    Removable: %s\n"
-         "    BusType:   0x%02x\n",
-         (data->desc.VendorIdOffset        ? data->raw+data->desc.VendorIdOffset : "(null)"),
-         (data->desc.ProductIdOffset       ? data->raw+data->desc.ProductIdOffset : "(null)"),
-         (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : "(null)"),
-         (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
-    );
-  }
-  return 0;
-}
 
+//////////////////////////////////////////////////////////////////////
 
-/////////////////////////////////////////////////////////////////////////////
-// IOCTL_STORAGE_PREDICT_FAILURE
+bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  if (!ata_cmd_is_supported(in,
+    ata_device::supports_data_out |
+    ata_device::supports_output_regs |
+    ata_device::supports_multi_sector |
+    ata_device::supports_48bit,
+    "CMSI")
+  )
+    return false;
 
-// Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
-// or -1 on error, opionally return VendorSpecific data.
-// (This works without admin rights)
+  // Create buffer with appropriate size
+  raw_buffer pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) + in.size);
+  CSMI_SAS_STP_PASSTHRU_BUFFER * pthru_buf = (CSMI_SAS_STP_PASSTHRU_BUFFER *)pthru_raw_buf.data();
 
-static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
-{
-  STORAGE_PREDICT_FAILURE pred;
-  memset(&pred, 0, sizeof(pred));
+  // Set addresses from Phy info
+  CSMI_SAS_STP_PASSTHRU & pthru = pthru_buf->Parameters;
+  const CSMI_SAS_PHY_ENTITY & phy_ent = get_phy_ent();
+  pthru.bPhyIdentifier = phy_ent.Identify.bPhyIdentifier;
+  pthru.bPortIdentifier = phy_ent.bPortIdentifier;
+  memcpy(pthru.bDestinationSASAddress, phy_ent.Attached.bSASAddress,
+    sizeof(pthru.bDestinationSASAddress));
+  pthru.bConnectionRate = CSMI_SAS_LINK_RATE_NEGOTIATED;
 
-  DWORD num_out;
-  if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
-    0, 0, &pred, sizeof(pred), &num_out, NULL)) {
-    if (ata_debugmode > 1)
-      pout("  IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError());
-    errno = ENOSYS;
-    return -1;
+  // Set transfer mode
+  switch (in.direction) {
+    case ata_cmd_in::no_data:
+      pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_UNSPECIFIED;
+      break;
+    case ata_cmd_in::data_in:
+      pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_READ;
+      pthru.uDataLength = in.size;
+      break;
+    case ata_cmd_in::data_out:
+      pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_WRITE;
+      pthru.uDataLength = in.size;
+      memcpy(pthru_buf->bDataBuffer, in.buffer, in.size);
+      break;
+    default:
+      return set_err(EINVAL, "csmi_ata_device::ata_pass_through: invalid direction=%d",
+        (int)in.direction);
   }
 
-  if (ata_debugmode > 1) {
-    pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
-         "    PredictFailure: 0x%08x\n"
-         "    VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
-         (unsigned)pred.PredictFailure,
-         pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
-         pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
-    );
+  // Set host-to-device FIS
+  {
+    unsigned char * fis = pthru.bCommandFIS;
+    const ata_in_regs & lo = in.in_regs;
+    const ata_in_regs & hi = in.in_regs.prev;
+    fis[ 0] = 0x27; // Type: host-to-device FIS
+    fis[ 1] = 0x80; // Bit7: Update command register
+    fis[ 2] = lo.command;
+    fis[ 3] = lo.features;
+    fis[ 4] = lo.lba_low;
+    fis[ 5] = lo.lba_mid;
+    fis[ 6] = lo.lba_high;
+    fis[ 7] = lo.device;
+    fis[ 8] = hi.lba_low;
+    fis[ 9] = hi.lba_mid;
+    fis[10] = hi.lba_high;
+    fis[11] = hi.features;
+    fis[12] = lo.sector_count;
+    fis[13] = hi.sector_count;
   }
-  if (data)
-    memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
-  return (!pred.PredictFailure ? 0 : 1);
-}
 
+  // Call ioctl
+  if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU, &pthru_buf->IoctlHeader, pthru_raw_buf.size())) {
+    return false;
+  }
 
-/////////////////////////////////////////////////////////////////////////////
+  // Get device-to-host FIS
+  {
+    const unsigned char * fis = pthru_buf->Status.bStatusFIS;
+    ata_out_regs & lo = out.out_regs;
+    lo.status       = fis[ 2];
+    lo.error        = fis[ 3];
+    lo.lba_low      = fis[ 4];
+    lo.lba_mid      = fis[ 5];
+    lo.lba_high     = fis[ 6];
+    lo.device       = fis[ 7];
+    lo.sector_count = fis[12];
+    if (in.in_regs.is_48bit_cmd()) {
+      ata_out_regs & hi = out.out_regs.prev;
+      hi.lba_low      = fis[ 8];
+      hi.lba_mid      = fis[ 9];
+      hi.lba_high     = fis[10];
+      hi.sector_count = fis[13];
+    }
+  }
+
+  // Get data
+  if (in.direction == ata_cmd_in::data_in)
+    // TODO: Check ptru_buf->Status.uDataBytes
+    memcpy(in.buffer, pthru_buf->bDataBuffer, in.size);
 
-// Return true if Intel ICHxR RAID volume
-static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
-{
-  if (!(data->desc.VendorIdOffset && data->desc.ProductIdOffset))
-    return false;
-  const char * vendor = data->raw + data->desc.VendorIdOffset;
-  if (!(!strnicmp(vendor, "Intel", 5) && strspn(vendor+5, " ") == strlen(vendor+5)))
-    return false;
-  if (strnicmp(data->raw + data->desc.ProductIdOffset, "Raid ", 5))
-    return false;
   return true;
 }
 
-// get DEV_* for open handle
-static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
+
+//////////////////////////////////////////////////////////////////////
+// win_csmi_device
+
+class win_csmi_device
+: public /*implements*/ csmi_ata_device
 {
-  // Get BusType from device descriptor
-  STORAGE_DEVICE_DESCRIPTOR_DATA data;
-  if (storage_query_property_ioctl(hdevice, &data))
-    return DEV_UNKNOWN;
+public:
+  win_csmi_device(smart_interface * intf, const char * dev_name,
+    const char * req_type);
 
-  // Newer BusType* values are missing in older includes
-  switch ((int)data.desc.BusType) {
-    case BusTypeAta:
-    case 0x0b: // BusTypeSata
-      if (ata_version_ex)
-        memset(ata_version_ex, 0, sizeof(*ata_version_ex));
-      return DEV_ATA;
+  virtual ~win_csmi_device() throw();
 
-    case BusTypeScsi:
-    case BusTypeRAID:
-      // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
-      if (is_intel_raid_volume(&data))
-        return DEV_SCSI;
-      // LSI/3ware RAID volume: supports SMART_*
-      if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
-        return DEV_ATA;
-      return DEV_SCSI;
+  virtual bool open();
 
-    case 0x09: // BusTypeiScsi
-    case 0x0a: // BusTypeSas
-      return DEV_SCSI;
+  virtual bool close();
 
-    case BusTypeUsb:
-      return DEV_USB;
+  virtual bool is_open() const;
 
-    default:
-      return DEV_UNKNOWN;
-  }
-  /*NOTREACHED*/
-}
+  bool open_scsi();
 
-// get DEV_* for device path
-static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
+protected:
+  virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
+    unsigned csmi_bufsiz);
+
+private:
+  HANDLE m_fh; ///< Controller device handle
+  int m_port; ///< Port number
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+win_csmi_device::win_csmi_device(smart_interface * intf, const char * dev_name,
+  const char * req_type)
+: smart_device(intf, dev_name, "ata", req_type),
+  m_fh(INVALID_HANDLE_VALUE), m_port(-1)
 {
-  bool admin = true;
-  HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
-    FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
-  if (h == INVALID_HANDLE_VALUE) {
-    admin = false;
-    h = CreateFileA(path, 0,
-      FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
-    if (h == INVALID_HANDLE_VALUE)
-      return DEV_UNKNOWN;
-  }
-  if (ata_debugmode > 1 || scsi_debugmode > 1)
-    pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
-  win_dev_type type = get_controller_type(h, admin, ata_version_ex);
-  CloseHandle(h);
-  return type;
 }
 
-// get DEV_* for physical drive number
-static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
+win_csmi_device::~win_csmi_device() throw()
 {
-  char path[30];
-  snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
-  return get_controller_type(path, ata_version_ex);
+  if (m_fh != INVALID_HANDLE_VALUE)
+    CloseHandle(m_fh);
 }
 
-static win_dev_type get_phy_drive_type(int drive)
+bool win_csmi_device::is_open() const
 {
-  return get_phy_drive_type(drive, 0);
+  return (m_fh != INVALID_HANDLE_VALUE);
 }
 
-// get DEV_* for logical drive number
-static win_dev_type get_log_drive_type(int drive)
+bool win_csmi_device::close()
 {
-  char path[30];
-  snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
-  return get_controller_type(path);
+  if (m_fh == INVALID_HANDLE_VALUE)
+    return true;
+  BOOL rc = CloseHandle(m_fh);
+  m_fh = INVALID_HANDLE_VALUE;
+  return !!rc;
 }
 
-// Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
-static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
-{
-  STORAGE_DEVICE_DESCRIPTOR_DATA data;
-  if (storage_query_property_ioctl(hdevice, &data))
-    return -1;
 
-  memset(id, 0, sizeof(*id));
+bool win_csmi_device::open_scsi()
+{
+  // Parse name
+  unsigned contr_no = ~0, port = ~0; int nc = -1;
+  const char * name = skipdev(get_dev_name());
+  if (!(   sscanf(name, "csmi%u,%u%n", &contr_no, &port, &nc) >= 0
+        && nc == (int)strlen(name) && contr_no <= 9 && port < 32)  )
+    return set_err(EINVAL);
 
-  // Some drivers split ATA model string into VendorId and ProductId,
-  // others return it as ProductId only.
-  char model[sizeof(id->model) + 1] = "";
+  // Open controller handle
+  char devpath[30];
+  snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%u:", contr_no);
 
-  unsigned i = 0;
-  if (data.desc.VendorIdOffset) {
-    for ( ;i < sizeof(model)-1 && data.raw[data.desc.VendorIdOffset+i]; i++)
-      model[i] = data.raw[data.desc.VendorIdOffset+i];
-  }
+  HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
+    FILE_SHARE_READ|FILE_SHARE_WRITE,
+    (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0);
 
-  if (data.desc.ProductIdOffset) {
-    while (i > 1 && model[i-2] == ' ') // Keep last blank from VendorId
-      i--;
-    // Ignore VendorId "ATA"
-    if (i <= 4 && !strncmp(model, "ATA", 3) && (i == 3 || model[3] == ' '))
-      i = 0;
-    for (unsigned j = 0; i < sizeof(model)-1 && data.raw[data.desc.ProductIdOffset+j]; i++, j++)
-      model[i] = data.raw[data.desc.ProductIdOffset+j];
+  if (h == INVALID_HANDLE_VALUE) {
+    long err = GetLastError();
+    if (err == ERROR_FILE_NOT_FOUND)
+      set_err(ENOENT, "%s: not found", devpath);
+    else if (err == ERROR_ACCESS_DENIED)
+      set_err(EACCES, "%s: access denied", devpath);
+    else
+      set_err(EIO, "%s: Error=%ld", devpath, err);
+    return false;
   }
 
-  while (i > 0 && model[i-1] == ' ')
-    i--;
-  model[i] = 0;
-  copy_swapped(id->model, model, sizeof(id->model));
-
-  if (data.desc.ProductRevisionOffset)
-    copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
+  if (scsi_debugmode > 1)
+    pout(" %s: successfully opened\n", devpath);
 
-  id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
-  id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
-  return 0;
+  m_fh = h;
+  m_port = port;
+  return true;
 }
 
-// Get Serial Number in IDENTIFY from WMI
-static bool get_serial_from_wmi(int drive, ata_identify_device * id)
-{
-  bool debug = (ata_debugmode > 1);
 
-  wbem_services ws;
-  if (!ws.connect()) {
-    if (debug)
-      pout("WMI connect failed\n");
+bool win_csmi_device::open()
+{
+  if (!open_scsi())
     return false;
-  }
 
-  wbem_object wo;
-  if (!ws.query1(wo, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE "
-                     "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
+  // Get Phy info for this drive
+  if (!select_port(m_port)) {
+    close();
     return false;
+  }
 
-  std::string serial = wo.get_str("SerialNumber");
-  if (debug)
-    pout("  WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive, wo.get_str("Model").c_str(), serial.c_str());
-
-  copy_swapped(id->serial_no, serial.c_str(), sizeof(id->serial_no));
   return true;
 }
 
 
-/////////////////////////////////////////////////////////////////////////////
-// USB ID detection using WMI
-
-// Get USB ID for a physical drive number
-static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id)
+bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
+  unsigned csmi_bufsiz)
 {
-  bool debug = (scsi_debugmode > 1);
+  // Determine signature
+  const char * sig;
+  switch (code) {
+    case CC_CSMI_SAS_GET_DRIVER_INFO:
+      sig = CSMI_ALL_SIGNATURE; break;
+    case CC_CSMI_SAS_GET_PHY_INFO:
+    case CC_CSMI_SAS_STP_PASSTHRU:
+      sig = CSMI_SAS_SIGNATURE; break;
+    default:
+      return set_err(ENOSYS, "Unknown CSMI code=%u", code);
+  }
 
-  wbem_services ws;
-  if (!ws.connect()) {
-    if (debug)
-      pout("WMI connect failed\n");
-    return false;
+  // Set header
+  csmi_buffer->HeaderLength = sizeof(IOCTL_HEADER);
+  strncpy((char *)csmi_buffer->Signature, sig, sizeof(csmi_buffer->Signature));
+  csmi_buffer->Timeout = CSMI_SAS_TIMEOUT;
+  csmi_buffer->ControlCode = code;
+  csmi_buffer->ReturnCode = 0;
+  csmi_buffer->Length = csmi_bufsiz - sizeof(IOCTL_HEADER);
+
+  // Call function
+  DWORD num_out = 0;
+  if (!DeviceIoControl(m_fh, IOCTL_SCSI_MINIPORT,
+    csmi_buffer, csmi_bufsiz, csmi_buffer, csmi_bufsiz, &num_out, (OVERLAPPED*)0)) {
+    long err = GetLastError();
+    if (scsi_debugmode)
+      pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code, err);
+    if (   err == ERROR_INVALID_FUNCTION
+        || err == ERROR_NOT_SUPPORTED
+        || err == ERROR_DEV_NOT_EXIST)
+      return set_err(ENOSYS, "CSMI is not supported (Error=%ld)", err);
+    else
+      return set_err(EIO, "CSMI(%u) failed with Error=%ld", code, err);
   }
 
-  // Get device name
-  wbem_object wo;
-  if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
-    return false;
+  // Check result
+  if (csmi_buffer->ReturnCode) {
+    if (scsi_debugmode) {
+      pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n",
+        code, (unsigned)csmi_buffer->ReturnCode);
+    }
+    return set_err(EIO, "CSMI(%u) failed with ReturnCode=%u", code, (unsigned)csmi_buffer->ReturnCode);
+  }
 
-  std::string name = wo.get_str("Model");
-  if (debug)
-    pout("PhysicalDrive%d, \"%s\":\n", drive, name.c_str());
+  if (scsi_debugmode > 1)
+    pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code, (unsigned)num_out);
 
-  // Get USB_CONTROLLER -> DEVICE associations
-  wbem_enumerator we;
-  if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
-    return false;
+  return true;
+}
 
-  unsigned short usb_venid = 0, prev_usb_venid = 0;
-  unsigned short usb_proid = 0, prev_usb_proid = 0;
-  std::string prev_usb_ant;
-  std::string prev_ant, ant, dep;
 
-  const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED);
+//////////////////////////////////////////////////////////////////////
+// win_tw_cli_device
 
-  while (we.next(wo)) {
-    prev_ant = ant;
-    // Find next 'USB_CONTROLLER, DEVICE' pair
-    ant = wo.get_str("Antecedent");
-    dep = wo.get_str("Dependent");
+// Routines for pseudo device /dev/tw_cli/*
+// Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
+// TODO: This is OS independent
 
-    if (debug && ant != prev_ant)
-      pout(" %s:\n", ant.c_str());
+class win_tw_cli_device
+: public /*implements*/ ata_device_with_command_set
+{
+public:
+  win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type);
 
-    // Extract DeviceID
-    regmatch_t match[2];
-    if (!(regex.execute(dep.c_str(), 2, match) && match[1].rm_so >= 0)) {
-      if (debug)
-        pout("  | (\"%s\")\n", dep.c_str());
-      continue;
-    }
+  virtual bool is_open() const;
+
+  virtual bool open();
+
+  virtual bool close();
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  bool m_ident_valid, m_smart_valid;
+  ata_identify_device m_ident_buf;
+  ata_smart_values m_smart_buf;
+};
 
-    std::string devid(dep.c_str()+match[1].rm_so, match[1].rm_eo-match[1].rm_so);
 
-    if (str_starts_with(devid, "USB\\\\VID_")) {
-      // USB bridge entry, save CONTROLLER, ID
-      int nc = -1;
-      if (!(sscanf(devid.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
-            &prev_usb_venid, &prev_usb_proid, &nc) == 2 && nc == 9+4+5+4)) {
-        prev_usb_venid = prev_usb_proid = 0;
-      }
-      prev_usb_ant = ant;
-      if (debug)
-        pout("  +-> \"%s\" [0x%04x:0x%04x]\n", devid.c_str(), prev_usb_venid, prev_usb_proid);
-      continue;
-    }
-    else if (str_starts_with(devid, "USBSTOR\\\\")) {
-      // USBSTOR device found
-      if (debug)
-        pout("  +--> \"%s\"\n", devid.c_str());
+/////////////////////////////////////////////////////////////////////////////
 
-      // Retrieve name
-      wbem_object wo2;
-      if (!ws.query1(wo2, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid.c_str()))
-        continue;
-      std::string name2 = wo2.get_str("Name");
+win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "tw_cli", req_type),
+  m_ident_valid(false), m_smart_valid(false)
+{
+  memset(&m_ident_buf, 0, sizeof(m_ident_buf));
+  memset(&m_smart_buf, 0, sizeof(m_smart_buf));
+}
 
-      // Continue if not name of physical disk drive
-      if (name2 != name) {
-        if (debug)
-          pout("  +---> (\"%s\")\n", name2.c_str());
-        continue;
-      }
 
-      // Fail if previous USB bridge is associated to other controller or ID is unknown
-      if (!(ant == prev_usb_ant && prev_usb_venid)) {
-        if (debug)
-          pout("  +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str());
-        return false;
-      }
+bool win_tw_cli_device::is_open() const
+{
+  return (m_ident_valid || m_smart_valid);
+}
 
-      // Handle multiple devices with same name
-      if (usb_venid) {
-        // Fail if multiple devices with same name have different USB bridge types
-        if (!(usb_venid == prev_usb_venid && usb_proid == prev_usb_proid)) {
-          if (debug)
-            pout("  +---> \"%s\" (Error: More than one USB ID found)\n", name2.c_str());
-          return false;
-        }
-      }
 
-      // Found
-      usb_venid = prev_usb_venid;
-      usb_proid = prev_usb_proid;
-      if (debug)
-        pout("  +===> \"%s\" [0x%04x:0x%04x]\n", name2.c_str(), usb_venid, usb_proid);
+// Get clipboard data
 
-      // Continue to check for duplicate names ...
-    }
-    else {
-      if (debug)
-        pout("  |   \"%s\"\n", devid.c_str());
-    }
+static int get_clipboard(char * data, int datasize)
+{
+  if (!OpenClipboard(NULL))
+    return -1;
+  HANDLE h = GetClipboardData(CF_TEXT);
+  if (!h) {
+    CloseClipboard();
+    return 0;
   }
+  const void * p = GlobalLock(h);
+  int n = GlobalSize(h);
+  if (n > datasize)
+    n = datasize;
+  memcpy(data, p, n);
+  GlobalFree(h);
+  CloseClipboard();
+  return n;
+}
 
-  if (!usb_venid)
-    return false;
 
-  vendor_id = usb_venid;
-  product_id = usb_proid;
+// Run a command, write stdout to dataout
+// TODO: Combine with daemon_win32.cpp:daemon_spawn()
 
-  return true;
+static int run_cmd(const char * cmd, char * dataout, int outsize)
+{
+  // Create stdout pipe
+  SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
+  HANDLE pipe_out_w, h;
+  if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
+    return -1;
+  HANDLE self = GetCurrentProcess();
+  HANDLE pipe_out_r;
+  if (!DuplicateHandle(self, h, self, &pipe_out_r,
+    GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
+    CloseHandle(pipe_out_w);
+    return -1;
+  }
+  HANDLE pipe_err_w;
+  if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
+    0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
+    CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+    return -1;
+  }
+
+  // Create process
+  STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+  si.hStdInput  = INVALID_HANDLE_VALUE;
+  si.hStdOutput = pipe_out_w; si.hStdError  = pipe_err_w;
+  si.dwFlags = STARTF_USESTDHANDLES;
+  PROCESS_INFORMATION pi;
+  if (!CreateProcess(
+    NULL, const_cast<char *>(cmd),
+    NULL, NULL, TRUE/*inherit*/,
+    CREATE_NO_WINDOW/*do not create a new console window*/,
+    NULL, NULL, &si, &pi)) {
+    CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+    return -1;
+  }
+  CloseHandle(pi.hThread);
+  CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
+
+  // Copy stdout to output buffer
+  int i = 0;
+  while (i < outsize) {
+    DWORD num_read;
+    if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
+      break;
+    i += num_read;
+  }
+  CloseHandle(pipe_out_r);
+  // Wait for process
+  WaitForSingleObject(pi.hProcess, INFINITE);
+  CloseHandle(pi.hProcess);
+  return i;
 }
 
 
-/////////////////////////////////////////////////////////////////////////////
+static const char * findstr(const char * str, const char * sub)
+{
+  const char * s = strstr(str, sub);
+  return (s ? s+strlen(sub) : "");
+}
 
-// Call GetDevicePowerState()
-// returns: 1=active, 0=standby, -1=error
-// (This would also work for SCSI drives)
 
-static int get_device_power_state(HANDLE hdevice)
+bool win_tw_cli_device::open()
 {
-  BOOL state = TRUE;
-  if (!GetDevicePowerState(hdevice, &state)) {
-    long err = GetLastError();
-    if (ata_debugmode)
-      pout("  GetDevicePowerState() failed, Error=%ld\n", err);
-    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
-    // TODO: This may not work as expected on transient errors,
-    // because smartd interprets -1 as SLEEP mode regardless of errno.
-    return -1;
+  m_ident_valid = m_smart_valid = false;
+  const char * name = skipdev(get_dev_name());
+  // Read tw_cli or 3DM browser output into buffer
+  char buffer[4096];
+  int size = -1, n1 = -1, n2 = -1;
+  if (!strcmp(name, "tw_cli/clip")) { // read clipboard
+    size = get_clipboard(buffer, sizeof(buffer));
+  }
+  else if (!strcmp(name, "tw_cli/stdin")) {  // read stdin
+    size = fread(buffer, 1, sizeof(buffer), stdin);
+  }
+  else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) {
+    // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
+    char cmd[100];
+    snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
+    if (ata_debugmode > 1)
+      pout("%s: Run: \"%s\"\n", name, cmd);
+    size = run_cmd(cmd, buffer, sizeof(buffer));
+  }
+  else {
+    return set_err(EINVAL);
   }
 
   if (ata_debugmode > 1)
-    pout("  GetDevicePowerState() succeeded, state=%d\n", state);
-  return state;
+    pout("%s: Read %d bytes\n", name, size);
+  if (size <= 0)
+    return set_err(ENOENT);
+  if (size >= (int)sizeof(buffer))
+    return set_err(EIO);
+
+  buffer[size] = 0;
+  if (ata_debugmode > 1)
+    pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
+
+  // Fake identify sector
+  ASSERT_SIZEOF(ata_identify_device, 512);
+  ata_identify_device * id = &m_ident_buf;
+  memset(id, 0, sizeof(*id));
+  copy_swapped(id->model    , findstr(buffer, " Model = "   ), sizeof(id->model));
+  copy_swapped(id->fw_rev   , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
+  copy_swapped(id->serial_no, findstr(buffer, " Serial = "  ), sizeof(id->serial_no));
+  unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
+  sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
+  if (nblocks) {
+    id->words047_079[49-47] = 0x0200; // size valid
+    id->words047_079[60-47] = (unsigned short)(nblocks    ); // secs_16
+    id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
+  }
+  id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
+  id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
+
+  // Parse smart data hex dump
+  const char * s = findstr(buffer, "Drive Smart Data:");
+  if (!*s)
+    s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x
+  if (!*s) {
+    s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
+    if (*s) {
+      const char * s1 = findstr(s, "<td class"); // html version
+      if (*s1)
+        s = s1;
+      s += strcspn(s, "\r\n");
+    }
+    else
+      s = buffer; // try raw hex dump without header
+  }
+  unsigned char * sd = (unsigned char *)&m_smart_buf;
+  int i = 0;
+  for (;;) {
+    unsigned x = ~0; int n = -1;
+    if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
+      break;
+    sd[i] = (unsigned char)x;
+    if (!(++i < 512 && n > 0))
+      break;
+    s += n;
+    if (*s == '<') // "<br>"
+      s += strcspn(s, "\r\n");
+  }
+  if (i < 512) {
+    if (!id->model[1]) {
+      // No useful data found
+      char * err = strstr(buffer, "Error:");
+      if (!err)
+        err = strstr(buffer, "error :");
+      if (err && (err = strchr(err, ':'))) {
+        // Show tw_cli error message
+        err++;
+        err[strcspn(err, "\r\n")] = 0;
+        return set_err(EIO, "%s", err);
+      }
+      return set_err(EIO);
+    }
+    sd = 0;
+  }
+
+  m_ident_valid = true;
+  m_smart_valid = !!sd;
+  return true;
 }
 
 
-/////////////////////////////////////////////////////////////////////////////
+bool win_tw_cli_device::close()
+{
+  m_ident_valid = m_smart_valid = false;
+  return true;
+}
 
-// Get default ATA device options
 
-static const char * ata_get_def_options()
+int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data)
 {
-  return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
-                   // STORAGE_*, SCSI_MINIPORT_*
+  switch (command) {
+    case IDENTIFY:
+      if (!m_ident_valid)
+        break;
+      memcpy(data, &m_ident_buf, 512);
+      return 0;
+    case READ_VALUES:
+      if (!m_smart_valid)
+        break;
+      memcpy(data, &m_smart_buf, 512);
+      return 0;
+    case ENABLE:
+    case STATUS:
+    case STATUS_CHECK: // Fake "good" SMART status
+      return 0;
+    default:
+      break;
+  }
+  // Arrive here for all unsupported commands
+  set_err(ENOSYS);
+  return -1;
 }
 
 
-// Common routines for devices with HANDLEs
+/////////////////////////////////////////////////////////////////////////////
+// win_scsi_device
+// SPT Interface (for SCSI devices and ATA devices behind SATLs)
 
-win_smart_device::~win_smart_device() throw()
+class win_scsi_device
+: public /*implements*/ scsi_device,
+  virtual public /*extends*/ win_smart_device
 {
-  if (m_fh != INVALID_HANDLE_VALUE)
-    ::CloseHandle(m_fh);
-}
+public:
+  win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
 
-bool win_smart_device::is_open() const
-{
-  return (m_fh != INVALID_HANDLE_VALUE);
-}
+  virtual bool open();
 
-bool win_smart_device::close()
-{
-  if (m_fh == INVALID_HANDLE_VALUE)
-    return true;
-  BOOL rc = ::CloseHandle(m_fh);
-  m_fh = INVALID_HANDLE_VALUE;
-  return !!rc;
-}
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+  bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
+};
 
-// ATA
 
-win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
-: smart_device(intf, dev_name, "ata", req_type),
-  m_usr_options(false),
-  m_admin(false),
-  m_phydrive(-1),
-  m_id_is_cached(false),
-  m_is_3ware(false),
-  m_port(-1),
-  m_smartver_state(0)
-{
-}
+/////////////////////////////////////////////////////////////////////////////
 
-win_ata_device::~win_ata_device() throw()
+win_scsi_device::win_scsi_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type)
 {
 }
 
-
-// Open ATA device
-
-bool win_ata_device::open()
+bool win_scsi_device::open()
 {
   const char * name = skipdev(get_dev_name()); int len = strlen(name);
-  // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options
-  char drive[2+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;
-  if (   sscanf(name, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive, &n1, options, &n2) >= 1
-      && ((n1 == len && !options[0]) || n2 == len)                                   ) {
-    return open(sdxy_to_phydrive(drive), -1, options, -1);
-  }
-  // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options
-  drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
-  unsigned port = ~0;
-  if (   sscanf(name, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive, &port, &n1, options, &n2) >= 2
-      && port < 32 && ((n1 == len && !options[0]) || n2 == len)                                  ) {
-    return open(sdxy_to_phydrive(drive), -1, options, port);
+  // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N
+  char drive[2+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1;
+  if (   sscanf(name, "sd%2[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1
+      && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))  ) {
+    return open(sdxy_to_phydrive(drive), -1, -1, sub_addr);
   }
   // pd<m>,N => Physical drive <m>, RAID port N
-  int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
-  if (   sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
-      && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
-    return open(phydrive, -1, "", (int)port);
+  int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
+  if (   sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
+      && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
+    return open(pd_num, -1, -1, sub_addr);
   }
   // [a-zA-Z]: => Physical drive behind logical drive 0-25
   int logdrive = drive_letter(name);
   if (logdrive >= 0) {
-    return open(-1, logdrive, "", -1);
+    return open(-1, logdrive, -1, -1);
+  }
+  // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
+  int tape_num = -1; n1 = -1;
+  if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+    return open(-1, -1, tape_num, -1);
+  }
+  tape_num = -1; n1 = -1;
+  if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+    return open(-1, -1, tape_num, -1);
+  }
+  // tape<m> => tape drive <m>
+  tape_num = -1; n1 = -1;
+  if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+    return open(-1, -1, tape_num, -1);
   }
 
   return set_err(EINVAL);
 }
 
-
-bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)
+bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/)
 {
-  m_phydrive = -1;
-  char devpath[30];
-  if (0 <= phydrive && phydrive <= 255)
-    snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive = phydrive));
-  else if (0 <= logdrive && logdrive <= 'Z'-'A')
-    snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
-  else
-    return set_err(ENOENT);
+  char b[128];
+  b[sizeof(b) - 1] = '\0';
+  if (pd_num >= 0)
+    snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
+  else if (ld_num >= 0)
+    snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
+  else if (tape_num >= 0)
+    snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
+  else {
+    set_err(EINVAL);
+    return false;
+  }
 
   // Open device
-  HANDLE h = INVALID_HANDLE_VALUE;
-  if (!(*options && !options[strspn(options, "fp")])) {
-    // Open with admin rights
-    m_admin = true;
-    h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
-      FILE_SHARE_READ|FILE_SHARE_WRITE,
-      NULL, OPEN_EXISTING, 0, 0);
-  }
-  if (h == INVALID_HANDLE_VALUE) {
-    // Open without admin rights
-    m_admin = false;
-    h = CreateFileA(devpath, 0,
-      FILE_SHARE_READ|FILE_SHARE_WRITE,
-      NULL, OPEN_EXISTING, 0, 0);
-  }
+  HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
+           FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+           OPEN_EXISTING, 0, 0);
   if (h == INVALID_HANDLE_VALUE) {
-    long err = GetLastError();
-    if (err == ERROR_FILE_NOT_FOUND)
-      set_err(ENOENT, "%s: not found", devpath);
-    else if (err == ERROR_ACCESS_DENIED)
-      set_err(EACCES, "%s: access denied", devpath);
-    else
-      set_err(EIO, "%s: Error=%ld", devpath, err);
+    set_err(ENODEV, "%s: Open failed, Error=%u", b, (unsigned)GetLastError());
     return false;
   }
-  set_fh(h);
+  set_fh(h);
+  return true;
+}
+
+
+typedef struct {
+  SCSI_PASS_THROUGH_DIRECT spt;
+  ULONG           Filler;
+  UCHAR           ucSenseBuf[64];
+} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
+
+
+// Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
+// Used if DataTransferLength not supported by *_DIRECT.
+static long scsi_pass_through_indirect(HANDLE h,
+  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd)
+{
+  struct SCSI_PASS_THROUGH_WITH_BUFFERS {
+    SCSI_PASS_THROUGH spt;
+    ULONG Filler;
+    UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)];
+    UCHAR ucDataBuf[512];
+  };
+
+  SCSI_PASS_THROUGH_WITH_BUFFERS sb;
+  memset(&sb, 0, sizeof(sb));
+
+  // DATA_OUT not implemented yet
+  if (!(   sbd->spt.DataIn == SCSI_IOCTL_DATA_IN
+        && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf)))
+    return ERROR_INVALID_PARAMETER;
+
+  sb.spt.Length = sizeof(sb.spt);
+  sb.spt.CdbLength = sbd->spt.CdbLength;
+  memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb));
+  sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+  sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+  sb.spt.DataIn = sbd->spt.DataIn;
+  sb.spt.DataTransferLength = sbd->spt.DataTransferLength;
+  sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
+  sb.spt.TimeOutValue = sbd->spt.TimeOutValue;
+
+  DWORD num_out;
+  if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH,
+         &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+    return GetLastError();
+
+  sbd->spt.ScsiStatus = sb.spt.ScsiStatus;
+  if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION)
+    memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf));
+
+  sbd->spt.DataTransferLength = sb.spt.DataTransferLength;
+  if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0)
+    memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength);
+  return 0;
+}
+
+
+// Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
+bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
+{
+  int report = scsi_debugmode; // TODO
+
+  if (report > 0) {
+    int k, j;
+    const unsigned char * ucp = iop->cmnd;
+    const char * np;
+    char buff[256];
+    const int sz = (int)sizeof(buff);
+
+    np = scsi_get_opcode_name(ucp[0]);
+    j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+    for (k = 0; k < (int)iop->cmnd_len; ++k)
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+    if ((report > 1) &&
+      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+              "data, len=%d%s:\n", (int)iop->dxfer_len,
+              (trunc ? " [only first 256 bytes shown]" : ""));
+      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+    }
+    else
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+    pout("%s", buff);
+  }
 
-  // Warn once if admin rights are missing
-  if (!m_admin) {
-    static bool noadmin_warning = false;
-    if (!noadmin_warning) {
-      pout("Warning: Limited functionality due to missing admin rights\n");
-      noadmin_warning = true;
-    }
+  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
+  if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
+    set_err(EINVAL, "cmnd_len too large");
+    return false;
   }
 
-  if (ata_debugmode > 1)
-    pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :""));
+  memset(&sb, 0, sizeof(sb));
+  sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
+  sb.spt.CdbLength = iop->cmnd_len;
+  memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
+  sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+  sb.spt.SenseInfoOffset =
+    offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
+  sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
 
-  m_usr_options = false;
-  if (*options) {
-    // Save user options
-    m_options = options; m_usr_options = true;
-  }
-  else if (port >= 0)
-    // RAID: SMART_* and SCSI_MINIPORT
-    m_options = "s3";
-  else {
-    // Set default options according to Windows version
-    static const char * def_options = ata_get_def_options();
-    m_options = def_options;
+  bool direct = true;
+  switch (iop->dxfer_dir) {
+    case DXFER_NONE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+      break;
+    case DXFER_FROM_DEVICE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+      sb.spt.DataTransferLength = iop->dxfer_len;
+      sb.spt.DataBuffer = iop->dxferp;
+      // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
+      // transfers (needed for SMART STATUS check of JMicron USB bridges)
+      if (sb.spt.DataTransferLength == 1)
+        direct = false;
+      break;
+    case DXFER_TO_DEVICE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+      sb.spt.DataTransferLength = iop->dxfer_len;
+      sb.spt.DataBuffer = iop->dxferp;
+      break;
+    default:
+      set_err(EINVAL, "bad dxfer_dir");
+      return false;
   }
 
-  // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
-  m_port = port;
-  if (port < 0)
-    return true;
+  long err = 0;
+  if (direct) {
+    DWORD num_out;
+    if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
+           &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+      err = GetLastError();
+  }
+  else
+    err = scsi_pass_through_indirect(get_fh(), &sb);
 
-  // 3ware RAID: Get port map
-  GETVERSIONINPARAMS_EX vers_ex;
-  int devmap = smart_get_version(h, &vers_ex);
+  if (err)
+    return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO),
+      "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
+      (direct ? "_DIRECT" : ""), err);
 
-  // 3ware RAID if vendor id present
-  m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
+  iop->scsi_status = sb.spt.ScsiStatus;
+  if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+    int slen = sb.ucSenseBuf[7] + 8;
 
-  unsigned long portmap = 0;
-  if (port >= 0 && devmap >= 0) {
-    // 3ware RAID: check vendor id
-    if (!m_is_3ware) {
-      pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
-           "This is no 3ware 9000 controller or driver has no SMART support.\n",
-           vers_ex.wIdentifier);
-      devmap = -1;
-    }
-    else
-      portmap = vers_ex.dwDeviceMapEx;
-  }
-  if (devmap < 0) {
-    pout("%s: ATA driver has no SMART support\n", devpath);
-    if (!is_permissive()) {
-      close();
-      return set_err(ENOSYS);
+    if (slen > (int)sizeof(sb.ucSenseBuf))
+      slen = sizeof(sb.ucSenseBuf);
+    if (slen > (int)iop->max_sense_len)
+      slen = iop->max_sense_len;
+    memcpy(iop->sensep, sb.ucSenseBuf, slen);
+    iop->resp_sense_len = slen;
+    if (report) {
+      if (report > 1) {
+        pout("  >>> Sense buffer, len=%d:\n", slen);
+        dStrHex(iop->sensep, slen , 1);
+      }
+      if ((iop->sensep[0] & 0x7f) > 0x71)
+        pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
+             iop->scsi_status, iop->sensep[1] & 0xf,
+             iop->sensep[2], iop->sensep[3]);
+      else
+        pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
+             iop->scsi_status, iop->sensep[2] & 0xf,
+             iop->sensep[12], iop->sensep[13]);
     }
-    devmap = 0x0f;
-  }
-  m_smartver_state = 1;
+  } else
+    iop->resp_sense_len = 0;
 
-  {
-    // 3ware RAID: update devicemap first
+  if (iop->dxfer_len > sb.spt.DataTransferLength)
+    iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
+  else
+    iop->resid = 0;
 
-    if (!update_3ware_devicemap_ioctl(h)) {
-      if (   smart_get_version(h, &vers_ex) >= 0
-          && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
-        portmap = vers_ex.dwDeviceMapEx;
-    }
-    // Check port existence
-    if (!(portmap & (1L << port))) {
-      if (!is_permissive()) {
-        close();
-        return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
-      }
-    }
+  if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
+     int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+     pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
+        (trunc ? " [only first 256 bytes shown]" : ""));
+        dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
   }
-
   return true;
 }
 
 
 /////////////////////////////////////////////////////////////////////////////
+/// Areca RAID support
 
-// Interface to ATA devices
-bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+// TODO: combine with above scsi_pass_through_direct()
+static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop)
 {
-  // No multi-sector support for now, see above
-  // warning about IOCTL_ATA_PASS_THROUGH
-  if (!ata_cmd_is_supported(in,
-    ata_device::supports_data_out |
-    ata_device::supports_output_regs |
-    ata_device::supports_48bit)
-  )
-    return false;
-
-  // 3ware RAID: SMART DISABLE without port number disables SMART functions
-  if (   m_is_3ware && m_port < 0
-      && in.in_regs.command == ATA_SMART_CMD
-      && in.in_regs.features == ATA_SMART_DISABLE)
-    return set_err(ENOSYS, "SMART DISABLE requires 3ware port number");
-
-  // Determine ioctl functions valid for this ATA cmd
-  const char * valid_options = 0;
-
-  switch (in.in_regs.command) {
-    case ATA_IDENTIFY_DEVICE:
-    case ATA_IDENTIFY_PACKET_DEVICE:
-      // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
-      // and SCSI_MINIPORT_* if requested by user
-      valid_options = (m_usr_options ? "saimf" : "saif");
-      break;
-
-    case ATA_CHECK_POWER_MODE:
-      // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
-      valid_options = "pai3";
-      break;
-
-    case ATA_SMART_CMD:
-      switch (in.in_regs.features) {
-        case ATA_SMART_READ_VALUES:
-        case ATA_SMART_READ_THRESHOLDS:
-        case ATA_SMART_AUTOSAVE:
-        case ATA_SMART_ENABLE:
-        case ATA_SMART_DISABLE:
-        case ATA_SMART_AUTO_OFFLINE:
-          // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
-          // and SCSI_MINIPORT_* if requested by user
-          valid_options = (m_usr_options ? "saimf" : "saif");
-          break;
-
-        case ATA_SMART_IMMEDIATE_OFFLINE:
-          // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST
-          valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ ?
-                           "saim3" : "aim3");
-          break;
-
-        case ATA_SMART_READ_LOG_SECTOR:
-          // SMART_RCV_DRIVE_DATA does not support READ_LOG
-          // Try SCSI_MINIPORT also to skip buggy class driver
-          // SMART functions do not support multi sector I/O.
-          if (in.size == 512)
-            valid_options = (m_usr_options ? "saim3" : "aim3");
-          else
-            valid_options = "a";
-          break;
-
-        case ATA_SMART_WRITE_LOG_SECTOR:
-          // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
-          // but SCSI_MINIPORT_* only if requested by user and single sector.
-          valid_options = (in.size == 512 && m_usr_options ? "am" : "a");
-          break;
-
-        case ATA_SMART_STATUS:
-          valid_options = (m_usr_options ? "saimf" : "saif");
-          break;
-
-        default:
-          // Unknown SMART command, handle below
-          break;
-      }
-      break;
+  int report = scsi_debugmode; // TODO
 
-    default:
-      // Other ATA command, handle below
-      break;
-  }
+  if (report > 0) {
+    int k, j;
+    const unsigned char * ucp = iop->cmnd;
+    const char * np;
+    char buff[256];
+    const int sz = (int)sizeof(buff);
 
-  if (!valid_options) {
-    // No special ATA command found above, select a generic pass through ioctl.
-    if (!(   in.direction == ata_cmd_in::no_data
-          || (in.direction == ata_cmd_in::data_in && in.size == 512))
-         ||  in.in_regs.is_48bit_cmd()                               )
-      // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
-      valid_options = "a";
-    else
-      // ATA/IDE_PASS_THROUGH
-      valid_options = "ai";
-  }
+    np = scsi_get_opcode_name(ucp[0]);
+    j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+    for (k = 0; k < (int)iop->cmnd_len; ++k)
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+    if ((report > 1) &&
+      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 
-  if (!m_admin) {
-    // Restrict to IOCTL_STORAGE_*
-    if (strchr(valid_options, 'f'))
-      valid_options = "f";
-    else if (strchr(valid_options, 'p'))
-      valid_options = "p";
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+              "data, len=%d%s:\n", (int)iop->dxfer_len,
+              (trunc ? " [only first 256 bytes shown]" : ""));
+      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+    }
     else
-      return set_err(ENOSYS, "Function requires admin rights");
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+    pout("%s", buff);
   }
 
-  // Set IDEREGS
-  IDEREGS regs, prev_regs;
-  {
-    const ata_in_regs & lo = in.in_regs;
-    regs.bFeaturesReg     = lo.features;
-    regs.bSectorCountReg  = lo.sector_count;
-    regs.bSectorNumberReg = lo.lba_low;
-    regs.bCylLowReg       = lo.lba_mid;
-    regs.bCylHighReg      = lo.lba_high;
-    regs.bDriveHeadReg    = lo.device;
-    regs.bCommandReg      = lo.command;
-    regs.bReserved        = 0;
-  }
-  if (in.in_regs.is_48bit_cmd()) {
-    const ata_in_regs & hi = in.in_regs.prev;
-    prev_regs.bFeaturesReg     = hi.features;
-    prev_regs.bSectorCountReg  = hi.sector_count;
-    prev_regs.bSectorNumberReg = hi.lba_low;
-    prev_regs.bCylLowReg       = hi.lba_mid;
-    prev_regs.bCylHighReg      = hi.lba_high;
-    prev_regs.bDriveHeadReg    = hi.device;
-    prev_regs.bCommandReg      = hi.command;
-    prev_regs.bReserved        = 0;
+  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
+  if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
+    return EINVAL;
   }
 
-  // Set data direction
-  int datasize = 0;
-  char * data = 0;
-  switch (in.direction) {
-    case ata_cmd_in::no_data:
+  memset(&sb, 0, sizeof(sb));
+  sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
+  //sb.spt.PathId = 0;
+  sb.spt.TargetId = targetid;
+  //sb.spt.Lun = 0;
+  sb.spt.CdbLength = iop->cmnd_len;
+  memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
+  sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+  sb.spt.SenseInfoOffset =
+    offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
+  sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
+
+  bool direct = true;
+  switch (iop->dxfer_dir) {
+    case DXFER_NONE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
       break;
-    case ata_cmd_in::data_in:
-      datasize = (int)in.size;
-      data = (char *)in.buffer;
+    case DXFER_FROM_DEVICE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+      sb.spt.DataTransferLength = iop->dxfer_len;
+      sb.spt.DataBuffer = iop->dxferp;
+      // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
+      // transfers (needed for SMART STATUS check of JMicron USB bridges)
+      if (sb.spt.DataTransferLength == 1)
+        direct = false;
       break;
-    case ata_cmd_in::data_out:
-      datasize = -(int)in.size;
-      data = (char *)in.buffer;
+    case DXFER_TO_DEVICE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+      sb.spt.DataTransferLength = iop->dxfer_len;
+      sb.spt.DataBuffer = iop->dxferp;
       break;
     default:
-      return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d",
-          (int)in.direction);
+      return EINVAL;
   }
 
+  long err = 0;
+  if (direct) {
+    DWORD num_out;
+    if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
+           &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+      err = GetLastError();
+  }
+  else
+    err = scsi_pass_through_indirect(fd, &sb);
 
-  // Try all valid ioctls in the order specified in m_options
-  bool powered_up = false;
-  bool out_regs_set = false;
-  bool id_is_cached = false;
-  const char * options = m_options.c_str();
+  if (err)
+  {
+    return err;
+  }
 
-  for (int i = 0; ; i++) {
-    char opt = options[i];
+  iop->scsi_status = sb.spt.ScsiStatus;
+  if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+    int slen = sb.ucSenseBuf[7] + 8;
 
-    if (!opt) {
-      if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) {
-        // Power up reported by GetDevicePowerState() and no ioctl available
-        // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
-        regs.bSectorCountReg = 0xff;
-        out_regs_set = true;
-        break;
+    if (slen > (int)sizeof(sb.ucSenseBuf))
+      slen = sizeof(sb.ucSenseBuf);
+    if (slen > (int)iop->max_sense_len)
+      slen = iop->max_sense_len;
+    memcpy(iop->sensep, sb.ucSenseBuf, slen);
+    iop->resp_sense_len = slen;
+    if (report) {
+      if (report > 1) {
+        pout("  >>> Sense buffer, len=%d:\n", slen);
+        dStrHex(iop->sensep, slen , 1);
       }
-      // No IOCTL found
-      return set_err(ENOSYS);
+      if ((iop->sensep[0] & 0x7f) > 0x71)
+        pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
+             iop->scsi_status, iop->sensep[1] & 0xf,
+             iop->sensep[2], iop->sensep[3]);
+      else
+        pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
+             iop->scsi_status, iop->sensep[2] & 0xf,
+             iop->sensep[12], iop->sensep[13]);
     }
-    if (!strchr(valid_options, opt))
-      // Invalid for this command
-      continue;
+  } else
+    iop->resp_sense_len = 0;
 
-    errno = 0;
-    assert(   datasize == 0 || datasize == 512
-           || (datasize == -512 && strchr("am", opt))
-           || (datasize > 512 && opt == 'a'));
-    int rc;
-    switch (opt) {
-      default: assert(0);
-      case 's':
-        // call SMART_GET_VERSION once for each drive
-        if (m_smartver_state > 1) {
-          rc = -1; errno = ENOSYS;
-          break;
-        }
-        if (!m_smartver_state) {
-          assert(m_port == -1);
-          GETVERSIONINPARAMS_EX vers_ex;
-          if (smart_get_version(get_fh(), &vers_ex) < 0) {
-            if (!failuretest_permissive) {
-              m_smartver_state = 2;
-              rc = -1; errno = ENOSYS;
-              break;
-            }
-            failuretest_permissive--;
-          }
-          else  {
-            // 3ware RAID if vendor id present
-            m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
-          }
+  if (iop->dxfer_len > sb.spt.DataTransferLength)
+    iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
+  else
+    iop->resid = 0;
 
-          m_smartver_state = 1;
-        }
-        rc = smart_ioctl(get_fh(), &regs, data, datasize, m_port);
-        out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
-        id_is_cached = (m_port < 0); // Not cached by 3ware driver
-        break;
-      case 'm':
-        rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);
-        id_is_cached = (m_port < 0);
-        break;
-      case 'a':
-        rc = ata_pass_through_ioctl(get_fh(), &regs,
-          (in.in_regs.is_48bit_cmd() ? &prev_regs : 0),
-          data, datasize);
-        out_regs_set = true;
-        break;
-      case 'i':
-        rc = ide_pass_through_ioctl(get_fh(), &regs, data, datasize);
-        out_regs_set = true;
-        break;
-      case 'f':
-        if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
-            rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);
-            if (rc == 0 && m_phydrive >= 0)
-              get_serial_from_wmi(m_phydrive, (ata_identify_device *)data);
-            id_is_cached = true;
-        }
-        else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
-          case ATA_SMART_READ_VALUES:
-            rc = storage_predict_failure_ioctl(get_fh(), data);
-            if (rc > 0)
-              rc = 0;
-            break;
-          case ATA_SMART_ENABLE:
-            rc = 0;
-            break;
-          case ATA_SMART_STATUS:
-            rc = storage_predict_failure_ioctl(get_fh());
-            if (rc == 0) {
-              // Good SMART status
-              out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
-            }
-            else if (rc > 0) {
-              // Bad SMART status
-              out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
-              rc = 0;
-            }
-            break;
-          default:
-            errno = ENOSYS; rc = -1;
-        }
-        else {
-            errno = ENOSYS; rc = -1;
-        }
-        break;
-      case '3':
-        rc = ata_via_3ware_miniport_ioctl(get_fh(), &regs, data, datasize, m_port);
-        out_regs_set = true;
-        break;
-      case 'p':
-        assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0);
-        rc = get_device_power_state(get_fh());
-        if (rc == 0) {
-          // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
-          // spin up the drive => simulate ATA result STANDBY.
-          regs.bSectorCountReg = 0x00;
-          out_regs_set = true;
-        }
-        else if (rc > 0) {
-          // Power up reported by GetDevicePowerState(), but this reflects the actual mode
-          // only if it is selected by the device driver => try a passthrough ioctl to get the
-          // actual mode, if none available simulate ACTIVE/IDLE.
-          powered_up = true;
-          rc = -1; errno = ENOSYS;
-        }
-        break;
-    }
+  if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
+     int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+     pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
+        (trunc ? " [only first 256 bytes shown]" : ""));
+        dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+  }
+
+  return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// win_areca_scsi_device
+// SAS(SCSI) device behind Areca RAID Controller
 
-    if (!rc)
-      // Working ioctl found
-      break;
+class win_areca_scsi_device
+: public /*implements*/ areca_scsi_device,
+  public /*extends*/ win_smart_device
+{
+public:
+  win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
+  virtual bool open();
+  virtual smart_device * autodetect_open();
+  virtual bool arcmsr_lock();
+  virtual bool arcmsr_unlock();
+  virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
 
-    if (errno != ENOSYS)
-      // Abort on I/O error
-      return set_err(errno);
+private:
+  HANDLE m_mutex;
+};
 
-    out_regs_set = false;
-    // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
+
+/////////////////////////////////////////////////////////////////////////////
+
+win_areca_scsi_device::win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
+: smart_device(intf, dev_name, "areca", "areca")
+{
+    set_fh(INVALID_HANDLE_VALUE);
+    set_disknum(disknum);
+    set_encnum(encnum);
+    set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
+}
+
+bool win_areca_scsi_device::open()
+{
+  HANDLE hFh;
+
+  if( is_open() )
+  {
+    return true;
+  }
+  hFh = CreateFile( get_dev_name(),
+                    GENERIC_READ|GENERIC_WRITE,
+                    FILE_SHARE_READ|FILE_SHARE_WRITE,
+                    NULL,
+                    OPEN_EXISTING,
+                    0,
+                    NULL );
+  if(hFh == INVALID_HANDLE_VALUE)
+  {
+    return false;
   }
 
-  // Return IDEREGS if set
-  if (out_regs_set) {
-    ata_out_regs & lo = out.out_regs;
-    lo.error        = regs.bFeaturesReg;
-    lo.sector_count = regs.bSectorCountReg;
-    lo.lba_low      = regs.bSectorNumberReg;
-    lo.lba_mid      = regs.bCylLowReg;
-    lo.lba_high     = regs.bCylHighReg;
-    lo.device       = regs.bDriveHeadReg;
-    lo.status       = regs.bCommandReg;
-    if (in.in_regs.is_48bit_cmd()) {
-      ata_out_regs & hi = out.out_regs.prev;
-      hi.sector_count = prev_regs.bSectorCountReg;
-      hi.lba_low      = prev_regs.bSectorNumberReg;
-      hi.lba_mid      = prev_regs.bCylLowReg;
-      hi.lba_high     = prev_regs.bCylHighReg;
-    }
+  set_fh(hFh);
+  return true;
+}
+
+smart_device * win_areca_scsi_device::autodetect_open()
+{
+  return this;
+}
+
+int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
+{
+   int ioctlreturn = 0;
+
+   ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
+   if ( ioctlreturn || iop->scsi_status )
+   {
+     ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
+     if ( ioctlreturn || iop->scsi_status )
+     {
+       // errors found
+       return -1;
+     }
+   }
+
+   return ioctlreturn;
+}
+
+bool win_areca_scsi_device::arcmsr_lock()
+{
+#define    SYNCOBJNAME "Global\\SynIoctlMutex"
+  int ctlrnum = -1;
+  char mutexstr[64];
+
+  if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
+    return set_err(EINVAL, "unable to parse device name");
+
+  snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
+  m_mutex = CreateMutex(NULL, FALSE, mutexstr);
+  if ( m_mutex == NULL )
+  {
+    return set_err(EIO, "CreateMutex failed");
   }
 
-  if (   in.in_regs.command == ATA_IDENTIFY_DEVICE
-      || in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE)
-    // Update ata_identify_is_cached() result according to ioctl used.
-    m_id_is_cached = id_is_cached;
+  // atomic access to driver
+  WaitForSingleObject(m_mutex, INFINITE);
 
   return true;
 }
 
-// Return true if OS caches the ATA identify sector
-bool win_ata_device::ata_identify_is_cached() const
+
+bool win_areca_scsi_device::arcmsr_unlock()
 {
-  return m_id_is_cached;
+  if( m_mutex != NULL)
+  {
+      ReleaseMutex(m_mutex);
+      CloseHandle(m_mutex);
+  }
+
+  return true;
 }
 
 
-//////////////////////////////////////////////////////////////////////
-// csmi_ata_device
+/////////////////////////////////////////////////////////////////////////////
+// win_areca_ata_device
+// SATA(ATA) device behind Areca RAID Controller
 
-bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
+class win_areca_ata_device
+: public /*implements*/ areca_ata_device,
+  public /*extends*/ win_smart_device
 {
-  // Get driver info to check CSMI support
-  CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf;
-  memset(&driver_info_buf, 0, sizeof(driver_info_buf));
-  if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO, &driver_info_buf.IoctlHeader, sizeof(driver_info_buf)))
+public:
+  win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
+  virtual bool open();
+  virtual smart_device * autodetect_open();
+  virtual bool arcmsr_lock();
+  virtual bool arcmsr_unlock();
+  virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
+
+private:
+  HANDLE m_mutex;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+win_areca_ata_device::win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
+: smart_device(intf, dev_name, "areca", "areca")
+{
+  set_fh(INVALID_HANDLE_VALUE);
+  set_disknum(disknum);
+  set_encnum(encnum);
+  set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
+}
+
+bool win_areca_ata_device::open()
+{
+  HANDLE hFh;
+
+  if( is_open() )
+  {
+    return true;
+  }
+  hFh = CreateFile( get_dev_name(),
+                    GENERIC_READ|GENERIC_WRITE,
+                    FILE_SHARE_READ|FILE_SHARE_WRITE,
+                    NULL,
+                    OPEN_EXISTING,
+                    0,
+                    NULL );
+  if(hFh == INVALID_HANDLE_VALUE)
+  {
     return false;
+  }
 
-  if (scsi_debugmode > 1) {
-    const CSMI_SAS_DRIVER_INFO & driver_info = driver_info_buf.Information;
-    pout("CSMI_SAS_DRIVER_INFO:\n");
-    pout("  Name:        \"%.81s\"\n", driver_info.szName);
-    pout("  Description: \"%.81s\"\n", driver_info.szDescription);
-    pout("  Revision:    %d.%d\n", driver_info.usMajorRevision, driver_info.usMinorRevision);
+  set_fh(hFh);
+  return true;
+}
+
+smart_device * win_areca_ata_device::autodetect_open()
+{
+  // autodetect device type
+  int is_ata = arcmsr_get_dev_type();
+  if(is_ata < 0)
+  {
+    set_err(EIO);
+    return this;
   }
 
-  // Get Phy info
-  CSMI_SAS_PHY_INFO_BUFFER phy_info_buf;
-  memset(&phy_info_buf, 0, sizeof(phy_info_buf));
-  if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO, &phy_info_buf.IoctlHeader, sizeof(phy_info_buf)))
-    return false;
+  if(is_ata == 1)
+  {
+    // SATA device
+    return this;
+  }
 
-  phy_info = phy_info_buf.Information;
+  // SAS device
+  smart_device_auto_ptr newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
+  close();
+  delete this;
+  newdev->open(); // TODO: Can possibly pass open fd
 
-  const int max_number_of_phys = sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]);
-  if (phy_info.bNumberOfPhys > max_number_of_phys)
-    return set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys);
+  return newdev.release();
+}
 
-  if (scsi_debugmode > 1) {
-    pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info.bNumberOfPhys);
-    for (int i = 0; i < max_number_of_phys; i++) {
-      const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
-      const CSMI_SAS_IDENTIFY & id = pe.Identify, & at = pe.Attached;
-      if (id.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
-        continue;
+int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
+{
+   int ioctlreturn = 0;
 
-      pout("Phy[%d] Port:   0x%02x\n", i, pe.bPortIdentifier);
-      pout("  Type:        0x%02x, 0x%02x\n", id.bDeviceType, at.bDeviceType);
-      pout("  InitProto:   0x%02x, 0x%02x\n", id.bInitiatorPortProtocol, at.bInitiatorPortProtocol);
-      pout("  TargetProto: 0x%02x, 0x%02x\n", id.bTargetPortProtocol, at.bTargetPortProtocol);
-      pout("  PhyIdent:    0x%02x, 0x%02x\n", id.bPhyIdentifier, at.bPhyIdentifier);
-      const unsigned char * b = id.bSASAddress;
-      pout("  SASAddress:  %02x %02x %02x %02x %02x %02x %02x %02x, ",
-        b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
-      b = at.bSASAddress;
-      pout(               "%02x %02x %02x %02x %02x %02x %02x %02x\n",
-        b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
-    }
+   ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
+   if ( ioctlreturn || iop->scsi_status )
+   {
+     ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
+     if ( ioctlreturn || iop->scsi_status )
+     {
+       // errors found
+       return -1;
+     }
+   }
+
+   return ioctlreturn;
+}
+
+bool win_areca_ata_device::arcmsr_lock()
+{
+#define    SYNCOBJNAME "Global\\SynIoctlMutex"
+  int ctlrnum = -1;
+  char mutexstr[64];
+
+  if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
+    return set_err(EINVAL, "unable to parse device name");
+
+  snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
+  m_mutex = CreateMutex(NULL, FALSE, mutexstr);
+  if ( m_mutex == NULL )
+  {
+    return set_err(EIO, "CreateMutex failed");
   }
 
+  // atomic access to driver
+  WaitForSingleObject(m_mutex, INFINITE);
+
   return true;
 }
 
-unsigned csmi_device::get_ports_used()
-{
-  CSMI_SAS_PHY_INFO phy_info;
-  if (!get_phy_info(phy_info))
-    return 0;
-
-  unsigned ports_used = 0;
-  for (unsigned i = 0; i < sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); i++) {
-    const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
-    if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
-      continue;
-    if (pe.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
-      continue;
-    switch (pe.Attached.bTargetPortProtocol) {
-      case CSMI_SAS_PROTOCOL_SATA:
-      case CSMI_SAS_PROTOCOL_STP:
-        break;
-      default:
-        continue;
-    }
 
-    if (pe.bPortIdentifier == 0xff)
-      // Older (<= 9.*) Intel RST driver
-      ports_used |= (1 << i);
-    else
-      ports_used |= (1 << pe.bPortIdentifier);
+bool win_areca_ata_device::arcmsr_unlock()
+{
+  if( m_mutex != NULL)
+  {
+      ReleaseMutex(m_mutex);
+      CloseHandle(m_mutex);
   }
 
-  return ports_used;
+  return true;
 }
 
 
-bool csmi_device::select_port(int port)
+/////////////////////////////////////////////////////////////////////////////
+// win_aacraid_device
+// PMC aacraid Support
+
+class win_aacraid_device
+:public /*implements*/ scsi_device,
+public /*extends*/ win_smart_device
 {
-  CSMI_SAS_PHY_INFO phy_info;
-  if (!get_phy_info(phy_info))
-    return false;
+public:
+  win_aacraid_device(smart_interface *intf, const char *dev_name,unsigned int ctrnum, unsigned int target, unsigned int lun);
 
-  // Find port
-  int max_port = -1, port_index = -1;
-  for (unsigned i = 0; i < sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); i++) {
-    const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
-    if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
-      continue;
+  virtual ~win_aacraid_device() throw();
 
-    if (pe.bPortIdentifier == 0xff) {
-      // Older (<= 9.*) Intel RST driver
-      max_port = phy_info.bNumberOfPhys - 1;
-      if (i >= phy_info.bNumberOfPhys)
-        break;
-      if ((int)i != port)
-        continue;
-    }
-    else {
-      if (pe.bPortIdentifier > max_port)
-        max_port = pe.bPortIdentifier;
-      if (pe.bPortIdentifier != port)
-        continue;
-    }
+  virtual bool open();
 
-    port_index = i;
-    break;
-  }
+  virtual bool scsi_pass_through(struct scsi_cmnd_io *iop);
 
-  if (port_index < 0) {
-    if (port <= max_port)
-      return set_err(ENOENT, "Port %d is disabled", port);
-    else
-      return set_err(ENOENT, "Port %d does not exist (#ports: %d)", port,
-        max_port + 1);
-  }
+private:
+  //Device Host number
+  int m_ctrnum;
 
-  const CSMI_SAS_PHY_ENTITY & phy_ent = phy_info.Phy[port_index];
-  if (phy_ent.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
-    return set_err(ENOENT, "No device on port %d", port);
+  //Channel(Lun) of the device
+  int m_lun;
 
-  switch (phy_ent.Attached.bTargetPortProtocol) {
-    case CSMI_SAS_PROTOCOL_SATA:
-    case CSMI_SAS_PROTOCOL_STP:
-      break;
-    default:
-      return set_err(ENOENT, "No SATA device on port %d (protocol: %d)",
-        port, phy_ent.Attached.bTargetPortProtocol);
-  }
+  //Id of the device
+  int m_target;
+};
 
-  m_phy_ent = phy_ent;
-  return true;
+
+/////////////////////////////////////////////////////////////////////////////
+
+win_aacraid_device::win_aacraid_device(smart_interface * intf,
+  const char *dev_name, unsigned ctrnum, unsigned target, unsigned lun)
+: smart_device(intf, dev_name, "aacraid", "aacraid"),
+  m_ctrnum(ctrnum), m_lun(lun), m_target(target)
+{
+  set_info().info_name = strprintf("%s [aacraid_disk_%02d_%02d_%d]", dev_name, m_ctrnum, m_lun, m_target);
+  set_info().dev_type  = strprintf("aacraid,%d,%d,%d", m_ctrnum, m_lun, m_target);
 }
 
+win_aacraid_device::~win_aacraid_device() throw()
+{
+}
 
-bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+bool win_aacraid_device::open()
 {
-  if (!ata_cmd_is_supported(in,
-    ata_device::supports_data_out |
-    ata_device::supports_output_regs |
-    ata_device::supports_multi_sector |
-    ata_device::supports_48bit,
-    "CMSI")
-  )
-    return false;
+  if (is_open())
+    return true;
 
-  // Create buffer with appropriate size
-  raw_buffer pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) + in.size);
-  CSMI_SAS_STP_PASSTHRU_BUFFER * pthru_buf = (CSMI_SAS_STP_PASSTHRU_BUFFER *)pthru_raw_buf.data();
+  HANDLE hFh = CreateFile( get_dev_name(),
+          GENERIC_READ|GENERIC_WRITE,
+          FILE_SHARE_READ|FILE_SHARE_WRITE,
+          NULL,
+          OPEN_EXISTING,
+          0,
+          0);
+  if (hFh == INVALID_HANDLE_VALUE)
+    return set_err(ENODEV, "Open failed, Error=%u", (unsigned)GetLastError());
 
-  // Set addresses from Phy info
-  CSMI_SAS_STP_PASSTHRU & pthru = pthru_buf->Parameters;
-  const CSMI_SAS_PHY_ENTITY & phy_ent = get_phy_ent();
-  pthru.bPhyIdentifier = phy_ent.Identify.bPhyIdentifier;
-  pthru.bPortIdentifier = phy_ent.bPortIdentifier;
-  memcpy(pthru.bDestinationSASAddress, phy_ent.Attached.bSASAddress,
-    sizeof(pthru.bDestinationSASAddress));
-  pthru.bConnectionRate = CSMI_SAS_LINK_RATE_NEGOTIATED;
+  set_fh(hFh);
+  return true;
+}
 
-  // Set transfer mode
-  switch (in.direction) {
-    case ata_cmd_in::no_data:
-      pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_UNSPECIFIED;
+bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop)
+{
+  int report = scsi_debugmode;
+  if (report > 0)
+  {
+    int k, j;
+    const unsigned char * ucp = iop->cmnd;
+    const char * np;
+    char buff[256];
+    const int sz = (int)sizeof(buff);
+    np = scsi_get_opcode_name(ucp[0]);
+    j  = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+    for (k = 0; k < (int)iop->cmnd_len; ++k)
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+    if ((report > 1) &&
+      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+        "data, len=%d%s:\n", (int)iop->dxfer_len,
+        (trunc ? " [only first 256 bytes shown]" : ""));
+        dStrHex((const char *)iop->dxferp,
+        (trunc ? 256 : (int)iop->dxfer_len) , 1);
+      }
+    else
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+      pout("buff %s\n",buff);
+  }
+
+  char ioBuffer[1000];
+  SRB_IO_CONTROL * pSrbIO = (SRB_IO_CONTROL *) ioBuffer;
+  SCSI_REQUEST_BLOCK * pScsiIO = (SCSI_REQUEST_BLOCK *) (ioBuffer + sizeof(SRB_IO_CONTROL));
+  DWORD scsiRequestBlockSize = sizeof(SCSI_REQUEST_BLOCK);
+  char *pRequestSenseIO = (char *) (ioBuffer + sizeof(SRB_IO_CONTROL) + scsiRequestBlockSize);
+  DWORD dataOffset = (sizeof(SRB_IO_CONTROL) + scsiRequestBlockSize  + 7) & 0xfffffff8;
+  char *pDataIO = (char *) (ioBuffer + dataOffset);
+  memset(pScsiIO, 0, scsiRequestBlockSize);
+  pScsiIO->Length    = (USHORT) scsiRequestBlockSize;
+  pScsiIO->Function  = SRB_FUNCTION_EXECUTE_SCSI;
+  pScsiIO->PathId    = 0;
+  pScsiIO->TargetId  = m_target;
+  pScsiIO->Lun       = m_lun;
+  pScsiIO->CdbLength = (int)iop->cmnd_len;
+  switch(iop->dxfer_dir){
+    case DXFER_NONE:
+      pScsiIO->SrbFlags = SRB_NoDataXfer;
       break;
-    case ata_cmd_in::data_in:
-      pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_READ;
-      pthru.uDataLength = in.size;
+    case DXFER_FROM_DEVICE:
+      pScsiIO->SrbFlags |= SRB_DataIn;
       break;
-    case ata_cmd_in::data_out:
-      pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_WRITE;
-      pthru.uDataLength = in.size;
-      memcpy(pthru_buf->bDataBuffer, in.buffer, in.size);
+    case DXFER_TO_DEVICE:
+      pScsiIO->SrbFlags |= SRB_DataOut;
       break;
     default:
-      return set_err(EINVAL, "csmi_ata_device::ata_pass_through: invalid direction=%d",
-        (int)in.direction);
+      pout("aacraid: bad dxfer_dir\n");
+      return set_err(EINVAL, "aacraid: bad dxfer_dir\n");
+  }
+  pScsiIO->DataTransferLength = (ULONG)iop->dxfer_len;
+  pScsiIO->TimeOutValue = iop->timeout;
+  UCHAR *pCdb = (UCHAR *) pScsiIO->Cdb;
+  memcpy(pCdb, iop->cmnd, 16);
+  if (iop->max_sense_len){
+    memset(pRequestSenseIO, 0, iop->max_sense_len);
+  }
+  if (pScsiIO->SrbFlags & SRB_FLAGS_DATA_OUT){
+    memcpy(pDataIO, iop->dxferp, iop->dxfer_len);
+  }
+  else if (pScsiIO->SrbFlags & SRB_FLAGS_DATA_IN){
+    memset(pDataIO, 0, iop->dxfer_len);
+  }
+
+  DWORD bytesReturned = 0;
+  memset(pSrbIO, 0, sizeof(SRB_IO_CONTROL));
+  pSrbIO->HeaderLength = sizeof(SRB_IO_CONTROL);
+  memcpy(pSrbIO->Signature, "AACAPI", 7);
+  pSrbIO->ControlCode = ARCIOCTL_SEND_RAW_SRB;
+  pSrbIO->Length = (dataOffset + iop->dxfer_len - sizeof(SRB_IO_CONTROL) + 7) & 0xfffffff8;
+  pSrbIO->Timeout = 3*60;
+
+  if (!DeviceIoControl(
+                   get_fh(),
+                   IOCTL_SCSI_MINIPORT,
+                   ioBuffer,
+                   sizeof(SRB_IO_CONTROL) + pSrbIO->Length,
+                   ioBuffer,
+                   sizeof(SRB_IO_CONTROL) + pSrbIO->Length,
+                   &bytesReturned,
+                   NULL)
+     ) {
+    return set_err(EIO, "ARCIOCTL_SEND_RAW_SRB failed, Error=%u", (unsigned)GetLastError());
+  }
+
+  iop->scsi_status = pScsiIO->ScsiStatus;
+  if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+    int slen = sizeof(pRequestSenseIO) + 8;
+    if (slen > (int)sizeof(pRequestSenseIO))
+      slen = sizeof(pRequestSenseIO);
+    if (slen > (int)iop->max_sense_len)
+      slen = (int)iop->max_sense_len;
+    memcpy(iop->sensep, pRequestSenseIO, slen);
+    iop->resp_sense_len = slen;
+    if (report) {
+      if (report > 1) {
+        pout("  >>> Sense buffer, len=%d:\n", slen);
+        dStrHex(iop->sensep, slen , 1);
+      }
+      if ((iop->sensep[0] & 0x7f) > 0x71)
+        pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
+          iop->scsi_status, iop->sensep[1] & 0xf,
+          iop->sensep[2], iop->sensep[3]);
+      else
+        pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
+          iop->scsi_status, iop->sensep[2] & 0xf,
+          iop->sensep[12], iop->sensep[13]);
+    }
   }
-
-  // Set host-to-device FIS
-  {
-    unsigned char * fis = pthru.bCommandFIS;
-    const ata_in_regs & lo = in.in_regs;
-    const ata_in_regs & hi = in.in_regs.prev;
-    fis[ 0] = 0x27; // Type: host-to-device FIS
-    fis[ 1] = 0x80; // Bit7: Update command register
-    fis[ 2] = lo.command;
-    fis[ 3] = lo.features;
-    fis[ 4] = lo.lba_low;
-    fis[ 5] = lo.lba_mid;
-    fis[ 6] = lo.lba_high;
-    fis[ 7] = lo.device;
-    fis[ 8] = hi.lba_low;
-    fis[ 9] = hi.lba_mid;
-    fis[10] = hi.lba_high;
-    fis[11] = hi.features;
-    fis[12] = lo.sector_count;
-    fis[13] = hi.sector_count;
+  else {
+    iop->resp_sense_len = 0;
   }
 
-  // Call ioctl
-  if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU, &pthru_buf->IoctlHeader, pthru_raw_buf.size())) {
-    return false;
+  if (iop->dxfer_dir == DXFER_FROM_DEVICE){
+     memcpy(iop->dxferp,pDataIO, iop->dxfer_len);
   }
-
-  // Get device-to-host FIS
-  {
-    const unsigned char * fis = pthru_buf->Status.bStatusFIS;
-    ata_out_regs & lo = out.out_regs;
-    lo.status       = fis[ 2];
-    lo.error        = fis[ 3];
-    lo.lba_low      = fis[ 4];
-    lo.lba_mid      = fis[ 5];
-    lo.lba_high     = fis[ 6];
-    lo.device       = fis[ 7];
-    lo.sector_count = fis[12];
-    if (in.in_regs.is_48bit_cmd()) {
-      ata_out_regs & hi = out.out_regs.prev;
-      hi.lba_low      = fis[ 8];
-      hi.lba_mid      = fis[ 9];
-      hi.lba_high     = fis[10];
-      hi.sector_count = fis[13];
-    }
+  if((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)){
+    int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+    pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
+      (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex((CHAR*)pDataIO, (trunc ? 256 : (int)(iop->dxfer_len)) , 1);
   }
-
-  // Get data
-  if (in.direction == ata_cmd_in::data_in)
-    // TODO: Check ptru_buf->Status.uDataBytes
-    memcpy(in.buffer, pthru_buf->bDataBuffer, in.size);
-
   return true;
 }
 
 
-//////////////////////////////////////////////////////////////////////
-// win_csmi_device
+/////////////////////////////////////////////////////////////////////////////
+// win_nvme_device
 
-win_csmi_device::win_csmi_device(smart_interface * intf, const char * dev_name,
-  const char * req_type)
-: smart_device(intf, dev_name, "ata", req_type),
-  m_fh(INVALID_HANDLE_VALUE), m_port(-1)
+class win_nvme_device
+: public /*implements*/ nvme_device,
+  public /*extends*/ win_smart_device
 {
-}
+public:
+  win_nvme_device(smart_interface * intf, const char * dev_name,
+    const char * req_type, unsigned nsid);
 
-win_csmi_device::~win_csmi_device() throw()
-{
-  if (m_fh != INVALID_HANDLE_VALUE)
-    CloseHandle(m_fh);
-}
+  virtual bool open();
 
-bool win_csmi_device::is_open() const
-{
-  return (m_fh != INVALID_HANDLE_VALUE);
-}
+  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
+
+  bool open_scsi(int n);
+
+  bool probe();
+
+private:
+  int m_scsi_no;
+};
 
-bool win_csmi_device::close()
-{
-  if (m_fh == INVALID_HANDLE_VALUE)
-    return true;
-  BOOL rc = CloseHandle(m_fh);
-  m_fh = INVALID_HANDLE_VALUE;
-  return !!rc;
-}
 
+/////////////////////////////////////////////////////////////////////////////
 
-bool win_csmi_device::open_scsi()
+win_nvme_device::win_nvme_device(smart_interface * intf, const char * dev_name,
+  const char * req_type, unsigned nsid)
+: smart_device(intf, dev_name, "nvme", req_type),
+  nvme_device(nsid),
+  m_scsi_no(-1)
 {
-  // Parse name
-  unsigned contr_no = ~0, port = ~0; int nc = -1;
-  const char * name = skipdev(get_dev_name());
-  if (!(   sscanf(name, "csmi%u,%u%n", &contr_no, &port, &nc) >= 0
-        && nc == (int)strlen(name) && contr_no <= 9 && port < 32)  )
-    return set_err(EINVAL);
+}
 
-  // Open controller handle
-  char devpath[30];
-  snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%u:", contr_no);
+bool win_nvme_device::open_scsi(int n)
+{
+  // TODO: Use common open function for all devices using "\\.\ScsiN:"
+  char devpath[32];
+  snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%d:", n);
 
   HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
     FILE_SHARE_READ|FILE_SHARE_WRITE,
@@ -3157,6 +3575,8 @@ bool win_csmi_device::open_scsi()
 
   if (h == INVALID_HANDLE_VALUE) {
     long err = GetLastError();
+    if (nvme_debugmode > 1)
+      pout("  %s: Open failed, Error=%ld\n", devpath, err);
     if (err == ERROR_FILE_NOT_FOUND)
       set_err(ENOENT, "%s: not found", devpath);
     else if (err == ERROR_ACCESS_DENIED)
@@ -3166,675 +3586,855 @@ bool win_csmi_device::open_scsi()
     return false;
   }
 
-  if (scsi_debugmode > 1)
-    pout(" %s: successfully opened\n", devpath);
+  if (nvme_debugmode > 1)
+    pout("  %s: successfully opened\n", devpath);
 
-  m_fh = h;
-  m_port = port;
+  set_fh(h);
   return true;
 }
 
+// Check if NVMe pass-through works
+bool win_nvme_device::probe()
+{
+  smartmontools::nvme_id_ctrl id_ctrl;
+  nvme_cmd_in in;
+  in.set_data_in(smartmontools::nvme_admin_identify, &id_ctrl, sizeof(id_ctrl));
+  // in.nsid = 0;
+  in.cdw10 = 0x1;
+  nvme_cmd_out out;
+
+  bool ok = nvme_pass_through(in, out);
+  if (!ok && nvme_debugmode > 1)
+    pout("  nvme probe failed: %s\n", get_errmsg());
+  return ok;
+}
 
-bool win_csmi_device::open()
+bool win_nvme_device::open()
 {
-  if (!open_scsi())
-    return false;
+  if (m_scsi_no < 0) {
+    // First open -> search of NVMe devices
+    const char * name = skipdev(get_dev_name());
+    char s[2+1] = ""; int n1 = -1, n2 = -1, len = strlen(name);
+    unsigned no = ~0, nsid = 0xffffffff;
+    sscanf(name, "nvm%2[es]%u%nn%u%n", s, &no, &n1, &nsid, &n2);
+
+    if (!(   (n1 == len || (n2 == len && nsid > 0))
+          && s[0] == 'e' && (!s[1] || s[1] == 's') ))
+      return set_err(EINVAL);
+
+    if (!s[1]) {
+      // /dev/nvmeN* -> search for nth NVMe device
+      unsigned nvme_cnt = 0;
+      for (int i = 0; i < 32; i++) {
+        if (!open_scsi(i)) {
+          if (get_errno() == EACCES)
+            return false;
+          continue;
+        }
+        // Done if pass-through works and correct number
+        if (probe()) {
+          if (nvme_cnt == no) {
+            m_scsi_no = i;
+            break;
+          }
+          nvme_cnt++;
+        }
+        close();
+      }
 
-  // Get Phy info for this drive
-  if (!select_port(m_port)) {
-    close();
-    return false;
+      if (!is_open())
+        return set_err(ENOENT);
+      clear_err();
+    }
+    else {
+      // /dev/nvmesN* -> use "\\.\ScsiN:"
+      if (!open_scsi(no))
+        return false;
+      m_scsi_no = no;
+    }
+
+    if (!get_nsid())
+      set_nsid(nsid);
+  }
+  else {
+    // Reopen same "\\.\ScsiN:"
+    if (!open_scsi(m_scsi_no))
+      return false;
   }
 
   return true;
 }
 
-
-bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
-  unsigned csmi_bufsiz)
+bool win_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
 {
-  // Determine signature
-  const char * sig;
-  switch (code) {
-    case CC_CSMI_SAS_GET_DRIVER_INFO:
-      sig = CSMI_ALL_SIGNATURE; break;
-    case CC_CSMI_SAS_GET_PHY_INFO:
-    case CC_CSMI_SAS_STP_PASSTHRU:
-      sig = CSMI_SAS_SIGNATURE; break;
-    default:
-      return set_err(ENOSYS, "Unknown CSMI code=%u", code);
-  }
-
-  // Set header
-  csmi_buffer->HeaderLength = sizeof(IOCTL_HEADER);
-  strncpy((char *)csmi_buffer->Signature, sig, sizeof(csmi_buffer->Signature));
-  csmi_buffer->Timeout = CSMI_SAS_TIMEOUT;
-  csmi_buffer->ControlCode = code;
-  csmi_buffer->ReturnCode = 0;
-  csmi_buffer->Length = csmi_bufsiz - sizeof(IOCTL_HEADER);
-
-  // Call function
+  // Create buffer with appropriate size
+  raw_buffer pthru_raw_buf(offsetof(NVME_PASS_THROUGH_IOCTL, DataBuffer) + in.size);
+  NVME_PASS_THROUGH_IOCTL * pthru =
+    reinterpret_cast<NVME_PASS_THROUGH_IOCTL *>(pthru_raw_buf.data());
+
+  // Set NVMe command
+  pthru->SrbIoCtrl.HeaderLength = sizeof(SRB_IO_CONTROL);
+  memcpy(pthru->SrbIoCtrl.Signature, NVME_SIG_STR, sizeof(NVME_SIG_STR));
+  pthru->SrbIoCtrl.Timeout = 60;
+  pthru->SrbIoCtrl.ControlCode = NVME_PASS_THROUGH_SRB_IO_CODE;
+  pthru->SrbIoCtrl.ReturnCode = 0;
+  pthru->SrbIoCtrl.Length = pthru_raw_buf.size() - sizeof(SRB_IO_CONTROL);
+
+  pthru->NVMeCmd[0] = in.opcode;
+  pthru->NVMeCmd[1] = in.nsid;
+  pthru->NVMeCmd[10] = in.cdw10;
+  pthru->NVMeCmd[11] = in.cdw11;
+  pthru->NVMeCmd[12] = in.cdw12;
+  pthru->NVMeCmd[13] = in.cdw13;
+  pthru->NVMeCmd[14] = in.cdw14;
+  pthru->NVMeCmd[15] = in.cdw15;
+
+  pthru->Direction = in.direction();
+  // pthru->QueueId = 0; // AdminQ
+  // pthru->DataBufferLen = 0;
+  if (in.direction() & nvme_cmd_in::data_out) {
+    pthru->DataBufferLen = in.size;
+    memcpy(pthru->DataBuffer, in.buffer, in.size);
+  }
+  // pthru->MetaDataLen = 0;
+  pthru->ReturnBufferLen = pthru_raw_buf.size();
+
+  // Call NVME_PASS_THROUGH
   DWORD num_out = 0;
-  if (!DeviceIoControl(m_fh, IOCTL_SCSI_MINIPORT,
-    csmi_buffer, csmi_bufsiz, csmi_buffer, csmi_bufsiz, &num_out, (OVERLAPPED*)0)) {
-    long err = GetLastError();
-    if (scsi_debugmode)
-      pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code, err);
-    if (   err == ERROR_INVALID_FUNCTION
-        || err == ERROR_NOT_SUPPORTED
-        || err == ERROR_DEV_NOT_EXIST)
-      return set_err(ENOSYS, "CSMI is not supported (Error=%ld)", err);
-    else
-      return set_err(EIO, "CSMI(%u) failed with Error=%ld", code, err);
-  }
+  BOOL ok = DeviceIoControl(get_fh(), IOCTL_SCSI_MINIPORT,
+    pthru, pthru_raw_buf.size(), pthru, pthru_raw_buf.size(),
+    &num_out, (OVERLAPPED*)0);
 
-  // Check result
-  if (csmi_buffer->ReturnCode) {
-    if (scsi_debugmode) {
-      pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n",
-        code, (unsigned)csmi_buffer->ReturnCode);
-    }
-    return set_err(EIO, "CSMI(%u) failed with ReturnCode=%u", code, (unsigned)csmi_buffer->ReturnCode);
-  }
+  // Check status
+  unsigned status = pthru->CplEntry[3] >> 17;
+  if (status)
+    return set_nvme_err(out, status);
 
-  if (scsi_debugmode > 1)
-    pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code, (unsigned)num_out);
+  if (!ok)
+    return set_err(EIO, "NVME_PASS_THROUGH failed, Error=%u", (unsigned)GetLastError());
+
+  if (in.direction() & nvme_cmd_in::data_in)
+    memcpy(in.buffer, pthru->DataBuffer, in.size);
 
+  out.result = pthru->CplEntry[0];
   return true;
 }
 
 
 /////////////////////////////////////////////////////////////////////////////
-// SPT Interface (for SCSI devices and ATA devices behind SATLs)
-// Only supported in NT and later
-/////////////////////////////////////////////////////////////////////////////
+// win_smart_interface
+// Platform specific interface
 
-win_scsi_device::win_scsi_device(smart_interface * intf,
-  const char * dev_name, const char * req_type)
-: smart_device(intf, dev_name, "scsi", req_type)
+class win_smart_interface
+: public /*implements*/ smart_interface
 {
-}
+public:
+  virtual std::string get_os_version_str();
 
-bool win_scsi_device::open()
-{
-  const char * name = skipdev(get_dev_name()); int len = strlen(name);
-  // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N
-  char drive[2+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1;
-  if (   sscanf(name, "sd%2[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1
-      && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))  ) {
-    return open(sdxy_to_phydrive(drive), -1, -1, sub_addr);
-  }
-  // pd<m>,N => Physical drive <m>, RAID port N
-  int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
-  if (   sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
-      && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
-    return open(pd_num, -1, -1, sub_addr);
-  }
-  // [a-zA-Z]: => Physical drive behind logical drive 0-25
-  int logdrive = drive_letter(name);
-  if (logdrive >= 0) {
-    return open(-1, logdrive, -1, -1);
-  }
-  // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
-  int tape_num = -1; n1 = -1;
-  if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
-    return open(-1, -1, tape_num, -1);
-  }
-  tape_num = -1; n1 = -1;
-  if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
-    return open(-1, -1, tape_num, -1);
-  }
-  // tape<m> => tape drive <m>
-  tape_num = -1; n1 = -1;
-  if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
-    return open(-1, -1, tape_num, -1);
-  }
+  virtual std::string get_app_examples(const char * appname);
 
-  return set_err(EINVAL);
-}
+#ifndef __CYGWIN__
+  virtual int64_t get_timer_usec();
+#endif
 
-bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/)
-{
-  char b[128];
-  b[sizeof(b) - 1] = '\0';
-  if (pd_num >= 0)
-    snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
-  else if (ld_num >= 0)
-    snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
-  else if (tape_num >= 0)
-    snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
-  else {
-    set_err(EINVAL);
-    return false;
-  }
+  virtual bool disable_system_auto_standby(bool disable);
 
-  // Open device
-  HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
-           FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
-           OPEN_EXISTING, 0, 0);
-  if (h == INVALID_HANDLE_VALUE) {
-    set_err(ENODEV, "%s: Open failed, Error=%u", b, (unsigned)GetLastError());
-    return false;
-  }
-  set_fh(h);
-  return true;
-}
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0);
 
+protected:
+  virtual ata_device * get_ata_device(const char * name, const char * type);
 
-typedef struct {
-  SCSI_PASS_THROUGH_DIRECT spt;
-  ULONG           Filler;
-  UCHAR           ucSenseBuf[64];
-} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
+  virtual scsi_device * get_scsi_device(const char * name, const char * type);
 
+  virtual nvme_device * get_nvme_device(const char * name, const char * type, unsigned nsid);
 
-// Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
-// Used if DataTransferLength not supported by *_DIRECT.
-static long scsi_pass_through_indirect(HANDLE h,
-  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd)
-{
-  struct SCSI_PASS_THROUGH_WITH_BUFFERS {
-    SCSI_PASS_THROUGH spt;
-    ULONG Filler;
-    UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)];
-    UCHAR ucDataBuf[512];
-  };
+  virtual smart_device * autodetect_smart_device(const char * name);
 
-  SCSI_PASS_THROUGH_WITH_BUFFERS sb;
-  memset(&sb, 0, sizeof(sb));
+  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
 
-  // DATA_OUT not implemented yet
-  if (!(   sbd->spt.DataIn == SCSI_IOCTL_DATA_IN
-        && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf)))
-    return ERROR_INVALID_PARAMETER;
+  virtual std::string get_valid_custom_dev_types_str();
 
-  sb.spt.Length = sizeof(sb.spt);
-  sb.spt.CdbLength = sbd->spt.CdbLength;
-  memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb));
-  sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
-  sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
-  sb.spt.DataIn = sbd->spt.DataIn;
-  sb.spt.DataTransferLength = sbd->spt.DataTransferLength;
-  sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
-  sb.spt.TimeOutValue = sbd->spt.TimeOutValue;
+private:
+  ata_device * get_usb_device(const char * name, int phydrive, int logdrive = -1);
+};
 
-  DWORD num_out;
-  if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH,
-         &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
-    return GetLastError();
 
-  sbd->spt.ScsiStatus = sb.spt.ScsiStatus;
-  if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION)
-    memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf));
+/////////////////////////////////////////////////////////////////////////////
 
-  sbd->spt.DataTransferLength = sb.spt.DataTransferLength;
-  if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0)
-    memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength);
-  return 0;
+#ifndef _WIN64
+// Running on 64-bit Windows as 32-bit app ?
+static bool is_wow64()
+{
+  BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
+    (BOOL (WINAPI *)(HANDLE, PBOOL))
+    GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
+  if (!IsWow64Process_p)
+    return false;
+  BOOL w64 = FALSE;
+  if (!IsWow64Process_p(GetCurrentProcess(), &w64))
+    return false;
+  return !!w64;
 }
+#endif // _WIN64
 
-
-// Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
-bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
+// Return info string about build host and OS version
+std::string win_smart_interface::get_os_version_str()
 {
-  int report = scsi_debugmode; // TODO
+  char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
+    = SMARTMONTOOLS_BUILD_HOST;
+  if (vstr[1] < '6')
+    vstr[1] = '6';
+  char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1;
+  const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
+  assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
 
-  if (report > 0) {
-    int k, j;
-    const unsigned char * ucp = iop->cmnd;
-    const char * np;
-    char buff[256];
-    const int sz = (int)sizeof(buff);
+  // Starting with Windows 8.1, GetVersionEx() does no longer report the
+  // actual OS version, see:
+  // http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
 
-    np = scsi_get_opcode_name(ucp[0]);
-    j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
-    for (k = 0; k < (int)iop->cmnd_len; ++k)
-      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
-    if ((report > 1) &&
-      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+  // RtlGetVersion() is not affected
+  LONG /*NTSTATUS*/ (WINAPI /*NTAPI*/ * RtlGetVersion_p)(LPOSVERSIONINFOEXW) =
+    (LONG (WINAPI *)(LPOSVERSIONINFOEXW))
+    GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
 
-      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-              "data, len=%d%s:\n", (int)iop->dxfer_len,
-              (trunc ? " [only first 256 bytes shown]" : ""));
-      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-    }
-    else
-      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
-    pout("%s", buff);
+  OSVERSIONINFOEXW vi; memset(&vi, 0, sizeof(vi));
+  vi.dwOSVersionInfoSize = sizeof(vi);
+  if (!RtlGetVersion_p || RtlGetVersion_p(&vi)) {
+    if (!GetVersionExW((OSVERSIONINFOW *)&vi))
+      return vstr;
   }
 
-  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
-  if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
-    set_err(EINVAL, "cmnd_len too large");
-    return false;
+  const char * w = 0;
+  if (   vi.dwPlatformId == VER_PLATFORM_WIN32_NT
+      && vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
+    bool ws = (vi.wProductType <= VER_NT_WORKSTATION);
+    switch (vi.dwMajorVersion << 4 | vi.dwMinorVersion) {
+      case 0x50: w =       "2000";              break;
+      case 0x51: w =       "xp";                break;
+      case 0x52: w = (!GetSystemMetrics(89/*SM_SERVERR2*/)
+                         ? "2003"  : "2003r2"); break;
+      case 0x60: w = (ws ? "vista" : "2008"  ); break;
+      case 0x61: w = (ws ? "win7"  : "2008r2"); break;
+      case 0x62: w = (ws ? "win8"  : "2012"  ); break;
+      case 0x63: w = (ws ? "win8.1": "2012r2"); break;
+      case 0x64: w = (ws ? "w10tp" : "w10tps"); break; //  6.4 = Win 10 Technical Preview
+      case 0xa0: w = (ws ? "win10" : "2016"  ); break; // 10.0 = Win 10 : Win Server 2016 (TP only?)
+    }
   }
 
-  memset(&sb, 0, sizeof(sb));
-  sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
-  sb.spt.CdbLength = iop->cmnd_len;
-  memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
-  sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
-  sb.spt.SenseInfoOffset =
-    offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
-  sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
-
-  bool direct = true;
-  switch (iop->dxfer_dir) {
-    case DXFER_NONE:
-      sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
-      break;
-    case DXFER_FROM_DEVICE:
-      sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
-      sb.spt.DataTransferLength = iop->dxfer_len;
-      sb.spt.DataBuffer = iop->dxferp;
-      // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
-      // transfers (needed for SMART STATUS check of JMicron USB bridges)
-      if (sb.spt.DataTransferLength == 1)
-        direct = false;
-      break;
-    case DXFER_TO_DEVICE:
-      sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
-      sb.spt.DataTransferLength = iop->dxfer_len;
-      sb.spt.DataBuffer = iop->dxferp;
-      break;
-    default:
-      set_err(EINVAL, "bad dxfer_dir");
-      return false;
-  }
+  const char * w64 = "";
+#ifndef _WIN64
+  if (is_wow64())
+    w64 = "(64)";
+#endif
 
-  long err = 0;
-  if (direct) {
-    DWORD num_out;
-    if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
-           &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
-      err = GetLastError();
-  }
+  if (!w)
+    snprintf(vptr, vlen, "-%s%u.%u%s",
+      (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "??"),
+      (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
+  else if (vi.wServicePackMinor)
+    snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
+  else if (vi.wServicePackMajor)
+    snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
   else
-    err = scsi_pass_through_indirect(get_fh(), &sb);
-
-  if (err)
-    return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO),
-      "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
-      (direct ? "_DIRECT" : ""), err);
+    snprintf(vptr, vlen, "-%s%s", w, w64);
+  return vstr;
+}
 
-  iop->scsi_status = sb.spt.ScsiStatus;
-  if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
-    int slen = sb.ucSenseBuf[7] + 8;
+#ifndef __CYGWIN__
+// MSVCRT only provides ftime() which uses GetSystemTime()
+// This provides only ~15ms resolution by default.
+// Use QueryPerformanceCounter instead (~300ns).
+// (Cygwin provides CLOCK_MONOTONIC which has the same effect)
+int64_t win_smart_interface::get_timer_usec()
+{
+  static int64_t freq = 0;
 
-    if (slen > (int)sizeof(sb.ucSenseBuf))
-      slen = sizeof(sb.ucSenseBuf);
-    if (slen > (int)iop->max_sense_len)
-      slen = iop->max_sense_len;
-    memcpy(iop->sensep, sb.ucSenseBuf, slen);
-    iop->resp_sense_len = slen;
-    if (report) {
-      if (report > 1) {
-        pout("  >>> Sense buffer, len=%d:\n", slen);
-        dStrHex(iop->sensep, slen , 1);
-      }
-      if ((iop->sensep[0] & 0x7f) > 0x71)
-        pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
-             iop->scsi_status, iop->sensep[1] & 0xf,
-             iop->sensep[2], iop->sensep[3]);
-      else
-        pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
-             iop->scsi_status, iop->sensep[2] & 0xf,
-             iop->sensep[12], iop->sensep[13]);
-    }
-  } else
-    iop->resp_sense_len = 0;
+  LARGE_INTEGER t;
+  if (freq == 0)
+    freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1);
+  if (freq <= 0)
+    return smart_interface::get_timer_usec();
 
-  if (iop->dxfer_len > sb.spt.DataTransferLength)
-    iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
-  else
-    iop->resid = 0;
+  if (!QueryPerformanceCounter(&t))
+    return -1;
+  if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000))
+    return -1;
 
-  if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
-     int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-     pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
-        (trunc ? " [only first 256 bytes shown]" : ""));
-        dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-  }
-  return true;
+  return (t.QuadPart * 1000000LL) / freq;
 }
+#endif // __CYGWIN__
 
-// Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
-static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop)
-{
-  int report = scsi_debugmode; // TODO
 
-  if (report > 0) {
-    int k, j;
-    const unsigned char * ucp = iop->cmnd;
-    const char * np;
-    char buff[256];
-    const int sz = (int)sizeof(buff);
+ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
+{
+  const char * testname = skipdev(name);
+  if (!strncmp(testname, "csmi", 4))
+    return new win_csmi_device(this, name, type);
+  if (!strncmp(testname, "tw_cli", 6))
+    return new win_tw_cli_device(this, name, type);
+  return new win_ata_device(this, name, type);
+}
 
-    np = scsi_get_opcode_name(ucp[0]);
-    j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
-    for (k = 0; k < (int)iop->cmnd_len; ++k)
-      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
-    if ((report > 1) &&
-      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+scsi_device * win_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+  return new win_scsi_device(this, name, type);
+}
 
-      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-              "data, len=%d%s:\n", (int)iop->dxfer_len,
-              (trunc ? " [only first 256 bytes shown]" : ""));
-      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-    }
-    else
-      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
-    pout("%s", buff);
-  }
+nvme_device * win_smart_interface::get_nvme_device(const char * name, const char * type,
+  unsigned nsid)
+{
+  return new win_nvme_device(this, name, type, nsid);
+}
 
-  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
-  if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
-    return EINVAL;
-  }
 
-  memset(&sb, 0, sizeof(sb));
-  sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
-  //sb.spt.PathId = 0;
-  sb.spt.TargetId = targetid;
-  //sb.spt.Lun = 0;
-  sb.spt.CdbLength = iop->cmnd_len;
-  memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
-  sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
-  sb.spt.SenseInfoOffset =
-    offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
-  sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
+smart_device * win_smart_interface::get_custom_smart_device(const char * name, const char * type)
+{
+  // Areca?
+  int disknum = -1, n1 = -1, n2 = -1;
+  int encnum = 1;
+  char devpath[32];
 
-  bool direct = true;
-  switch (iop->dxfer_dir) {
-    case DXFER_NONE:
-      sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
-      break;
-    case DXFER_FROM_DEVICE:
-      sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
-      sb.spt.DataTransferLength = iop->dxfer_len;
-      sb.spt.DataBuffer = iop->dxferp;
-      // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
-      // transfers (needed for SMART STATUS check of JMicron USB bridges)
-      if (sb.spt.DataTransferLength == 1)
-        direct = false;
-      break;
-    case DXFER_TO_DEVICE:
-      sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
-      sb.spt.DataTransferLength = iop->dxfer_len;
-      sb.spt.DataBuffer = iop->dxferp;
-      break;
-    default:
-      return EINVAL;
-  }
+  if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
+    if (!(1 <= disknum && disknum <= 128)) {
+      set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum);
+      return 0;
+    }
+    if (!(1 <= encnum && encnum <= 8)) {
+      set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum);
+      return 0;
+    }
 
-  long err = 0;
-  if (direct) {
-    DWORD num_out;
-    if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
-           &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
-      err = GetLastError();
+    name = skipdev(name);
+#define ARECA_MAX_CTLR_NUM  16
+    n1 = -1;
+    int ctlrindex = 0;
+    if (sscanf(name, "arcmsr%d%n", &ctlrindex, &n1) >= 1 && n1 == (int)strlen(name)) {
+      /*
+       1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
+       2. map arcmsrX into "\\\\.\\scsiX"
+      */
+     for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) {
+        memset(devpath, 0, sizeof(devpath));
+        snprintf(devpath, sizeof(devpath), "\\\\.\\scsi%d:", idx);
+        win_areca_ata_device *arcdev = new win_areca_ata_device(this, devpath, disknum, encnum);
+        if(arcdev->arcmsr_probe()) {
+          if(ctlrindex-- == 0) {
+            return arcdev;
+          }
+        }
+        delete arcdev;
+      }
+      set_err(ENOENT, "No Areca controller found");
+    }
+    else
+      set_err(EINVAL, "Option -d areca,N/E requires device name /dev/arcmsrX");
+    return 0;
   }
-  else
-    err = scsi_pass_through_indirect(fd, &sb);
 
-  if (err)
-  {
-    return err;
-  }
+  // aacraid?
+  unsigned ctrnum, lun, target;
+  n1 = -1;
 
-  iop->scsi_status = sb.spt.ScsiStatus;
-  if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
-    int slen = sb.ucSenseBuf[7] + 8;
+  if (   sscanf(type, "aacraid,%u,%u,%u%n", &ctrnum, &lun, &target, &n1) >= 3
+      && n1 == (int)strlen(type)) {
+#define aacraid_MAX_CTLR_NUM  16
+    if (ctrnum >= aacraid_MAX_CTLR_NUM) {
+      set_err(EINVAL, "aacraid: invalid host number %u", ctrnum);
+      return 0;
+    }
 
-    if (slen > (int)sizeof(sb.ucSenseBuf))
-      slen = sizeof(sb.ucSenseBuf);
-    if (slen > (int)iop->max_sense_len)
-      slen = iop->max_sense_len;
-    memcpy(iop->sensep, sb.ucSenseBuf, slen);
-    iop->resp_sense_len = slen;
-    if (report) {
-      if (report > 1) {
-        pout("  >>> Sense buffer, len=%d:\n", slen);
-        dStrHex(iop->sensep, slen , 1);
+    /*
+    1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[AACRAID_MAX_CTLR_NUM]:" and
+    2. map ARCX into "\\\\.\\scsiX"
+    */
+    memset(devpath, 0, sizeof(devpath));
+    unsigned ctlrindex = 0;
+    for (int portNum = 0; portNum < aacraid_MAX_CTLR_NUM; portNum++){
+      char subKey[63];
+      snprintf(subKey, sizeof(subKey), "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port %d", portNum);
+      HKEY hScsiKey = 0;
+      long regStatus = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hScsiKey);
+      if (regStatus == ERROR_SUCCESS){
+        char driverName[20];
+        DWORD driverNameSize = sizeof(driverName);
+        DWORD regType = 0;
+        regStatus = RegQueryValueExA(hScsiKey, "Driver", NULL, &regType, (LPBYTE) driverName, &driverNameSize);
+        if (regStatus == ERROR_SUCCESS){
+          if (regType == REG_SZ){
+            if (stricmp(driverName, "arcsas") == 0){
+              if(ctrnum == ctlrindex){
+                snprintf(devpath, sizeof(devpath), "\\\\.\\Scsi%d:", portNum);
+                return get_sat_device("sat,auto",
+                  new win_aacraid_device(this, devpath, ctrnum, target, lun));
+              }
+              ctlrindex++;
+            }
+          }
+        }
+        RegCloseKey(hScsiKey);
       }
-      if ((iop->sensep[0] & 0x7f) > 0x71)
-        pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
-             iop->scsi_status, iop->sensep[1] & 0xf,
-             iop->sensep[2], iop->sensep[3]);
-      else
-        pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
-             iop->scsi_status, iop->sensep[2] & 0xf,
-             iop->sensep[12], iop->sensep[13]);
     }
-  } else
-    iop->resp_sense_len = 0;
 
-  if (iop->dxfer_len > sb.spt.DataTransferLength)
-    iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
-  else
-    iop->resid = 0;
-
-  if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
-     int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-     pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
-        (trunc ? " [only first 256 bytes shown]" : ""));
-        dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+    set_err(EINVAL, "aacraid: host %u not found", ctrnum);
+    return 0;
   }
 
   return 0;
 }
 
-// Areca RAID Controller(SAS Device)
-win_areca_scsi_device::win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
-: smart_device(intf, dev_name, "areca", "areca")
+std::string win_smart_interface::get_valid_custom_dev_types_str()
 {
-    set_fh(INVALID_HANDLE_VALUE);
-    set_disknum(disknum);
-    set_encnum(encnum);
-    set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
+  return "aacraid,H,L,ID, areca,N[/E]";
 }
 
-bool win_areca_scsi_device::open()
-{
-  HANDLE hFh;
 
-  if( is_open() )
-  {
-    return true;
-  }
-  hFh = CreateFile( get_dev_name(),
-                    GENERIC_READ|GENERIC_WRITE,
-                    FILE_SHARE_READ|FILE_SHARE_WRITE,
-                    NULL,
-                    OPEN_EXISTING,
-                    0,
-                    NULL );
-  if(hFh == INVALID_HANDLE_VALUE)
-  {
+// Return value for device detection functions
+enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_SAT, DEV_USB };
+
+// Return true if ATA drive behind a SAT layer
+static bool is_sat(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
+{
+  if (!data->desc.VendorIdOffset)
     return false;
-  }
+  if (strcmp(data->raw + data->desc.VendorIdOffset, "ATA     "))
+    return false;
+  return true;
+}
 
-  set_fh(hFh);
+// Return true if Intel ICHxR RAID volume
+static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
+{
+  if (!(data->desc.VendorIdOffset && data->desc.ProductIdOffset))
+    return false;
+  const char * vendor = data->raw + data->desc.VendorIdOffset;
+  if (!(!strnicmp(vendor, "Intel", 5) && strspn(vendor+5, " ") == strlen(vendor+5)))
+    return false;
+  if (strnicmp(data->raw + data->desc.ProductIdOffset, "Raid ", 5))
+    return false;
   return true;
 }
 
-smart_device * win_areca_scsi_device::autodetect_open()
+// get DEV_* for open handle
+static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
 {
-  return this;
+  // Get BusType from device descriptor
+  STORAGE_DEVICE_DESCRIPTOR_DATA data;
+  if (storage_query_property_ioctl(hdevice, &data))
+    return DEV_UNKNOWN;
+
+  // Newer BusType* values are missing in older includes
+  switch ((int)data.desc.BusType) {
+    case BusTypeAta:
+    case 0x0b: // BusTypeSata
+      // Certain Intel AHCI drivers (C600+/C220+) have broken
+      // IOCTL_ATA_PASS_THROUGH support and a working SAT layer
+      if (is_sat(&data))
+        return DEV_SAT;
+
+      if (ata_version_ex)
+        memset(ata_version_ex, 0, sizeof(*ata_version_ex));
+      return DEV_ATA;
+
+    case BusTypeScsi:
+    case BusTypeRAID:
+      if (is_sat(&data))
+        return DEV_SAT;
+
+      // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
+      if (is_intel_raid_volume(&data))
+        return DEV_SCSI;
+      // LSI/3ware RAID volume: supports SMART_*
+      if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
+        return DEV_ATA;
+
+      return DEV_SCSI;
+
+    case 0x09: // BusTypeiScsi
+    case 0x0a: // BusTypeSas
+      if (is_sat(&data))
+        return DEV_SAT;
+
+      return DEV_SCSI;
+
+    case BusTypeUsb:
+      return DEV_USB;
+
+    default:
+      return DEV_UNKNOWN;
+  }
+  /*NOTREACHED*/
 }
 
-int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
+// get DEV_* for device path
+static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 {
-   int ioctlreturn = 0;
+  bool admin = true;
+  HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
+    FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+  if (h == INVALID_HANDLE_VALUE) {
+    admin = false;
+    h = CreateFileA(path, 0,
+      FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+    if (h == INVALID_HANDLE_VALUE)
+      return DEV_UNKNOWN;
+  }
+  if (ata_debugmode > 1 || scsi_debugmode > 1)
+    pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
+  win_dev_type type = get_controller_type(h, admin, ata_version_ex);
+  CloseHandle(h);
+  return type;
+}
 
-   ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
-   if ( ioctlreturn || iop->scsi_status )
-   {
-     ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
-     if ( ioctlreturn || iop->scsi_status )
-     {
-       // errors found
-       return -1;
-     }
-   }
+// get DEV_* for physical drive number
+static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
+{
+  char path[30];
+  snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
+  return get_controller_type(path, ata_version_ex);
+}
 
-   return ioctlreturn;
+static win_dev_type get_phy_drive_type(int drive)
+{
+  return get_phy_drive_type(drive, 0);
 }
 
-bool win_areca_scsi_device::arcmsr_lock()
+// get DEV_* for logical drive number
+static win_dev_type get_log_drive_type(int drive)
 {
-#define    SYNCOBJNAME "Global\\SynIoctlMutex"
-  int ctlrnum = -1;
-  char mutexstr[64];
+  char path[30];
+  snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
+  return get_controller_type(path);
+}
 
-  if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
-    return set_err(EINVAL, "unable to parse device name");
+static win_dev_type get_dev_type(const char * name, int & phydrive, int & logdrive)
+{
+  phydrive = logdrive = -1;
 
-  snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
-  m_mutex = CreateMutex(NULL, FALSE, mutexstr);
-  if ( m_mutex == NULL )
-  {
-    return set_err(EIO, "CreateMutex failed");
+  name = skipdev(name);
+  if (!strncmp(name, "st", 2))
+    return DEV_SCSI;
+  if (!strncmp(name, "nst", 3))
+    return DEV_SCSI;
+  if (!strncmp(name, "tape", 4))
+    return DEV_SCSI;
+
+  logdrive = drive_letter(name);
+  if (logdrive >= 0) {
+    win_dev_type type = get_log_drive_type(logdrive);
+    return (type != DEV_UNKNOWN ? type : DEV_SCSI);
   }
 
-  // atomic access to driver
-  WaitForSingleObject(m_mutex, INFINITE);
+  char drive[2+1] = "";
+  if (sscanf(name, "sd%2[a-z]", drive) == 1) {
+    phydrive = sdxy_to_phydrive(drive);
+    return get_phy_drive_type(phydrive);
+  }
 
-  return true;
+  if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
+    return get_phy_drive_type(phydrive);
+
+  return DEV_UNKNOWN;
 }
 
 
-bool win_areca_scsi_device::arcmsr_unlock()
+ata_device * win_smart_interface::get_usb_device(const char * name,
+  int phydrive, int logdrive /* = -1 */)
 {
-  if( m_mutex != NULL)
-  {
-      ReleaseMutex(m_mutex);
-      CloseHandle(m_mutex);
+  // Get USB bridge ID
+  unsigned short vendor_id = 0, product_id = 0;
+  if (!get_usb_id(phydrive, logdrive, vendor_id, product_id)) {
+    set_err(EINVAL, "Unable to read USB device ID");
+    return 0;
   }
 
-  return true;
+  // Get type name for this ID
+  const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
+  if (!usbtype)
+    return 0;
+
+  // Return SAT/USB device for this type
+  return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
+}
+
+smart_device * win_smart_interface::autodetect_smart_device(const char * name)
+{
+  const char * testname = skipdev(name);
+  if (str_starts_with(testname, "hd"))
+    return new win_ata_device(this, name, "");
+
+  if (str_starts_with(testname, "tw_cli"))
+    return new win_tw_cli_device(this, name, "");
+
+  if (str_starts_with(testname, "csmi"))
+    return new win_csmi_device(this, name, "");
+
+  if (str_starts_with(testname, "nvme"))
+    return new win_nvme_device(this, name, "", 0 /* use default nsid */);
+
+  int phydrive = -1, logdrive = -1;
+  win_dev_type type = get_dev_type(name, phydrive, logdrive);
+
+  if (type == DEV_ATA)
+    return new win_ata_device(this, name, "");
+
+  if (type == DEV_SCSI)
+    return new win_scsi_device(this, name, "");
+
+  if (type == DEV_SAT)
+    return get_sat_device("sat", new win_scsi_device(this, name, ""));
+
+  if (type == DEV_USB)
+    return get_usb_device(name, phydrive, logdrive);
+
+  return 0;
 }
 
 
-// Areca RAID Controller(SATA Disk)
-win_areca_ata_device::win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
-: smart_device(intf, dev_name, "areca", "areca")
+// Scan for devices
+bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const char * type, const char * pattern /* = 0*/)
 {
-  set_fh(INVALID_HANDLE_VALUE);
-  set_disknum(disknum);
-  set_encnum(encnum);
-  set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
-}
+  if (pattern) {
+    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+    return false;
+  }
 
-bool win_areca_ata_device::open()
-{
-  HANDLE hFh;
+  // Check for "[*,]pd" type
+  bool pd = false;
+  char type2[16+1] = "";
+  if (type) {
+    int nc = -1;
+    if (!strcmp(type, "pd")) {
+      pd = true;
+      type = 0;
+    }
+    else if (sscanf(type, "%16[^,],pd%n", type2, &nc) == 1 &&
+             nc == (int)strlen(type)) {
+      pd = true;
+      type = type2;
+    }
+  }
 
-  if( is_open() )
-  {
-    return true;
+  // Set valid types
+  bool ata, scsi, sat, usb, csmi, nvme;
+  if (!type) {
+    ata = scsi = usb = sat = csmi = true;
+#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
+    nvme = true;
+#else
+    nvme = false;
+#endif
   }
-  hFh = CreateFile( get_dev_name(),
-                    GENERIC_READ|GENERIC_WRITE,
-                    FILE_SHARE_READ|FILE_SHARE_WRITE,
-                    NULL,
-                    OPEN_EXISTING,
-                    0,
-                    NULL );
-  if(hFh == INVALID_HANDLE_VALUE)
-  {
-    return false;
+  else {
+    ata = scsi = usb = sat = csmi = nvme = false;
+    if (!strcmp(type, "ata"))
+      ata = true;
+    else if (!strcmp(type, "scsi"))
+      scsi = true;
+    else if (!strcmp(type, "sat"))
+      sat = true;
+    else if (!strcmp(type, "usb"))
+      usb = true;
+    else if (!strcmp(type, "csmi"))
+      csmi = true;
+    else if (!strcmp(type, "nvme"))
+      nvme = true;
+    else {
+      set_err(EINVAL,
+              "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], "
+              "sat[,pd], usb[,pd], csmi, nvme, pd", type);
+      return false;
+    }
   }
 
-  set_fh(hFh);
-  return true;
-}
+  char name[20];
 
-smart_device * win_areca_ata_device::autodetect_open()
-{
-  int is_ata = 1;
+  if (ata || scsi || sat || usb) {
+    // Scan up to 128 drives and 2 3ware controllers
+    const int max_raid = 2;
+    bool raid_seen[max_raid] = {false, false};
 
-  // autodetect device type
-  is_ata = arcmsr_get_dev_type();
-  if(is_ata < 0)
-  {
-    set_err(EIO);
-    return this;
-  }
+    for (int i = 0; i < 128; i++) {
+      if (pd)
+        snprintf(name, sizeof(name), "/dev/pd%d", i);
+      else if (i + 'a' <= 'z')
+        snprintf(name, sizeof(name), "/dev/sd%c", i + 'a');
+      else
+        snprintf(name, sizeof(name), "/dev/sd%c%c",
+                 i / ('z'-'a'+1) - 1 + 'a',
+                 i % ('z'-'a'+1)     + 'a');
 
-  if(is_ata == 1)
-  {
-    // SATA device
-    return this;
-  }
+      smart_device * dev = 0;
+      GETVERSIONINPARAMS_EX vers_ex;
 
-  // SAS device
-  smart_device_auto_ptr newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
-  close();
-  delete this;
-  newdev->open(); // TODO: Can possibly pass open fd
+      switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) {
+        case DEV_ATA:
+          // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
+          if (!ata)
+            continue;
 
-  return newdev.release();
-}
+          // Interpret RAID drive map if present
+          if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
+            // Skip if too many controllers or logical drive from this controller already seen
+            if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId]))
+              continue;
+            raid_seen[vers_ex.wControllerId] = true;
+            // Add physical drives
+            int len = strlen(name);
+            for (unsigned int pi = 0; pi < 32; pi++) {
+              if (vers_ex.dwDeviceMapEx & (1L << pi)) {
+                snprintf(name+len, sizeof(name)-1-len, ",%u", pi);
+                devlist.push_back( new win_ata_device(this, name, "ata") );
+              }
+            }
+            continue;
+          }
 
-int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
-{
-   int ioctlreturn = 0;
+          dev = new win_ata_device(this, name, "ata");
+          break;
 
-   ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
-   if ( ioctlreturn || iop->scsi_status )
-   {
-     ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
-     if ( ioctlreturn || iop->scsi_status )
-     {
-       // errors found
-       return -1;
-     }
-   }
+        case DEV_SCSI:
+          // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
+          if (!scsi)
+            continue;
+          dev = new win_scsi_device(this, name, "scsi");
+          break;
 
-   return ioctlreturn;
-}
+        case DEV_SAT:
+          // STORAGE_QUERY_PROPERTY returned VendorId "ATA     "
+          if (!sat)
+            continue;
+          dev = get_sat_device("sat", new win_scsi_device(this, name, ""));
+          break;
 
-bool win_areca_ata_device::arcmsr_lock()
-{
-#define    SYNCOBJNAME "Global\\SynIoctlMutex"
-  int ctlrnum = -1;
-  char mutexstr[64];
+        case DEV_USB:
+          // STORAGE_QUERY_PROPERTY returned USB
+          if (!usb)
+            continue;
+          dev = get_usb_device(name, i);
+          if (!dev)
+            // Unknown or unsupported USB ID, return as SCSI
+            dev = new win_scsi_device(this, name, "");
+          break;
 
-  if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
-    return set_err(EINVAL, "unable to parse device name");
+        default:
+          // Unknown type
+          continue;
+      }
 
-  snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
-  m_mutex = CreateMutex(NULL, FALSE, mutexstr);
-  if ( m_mutex == NULL )
-  {
-    return set_err(EIO, "CreateMutex failed");
+      devlist.push_back(dev);
+    }
   }
 
-  // atomic access to driver
-  WaitForSingleObject(m_mutex, INFINITE);
+  if (csmi) {
+    // Scan CSMI devices
+    for (int i = 0; i <= 9; i++) {
+      snprintf(name, sizeof(name)-1, "/dev/csmi%d,0", i);
+      win_csmi_device test_dev(this, name, "");
+      if (!test_dev.open_scsi())
+        continue;
+
+      unsigned ports_used = test_dev.get_ports_used();
+      if (!ports_used)
+        continue;
+
+      for (int pi = 0; pi < 32; pi++) {
+        if (!(ports_used & (1 << pi)))
+          continue;
+        snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi);
+        devlist.push_back( new win_csmi_device(this, name, "ata") );
+      }
+    }
+  }
+
+  if (nvme) {
+    // Scan \\.\Scsi[0-31] for up to 10 NVMe devices
+    int nvme_cnt = 0;
+    for (int i = 0; i < 32; i++) {
+      snprintf(name, sizeof(name)-1, "/dev/nvme%d", i);
+      win_nvme_device test_dev(this, name, "", 0);
+      if (!test_dev.open_scsi(i)) {
+        if (test_dev.get_errno() == EACCES)
+          break;
+        continue;
+      }
+
+      if (!test_dev.probe())
+        continue;
+      if (++nvme_cnt >= 10)
+        break;
+    }
 
+    for (int i = 0; i < nvme_cnt; i++) {
+      snprintf(name, sizeof(name)-1, "/dev/nvme%d", i);
+      devlist.push_back( new win_nvme_device(this, name, "nvme", 0) );
+    }
+  }
   return true;
 }
 
 
-bool win_areca_ata_device::arcmsr_unlock()
+// get examples for smartctl
+std::string win_smart_interface::get_app_examples(const char * appname)
 {
-  if( m_mutex != NULL)
-  {
-      ReleaseMutex(m_mutex);
-      CloseHandle(m_mutex);
+  if (strcmp(appname, "smartctl"))
+    return "";
+  return "=================================================== SMARTCTL EXAMPLES =====\n\n"
+         "  smartctl -a /dev/sda                       (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/sda              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a /dev/sda\n"
+         "             (Prints all information for disk on PhysicalDrive 0)\n"
+         "  smartctl -a /dev/pd3\n"
+         "             (Prints all information for disk on PhysicalDrive 3)\n"
+         "  smartctl -a /dev/tape1\n"
+         "             (Prints all information for SCSI tape on Tape 1)\n"
+         "  smartctl -A /dev/hdb,3\n"
+         "                (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
+         "  smartctl -A /dev/tw_cli/c0/p1\n"
+         "            (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
+         "  smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
+         "           (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
+         "            on 1st Areca RAID controller)\n"
+         "\n"
+         "  ATA SMART access methods and ordering may be specified by modifiers\n"
+         "  following the device name: /dev/hdX:[saicm], where\n"
+         "  's': SMART_* IOCTLs,         'a': IOCTL_ATA_PASS_THROUGH,\n"
+         "  'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n"
+         "  'm': IOCTL_SCSI_MINIPORT_*.\n"
+      + strprintf(
+         "  The default on this system is /dev/sdX:%s\n", ata_get_def_options()
+        );
+}
+
+
+bool win_smart_interface::disable_system_auto_standby(bool disable)
+{
+  if (disable) {
+    SYSTEM_POWER_STATUS ps;
+    if (!GetSystemPowerStatus(&ps))
+      return set_err(ENOSYS, "Unknown power status");
+    if (ps.ACLineStatus != 1) {
+      SetThreadExecutionState(ES_CONTINUOUS);
+      if (ps.ACLineStatus == 0)
+        set_err(EIO, "AC offline");
+      else
+        set_err(EIO, "Unknown AC line status");
+      return false;
+    }
   }
 
+  if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0)))
+    return set_err(ENOSYS);
   return true;
 }
 
 
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-
 } // namespace
 
 /////////////////////////////////////////////////////////////////////////////
index 56b0d79c552abe42238ff693929e00498f4fd588..c4cce584adfeec01bdd0a62c037da0c2970d4677 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_win32/daemon_win32.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -20,7 +20,7 @@
 
 #include "daemon_win32.h"
 
-const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 3959 2014-07-18 19:22:18Z chrfranke $"
+const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 4120 2015-08-27 16:12:21Z samm2 $"
   DAEMON_WIN32_H_CVSID;
 
 #include <stdio.h>
index 2d9c1c5baf8725dbe353f689ea8c55f9183987f8..e87d0025dc46d68d637826200f33df6f90f7f167 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_win32/daemon_win32.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -18,7 +18,7 @@
 #ifndef DAEMON_WIN32_H
 #define DAEMON_WIN32_H
 
-#define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h 3584 2012-08-05 17:05:32Z chrfranke $"
+#define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h 4120 2015-08-27 16:12:21Z samm2 $"
 
 #include <signal.h>
 
diff --git a/os_win32/default.manifest b/os_win32/default.manifest
new file mode 100644 (file)
index 0000000..01379b9
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel
+          level="asInvoker"
+          uiAccess="false"
+        />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+      <!-- Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+      <!-- Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+      <!-- Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+    </application>
+  </compatibility>
+</assembly>
index 23a8f5b9c5fa92213be0f56f31a61bfe813ffb22..78ce0145279047fdb7198798eaca9a128d878f23 100644 (file)
@@ -1,9 +1,9 @@
 ;
-; smartmontools install NSIS script
+; os_win32/installer.nsi - smartmontools install NSIS script
 ;
-; Home page of code is: http://smartmontools.sourceforge.net
+; Home page of code is: http://www.smartmontools.org
 ;
-; Copyright (C) 2006-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+; Copyright (C) 2006-15 Christian Franke
 ;
 ; This program is free software; you can redistribute it and/or modify
 ; it under the terms of the GNU General Public License as published by
@@ -13,7 +13,7 @@
 ; You should have received a copy of the GNU General Public License
 ; (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
 ;
-; $Id: installer.nsi 3912 2014-06-18 19:03:30Z chrfranke $
+; $Id: installer.nsi 4174 2015-11-22 16:19:29Z chrfranke $
 ;
 
 
@@ -36,6 +36,8 @@
 Name "smartmontools"
 OutFile "${OUTFILE}"
 
+RequestExecutionLevel admin
+
 SetCompressor /solid lzma
 
 XPStyle on
@@ -43,7 +45,7 @@ InstallColors /windows
 
 ; Set in .onInit
 ;InstallDir "$PROGRAMFILES\smartmontools"
-;InstallDirRegKey HKLM "Software\smartmontools" "Install_Dir"
+;InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation"
 
 Var EDITOR
 
@@ -56,12 +58,9 @@ Var EDITOR
 LicenseData "${INPDIR}\doc\COPYING.txt"
 
 !include "FileFunc.nsh"
+!include "LogicLib.nsh"
 !include "Sections.nsh"
 
-!insertmacro GetParameters
-!insertmacro GetOptions
-
-RequestExecutionLevel admin
 
 ;--------------------------------------------------------------------
 ; Pages
@@ -81,6 +80,11 @@ UninstPage instfiles
 InstType "Full"
 InstType "Extract files only"
 InstType "Drive menu"
+!ifdef INPDIR64
+InstType "Full (x64)"
+InstType "Extract files only (x64)"
+InstType "Drive menu (x64)"
+!endif
 
 
 ;--------------------------------------------------------------------
@@ -88,8 +92,17 @@ InstType "Drive menu"
 
 !ifdef INPDIR64
   Section "64-bit version" X64_SECTION
+    SectionIn 4 5 6
     ; Handled in Function CheckX64
   SectionEnd
+
+  !define FULL_TYPES "1 4"
+  !define EXTRACT_TYPES  "2 5"
+  !define DRIVEMENU_TYPE     "3 6"
+!else
+  !define FULL_TYPES "1"
+  !define EXTRACT_TYPES  "2"
+  !define DRIVEMENU_TYPE     "3"
 !endif
 
 SectionGroup "!Program files"
@@ -97,14 +110,15 @@ SectionGroup "!Program files"
   !macro FileExe path option
     !ifdef INPDIR64
       ; Use dummy SetOutPath to control archive location of executables
-      StrCmp $X64 "" +5
+      ${If} $X64 != ""
         Goto +2
           SetOutPath "$INSTDIR\bin64"
         File ${option} '${INPDIR64}\${path}'
-      GoTo +4
+      ${Else}
         Goto +2
           SetOutPath "$INSTDIR\bin"
         File ${option} '${INPDIR}\${path}'
+      ${EndIf}
     !else
       File ${option} '${INPDIR}\${path}'
     !endif
@@ -112,7 +126,7 @@ SectionGroup "!Program files"
 
   Section "smartctl" SMARTCTL_SECTION
 
-    SectionIn 1 2
+    SectionIn ${FULL_TYPES} ${EXTRACT_TYPES}
 
     SetOutPath "$INSTDIR\bin"
     !insertmacro FileExe "bin\smartctl.exe" ""
@@ -121,17 +135,18 @@ SectionGroup "!Program files"
 
   Section "smartd" SMARTD_SECTION
 
-    SectionIn 1 2
+    SectionIn ${FULL_TYPES} ${EXTRACT_TYPES}
 
     SetOutPath "$INSTDIR\bin"
 
     ; Stop service ?
     StrCpy $1 ""
-    IfFileExists "$INSTDIR\bin\smartd.exe" 0 nosrv
+    ${If} ${FileExists} "$INSTDIR\bin\smartd.exe"
       ReadRegStr $0 HKLM "System\CurrentControlSet\Services\smartd" "ImagePath"
-      StrCmp $0 "" nosrv
+      ${If} $0 != ""
         ExecWait "net stop smartd" $1
-  nosrv:
+      ${EndIf}
+    ${EndIf}
     !insertmacro FileExe "bin\smartd.exe" ""
 
     IfFileExists "$INSTDIR\bin\smartd.conf" 0 +2
@@ -142,15 +157,16 @@ SectionGroup "!Program files"
     !insertmacro FileExe "bin\wtssendmsg.exe" ""
 
     ; Restart service ?
-    StrCmp $1 "0" 0 +3
+    ${If} $1 == "0"
       MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Restart smartd service ?" /SD IDNO IDYES 0 IDNO +2
         ExecWait "net start smartd"
+    ${EndIf}
 
   SectionEnd
 
   Section "smartctl-nc (GSmartControl)" SMARTCTL_NC_SECTION
 
-    SectionIn 1 2
+    SectionIn ${FULL_TYPES} ${EXTRACT_TYPES}
 
     SetOutPath "$INSTDIR\bin"
     !insertmacro FileExe "bin\smartctl-nc.exe" ""
@@ -159,7 +175,7 @@ SectionGroup "!Program files"
 
   Section "drivedb.h (Drive Database)" DRIVEDB_SECTION
 
-    SectionIn 1 2
+    SectionIn ${FULL_TYPES} ${EXTRACT_TYPES}
 
     SetOutPath "$INSTDIR\bin"
     File "${INPDIR}\bin\drivedb.h"
@@ -171,7 +187,7 @@ SectionGroupEnd
 
 Section "!Documentation" DOC_SECTION
 
-  SectionIn 1 2
+  SectionIn ${FULL_TYPES} ${EXTRACT_TYPES}
 
   SetOutPath "$INSTDIR\doc"
   File "${INPDIR}\doc\AUTHORS.txt"
@@ -182,12 +198,12 @@ Section "!Documentation" DOC_SECTION
   File "${INPDIR}\doc\NEWS.txt"
   File "${INPDIR}\doc\README.txt"
   File "${INPDIR}\doc\TODO.txt"
-  File "${INPDIR}\doc\WARNINGS.txt"
 !ifdef INPDIR64
-  StrCmp $X64 "" +3
+  ${If} $X64 != ""
     File "${INPDIR64}\doc\checksums64.txt"
-  GoTo +2
+  ${Else}
     File "${INPDIR}\doc\checksums32.txt"
+  ${EndIf}
 !else
   File "${INPDIR}\doc\checksums??.txt"
 !endif
@@ -203,7 +219,7 @@ SectionEnd
 
 Section "Uninstaller" UNINST_SECTION
 
-  SectionIn 1
+  SectionIn ${FULL_TYPES}
   AddSize 40
 
   CreateDirectory "$INSTDIR"
@@ -211,8 +227,9 @@ Section "Uninstaller" UNINST_SECTION
   ; Keep old Install_Dir registry entry for GSmartControl
   ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GSmartControl" "InstallLocation"
   ReadRegStr $1 HKLM "Software\smartmontools" "Install_Dir"
-  StrCmp "$0$1" "" +2 0
+  ${If} "$0$1" != ""
     WriteRegStr HKLM "Software\smartmontools" "Install_Dir" "$INSTDIR"
+  ${EndIf}
 
   ; Write uninstall keys and program
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "DisplayName" "smartmontools"
@@ -222,9 +239,9 @@ Section "Uninstaller" UNINST_SECTION
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation" "$INSTDIR"
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "UninstallString" '"$INSTDIR\uninst-smartmontools.exe"'
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "Publisher"     "smartmontools.org"
-  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "URLInfoAbout"  "http://www.smartmontools.org/"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "URLInfoAbout"  "https://www.smartmontools.org/"
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "HelpLink"      "http://sourceforge.net/projects/smartmontools/support"
-  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "URLUpdateInfo" "http://smartmontools.no-ip.org/"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "URLUpdateInfo" "http://builds.smartmontools.org/"
   WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "NoModify" 1
   WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "NoRepair" 1
   WriteUninstaller "uninst-smartmontools.exe"
@@ -233,7 +250,7 @@ SectionEnd
 
 Section "Start Menu Shortcuts" MENU_SECTION
 
-  SectionIn 1
+  SectionIn ${FULL_TYPES}
 
   SetShellVarContext all
 
@@ -246,15 +263,15 @@ Section "Start Menu Shortcuts" MENU_SECTION
   !macroend
 
   ; runcmdu
-  IfFileExists "$INSTDIR\bin\smartctl.exe" 0 +2
-  IfFileExists "$INSTDIR\bin\smartd.exe" 0 noruncmd
+  ${If}   ${FileExists} "$INSTDIR\bin\smartctl.exe"
+  ${OrIf} ${FileExists} "$INSTDIR\bin\smartd.exe"
     SetOutPath "$INSTDIR\bin"
     !insertmacro FileExe "bin\runcmdu.exe" ""
-    File "${INPDIR}\bin\runcmdu.exe.manifest"
-  noruncmd:
+    Delete "$INSTDIR\bin\runcmdu.exe.manifest" ; TODO: Remove after smartmontools 6.5
+  ${EndIf}
 
   ; smartctl
-  IfFileExists "$INSTDIR\bin\smartctl.exe" 0 noctl
+  ${If} ${FileExists} "$INSTDIR\bin\smartctl.exe"
     SetOutPath "$INSTDIR\bin"
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl (Admin CMD).lnk" "$WINDIR\system32\cmd.exe" '/k PATH=$INSTDIR\bin;%PATH%&cd /d "$INSTDIR\bin"'
     CreateDirectory "$SMPROGRAMS\smartmontools\smartctl Examples"
@@ -274,10 +291,10 @@ Section "Start Menu Shortcuts" MENU_SECTION
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Stop(Abort) selftest (-X).lnk"        "$INSTDIR\bin\runcmdu.exe" "smartctl -X sda"
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Turn SMART off (-s off).lnk"          "$INSTDIR\bin\runcmdu.exe" "smartctl -s off sda"
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Turn SMART on (-s on).lnk"            "$INSTDIR\bin\runcmdu.exe" "smartctl -s on sda"
-  noctl:
+  ${EndIf}
 
   ; smartd
-  IfFileExists "$INSTDIR\bin\smartd.exe" 0 nod
+  ${If} ${FileExists} "$INSTDIR\bin\smartd.exe"
     SetOutPath "$INSTDIR\bin"
     CreateDirectory "$SMPROGRAMS\smartmontools\smartd Examples"
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Daemon start, smartd.log.lnk"           "$INSTDIR\bin\runcmdu.exe" "smartd -l local0"
@@ -296,10 +313,10 @@ Section "Start Menu Shortcuts" MENU_SECTION
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service remove.lnk"                     "$INSTDIR\bin\runcmdu.exe" "smartd remove"
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service start.lnk"                      "$INSTDIR\bin\runcmdu.exe" "net start smartd"
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service stop.lnk"                       "$INSTDIR\bin\runcmdu.exe" "net stop smartd"
-  nod:
+  ${EndIf}
 
   ; Documentation
-  IfFileExists "$INSTDIR\doc\README.TXT" 0 nodoc
+  ${If} ${FileExists} "$INSTDIR\doc\README.TXT"
     SetOutPath "$INSTDIR\doc"
     CreateDirectory "$SMPROGRAMS\smartmontools\Documentation"
     CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartctl manual page (html).lnk"    "$INSTDIR\doc\smartctl.8.html"
@@ -309,36 +326,36 @@ Section "Start Menu Shortcuts" MENU_SECTION
     CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd manual page (txt).lnk"       "$INSTDIR\doc\smartd.8.txt"
     CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd.conf manual page (txt).lnk"  "$INSTDIR\doc\smartd.conf.5.txt"
     CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd.conf sample.lnk" "$EDITOR" "$INSTDIR\doc\smartd.conf"
-    IfFileExists "$INSTDIR\bin\drivedb.h" 0 nodb
+    ${If} ${FileExists} "$INSTDIR\bin\drivedb.h"
         CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\drivedb.h (view).lnk" "$EDITOR" "$INSTDIR\bin\drivedb.h"
         !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\Documentation\drivedb-add.h (create, edit).lnk" "$EDITOR" "$INSTDIR\bin\drivedb-add.h"
-    nodb:
+    ${EndIf}
     CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\ChangeLog.lnk" "$INSTDIR\doc\ChangeLog.txt"
     CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\COPYING.lnk"   "$INSTDIR\doc\COPYING.txt"
     CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\NEWS.lnk"      "$INSTDIR\doc\NEWS.txt"
-    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\Windows version download page.lnk" "http://smartmontools.no-ip.org/"
-  nodoc:
+  ${EndIf}
 
   ; Homepage
-  CreateShortCut "$SMPROGRAMS\smartmontools\smartmontools Home Page.lnk" "http://www.smartmontools.org/"
+  CreateShortCut "$SMPROGRAMS\smartmontools\smartmontools Home Page.lnk" "https://www.smartmontools.org/"
+  CreateShortCut "$SMPROGRAMS\smartmontools\smartmontools Daily Builds.lnk" "http://builds.smartmontools.org/"
 
   ; drivedb.h update
-  IfFileExists "$INSTDIR\bin\update-smart-drivedb.exe" 0 noupdb
+  ${If} ${FileExists} "$INSTDIR\bin\update-smart-drivedb.exe"
     SetOutPath "$INSTDIR\bin"
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\drivedb.h update.lnk" "$INSTDIR\bin\update-smart-drivedb.exe" ""
-  noupdb:
+  ${EndIf}
 
   ; Uninstall
-  IfFileExists "$INSTDIR\uninst-smartmontools.exe" 0 noinst
+  ${If} ${FileExists} "$INSTDIR\uninst-smartmontools.exe"
     SetOutPath "$INSTDIR"
     !insertmacro CreateAdminShortCut "$SMPROGRAMS\smartmontools\Uninstall smartmontools.lnk" "$INSTDIR\uninst-smartmontools.exe" ""
-  noinst:
+  ${EndIf}
 
 SectionEnd
 
 Section "Add install dir to PATH" PATH_SECTION
 
-  SectionIn 1
+  SectionIn ${FULL_TYPES}
 
   Push "$INSTDIR\bin"
   Call AddToPath
@@ -358,13 +375,13 @@ SectionGroup "Add smartctl to drive menu"
 !macroend
 
   Section "Remove existing entries first" DRIVE_REMOVE_SECTION
-    SectionIn 3
+    SectionIn ${DRIVEMENU_TYPE}
     !insertmacro DriveMenuRemove
   SectionEnd
 
 !macro DriveSection id name args
   Section 'smartctl ${args} ...' DRIVE_${id}_SECTION
-    SectionIn 3
+    SectionIn ${DRIVEMENU_TYPE}
     Call CheckRunCmdA
     DetailPrint 'Add drive menu entry "${name}": smartctl ${args} ...'
     WriteRegStr HKCR "Drive\shell\smartctl${id}" "" "${name}"
@@ -386,37 +403,40 @@ SectionGroupEnd
 Section "Uninstall"
   
   ; Stop & remove service
-  IfFileExists "$INSTDIR\bin\smartd.exe" 0 nosrv
+  ${If} ${FileExists} "$INSTDIR\bin\smartd.exe"
     ReadRegStr $0 HKLM "System\CurrentControlSet\Services\smartd" "ImagePath"
-    StrCmp $0 "" nosrv
+    ${If} $0 != ""
       ExecWait "net stop smartd"
-      MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Remove smartd service ?" /SD IDNO IDYES 0 IDNO nosrv
+      MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Remove smartd service ?" /SD IDNO IDYES 0 IDNO +2
         ExecWait "$INSTDIR\bin\smartd.exe remove"
-  nosrv:
+    ${EndIf}
+  ${EndIf}
 
   ; Remove installer registry keys
   DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools"
   DeleteRegKey HKLM "Software\smartmontools"
 
   ; Remove conf file ?
-  IfFileExists "$INSTDIR\bin\smartd.conf" 0 noconf
+  ${If} ${FileExists} "$INSTDIR\bin\smartd.conf"
     ; Assume unchanged if timestamp is equal to sample file
     GetFileTime "$INSTDIR\bin\smartd.conf" $0 $1
     GetFileTime "$INSTDIR\doc\smartd.conf" $2 $3
     StrCmp "$0:$1" "$2:$3" +2 0
-      MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Delete configuration file$\n$INSTDIR\bin\smartd.conf ?" /SD IDNO IDYES 0 IDNO noconf
+      MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Delete configuration file$\n$INSTDIR\bin\smartd.conf ?" /SD IDNO IDYES 0 IDNO +2
         Delete "$INSTDIR\bin\smartd.conf"
-  noconf:
+  ${EndIf}
 
   ; Remove log file ?
-  IfFileExists "$INSTDIR\bin\smartd.log" 0 +3
+  ${If} ${FileExists} "$INSTDIR\bin\smartd.log"
     MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Delete log file$\n$INSTDIR\bin\smartd.log ?" /SD IDNO IDYES 0 IDNO +2
       Delete "$INSTDIR\bin\smartd.log"
+  ${EndIf}
 
   ; Remove drivedb-add file ?
-  IfFileExists "$INSTDIR\bin\drivedb-add.h" 0 +3
+  ${If} ${FileExists} "$INSTDIR\bin\drivedb-add.h"
     MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Delete local drive database file$\n$INSTDIR\bin\drivedb-add.h ?" /SD IDNO IDYES 0 IDNO +2
       Delete "$INSTDIR\bin\drivedb-add.h"
+  ${EndIf}
 
   ; Remove files
   Delete "$INSTDIR\bin\smartctl.exe"
@@ -432,9 +452,9 @@ Section "Uninstall"
   Delete "$INSTDIR\bin\smartd-run.bat"
   Delete "$INSTDIR\bin\net-run.bat"
   Delete "$INSTDIR\bin\runcmda.exe"
-  Delete "$INSTDIR\bin\runcmda.exe.manifest"
+  Delete "$INSTDIR\bin\runcmda.exe.manifest" ; TODO: Remove after smartmontools 6.5
   Delete "$INSTDIR\bin\runcmdu.exe"
-  Delete "$INSTDIR\bin\runcmdu.exe.manifest"
+  Delete "$INSTDIR\bin\runcmdu.exe.manifest" ; TODO: Remove after smartmontools 6.5
   Delete "$INSTDIR\bin\wtssendmsg.exe"
   Delete "$INSTDIR\doc\AUTHORS.txt"
   Delete "$INSTDIR\doc\ChangeLog.txt"
@@ -444,7 +464,6 @@ Section "Uninstall"
   Delete "$INSTDIR\doc\NEWS.txt"
   Delete "$INSTDIR\doc\README.txt"
   Delete "$INSTDIR\doc\TODO.txt"
-  Delete "$INSTDIR\doc\WARNINGS.txt"
   Delete "$INSTDIR\doc\checksums*.txt"
   Delete "$INSTDIR\doc\smartctl.8.html"
   Delete "$INSTDIR\doc\smartctl.8.txt"
@@ -479,14 +498,15 @@ Section "Uninstall"
   !insertmacro DriveMenuRemove
 
   ; Check for still existing entries
-  IfFileExists "$INSTDIR\bin\smartd.exe" 0 +3
+  ${If} ${FileExists} "$INSTDIR\bin\smartd.exe"
     MessageBox MB_OK|MB_ICONEXCLAMATION "$INSTDIR\bin\smartd.exe could not be removed.$\nsmartd is possibly still running." /SD IDOK
-    Goto +3
-  IfFileExists "$INSTDIR" 0 +2
+  ${ElseIf} ${FileExists} "$INSTDIR"
     MessageBox MB_OK "Note: $INSTDIR could not be removed." /SD IDOK
+  ${EndIf}
 
-  IfFileExists "$SMPROGRAMS\smartmontools" 0 +2
+  ${If} ${FileExists} "$SMPROGRAMS\smartmontools"
     MessageBox MB_OK "Note: $SMPROGRAMS\smartmontools could not be removed." /SD IDOK
+  ${EndIf}
 
 SectionEnd
 
@@ -502,19 +522,32 @@ SectionEnd
 Function .onInit
 
   ; Set default install directories
-  StrCmp $INSTDIR "" 0 endinst ; /D=PATH option specified ?
-  ReadRegStr $INSTDIR HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation"
-  StrCmp $INSTDIR "" 0 endinst ; Already installed ?
-  ReadRegStr $INSTDIR HKLM "Software\smartmontools" "Install_Dir"
-  StrCmp $INSTDIR "" 0 endinst ; Already installed ?
-    StrCpy $INSTDIR "$PROGRAMFILES\smartmontools"
+  ${If} $INSTDIR == "" ; /D=PATH option not specified ?
+    ReadRegStr $INSTDIR HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation"
+    ${If} $INSTDIR == "" ; Not already installed ?
+      ReadRegStr $INSTDIR HKLM "Software\smartmontools" "Install_Dir"
+      ${If} $INSTDIR == "" ; Not already installed (smartmontools < r3911/6.3) ?
+        StrCpy $INSTDIR "$PROGRAMFILES\smartmontools"
 !ifdef INPDIR64
-    StrCpy $INSTDIR32 $INSTDIR
-    StrCpy $INSTDIR64 "$PROGRAMFILES64\smartmontools"
+        StrCpy $INSTDIR32 $INSTDIR
+        StrCpy $INSTDIR64 "$PROGRAMFILES64\smartmontools"
 !endif
-  endinst:
+      ${EndIf}
+    ${EndIf}
+  ${EndIf}
 
 !ifdef INPDIR64
+  ; Check for 64-bit unless already installed in 32-bit location
+  ${If} $INSTDIR64 != ""
+  ${OrIf} $INSTDIR != "$PROGRAMFILES\smartmontools"
+    ; $1 = IsWow64Process(GetCurrentProcess(), ($0=FALSE, &$0))
+    System::Call "kernel32::GetCurrentProcess() i.s"
+    System::Call "kernel32::IsWow64Process(i s, *i 0 r0) i.r1"
+    ${If} "$0 $1" == "1 1" ; 64-bit Windows ?
+      !insertmacro SelectSection ${X64_SECTION}
+    ${EndIf}
+  ${EndIf}
+
   ; Sizes of binary sections include 32-bit and 64-bit executables
   !insertmacro AdjustSectionSize ${SMARTCTL_SECTION}
   !insertmacro AdjustSectionSize ${SMARTD_SECTION}
@@ -523,47 +556,57 @@ Function .onInit
 
   ; Use Notepad++ if installed
   StrCpy $EDITOR "$PROGRAMFILES\Notepad++\notepad++.exe"
-  IfFileExists "$EDITOR" +2 0
+  ${IfNot} ${FileExists} "$EDITOR"
     StrCpy $EDITOR "notepad.exe"
+  ${EndIf}
 
   Call ParseCmdLine
+
+!ifdef INPDIR64
+  Call CheckX64
+!endif
 FunctionEnd
 
 ; Check x64 section and update INSTDIR accordingly
 
 !ifdef INPDIR64
 Function CheckX64
-  SectionGetFlags ${X64_SECTION} $0
-  IntOp $0 $0 & ${SF_SELECTED}
-  IntCmp $0 ${SF_SELECTED} x64
+  ${IfNot} ${SectionIsSelected} ${X64_SECTION}
     StrCpy $X64 ""
-    StrCmp $INSTDIR32 "" +3
+    ${If} $INSTDIR32 != ""
+    ${AndIf} $INSTDIR == $INSTDIR64
       StrCpy $INSTDIR $INSTDIR32
-      StrCpy $INSTDIR32 ""
-    Goto done
-  x64:
+    ${EndIf}
+  ${Else}
     StrCpy $X64 "t"
-    StrCmp $INSTDIR64 "" +3
+    ${If} $INSTDIR64 != ""
+    ${AndIf} $INSTDIR == $INSTDIR32
       StrCpy $INSTDIR $INSTDIR64
-      StrCpy $INSTDIR64 ""
-  done:
+    ${EndIf}
+  ${EndIf}
 FunctionEnd
 !endif
 
 ; Command line parsing
-!macro CheckCmdLineOption name section
-  StrCpy $allopts "$allopts,${name}"
+
+!macro GetCmdLineOption var name
   Push ",$opts,"
   Push ",${name},"
   Call StrStr
-  Pop $0
-  StrCmp $0 "" 0 sel_${name}
-  !insertmacro UnselectSection ${section}
-  Goto done_${name}
-sel_${name}:
-  !insertmacro SelectSection ${section}
-  StrCpy $nomatch ""
-done_${name}:
+  Pop ${var}
+  ${If} ${var} != ""
+    StrCpy $nomatch ""
+  ${EndIf}
+!macroend
+
+!macro CheckCmdLineOption name section
+  StrCpy $allopts "$allopts,${name}"
+  !insertmacro GetCmdLineOption $0 ${name}
+  ${If} $0 == ""
+    !insertmacro UnselectSection ${section}
+  ${Else}
+    !insertmacro SelectSection ${section}
+  ${EndIf}
 !macroend
 
 Function ParseCmdLine
@@ -571,19 +614,30 @@ Function ParseCmdLine
   Var /global opts
   ${GetParameters} $R0
   ${GetOptions} $R0 "/SO" $opts
-  IfErrors 0 +2
+  ${If} ${Errors}
     Return
+  ${EndIf}
   Var /global allopts
-  StrCpy $allopts ""
   Var /global nomatch
   StrCpy $nomatch "t"
-  ; turn sections on or off
 !ifdef INPDIR64
-  !insertmacro CheckCmdLineOption "x64" ${X64_SECTION}
-  Call CheckX64
-  StrCmp $opts "x64" 0 +2
-    Return ; leave sections unchanged if only "x64" is specified
+  ; Change previous 64-bit setting
+  StrCpy $allopts ",x32|x64"
+  !insertmacro GetCmdLineOption $0 "x32"
+  ${If} $0 != ""
+    !insertmacro UnselectSection ${X64_SECTION}
+  ${EndIf}
+  !insertmacro GetCmdLineOption $0 "x64"
+  ${If} $0 != ""
+    !insertmacro SelectSection ${X64_SECTION}
+  ${EndIf}
+  ; Leave other sections unchanged if only "x32" or "x64" is specified
+  ${If}   $opts == "x32"
+  ${OrIf} $opts == "x64"
+    Return
+  ${EndIf}
 !endif
+  ; Turn sections on or off
   !insertmacro CheckCmdLineOption "smartctl" ${SMARTCTL_SECTION}
   !insertmacro CheckCmdLineOption "smartd" ${SMARTD_SECTION}
   !insertmacro CheckCmdLineOption "smartctlnc" ${SMARTCTL_NC_SECTION}
@@ -599,22 +653,25 @@ Function ParseCmdLine
   !insertmacro CheckCmdLineOption "drive3" ${DRIVE_3_SECTION}
   !insertmacro CheckCmdLineOption "drive4" ${DRIVE_4_SECTION}
   !insertmacro CheckCmdLineOption "drive5" ${DRIVE_5_SECTION}
-  StrCmp $opts "-" done
-  StrCmp $nomatch "" done
-    StrCpy $0 "$allopts,-" "" 1
-    MessageBox MB_OK "Usage: smartmontools-VERSION.win32-setup [/S] [/SO component,...] [/D=INSTDIR]$\n$\ncomponents:$\n  $0"
-    Abort
-done:
+  ${If} $opts != "-"
+    ${If} $nomatch != ""
+      StrCpy $0 "$allopts,-" "" 1
+      MessageBox MB_OK "Usage: smartmontools-VERSION.win32-setup [/S] [/SO component,...] [/D=INSTDIR]$\n$\ncomponents:$\n  $0"
+      Abort
+    ${EndIf}
+  ${EndIf}
 FunctionEnd
 
-; Install runcmda.exe if missing
+; Install runcmda.exe only once
 
 Function CheckRunCmdA
-  IfFileExists "$INSTDIR\bin\runcmda.exe" done 0
+  Var /global runcmda
+  ${If} $runcmda == ""
+    StrCpy $runcmda "t"
     SetOutPath "$INSTDIR\bin"
     !insertmacro FileExe "bin\runcmda.exe" ""
-    File "${INPDIR}\bin\runcmda.exe.manifest"
-  done:
+    Delete "$INSTDIR\bin\runcmda.exe.manifest" ; TODO: Remove after smartmontools 6.5
+  ${EndIf}
 FunctionEnd
 
 
@@ -647,8 +704,34 @@ Function AddToPath
   Push $1
   Push $2
   Push $3
+  Push $4
 
-  ReadRegStr $1 ${Environ} "PATH"
+  ; NSIS ReadRegStr returns empty string on string overflow
+  ; Native calls are used here to check actual length of PATH
+
+  ; $4 = RegOpenKey(HKEY_CURRENT_USER, "Environment", &$3)
+  System::Call "advapi32::RegOpenKey(i 0x80000001, t'Environment', *i.r3) i.r4"
+  IntCmp $4 0 0 done done
+  ; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
+  ; RegCloseKey($3)
+  System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
+  System::Call "advapi32::RegCloseKey(i $3)"
+
+  ${If} $4 = 234 ; ERROR_MORE_DATA
+    DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}"
+    MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}" /SD IDOK
+    Goto done
+  ${EndIf}
+
+  ${If} $4 <> 0 ; NO_ERROR
+    ${If} $4 <> 2 ; ERROR_FILE_NOT_FOUND
+      DetailPrint "AddToPath: unexpected error code $4"
+      Goto done
+    ${EndIf}
+    StrCpy $1 ""
+  ${EndIf}
+
+  ; Check if already in PATH
   Push "$1;"
   Push "$0;"
   Call StrStr
@@ -660,16 +743,31 @@ Function AddToPath
   Pop $2
   StrCmp $2 "" 0 done
 
+  ; Prevent NSIS string overflow
+  StrLen $2 $0
+  StrLen $3 $1
+  IntOp $2 $2 + $3
+  IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
+  ${If} $2 > ${NSIS_MAX_STRLEN}
+    DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}"
+    MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}." /SD IDOK
+    Goto done
+  ${EndIf}
+
+  ; Append dir to PATH
   DetailPrint "Add to PATH: $0"
   StrCpy $2 $1 1 -1
-  StrCmp $2 ";" 0 +2
+  ${If} $2 == ";"
     StrCpy $1 $1 -1 ; remove trailing ';'
-  StrCmp $1 "" +2   ; no leading ';'
+  ${EndIf}
+  ${If} $1 != "" ; no leading ';'
     StrCpy $0 "$1;$0"
+  ${EndIf}
   WriteRegExpandStr ${Environ} "PATH" $0
   SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
 
 done:
+  Pop $4
   Pop $3
   Pop $2
   Pop $1
@@ -694,8 +792,9 @@ Function un.RemoveFromPath
 
   ReadRegStr $1 ${Environ} "PATH"
   StrCpy $5 $1 1 -1
-  StrCmp $5 ";" +2
+  ${If} $5 != ";"
     StrCpy $1 "$1;" ; ensure trailing ';'
+  ${EndIf}
   Push $1
   Push "$0;"
   Call un.StrStr
@@ -709,8 +808,9 @@ Function un.RemoveFromPath
   StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove
   StrCpy $3 "$5$6"
   StrCpy $5 $3 1 -1
-  StrCmp $5 ";" 0 +2
+  ${If} $5 == ";"
     StrCpy $3 $3 -1 ; remove trailing ';'
+  ${EndIf}
   WriteRegExpandStr ${Environ} "PATH" $3
   SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
 
@@ -745,13 +845,12 @@ Function ${un}StrStr
   StrCpy $R4 0
   ; $R1=substring, $R2=string, $R3=strlen(substring)
   ; $R4=count, $R5=tmp
-  loop:
+  ${Do}
     StrCpy $R5 $R2 $R3 $R4
-    StrCmp $R5 $R1 done
-    StrCmp $R5 "" done
+    ${IfThen} $R5 == $R1 ${|} ${ExitDo} ${|}
+    ${IfThen} $R5 == ""  ${|} ${ExitDo} ${|}
     IntOp $R4 $R4 + 1
-    Goto loop
-done:
+  ${Loop}
   StrCpy $R1 $R2 "" $R4
   Pop $R5
   Pop $R4
@@ -771,8 +870,6 @@ FunctionEnd
 ; http://nsis.sourceforge.net/IShellLink_Set_RunAs_flag
 ;
 
-!include "LogicLib.nsh"
-
 !define IPersistFile {0000010b-0000-0000-c000-000000000046}
 !define CLSID_ShellLink {00021401-0000-0000-C000-000000000046}
 !define IID_IShellLinkA {000214EE-0000-0000-C000-000000000046}
@@ -819,5 +916,5 @@ Function ShellLinkSetRunAs
   ${Else}
     DetailPrint "Set RunAsAdmin: $9"
   ${EndIf}
-  System::Store L ; push $0-$9, $R0-$R9
+  System::Store L ; pop $R9-$R0, $9-$0
 FunctionEnd
index 8be8198b1b0795ac3380afa1034cecc033f5324e..2a26cda31b1971982d9e04669456fc0c7d907b65 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Run console command and wait for user input
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2011 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -15,7 +15,7 @@
  *
  */
 
-char svnid[] = "$Id: runcmd.c 3453 2011-10-16 12:45:27Z chrfranke $";
+char svnid[] = "$Id: runcmd.c 4120 2015-08-27 16:12:21Z samm2 $";
 
 #include <stdio.h>
 #include <windows.h>
diff --git a/os_win32/runcmda.exe.manifest b/os_win32/runcmda.exe.manifest
deleted file mode 100644 (file)
index 2b57539..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
-  <assemblyIdentity
-    version="1.0.0.0"
-    name="runcmda.exe"
-    type="win32"
-  />
-  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
-    <security>
-      <requestedPrivileges>
-        <requestedExecutionLevel
-          level="requireAdministrator"
-          uiAccess="false"
-        />
-      </requestedPrivileges>
-    </security>
-  </trustInfo>
-</assembly>
diff --git a/os_win32/runcmdu.exe.manifest b/os_win32/runcmdu.exe.manifest
deleted file mode 100644 (file)
index f6eb707..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
-  <assemblyIdentity
-    version="1.0.0.0"
-    name="runcmdu.exe"
-    type="win32"
-  />
-  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
-    <security>
-      <requestedPrivileges>
-        <requestedExecutionLevel
-          level="asInvoker"
-          uiAccess="false"
-        />
-      </requestedPrivileges>
-    </security>
-  </trustInfo>
-</assembly>
index 3061d07705b7b32d920f8d800f438d310572b0ad..4d839997bae2bed6bc1bda19b562f2167f513ff5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_win32/syslog.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2004-8 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -19,7 +19,7 @@
 #ifndef SYSLOG_H
 #define SYSLOG_H
 
-#define SYSLOG_H_CVSID "$Id: syslog.h 3728 2012-12-13 17:57:50Z chrfranke $\n"
+#define SYSLOG_H_CVSID "$Id: syslog.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 #include <stdarg.h>
 
index 02fa4de237fe964838c24d9036f8ee43608aad8c..57e14e6c3de487c48f0181a1449078cdd9dce0f7 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * os_win32/syslog_win32.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2004-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2004-15 Christian Franke
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
 
-const char *syslog_win32_cpp_cvsid = "$Id: syslog_win32.cpp 3575 2012-07-19 21:32:56Z chrfranke $"
+const char *syslog_win32_cpp_cvsid = "$Id: syslog_win32.cpp 4149 2015-10-17 15:38:01Z chrfranke $"
   SYSLOG_H_CVSID;
 
 #ifdef _MSC_VER
@@ -420,7 +420,7 @@ int main(int argc, char* argv[])
                if (i % 13 == 0)
                        Sleep(1000L);
                sprintf(buf, "Log Line %d\n", i);
-               syslog(i % 17 ? LOG_INFO : LOG_ERR, buf);
+               syslog((i % 17) ? LOG_INFO : LOG_ERR, buf);
        }
        closelog();
        return 0;
index c6d3ac6a0e52e771c0c859bac10dd66cb5511698..8c34ef6e113c80a60cfca5d4cc191a772a0cb365 100644 (file)
@@ -1,7 +1,7 @@
 ;/*
 ; * os_win32/syslogevt.mc
 ; *
-; * Home page of code is: http://smartmontools.sourceforge.net
+; * Home page of code is: http://www.smartmontools.org
 ; *
 ; * Copyright (C) 2004-10 Christian Franke <smartmontools-support@lists.sourceforge.net>
 ; *
@@ -16,7 +16,7 @@
 ; *
 ; */
 ;
-;// $Id: syslogevt.mc 3727 2012-12-13 17:23:06Z samm2 $
+;// $Id: syslogevt.mc 4120 2015-08-27 16:12:21Z samm2 $
 ;
 ;// Use message compiler "mc" or "windmc" to generate
 ;//   syslogevt.rc, syslogevt.h, msg00001.bin
index aa2d525e5d1daf2200f092eaf6b2f6ea387203fa..7b8295a876c622866ec4dfdec57ae5ed6b04509c 100644 (file)
@@ -1,7 +1,7 @@
 ;
 ; smartmontools drive database update NSIS script
 ;
-; Home page of code is: http://smartmontools.sourceforge.net
+; Home page of code is: http://www.smartmontools.org
 ;
 ; Copyright (C) 2011-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
 ;
@@ -13,7 +13,7 @@
 ; You should have received a copy of the GNU General Public License
 ; (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
 ;
-; $Id: update-smart-drivedb.nsi 3815 2013-06-06 17:31:59Z chrfranke $
+; $Id: update-smart-drivedb.nsi 4120 2015-08-27 16:12:21Z samm2 $
 ;
 
 
index 20e0b18b448c62a82ee6932d299888ae88086f59..c4be9f1afb70089a8e6c5b9aa706683942f742d2 100644 (file)
@@ -47,7 +47,7 @@
     <ClCompile>
       <Optimization>Disabled</Optimization>
       <AdditionalIncludeDirectories>.;..\..\getopt;..\..\regex;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>_DEBUG;HAVE_CONFIG_H;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_DEBUG;__func__=__FUNCTION__;HAVE_CONFIG_H;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <MinimalRebuild>true</MinimalRebuild>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -66,7 +66,7 @@
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
       <AdditionalIncludeDirectories>.;..\..\getopt;..\..\regex;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>NDEBUG;HAVE_CONFIG_H;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>NDEBUG;__func__=__FUNCTION__;HAVE_CONFIG_H;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <PrecompiledHeader>
       </PrecompiledHeader>
@@ -85,6 +85,8 @@
   <ItemGroup>
     <ClCompile Include="..\..\ataidentify.cpp" />
     <ClCompile Include="..\..\dev_areca.cpp" />
+    <ClCompile Include="..\..\nvmecmds.cpp" />
+    <ClCompile Include="..\..\nvmeprint.cpp" />
     <ClCompile Include="..\daemon_win32.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     <ClCompile Include="..\..\utility.cpp" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\aacraid.h" />
     <ClInclude Include="..\..\ataidentify.h" />
     <ClInclude Include="..\..\dev_areca.h" />
+    <ClInclude Include="..\..\nvmecmds.h" />
+    <ClInclude Include="..\..\nvmeprint.h" />
     <ClInclude Include="config.h" />
     <ClInclude Include="svnversion.h" />
     <CustomBuildStep Include="..\daemon_win32.h">
index be8a021757afe2c2027b5122f5bff0ed5e8d8364..1033b07d23537d7bf406f0622e30afa5cb6933eb 100644 (file)
@@ -65,6 +65,8 @@
     <ClCompile Include="..\..\utility.cpp" />
     <ClCompile Include="..\..\ataidentify.cpp" />
     <ClCompile Include="..\..\dev_areca.cpp" />
+    <ClCompile Include="..\..\nvmecmds.cpp" />
+    <ClCompile Include="..\..\nvmeprint.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\wbemcli_small.h">
@@ -82,6 +84,7 @@
     <ClInclude Include="..\..\getopt\getopt.h">
       <Filter>getopt</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\aacraid.h" />
     <ClInclude Include="..\..\atacmdnames.h" />
     <ClInclude Include="..\..\atacmds.h" />
     <ClInclude Include="..\..\ataprint.h" />
     <ClInclude Include="..\..\dev_areca.h" />
     <ClInclude Include="config.h" />
     <ClInclude Include="svnversion.h" />
+    <ClInclude Include="..\..\nvmecmds.h" />
+    <ClInclude Include="..\..\nvmeprint.h" />
   </ItemGroup>
   <ItemGroup>
     <None Include="..\installer.nsi">
index 75304c789b5535bcee91acec4fcb359a59ed601c..2129a8bc2e2f04b3aafc5f97365e097381493e17 100644 (file)
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
     <ClCompile Include="..\..\dev_areca.cpp" />
+    <ClCompile Include="..\..\nvmecmds.cpp" />
+    <ClCompile Include="..\..\nvmeprint.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="..\daemon_win32.cpp" />
     <ClCompile Include="..\syslog_win32.cpp" />
     <ClCompile Include="..\wmiquery.cpp" />
     <ClCompile Include="..\..\utility.cpp" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\aacraid.h" />
     <ClInclude Include="..\..\ataidentify.h">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClInclude>
     <ClInclude Include="..\..\dev_areca.h" />
+    <ClInclude Include="..\..\nvmecmds.h" />
+    <ClInclude Include="..\..\nvmeprint.h">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClInclude>
     <ClInclude Include="..\daemon_win32.h" />
     <ClInclude Include="..\syslog.h" />
     <ClInclude Include="..\wbemcli_small.h" />
index 910d8294bb577901f65a221ce600d8a57a05479e..9efd8b2f28e4f388523518eaa6713e03dbce8d5d 100644 (file)
@@ -65,6 +65,8 @@
     <ClCompile Include="..\..\utility.cpp" />
     <ClCompile Include="..\..\ataidentify.cpp" />
     <ClCompile Include="..\..\dev_areca.cpp" />
+    <ClCompile Include="..\..\nvmecmds.cpp" />
+    <ClCompile Include="..\..\nvmeprint.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\daemon_win32.h">
@@ -88,6 +90,7 @@
     <ClInclude Include="..\..\getopt\getopt.h">
       <Filter>getopt</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\aacraid.h" />
     <ClInclude Include="..\..\atacmdnames.h" />
     <ClInclude Include="..\..\atacmds.h" />
     <ClInclude Include="..\..\cissio_freebsd.h" />
     <ClInclude Include="..\..\dev_areca.h" />
     <ClInclude Include="config.h" />
     <ClInclude Include="svnversion.h" />
+    <ClInclude Include="..\..\nvmecmds.h" />
+    <ClInclude Include="..\..\nvmeprint.h" />
   </ItemGroup>
   <ItemGroup>
     <None Include="..\installer.nsi">
index 97328903d98e5f9d57127437f932929cf707537b..ce85af083a0118587b171401ddfb01cd17b49623 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_win32/wbemcli_small.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * This file was extracted from wbemcli.h of the w64 mingw-runtime package
  * (http://mingw-w64.sourceforge.net/). See original copyright below.
index ade992d4ebf4e8b3d3679022910589d8d738da1b..861ae4acc37805d2a94a13298fdc7d8c70c1d9c9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * os_win32/wmiquery.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2011-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -23,7 +23,7 @@
 
 #include <stdio.h>
 
-const char * wmiquery_cpp_cvsid = "$Id: wmiquery.cpp 3802 2013-03-24 18:36:21Z chrfranke $"
+const char * wmiquery_cpp_cvsid = "$Id: wmiquery.cpp 4120 2015-08-27 16:12:21Z samm2 $"
   WMIQUERY_H_CVSID;
 
 
index 730dcb12f75469a3c69ab402b602ebd24a486d73..3000a37545ff62f795117b45bf43dc427d938a7a 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * os_win32/wmiquery.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2011 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2011-15 Christian Franke
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 #ifndef WMIQUERY_H
 #define WMIQUERY_H
 
-#define WMIQUERY_H_CVSID "$Id: wmiquery.h 3475 2011-11-10 21:43:40Z chrfranke $"
+#define WMIQUERY_H_CVSID "$Id: wmiquery.h 4152 2015-10-17 16:08:21Z chrfranke $"
 
 #ifdef HAVE_WBEMCLI_H
 #include <wbemcli.h>
@@ -45,7 +45,7 @@ class com_bstr
 {
 public:
   /// Construct from string.
-  com_bstr(const char * str);
+  explicit com_bstr(const char * str);
 
   /// Destructor frees BSTR.
   ~com_bstr()
index 3298dfceaab67e1a0519f4651932caa6a02aaa59..dca7fb7c74773a0f9a3ef6ace4e6bed33d8de189 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * WTSSendMessage() command line tool
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2012 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
@@ -18,7 +18,7 @@
 #define WINVER 0x0500
 #define _WIN32_WINNT WINVER
 
-char svnid[] = "$Id: wtssendmsg.c 3714 2012-11-24 16:34:47Z chrfranke $";
+char svnid[] = "$Id: wtssendmsg.c 4120 2015-08-27 16:12:21Z samm2 $";
 
 #include <stdio.h>
 #include <string.h>
@@ -30,7 +30,7 @@ char svnid[] = "$Id: wtssendmsg.c 3714 2012-11-24 16:34:47Z chrfranke $";
 
 static int usage()
 {
-  printf("wtssendmsg $Revision: 3714 $ - Display a message box on client desktops\n"
+  printf("wtssendmsg $Revision: 4120 $ - Display a message box on client desktops\n"
          "Copyright (C) 2012 Christian Franke, smartmontools.org\n\n"
          "Usage: wtssendmsg [-cas] [-v] [\"Caption\"] \"Message\"|-\n"
          "       wtssendmsg -v\n\n"
index abcb468d80161811c68f816e43b343a1884981c9..26a9290c4d0a32e718ed34a8eecc73cb0440b948 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * scsiata.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2006-12 Douglas Gilbert <dgilbert@interlog.com>
- * Copyright (C) 2009-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2006-15 Douglas Gilbert <dgilbert@interlog.com>
+ * Copyright (C) 2009-16 Christian Franke
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -62,7 +62,7 @@
 #include "dev_ata_cmd_set.h" // ata_device_with_command_set
 #include "dev_tunnelled.h" // tunnelled_device<>
 
-const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 3922 2014-06-23 19:17:18Z chrfranke $";
+const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 4276 2016-04-02 19:13:39Z chrfranke $";
 
 /* This is a slightly stretched SCSI sense "descriptor" format header.
    The addition is to allow the 0x70 and 0x71 response codes. The idea
@@ -203,6 +203,25 @@ sat_device::~sat_device() throw()
 // des[11]: lba_high (7:0)
 // des[12]: device
 // des[13]: status
+//
+//
+// ATA registers returned via fixed format sense (allowed >= SAT-2)
+// fxs[0]: info_valid (bit 7); response_code (6:0)
+// fxs[1]: (obsolete)
+// fxs[2]: sense_key (3:0) --> recovered error (formerly 'no sense')
+// fxs[3]: information (31:24) --> ATA Error register
+// fxs[4]: information (23:16) --> ATA Status register
+// fxs[5]: information (15:8) --> ATA Device register
+// fxs[6]: information (7:0) --> ATA Count (7:0)
+// fxs[7]: additional sense length [should be >= 10]
+// fxs[8]: command specific info (31:24) --> Extend (7), count_upper_nonzero
+//         (6), lba_upper_nonzero(5), log_index (3:0)
+// fxs[9]: command specific info (23:16) --> ATA LBA (7:0)
+// fxs[10]: command specific info (15:8) --> ATA LBA (15:8)
+// fxs[11]: command specific info (7:0) --> ATA LBA (23:16)
+// fxs[12]: additional sense code (asc) --> 0x0
+// fxs[13]: additional sense code qualifier (ascq) --> 0x1d
+//          asc,ascq = 0x0,0x1d --> 'ATA pass through information available'
 
 
 
@@ -243,7 +262,7 @@ bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
     unsigned char cdb[SAT_ATA_PASSTHROUGH_16LEN];
     unsigned char sense[32];
     const unsigned char * ardp;
-    int status, ard_len, have_sense;
+    int ard_len, have_sense;
     int extend = 0;
     int ck_cond = 0;    /* set to 1 to read register(s) back */
     int protocol = 3;   /* non-data */
@@ -251,6 +270,7 @@ bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
     int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
     int t_length = 0;   /* 0 -> no data transferred */
     int passthru_size = DEF_SAT_ATA_PASSTHRU_SIZE;
+    bool sense_descriptor = true;
 
     memset(cdb, 0, sizeof(cdb));
     memset(sense, 0, sizeof(sense));
@@ -358,20 +378,33 @@ bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
     have_sense = sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len,
                                          &ssh);
     if (have_sense) {
-        /* look for SAT ATA Return Descriptor */
-        ardp = sg_scsi_sense_desc_find(io_hdr.sensep,
-                                       io_hdr.resp_sense_len,
-                                       ATA_RETURN_DESCRIPTOR);
-        if (ardp) {
-            ard_len = ardp[1] + 2;
-            if (ard_len < 12)
-                ard_len = 12;
-            else if (ard_len > 14)
-                ard_len = 14;
+        sense_descriptor = ssh.response_code >= 0x72;
+        if (sense_descriptor) {
+            /* look for SAT ATA Return Descriptor */
+            ardp = sg_scsi_sense_desc_find(io_hdr.sensep,
+                                           io_hdr.resp_sense_len,
+                                           ATA_RETURN_DESCRIPTOR);
+            if (ardp) {
+                ard_len = ardp[1] + 2;
+                if (ard_len < 12)
+                    ard_len = 12;
+                else if (ard_len > 14)
+                    ard_len = 14;
+            }
         }
         scsi_do_sense_disect(&io_hdr, &sinfo);
-        status = scsiSimpleSenseFilter(&sinfo);
-        if (0 != status) {
+        int status = scsiSimpleSenseFilter(&sinfo);
+
+        // Workaround for bogus sense_key in sense data with SAT ATA Return Descriptor
+        if (   status && ck_cond && ardp && ard_len > 13
+            && (ardp[13] & 0xc1) == 0x40 /* !BSY && DRDY && !ERR */) {
+            if (scsi_debugmode > 0)
+                pout("ATA status (0x%02x) indicates success, ignoring SCSI sense_key\n",
+                     ardp[13]);
+            status = 0;
+        }
+
+        if (0 != status) {  /* other than no_sense and recovered_error */
             if (scsi_debugmode > 0) {
                 pout("sat_device::ata_pass_through: scsi error: %s\n",
                      scsiErrString(status));
@@ -408,26 +441,57 @@ bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
                     hi.lba_mid      = ardp[ 8];
                     hi.lba_high     = ardp[10];
                 }
+            } else if ((! sense_descriptor) &&
+                       (0 == ssh.asc) &&
+                       (SCSI_ASCQ_ATA_PASS_THROUGH == ssh.ascq) &&
+                       (0 != io_hdr.sensep[4] /* Some ATA STATUS bit must be set */)) {
+                /* in SAT-2 and later, ATA registers may be passed back via
+                 * fixed format sense data [ref: sat3r07 section 12.2.2.7] */
+                ata_out_regs & lo = out.out_regs;
+                lo.error        = io_hdr.sensep[ 3];
+                lo.status       = io_hdr.sensep[ 4];
+                lo.device       = io_hdr.sensep[ 5];
+                lo.sector_count = io_hdr.sensep[ 6];
+                lo.lba_low      = io_hdr.sensep[ 9];
+                lo.lba_mid      = io_hdr.sensep[10];
+                lo.lba_high     = io_hdr.sensep[11];
+                if (in.in_regs.is_48bit_cmd()) {
+                    if (0 == (0x60 & io_hdr.sensep[8])) {
+                        ata_out_regs & hi = out.out_regs.prev;
+                        hi.sector_count = 0;
+                        hi.lba_low      = 0;
+                        hi.lba_mid      = 0;
+                        hi.lba_high     = 0;
+                    } else {
+                        /* getting the "hi." values when either
+                         * count_upper_nonzero or lba_upper_nonzero are set
+                         * involves fetching the SCSI ATA PASS-THROUGH
+                         * Results log page and decoding the descriptor with
+                         * the matching log_index field. Painful. */ 
+                    }
+                }
             }
         }
-        if (ardp == NULL)
-            ck_cond = 0;       /* not the type of sense data expected */
-    }
-    if (0 == ck_cond) {
+    } else {    /* ck_cond == 0 */
         if (have_sense) {
-            if ((ssh.response_code >= 0x72) &&
-                ((SCSI_SK_NO_SENSE == ssh.sense_key) ||
+            if (((SCSI_SK_NO_SENSE == ssh.sense_key) ||
                  (SCSI_SK_RECOVERED_ERR == ssh.sense_key)) &&
                 (0 == ssh.asc) &&
                 (SCSI_ASCQ_ATA_PASS_THROUGH == ssh.ascq)) {
-                if (ardp) {
-                    if (scsi_debugmode > 0) {
+                if (scsi_debugmode > 0) {
+                    if (sense_descriptor && ardp) {
                         pout("Values from ATA Return Descriptor are:\n");
                         dStrHex((const char *)ardp, ard_len, 1);
+                    } else if (! sense_descriptor) {
+                        pout("Values from ATA fixed format sense are:\n");
+                        pout("  Error: 0x%x\n", io_hdr.sensep[3]);
+                        pout("  Status: 0x%x\n", io_hdr.sensep[4]);
+                        pout("  Device: 0x%x\n", io_hdr.sensep[5]);
+                        pout("  Count: 0x%x\n", io_hdr.sensep[6]);
                     }
-                    return set_err(EIO, "SAT command failed");
                 }
             }
+            return set_err(EIO, "SAT command failed");
         }
     }
     return true;
@@ -1118,6 +1182,148 @@ bool usbjmicron_device::get_registers(unsigned short addr,
 }
 
 
+/////////////////////////////////////////////////////////////////////////////
+
+/// Prolific USB Bridge support. (PL2773) (Probably works on PL2771 also...)
+
+class usbprolific_device
+: public tunnelled_device<
+    /*implements*/ ata_device,
+    /*by tunnelling through a*/ scsi_device
+  >
+{
+public:
+  usbprolific_device(smart_interface * intf, scsi_device * scsidev,
+                    const char * req_type);
+
+  virtual ~usbprolific_device() throw();
+
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+};
+
+
+usbprolific_device::usbprolific_device(smart_interface * intf, scsi_device * scsidev,
+                                     const char * req_type)
+: smart_device(intf, scsidev->get_dev_name(), "usbprolific", req_type),
+  tunnelled_device<ata_device, scsi_device>(scsidev)
+{
+  set_info().info_name = strprintf("%s [USB Prolific]", scsidev->get_info_name());
+}
+
+usbprolific_device::~usbprolific_device() throw()
+{
+}
+
+bool usbprolific_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  if (!ata_cmd_is_supported(in,
+    ata_device::supports_data_out |
+    ata_device::supports_48bit_hi_null |
+    ata_device::supports_output_regs |
+    ata_device::supports_smart_status,
+    "Prolific" )
+  )
+    return false;
+
+  scsi_cmnd_io io_hdr;
+  memset(&io_hdr, 0, sizeof(io_hdr));
+  unsigned char cmd_rw = 0x10;  // Read
+
+  switch (in.direction) {
+    case ata_cmd_in::no_data:
+      io_hdr.dxfer_dir = DXFER_NONE;
+      break;
+    case ata_cmd_in::data_in:
+      io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+      io_hdr.dxfer_len = in.size;
+      io_hdr.dxferp = (unsigned char *)in.buffer;
+      memset(in.buffer, 0, in.size);
+      break;
+    case ata_cmd_in::data_out:
+      io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+      io_hdr.dxfer_len = in.size;
+      io_hdr.dxferp = (unsigned char *)in.buffer;
+      cmd_rw = 0x0; // Write
+      break;
+    default:
+      return set_err(EINVAL);
+  }
+
+  // Based on reverse engineering of iSmart.exe with API Monitor.
+  // Seen commands:
+  // D0  0 0  0 06 7B 0 0 0 0 0 0                 // Read Firmware info?, reads 16 bytes
+  // F4  0 0  0 06 7B                             // ??
+  // D8 15 0 D8 06 7B 0 0 0 0 1 1 4F C2 A0 B0     // SMART Enable
+  // D8 15 0 D0 06 7B 0 0 2 0 1 1 4F C2 A0 B0     // SMART Read values
+  // D8 15 0 D1 06 7B 0 0 2 0 1 1 4F C2 A0 B0     // SMART Read thresholds
+  // D8 15 0 D4 06 7B 0 0 0 0 0 1 4F C2 A0 B0     // SMART Execute self test
+  // D7  0 0  0 06 7B 0 0 0 0 0 0 0 0 0 0         // Read status registers, Reads 16 bytes of data
+  // Additional DATA OUT support based on document from Prolific
+
+  // Build pass through command
+  unsigned char cdb[16];
+  cdb[ 0] = 0xD8;         // Operation Code (D8 = Prolific ATA pass through)
+  cdb[ 1] = cmd_rw|0x5;   // Read(0x10)/Write(0x0) | NORMAL(0x5)/PREFIX(0x0)(?)
+  cdb[ 2] = 0x0;          // Reserved
+  cdb[ 3] = in.in_regs.features;        // Feature register (SMART command)
+  cdb[ 4] = 0x06;         // Check Word (VendorID magic, Prolific: 0x067B)
+  cdb[ 5] = 0x7B;         // Check Word (VendorID magic, Prolific: 0x067B)
+  cdb[ 6] = (unsigned char)(io_hdr.dxfer_len >> 24);  // Length MSB
+  cdb[ 7] = (unsigned char)(io_hdr.dxfer_len >> 16);  // Length ...
+  cdb[ 8] = (unsigned char)(io_hdr.dxfer_len >>  8);  // Length ...
+  cdb[ 9] = (unsigned char)(io_hdr.dxfer_len      );  // Length LSB
+  cdb[10] = in.in_regs.sector_count;    // Sector Count
+  cdb[11] = in.in_regs.lba_low;         // LBA Low (7:0)
+  cdb[12] = in.in_regs.lba_mid;         // LBA Mid (15:8)
+  cdb[13] = in.in_regs.lba_high;        // LBA High (23:16)
+  cdb[14] = in.in_regs.device | 0xA0;   // Device/Head
+  cdb[15] = in.in_regs.command;         // ATA Command Register (only PIO supported)
+  // Use '-r scsiioctl,1' to print CDB for debug purposes
+
+  io_hdr.cmnd = cdb;
+  io_hdr.cmnd_len = 16;
+
+  scsi_device * scsidev = get_tunnel_dev();
+  if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+         "usbprolific_device::ata_pass_through: "))
+    return set_err(scsidev->get_err());
+
+  if (in.out_needed.is_set()) {
+    // Read ATA output registers
+    unsigned char regbuf[16] = {0, };
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = sizeof(regbuf);
+    io_hdr.dxferp = regbuf;
+
+    memset(cdb, 0, sizeof(cdb));
+    cdb[ 0] = 0xD7;  // Prolific read registers
+    cdb[ 4] = 0x06;  // Check Word (VendorID magic, Prolific: 0x067B)
+    cdb[ 5] = 0x7B;  // Check Word (VendorID magic, Prolific: 0x067B)
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+
+    if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+           "usbprolific_device::scsi_pass_through (get registers): "))
+      return set_err(scsidev->get_err());
+
+    // Use '-r scsiioctl,2' to print input registers for debug purposes
+    // Example: 50 00 00 00 00 01 4f 00  c2 00 a0 da 00 b0 00 50
+    out.out_regs.status       = regbuf[0];  // Status
+    out.out_regs.error        = regbuf[1];  // Error
+    out.out_regs.sector_count = regbuf[2];  // Sector Count (7:0)
+    out.out_regs.lba_low      = regbuf[4];  // LBA Low (7:0)
+    out.out_regs.lba_mid      = regbuf[6];  // LBA Mid (7:0)
+    out.out_regs.lba_high     = regbuf[8];  // LBA High (7:0)
+    out.out_regs.device       = regbuf[10]; // Device/Head
+    //                          = regbuf[11]; // ATA Feature (7:0)
+    //                          = regbuf[13]; // ATA Command
+  }
+
+  return true;
+}
+
+
 /////////////////////////////////////////////////////////////////////////////
 
 /// SunplusIT USB Bridge support.
@@ -1280,6 +1486,13 @@ using namespace sat;
 
 ata_device * smart_interface::get_sat_device(const char * type, scsi_device * scsidev)
 {
+  if (!scsidev)
+    throw std::logic_error("smart_interface: get_sat_device() called with scsidev=0");
+
+  // Take temporary ownership of 'scsidev' to delete it on error
+  scsi_device_auto_ptr scsidev_holder(scsidev);
+  ata_device * satdev = 0;
+
   if (!strncmp(type, "sat", 3)) {
     const char * t = type + 3;
     bool enable_auto = false;
@@ -1293,7 +1506,7 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc
       set_err(EINVAL, "Option '-d sat[,auto][,N]' requires N to be 0, 12 or 16");
       return 0;
     }
-    return new sat_device(this, scsidev, type, ptlen, enable_auto);
+    satdev = new sat_device(this, scsidev, type, ptlen, enable_auto);
   }
 
   else if (!strncmp(type, "usbcypress", 10)) {
@@ -1304,7 +1517,7 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc
                       "an hexadecimal number between 0x0 and 0xff");
       return 0;
     }
-    return new usbcypress_device(this, scsidev, type, signature);
+    satdev = new usbcypress_device(this, scsidev, type, signature);
   }
 
   else if (!strncmp(type, "usbjmicron", 10)) {
@@ -1325,17 +1538,25 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc
       set_err(EINVAL, "Option '-d usbjmicron[,p][,x],<n>' requires <n> to be 0 or 1");
       return 0;
     }
-    return new usbjmicron_device(this, scsidev, type, prolific, ata_48bit_support, port);
+    satdev = new usbjmicron_device(this, scsidev, type, prolific, ata_48bit_support, port);
+  }
+
+  else if (!strcmp(type, "usbprolific")) {
+    satdev = new usbprolific_device(this, scsidev, type);
   }
 
   else if (!strcmp(type, "usbsunplus")) {
-    return new usbsunplus_device(this, scsidev, type);
+    satdev = new usbsunplus_device(this, scsidev, type);
   }
 
   else {
     set_err(EINVAL, "Unknown USB device type '%s'", type);
     return 0;
   }
+
+  // 'scsidev' is now owned by 'satdev'
+  scsidev_holder.release();
+  return satdev;
 }
 
 // Try to detect a SAT device behind a SCSI interface.
index bb505c0c3e69dd9fd5addfaea0098f4b4e1a0294..eb4fb1dd1847e215261106bfb4c088d5ba597c3c 100644 (file)
@@ -1,13 +1,11 @@
 /*
  * scsicmds.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-8 Bruce Allen
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
- *
- * Additional SCSI work:
- * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.com>
+ * Copyright (C) 2003-16 Douglas Gilbert <dgilbert@interlog.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -49,7 +47,7 @@
 #include "dev_interface.h"
 #include "utility.h"
 
-const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 3915 2014-06-19 18:24:57Z dpgilbert $"
+const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 4243 2016-03-20 18:29:36Z chrfranke $"
   SCSICMDS_H_CVSID;
 
 // Print SCSI debug messages?
@@ -61,13 +59,11 @@ supported_vpd_pages * supported_vpd_pages_p = NULL;
 supported_vpd_pages::supported_vpd_pages(scsi_device * device) : num_valid(0)
 {
     unsigned char b[0xfc];     /* pre SPC-3 INQUIRY max response size */
-    int n;
-
     memset(b, 0, sizeof(b));
     if (device && (0 == scsiInquiryVpd(device, SCSI_VPD_SUPPORTED_VPD_PAGES,
                    b, sizeof(b)))) {
         num_valid = (b[2] << 8) + b[3];
-        n = sizeof(pages);
+        int n = sizeof(pages);
         if (num_valid > n)
             num_valid = n;
         memcpy(pages, b + 4, num_valid);
@@ -91,7 +87,6 @@ void
 dStrHex(const char* str, int len, int no_ascii)
 {
     const char* p = str;
-    unsigned char c;
     char buff[82];
     int a = 0;
     const int bpstart = 5;
@@ -110,7 +105,7 @@ dStrHex(const char* str, int len, int no_ascii)
 
     for(i = 0; i < len; i++)
     {
-        c = *p++;
+        unsigned char c = *p++;
         bpos += 3;
         if (bpos == (bpstart + (9 * 3)))
             bpos++;
@@ -125,6 +120,9 @@ dStrHex(const char* str, int len, int no_ascii)
         }
         if (cpos > (cpstart+15))
         {
+            while (cpos > 0 && buff[cpos-1] == ' ')
+              cpos--;
+            buff[cpos] = 0;
             pout("%s\n", buff);
             bpos = bpstart;
             cpos = cpstart;
@@ -136,6 +134,9 @@ dStrHex(const char* str, int len, int no_ascii)
     }
     if (cpos > cpstart)
     {
+        while (cpos > 0 && buff[cpos-1] == ' ')
+          cpos--;
+        buff[cpos] = 0;
         pout("%s\n", buff);
     }
 }
@@ -175,14 +176,12 @@ static const char * vendor_specific = "<vendor specific>";
 const char *
 scsi_get_opcode_name(UINT8 opcode)
 {
-    int k;
     int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
-    struct scsi_opcode_name * onp;
 
     if (opcode >= 0xc0)
         return vendor_specific;
-    for (k = 0; k < len; ++k) {
-        onp = &opcode_name_arr[k];
+    for (int k = 0; k < len; ++k) {
+        struct scsi_opcode_name * onp = &opcode_name_arr[k];
         if (opcode == onp->opcode)
             return onp->name;
         else if (opcode < onp->opcode)
@@ -195,11 +194,9 @@ void
 scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
                      struct scsi_sense_disect * out)
 {
-    int resp_code;
-
     memset(out, 0, sizeof(struct scsi_sense_disect));
     if (SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) {
-        resp_code = (io_buf->sensep[0] & 0x7f);
+        int resp_code = (io_buf->sensep[0] & 0x7f);
         out->resp_code = resp_code;
         if (resp_code >= 0x72) {
             out->sense_key = (io_buf->sensep[1] & 0xf);
@@ -302,19 +299,19 @@ scsi_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
                      int * off, int m_assoc, int m_desig_type, int m_code_set)
 {
     const unsigned char * ucp;
-    int k, c_set, assoc, desig_type;
+    int k;
 
     for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) {
         k = (k < 0) ? 0 : (k + ucp[k + 3] + 4);
         if ((k + 4) > page_len)
             break;
-        c_set = (ucp[k] & 0xf);
+        int c_set = (ucp[k] & 0xf);
         if ((m_code_set >= 0) && (m_code_set != c_set))
             continue;
-        assoc = ((ucp[k + 1] >> 4) & 0x3);
+        int assoc = ((ucp[k + 1] >> 4) & 0x3);
         if ((m_assoc >= 0) && (m_assoc != assoc))
             continue;
-        desig_type = (ucp[k + 1] & 0xf);
+        int desig_type = (ucp[k + 1] & 0xf);
         if ((m_desig_type >= 0) && (m_desig_type != desig_type))
             continue;
         *off = k;
@@ -330,11 +327,6 @@ int
 scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
                       int * transport)
 {
-    int m, c_set, assoc, desig_type, i_len, naa, off, u, have_scsi_ns;
-    const unsigned char * ucp;
-    const unsigned char * ip;
-    int si = 0;
-
     if (transport)
         *transport = -1;
     if (slen < 32) {
@@ -342,25 +334,29 @@ scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
             s[0] = '\0';
         return -1;
     }
-    have_scsi_ns = 0;
+
     s[0] = '\0';
-    off = -1;
+    int si = 0;
+    int have_scsi_ns = 0;
+    int off = -1;
+    int u;
     while ((u = scsi_vpd_dev_id_iter(b, blen, &off, -1, -1, -1)) == 0) {
-        ucp = b + off;
-        i_len = ucp[3];
+        const unsigned char * ucp = b + off;
+        int i_len = ucp[3];
         if ((off + i_len + 4) > blen) {
             snprintf(s+si, slen-si, "error: designator length");
             return -1;
         }
-        assoc = ((ucp[1] >> 4) & 0x3);
+        int assoc = ((ucp[1] >> 4) & 0x3);
         if (transport && assoc && (ucp[1] & 0x80) && (*transport < 0))
             *transport = (ucp[0] >> 4) & 0xf;
         if (0 != assoc)
             continue;
-        ip = ucp + 4;
-        c_set = (ucp[0] & 0xf);
-        desig_type = (ucp[1] & 0xf);
+        const unsigned char * ip = ucp + 4;
+        int c_set = (ucp[0] & 0xf);
+        int desig_type = (ucp[1] & 0xf);
 
+        int naa;
         switch (desig_type) {
         case 0: /* vendor specific */
         case 1: /* T10 vendor identification */
@@ -373,7 +369,7 @@ scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
             if (have_scsi_ns)
                 si = 0;
             si += snprintf(s+si, slen-si, "0x");
-            for (m = 0; m < i_len; ++m)
+            for (int m = 0; m < i_len; ++m)
                 si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
             break;
         case 3: /* NAA */
@@ -394,7 +390,7 @@ scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
                     return -1;
                 }
                 si += snprintf(s+si, slen-si, "0x");
-                for (m = 0; m < 8; ++m)
+                for (int m = 0; m < 8; ++m)
                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
             } else if ((3 == naa ) || (5 == naa)) {
                 /* NAA=3 Locally assigned; NAA=5 IEEE Registered */
@@ -403,7 +399,7 @@ scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
                     return -1;
                 }
                 si += snprintf(s+si, slen-si, "0x");
-                for (m = 0; m < 8; ++m)
+                for (int m = 0; m < 8; ++m)
                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
             } else if (6 == naa) {      /* NAA IEEE Registered extended */
                 if (16 != i_len) {
@@ -411,7 +407,7 @@ scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
                     return -1;
                 }
                 si += snprintf(s+si, slen-si, "0x");
-                for (m = 0; m < 16; ++m)
+                for (int m = 0; m < 16; ++m)
                     si += snprintf(s+si, slen-si, "%02x", (unsigned int)ip[m]);
             }
             break;
@@ -461,7 +457,6 @@ scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
     UINT8 cdb[10];
     UINT8 sense[32];
     int pageLen;
-    int status, res;
 
     if (known_resp_len > bufLen)
         return -EIO;
@@ -494,6 +489,7 @@ scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
         if (!device->scsi_pass_through(&io_hdr))
           return -device->get_errno();
         scsi_do_sense_disect(&io_hdr, &sinfo);
+        int res;
         if ((res = scsiSimpleSenseFilter(&sinfo)))
             return res;
         /* sanity check on response */
@@ -529,7 +525,7 @@ scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
     if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
-    status = scsiSimpleSenseFilter(&sinfo);
+    int status = scsiSimpleSenseFilter(&sinfo);
     if (0 != status)
         return status;
     /* sanity check on response */
@@ -589,7 +585,6 @@ scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
     struct scsi_sense_disect sinfo;
     UINT8 cdb[6];
     UINT8 sense[32];
-    int status;
 
     if ((bufLen < 0) || (bufLen > 255))
         return -EINVAL;
@@ -611,7 +606,7 @@ scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
     if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
-    status = scsiSimpleSenseFilter(&sinfo);
+    int status = scsiSimpleSenseFilter(&sinfo);
     if (SIMPLE_ERR_TRY_AGAIN == status) {
         if (!device->scsi_pass_through(&io_hdr))
           return -device->get_errno();
@@ -687,7 +682,6 @@ scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
     struct scsi_sense_disect sinfo;
     UINT8 cdb[10];
     UINT8 sense[32];
-    int status;
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -708,7 +702,7 @@ scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
     if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
-    status = scsiSimpleSenseFilter(&sinfo);
+    int status = scsiSimpleSenseFilter(&sinfo);
     if (SIMPLE_ERR_TRY_AGAIN == status) {
         if (!device->scsi_pass_through(&io_hdr))
           return -device->get_errno();
@@ -879,8 +873,6 @@ scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
     UINT8 cdb[6];
     UINT8 sense[32];
     UINT8 buff[18];
-    int len;
-    UINT8 resp_code;
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -898,13 +890,13 @@ scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
     if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();
     if (sense_info) {
-        resp_code = buff[0] & 0x7f;
+        UINT8 resp_code = buff[0] & 0x7f;
         sense_info->resp_code = resp_code;
         sense_info->sense_key = buff[2] & 0xf;
         sense_info->asc = 0;
         sense_info->ascq = 0;
         if ((0x70 == resp_code) || (0x71 == resp_code)) {
-            len = buff[7] + 8;
+            int len = buff[7] + 8;
             if (len > 13) {
                 sense_info->asc = buff[12];
                 sense_info->ascq = buff[13];
@@ -989,40 +981,6 @@ scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf,
     return scsiSimpleSenseFilter(&sinfo);
 }
 
-/* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if
- * command not supported, 3 if field in command not supported or returns
- * negated errno. SPC-3 section 6.18 (rev 22a) */
-int
-scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,
-                      int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = bufLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = RECEIVE_DIAGNOSTIC;
-    cdb[1] = pcv;
-    cdb[2] = pagenum;
-    cdb[3] = (bufLen >> 8) & 0xff;
-    cdb[4] = bufLen & 0xff;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    if (!device->scsi_pass_through(&io_hdr))
-      return -device->get_errno();
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    return scsiSimpleSenseFilter(&sinfo);
-}
-
 /* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */
 static int
 _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo)
@@ -1072,7 +1030,8 @@ scsiTestUnitReady(scsi_device * device)
 }
 
 /* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
- * command not supported, 3 if field in command not supported or returns
+ * command not supported, 3 if field in command not supported, 101 if
+ * defect list not found (e.g. SSD may not have defect list) or returns
  * negated errno. SBC-2 section 5.12 (rev 16) */
 int
 scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
@@ -1102,11 +1061,15 @@ scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
     if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
+    /* Look for "(Primary|Grown) defect list not found" */
+    if ((sinfo.resp_code >= 0x70) && (0x1c == sinfo.asc))
+        return 101;
     return scsiSimpleSenseFilter(&sinfo);
 }
 
 /* READ DEFECT (12) command. Returns 0 if ok, 1 if NOT READY, 2 if
- * command not supported, 3 if field in command not supported or returns
+ * command not supported, 3 if field in command not supported, 101 if
+ * defect list not found (e.g. SSD may not have defect list) or returns
  * negated errno. SBC-3 section 5.18 (rev 35; vale Mark Evans) */
 int
 scsiReadDefect12(scsi_device * device, int req_plist, int req_glist,
@@ -1142,6 +1105,9 @@ scsiReadDefect12(scsi_device * device, int req_plist, int req_glist,
     if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
+    /* Look for "(Primary|Grown) defect list not found" */
+    if ((sinfo.resp_code >= 0x70) && (0x1c == sinfo.asc))
+        return 101;
     return scsiSimpleSenseFilter(&sinfo);
 }
 
@@ -1229,7 +1195,7 @@ scsiGetSize(scsi_device * device, unsigned int * lb_sizep,
             int * lb_per_pb_expp)
 {
     unsigned int last_lba = 0, lb_size = 0;
-    int k, res;
+    int res;
     uint64_t ret_val = 0;
     UINT8 rc16resp[32];
 
@@ -1246,7 +1212,7 @@ scsiGetSize(scsi_device * device, unsigned int * lb_sizep,
                 pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
             return 0;
         }
-        for (k = 0; k < 8; ++k) {
+        for (int k = 0; k < 8; ++k) {
             if (k > 0)
                 ret_val <<= 8;
             ret_val |= rc16resp[k + 0];
@@ -1291,10 +1257,10 @@ scsiGetProtPBInfo(scsi_device * device, unsigned char * rc16_12_31p)
 int
 scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
 {
-    int resp_len, bd_len;
     int offset = -1;
 
     if (resp) {
+        int resp_len, bd_len;
         if (10 == modese_len) {
             resp_len = (resp[0] << 8) + resp[1] + 2;
             bd_len = (resp[6] << 8) + resp[7];
@@ -1381,11 +1347,9 @@ scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp,
 int
 scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)
 {
-    int offset;
-
     if (iecp && iecp->gotCurrent) {
-        offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
-                                    iecp->modese_len);
+        int offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
+                                        iecp->modese_len);
         if (offset >= 0)
             return (iecp->raw_curr[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
         else
@@ -1397,11 +1361,9 @@ scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)
 int
 scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
 {
-    int offset;
-
     if (iecp && iecp->gotCurrent) {
-        offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
-                                    iecp->modese_len);
+        int offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
+                                        iecp->modese_len);
         if (offset >= 0)
             return (iecp->raw_curr[offset + 2] & EWASC_ENABLE) ? 1 : 0;
         else
@@ -1429,10 +1391,9 @@ int
 scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
                                   const struct scsi_iec_mode_page *iecp)
 {
-    int k, offset, resp_len;
+    int offset, resp_len;
     int err = 0;
     UINT8 rout[SCSI_IECMP_RAW_LEN];
-    int sp, eCEnabled, wEnabled;
 
     if ((! iecp) || (! iecp->gotCurrent))
         return -EINVAL;
@@ -1449,7 +1410,7 @@ scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
         resp_len = rout[0] + 1;
         rout[2] &= 0xef;
     }
-    sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
+    int sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
     if (enabled) {
         rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
         if (scsi_debugmode > 2)
@@ -1468,7 +1429,7 @@ scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
 
             rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) :
                                       iecp->raw_curr[offset + 2];
-            for (k = 3; k < 12; ++k) {
+            for (int k = 3; k < 12; ++k) {
                 if (0 == iecp->raw_chg[offset + k])
                     rout[offset + k] = iecp->raw_curr[offset + k];
             }
@@ -1479,8 +1440,8 @@ scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
             return 0;
         }
     } else { /* disabling Exception Control and (temperature) Warnings */
-        eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
-        wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
+        int eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
+        int wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
         if ((! eCEnabled) && (! wEnabled)) {
             if (scsi_debugmode > 0)
                 pout("scsiSetExceptionControlAndWarning: already disabled\n");
@@ -1533,7 +1494,6 @@ scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
     struct scsi_sense_disect sense_info;
     int err;
     int temperatureSet = 0;
-    unsigned short pagesize;
     UINT8 currTemp, trTemp;
 
     *asc = 0;
@@ -1549,7 +1509,7 @@ scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
             return err;
         }
         // pull out page size from response, don't forget to add 4
-        pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4;
+        unsigned short pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4;
         if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
             pout("Log Sense failed, IE page, bad parameter code or length\n");
             return SIMPLE_ERR_BAD_PARAM;
@@ -2109,30 +2069,6 @@ scsiGetIEString(UINT8 asc, UINT8 ascq)
 }
 
 
-/* This is not documented in t10.org, page 0x80 is vendor specific */
-/* Some IBM disks do an offline read-scan when they get this command. */
-int
-scsiSmartIBMOfflineTest(scsi_device * device)
-{
-    UINT8 tBuf[256];
-    int res;
-
-    memset(tBuf, 0, sizeof(tBuf));
-    /* Build SMART Off-line Immediate Diag Header */
-    tBuf[0] = 0x80; /* Page Code */
-    tBuf[1] = 0x00; /* Reserved */
-    tBuf[2] = 0x00; /* Page Length MSB */
-    tBuf[3] = 0x04; /* Page Length LSB */
-    tBuf[4] = 0x03; /* SMART Revision */
-    tBuf[5] = 0x00; /* Reserved */
-    tBuf[6] = 0x00; /* Off-line Immediate Time MSB */
-    tBuf[7] = 0x00; /* Off-line Immediate Time LSB */
-    res = scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8);
-    if (res)
-        pout("IBM offline test failed [%s]\n", scsiErrString(res));
-    return res;
-}
-
 int
 scsiSmartDefaultSelfTest(scsi_device * device)
 {
@@ -2207,7 +2143,7 @@ int
 scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
                               int modese_len)
 {
-    int err, offset, res;
+    int err, offset;
     UINT8 buff[64];
 
     memset(buff, 0, sizeof(buff));
@@ -2233,7 +2169,7 @@ scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
     if (offset < 0)
         return -EINVAL;
     if (buff[offset + 1] >= 0xa) {
-        res = (buff[offset + 10] << 8) | buff[offset + 11];
+        int res = (buff[offset + 10] << 8) | buff[offset + 11];
         *durationSec = res;
         return 0;
     }
@@ -2244,17 +2180,13 @@ scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
 void
 scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp)
 {
-    int k, j, num, pl, pc;
-    unsigned char * ucp;
-    unsigned char * xp;
-    uint64_t * ullp;
-
     memset(ecp, 0, sizeof(*ecp));
-    num = (resp[2] << 8) | resp[3];
-    ucp = &resp[0] + 4;
+    int num = (resp[2] << 8) | resp[3];
+    unsigned char * ucp = &resp[0] + 4;
     while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
-        pl = ucp[3] + 4;
+        int pc = (ucp[0] << 8) | ucp[1];
+        int pl = ucp[3] + 4;
+        uint64_t * ullp;
         switch (pc) {
             case 0:
             case 1:
@@ -2271,14 +2203,14 @@ scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp)
                 ullp = &ecp->counter[7];
                 break;
         }
-        k = pl - 4;
-        xp = ucp + 4;
+        int k = pl - 4;
+        unsigned char * xp = ucp + 4;
         if (k > (int)sizeof(*ullp)) {
             xp += (k - sizeof(*ullp));
             k = sizeof(*ullp);
         }
         *ullp = 0;
-        for (j = 0; j < k; ++j) {
+        for (int j = 0; j < k; ++j) {
             if (j > 0)
                 *ullp <<= 8;
             *ullp |= xp[j];
@@ -2292,17 +2224,15 @@ void
 scsiDecodeNonMediumErrPage(unsigned char *resp,
                            struct scsiNonMediumError *nmep)
 {
-    int k, j, num, pl, pc, szof;
-    unsigned char * ucp;
-    unsigned char * xp;
-
     memset(nmep, 0, sizeof(*nmep));
-    num = (resp[2] << 8) | resp[3];
-    ucp = &resp[0] + 4;
-    szof = sizeof(nmep->counterPC0);
+    int num = (resp[2] << 8) | resp[3];
+    unsigned char * ucp = &resp[0] + 4;
+    int szof = sizeof(nmep->counterPC0);
     while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
-        pl = ucp[3] + 4;
+        int pc = (ucp[0] << 8) | ucp[1];
+        int pl = ucp[3] + 4;
+        int k;
+        unsigned char * xp;
         switch (pc) {
             case 0:
                 nmep->gotPC0 = 1;
@@ -2313,7 +2243,7 @@ scsiDecodeNonMediumErrPage(unsigned char *resp,
                     k = szof;
                 }
                 nmep->counterPC0 = 0;
-                for (j = 0; j < k; ++j) {
+                for (int j = 0; j < k; ++j) {
                     if (j > 0)
                         nmep->counterPC0 <<= 8;
                     nmep->counterPC0 |= xp[j];
@@ -2328,7 +2258,7 @@ scsiDecodeNonMediumErrPage(unsigned char *resp,
                     k = szof;
                 }
                 nmep->counterTFE_H = 0;
-                for (j = 0; j < k; ++j) {
+                for (int j = 0; j < k; ++j) {
                     if (j > 0)
                         nmep->counterTFE_H <<= 8;
                     nmep->counterTFE_H |= xp[j];
@@ -2343,7 +2273,7 @@ scsiDecodeNonMediumErrPage(unsigned char *resp,
                     k = szof;
                 }
                 nmep->counterPE_H = 0;
-                for (j = 0; j < k; ++j) {
+                for (int j = 0; j < k; ++j) {
                     if (j > 0)
                         nmep->counterPE_H <<= 8;
                     nmep->counterPE_H |= xp[j];
@@ -2369,7 +2299,7 @@ scsiDecodeNonMediumErrPage(unsigned char *resp,
 int
 scsiCountFailedSelfTests(scsi_device * fd, int noisy)
 {
-    int num, k, n, err, res, fails, fail_hour;
+    int num, k, err, fails, fail_hour;
     UINT8 * ucp;
     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
 
@@ -2398,13 +2328,13 @@ scsiCountFailedSelfTests(scsi_device * fd, int noisy)
     for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
 
         // timestamp in power-on hours (or zero if test in progress)
-        n = (ucp[6] << 8) | ucp[7];
+        int n = (ucp[6] << 8) | ucp[7];
 
         // The spec says "all 20 bytes will be zero if no test" but
         // DG has found otherwise.  So this is a heuristic.
         if ((0 == n) && (0 == ucp[4]))
             break;
-        res = ucp[4] & 0xf;
+        int res = ucp[4] & 0xf;
         if ((res > 2) && (res < 8)) {
             fails++;
             if (1 == fails)
@@ -2483,7 +2413,7 @@ int
 scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp,
            int * haw_zbcp)
 {
-    int err, offset, speed;
+    int err, offset;
     UINT8 buff[64];
     int pc = MPAGE_CONTROL_DEFAULT;
 
@@ -2491,7 +2421,7 @@ scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp,
     if ((0 == scsiInquiryVpd(device, SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS,
                              buff, sizeof(buff))) &&
         (((buff[2] << 8) + buff[3]) > 2)) {
-        speed = (buff[4] << 8) + buff[5];
+        int speed = (buff[4] << 8) + buff[5];
         if (form_factorp)
             *form_factorp = buff[7] & 0xf;
         if (haw_zbcp)
@@ -2737,7 +2667,7 @@ const unsigned char *
 sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len,
                         int desc_type)
 {
-    int add_sen_len, add_len, desc_len, k;
+    int add_sen_len;
     const unsigned char * descp;
 
     if ((sense_len < 8) || (0 == (add_sen_len = sensep[7])))
@@ -2747,9 +2677,9 @@ sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len,
     add_sen_len = (add_sen_len < (sense_len - 8)) ?
                          add_sen_len : (sense_len - 8);
     descp = &sensep[8];
-    for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
+    for (int desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
         descp += desc_len;
-        add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
+        int add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
         desc_len = add_len + 2;
         if (descp[0] == desc_type)
             return descp;
index c28e830742577148b9601a83618d03876a2dc1aa..65402fac56a06fec3fa81612a6a289fd8f8291fa 100644 (file)
@@ -1,13 +1,11 @@
 /*
  * scsicmds.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-8 Bruce Allen
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- *
- * Additional SCSI work:
- * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.com>
+ * Copyright (C) 2003-15 Douglas Gilbert <dgilbert@interlog.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -32,7 +30,7 @@
 #ifndef SCSICMDS_H_
 #define SCSICMDS_H_
 
-#define SCSICMDS_H_CVSID "$Id: scsicmds.h 3896 2014-04-28 04:31:25Z dpgilbert $\n"
+#define SCSICMDS_H_CVSID "$Id: scsicmds.h 4152 2015-10-17 16:08:21Z chrfranke $\n"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -322,7 +320,7 @@ class scsi_device;
 class supported_vpd_pages
 {
 public:
-    supported_vpd_pages(scsi_device * device);
+    explicit supported_vpd_pages(scsi_device * device);
     ~supported_vpd_pages() { num_valid = 0; }
 
     bool is_supported(int vpd_page_num) const;
@@ -385,9 +383,6 @@ int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info
 
 int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int bufLen);
 
-int scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,
-                      int bufLen);
-
 int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format,
                      UINT8 *pBuf, int bufLen);
 
@@ -433,8 +428,6 @@ const char* scsiGetIEString(UINT8 asc, UINT8 ascq);
 int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp);
 
 
-int scsiSmartIBMOfflineTest(scsi_device * device);
-
 int scsiSmartDefaultSelfTest(scsi_device * device);
 int scsiSmartShortSelfTest(scsi_device * device);
 int scsiSmartExtendSelfTest(scsi_device * device);
index 95568b4045b10088ee603a4ca59412abf9458e5b..42e43e65a4c4148871926c728ba6b3e2349abd2c 100644 (file)
@@ -1,13 +1,11 @@
 /*
  * scsiprint.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- *
- * Additional SCSI work:
- * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.com>
+ * Copyright (C) 2003-15 Douglas Gilbert <dgilbert@interlog.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -42,7 +40,7 @@
 
 #define GBUF_SIZE 65535
 
-const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 3898 2014-04-30 17:44:13Z dpgilbert $"
+const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 4292 2016-04-12 23:06:59Z dpgilbert $"
                                  SCSIPRINT_H_CVSID;
 
 
@@ -87,7 +85,15 @@ scsiGetSupportedLogPages(scsi_device * device)
         if (scsi_debugmode > 0)
             pout("Log Sense for supported pages failed [%s]\n",
                  scsiErrString(err));
-        return;
+        /* try one more time with defined length, workaround for the bug #678
+        found with ST8000NM0075/E001 */
+        err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
+                            LOG_RESP_LEN, 68); /* 64 max pages + 4b header */
+        if (scsi_debugmode > 0)
+            pout("Log Sense for supported pages failed (second attempt) [%s]\n",
+                 scsiErrString(err));
+        if (err)
+            return;
     }
 
     for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
@@ -151,8 +157,8 @@ scsiGetSmartData(scsi_device * device, bool attribs)
 {
     UINT8 asc;
     UINT8 ascq;
-    UINT8 currenttemp = 0;
-    UINT8 triptemp = 0;
+    UINT8 currenttemp = 255;
+    UINT8 triptemp = 255;
     const char * cp;
     int err = 0;
     print_on();
@@ -173,13 +179,13 @@ scsiGetSmartData(scsi_device * device, bool attribs)
         pout("SMART Health Status: OK\n");
 
     if (attribs && !gTempLPage) {
-        if (currenttemp) {
-            if (255 != currenttemp)
-                pout("Current Drive Temperature:     %d C\n", currenttemp);
-            else
-                pout("Current Drive Temperature:     <not available>\n");
-        }
-        if (triptemp)
+        if (255 == currenttemp)
+            pout("Current Drive Temperature:     <not available>\n");
+        else
+            pout("Current Drive Temperature:     %d C\n", currenttemp);
+        if (255 == triptemp)
+            pout("Drive Trip Temperature:        <not available>\n");
+        else
             pout("Drive Trip Temperature:        %d C\n", triptemp);
     }
     pout("\n");
@@ -204,7 +210,7 @@ scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
     print_on();
     if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf,
                         LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
-        pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err));
+        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
         print_off();
         return -1;
     }
@@ -244,14 +250,13 @@ scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
 static void
 scsiGetStartStopData(scsi_device * device)
 {
-    UINT32 u;
-    int err, len, k, extra, pc;
+    int err, len, k, extra;
     unsigned char * ucp;
 
     if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
                             LOG_RESP_LEN, 0))) {
         print_on();
-        pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
+        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
         print_off();
         return;
     }
@@ -271,7 +276,8 @@ scsiGetStartStopData(scsi_device * device)
             return;
         }
         extra = ucp[3] + 4;
-        pc = (ucp[0] << 8) + ucp[1];
+        int pc = (ucp[0] << 8) + ucp[1];
+        UINT32 u;
         switch (pc) {
         case 1:
             if (10 == extra)
@@ -321,7 +327,7 @@ scsiGetStartStopData(scsi_device * device)
 static void
 scsiPrintGrownDefectListLen(scsi_device * device)
 {
-    int err, dl_format, got_rd12, generation;
+    int err, dl_format, got_rd12;
     unsigned int dl_len, div;
 
     memset(gBuf, 0, 8);
@@ -339,7 +345,9 @@ scsiPrintGrownDefectListLen(scsi_device * device)
                 return;
             } else
                 got_rd12 = 0;
-        } else {
+        } else if (101 == err)    /* Defect list not found, leave quietly */
+            return;
+        else {
             if (scsi_debugmode > 0) {
                 print_on();
                 pout("Read defect list (12) Failed: %s\n", scsiErrString(err));
@@ -351,7 +359,7 @@ scsiPrintGrownDefectListLen(scsi_device * device)
         got_rd12 = 1;
 
     if (got_rd12) {
-        generation = (gBuf[2] << 8) + gBuf[3];
+        int generation = (gBuf[2] << 8) + gBuf[3];
         if ((generation > 1) && (scsi_debugmode > 0)) {
             print_on();
             pout("Read defect list (12): generation=%d\n", generation);
@@ -403,9 +411,8 @@ scsiPrintGrownDefectListLen(scsi_device * device)
 static void
 scsiPrintSeagateCacheLPage(scsi_device * device)
 {
-    int k, j, num, pl, pc, err, len;
+    int num, pl, pc, err, len;
     unsigned char * ucp;
-    unsigned char * xp;
     uint64_t ull;
 
     if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
@@ -458,14 +465,14 @@ scsiPrintSeagateCacheLPage(scsi_device * device)
                        "> segment size"); break;
         default: pout("  Unknown Seagate parameter code [0x%x]", pc); break;
         }
-        k = pl - 4;
-        xp = ucp + 4;
+        int k = pl - 4;
+        unsigned char * xp = ucp + 4;
         if (k > (int)sizeof(ull)) {
             xp += (k - (int)sizeof(ull));
             k = (int)sizeof(ull);
         }
         ull = 0;
-        for (j = 0; j < k; ++j) {
+        for (int j = 0; j < k; ++j) {
             if (j > 0)
                 ull <<= 8;
             ull |= xp[j];
@@ -480,15 +487,14 @@ scsiPrintSeagateCacheLPage(scsi_device * device)
 static void
 scsiPrintSeagateFactoryLPage(scsi_device * device)
 {
-    int k, j, num, pl, pc, len, err, good, bad;
+    int num, pl, pc, len, err, good, bad;
     unsigned char * ucp;
-    unsigned char * xp;
     uint64_t ull;
 
     if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
                             LOG_RESP_LEN, 0))) {
         print_on();
-        pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
+        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
         print_off();
         return;
     }
@@ -550,14 +556,14 @@ scsiPrintSeagateFactoryLPage(scsi_device * device)
             break;
         }
         if (good) {
-            k = pl - 4;
-            xp = ucp + 4;
+            int k = pl - 4;
+            unsigned char * xp = ucp + 4;
             if (k > (int)sizeof(ull)) {
                 xp += (k - (int)sizeof(ull));
                 k = (int)sizeof(ull);
             }
             ull = 0;
-            for (j = 0; j < k; ++j) {
+            for (int j = 0; j < k; ++j) {
                 if (j > 0)
                     ull <<= 8;
                 ull |= xp[j];
@@ -578,10 +584,7 @@ scsiPrintErrorCounterLog(scsi_device * device)
 {
     struct scsiErrorCounter errCounterArr[3];
     struct scsiErrorCounter * ecp;
-    struct scsiNonMediumError nme;
     int found[3] = {0, 0, 0};
-    const char * pageNames[3] = {"read:   ", "write:  ", "verify: "};
-    double processed_gb;
 
     if (gReadECounterLPage && (0 == scsiLogSense(device,
                 READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
@@ -616,10 +619,11 @@ scsiPrintErrorCounterLog(scsi_device * device)
             if (! found[k])
                 continue;
             ecp = &errCounterArr[k];
+            static const char * const pageNames[3] = {"read:   ", "write:  ", "verify: "};
             pout("%s%8" PRIu64 " %8" PRIu64 "  %8" PRIu64 "  %8" PRIu64 "   %8" PRIu64,
                  pageNames[k], ecp->counter[0], ecp->counter[1],
                  ecp->counter[2], ecp->counter[3], ecp->counter[4]);
-            processed_gb = ecp->counter[5] / 1000000000.0;
+            double processed_gb = ecp->counter[5] / 1000000000.0;
             pout("   %12.3f    %8" PRIu64 "\n", processed_gb, ecp->counter[6]);
         }
     }
@@ -627,6 +631,7 @@ scsiPrintErrorCounterLog(scsi_device * device)
         pout("Error Counter logging not supported\n");
     if (gNonMediumELPage && (0 == scsiLogSense(device,
                 NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
+        struct scsiNonMediumError nme;
         scsiDecodeNonMediumErrPage(gBuf, &nme);
         if (nme.gotPC0)
             pout("\nNon-medium error count: %8" PRIu64 "\n", nme.counterPC0);
@@ -718,7 +723,7 @@ static const char * self_test_result[] = {
 static int
 scsiPrintSelfTest(scsi_device * device)
 {
-    int num, k, n, res, err, durationSec;
+    int num, k, err, durationSec;
     int noheader = 1;
     int retval = 0;
     UINT8 * ucp;
@@ -736,7 +741,7 @@ scsiPrintSelfTest(scsi_device * device)
     if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf,
                             LOG_RESP_SELF_TEST_LEN, 0))) {
         print_on();
-        pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
+        pout("%s: Failed [%s]\n", __func__, scsiErrString(err));
         print_off();
         return FAILSMART;
     }
@@ -760,7 +765,7 @@ scsiPrintSelfTest(scsi_device * device)
         int i;
 
         // timestamp in power-on hours (or zero if test in progress)
-        n = (ucp[6] << 8) | ucp[7];
+        int n = (ucp[6] << 8) | ucp[7];
 
         // The spec says "all 20 bytes will be zero if no test" but
         // DG has found otherwise.  So this is a heuristic.
@@ -783,6 +788,7 @@ scsiPrintSelfTest(scsi_device * device)
 
         // check the self-test result nibble, using the self-test results
         // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
+        int res;
         switch ((res = ucp[4] & 0xf)) {
         case 0x3:
             // an unknown error occurred while the device server
@@ -901,7 +907,7 @@ static const char * reassign_status[] = {
 static int
 scsiPrintBackgroundResults(scsi_device * device)
 {
-    int num, j, m, err, pc, pl, truncated;
+    int num, j, m, err, truncated;
     int noheader = 1;
     int firstresult = 1;
     int retval = 0;
@@ -910,7 +916,7 @@ scsiPrintBackgroundResults(scsi_device * device)
     if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
                             LOG_RESP_LONG_LEN, 0))) {
         print_on();
-        pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err));
+        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
         print_off();
         return FAILSMART;
     }
@@ -935,9 +941,9 @@ scsiPrintBackgroundResults(scsi_device * device)
     ucp = gBuf + 4;
     num -= 4;
     while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
+        int pc = (ucp[0] << 8) | ucp[1];
         // pcb = ucp[2];
-        pl = ucp[3] + 4;
+        int pl = ucp[3] + 4;
         switch (pc) {
         case 0:
             if (noheader) {
@@ -1010,14 +1016,14 @@ scsiPrintBackgroundResults(scsi_device * device)
 static int
 scsiPrintSSMedia(scsi_device * device)
 {
-    int num, err, pc, pl, truncated;
+    int num, err, truncated;
     int retval = 0;
     UINT8 * ucp;
 
     if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf,
                             LOG_RESP_LONG_LEN, 0))) {
         print_on();
-        pout("scsiPrintSSMedia Failed [%s]\n", scsiErrString(err));
+        pout("%s: Failed [%s]\n", __func__, scsiErrString(err));
         print_off();
         return FAILSMART;
     }
@@ -1041,9 +1047,9 @@ scsiPrintSSMedia(scsi_device * device)
     ucp = gBuf + 4;
     num -= 4;
     while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
+        int pc = (ucp[0] << 8) | ucp[1];
         // pcb = ucp[2];
-        pl = ucp[3] + 4;
+        int pl = ucp[3] + 4;
         switch (pc) {
         case 1:
             if (pl < 8) {
@@ -1200,7 +1206,6 @@ show_sas_port_param(unsigned char * ucp, int param_len)
     int j, m, n, nphys, t, sz, spld_len;
     unsigned char * vcp;
     uint64_t ull;
-    unsigned int ui;
     char s[64];
 
     sz = sizeof(s);
@@ -1297,6 +1302,7 @@ show_sas_port_param(unsigned char * ucp, int param_len)
         }
         pout("    attached SAS address = 0x%" PRIx64 "\n", ull);
         pout("    attached phy identifier = %d\n", vcp[24]);
+        unsigned int ui;
         ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
         pout("    Invalid DWORD count = %u\n", ui);
         ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
@@ -1306,15 +1312,16 @@ show_sas_port_param(unsigned char * ucp, int param_len)
         ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
         pout("    Phy reset problem = %u\n", ui);
         if (spld_len > 51) {
-            int num_ped, peis;
+            int num_ped;
             unsigned char * xcp;
-            unsigned int pvdt;
 
             num_ped = vcp[51];
             if (num_ped > 0)
                pout("    Phy event descriptors:\n");
             xcp = vcp + 52;
             for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
+                int peis;
+                unsigned int pvdt;
                 peis = xcp[3];
                 ui = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) |
                      xcp[7];
@@ -1330,12 +1337,12 @@ show_sas_port_param(unsigned char * ucp, int param_len)
 static int
 show_protocol_specific_page(unsigned char * resp, int len)
 {
-    int k, num, param_len;
+    int k, num;
     unsigned char * ucp;
 
     num = len - 4;
     for (k = 0, ucp = resp + 4; k < num; ) {
-        param_len = ucp[3] + 4;
+        int param_len = ucp[3] + 4;
         if (6 != (0xf & ucp[4]))
             return 0;   /* only decode SAS log page */
         if (0 == k)
@@ -1359,7 +1366,7 @@ scsiPrintSasPhy(scsi_device * device, int reset)
     if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
                             LOG_RESP_LONG_LEN, 0))) {
         print_on();
-        pout("scsiPrintSasPhy Log Sense Failed [%s]\n\n", scsiErrString(err));
+        pout("%s Log Sense Failed [%s]\n\n", __func__, scsiErrString(err));
         print_off();
         return FAILSMART;
     }
@@ -1381,7 +1388,7 @@ scsiPrintSasPhy(scsi_device * device, int reset)
         if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */,
                                  PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) {
             print_on();
-            pout("scsiPrintSasPhy Log Select (reset) Failed [%s]\n\n",
+            pout("%s Log Select (reset) Failed [%s]\n\n", __func__,
                  scsiErrString(err));
             print_off();
             return FAILSMART;
@@ -1391,7 +1398,7 @@ scsiPrintSasPhy(scsi_device * device, int reset)
 }
 
 
-static const char * peripheral_dt_arr[] = {
+static const char * peripheral_dt_arr[32] = {
         "disk",
         "tape",
         "printer",
@@ -1407,23 +1414,23 @@ static const char * peripheral_dt_arr[] = {
         "storage array",
         "enclosure",
         "simplified disk",
-        "optical card reader"
-        "reserved [0x10]"
-        "object based storage"
-        "automation/driver interface"
-        "security manager device"
-        "host managed zoned block device"
-        "reserved [0x15]"
-        "reserved [0x16]"
-        "reserved [0x17]"
-        "reserved [0x18]"
-        "reserved [0x19]"
-        "reserved [0x1a]"
-        "reserved [0x1b]"
-        "reserved [0x1c]"
-        "reserved [0x1d]"
-        "well known logical unit"
-        "unknown or no device type"
+        "optical card reader",
+        "reserved [0x10]",
+        "object based storage",
+        "automation/driver interface",
+        "security manager device",
+        "host managed zoned block device",
+        "reserved [0x15]",
+        "reserved [0x16]",
+        "reserved [0x17]",
+        "reserved [0x18]",
+        "reserved [0x19]",
+        "reserved [0x1a]",
+        "reserved [0x1b]",
+        "reserved [0x1c]",
+        "reserved [0x1d]",
+        "well known logical unit",
+        "unknown or no device type",
 };
 
 static const char * transport_proto_arr[] = {
@@ -1451,10 +1458,9 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
 {
     char timedatetz[DATEANDEPOCHLEN];
     struct scsi_iec_mode_page iec;
-    int err, iec_err, len, req_len, avail_len, n, scsi_version;
-    int is_tape = 0;
+    int err, iec_err, len, req_len, avail_len, scsi_version;
+    bool is_tape = false;
     int peri_dt = 0;
-    int returnval = 0;
     int transport = -1;
     int form_factor = 0;
     int haw_zbc = 0;
@@ -1480,8 +1486,10 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
     avail_len = gBuf[4] + 5;
     len = (avail_len < req_len) ? avail_len : req_len;
     peri_dt = gBuf[0] & 0x1f;
-    if (peripheral_type)
-        *peripheral_type = peri_dt;
+    *peripheral_type = peri_dt;
+    if ((SCSI_PT_SEQUENTIAL_ACCESS == peri_dt) ||
+        (SCSI_PT_MEDIUM_CHANGER == peri_dt))
+        is_tape = true;
 
     if (len < 36) {
         print_on();
@@ -1521,16 +1529,15 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
 
     protect = gBuf[5] & 0x1;    /* from and including SPC-3 */
 
-    if (! is_tape) {    /* only do this for disks */
+    if (! is_tape) {    /* assume disk if not tape drive (or tape changer) */
         unsigned int lb_size = 0;
         unsigned char lb_prov_resp[8];
-        char cap_str[64];
-        char si_str[64];
         char lb_str[16];
         int lb_per_pb_exp = 0;
         uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp);
 
         if (capacity) {
+            char cap_str[64], si_str[64];
             format_with_thousands_sep(cap_str, sizeof(cap_str), capacity);
             format_capacity(si_str, sizeof(si_str), capacity);
             pout("User Capacity:        %s bytes [%s]\n", cap_str, si_str);
@@ -1548,7 +1555,7 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
                     snprintf(lb_str, sizeof(lb_str) - 1, "%u",
                              (lb_size * (1 << lb_per_pb_exp)));
                     pout("Physical block size:  %s bytes\n", lb_str);
-                    n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3];
+                    int n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3];
                     if (n > 0)  // not common so cut the clutter
                         pout("Lowest aligned LBA:   %d\n", n);
                 }
@@ -1581,16 +1588,31 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
                 lbprz = !! (rc16_12[2] & 0x40);
             }
         }
+        /* Thin Provisioning VPD page renamed Logical Block Provisioning VPD
+         * page in sbc3r25; some fields changed their meaning so that the
+         * new page covered both thin and resource provisioned LUs. */
         if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING,
                                 lb_prov_resp, sizeof(lb_prov_resp))) {
-            int prov_type = lb_prov_resp[6] & 0x7;
+            int prov_type = lb_prov_resp[6] & 0x7;      /* added sbc3r27 */
+            int vpd_lbprz = ((lb_prov_resp[5]  >> 2) & 0x7);  /* sbc4r07 */
 
             if (-1 == lbprz)
-                lbprz = !! (lb_prov_resp[5] & 0x4);
+                lbprz = vpd_lbprz;
+            else if ((0 == vpd_lbprz) && (1 == lbprz))
+                ;  /* vpd_lbprz introduced in sbc3r27, expanded in sbc4r07 */
+            else
+                lbprz = vpd_lbprz;
             switch (prov_type) {
             case 0:
-                pout("LB provisioning type: unreported, LBPME=%d, LBPRZ=%d\n",
-                     lbpme, lbprz);
+                if (lbpme <= 0) {
+                    pout("LU is fully provisioned");
+                    if (lbprz)
+                        pout(" [LBPRZ=%d]\n", lbprz);
+                    else
+                        pout("\n");
+                } else
+                     pout("LB provisioning type: not reported [LBPME=1, "
+                          "LBPRZ=%d]\n", lbprz);
                 break;
             case 1:
                 pout("LU is resource provisioned, LBPRZ=%d\n", lbprz);
@@ -1603,8 +1625,11 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
                      prov_type, lbprz);
                 break;
             }
-        } else if (1 == lbpme)
+        } else if (1 == lbpme) {
+            if (scsi_debugmode > 0)
+                pout("rcap_16 sets LBPME but no LB provisioning VPD page\n");
             pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz);
+        }
 
         int rpm = scsiGetRPM(device, modese_len, &form_factor, &haw_zbc);
         if (rpm >= 0) {
@@ -1709,9 +1734,6 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
     dateandtimezone(timedatetz);
     pout("Local Time is:        %s\n", timedatetz);
 
-    if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
-        (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
-        is_tape = 1;
     // See if unit accepts SCSI commmands from us
     if ((err = scsiTestUnitReady(device))) {
         if (SIMPLE_ERR_NOT_READY == err) {
@@ -1721,11 +1743,14 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
             else
                 pout("device is NOT READY (e.g. no tape)\n");
             print_off();
-         } else if (SIMPLE_ERR_NO_MEDIUM == err) {
+        } else if (SIMPLE_ERR_NO_MEDIUM == err) {
             print_on();
-            pout("NO MEDIUM present on device\n");
+            if (is_tape)
+                pout("NO tape present in drive\n");
+            else
+                pout("NO MEDIUM present in device\n");
             print_off();
-         } else if (SIMPLE_ERR_BECOMING_READY == err) {
+        } else if (SIMPLE_ERR_BECOMING_READY == err) {
             print_on();
             pout("device becoming ready (wait)\n");
             print_off();
@@ -1734,7 +1759,11 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
             pout("device Test Unit Ready  [%s]\n", scsiErrString(err));
             print_off();
         }
-        failuretest(MANDATORY_CMD, returnval|=FAILID);
+        if (! is_tape) {
+            int returnval = 0; // TODO: exit with FAILID if failuretest returns
+
+            failuretest(MANDATORY_CMD, returnval|=FAILID);
+        }
     }
 
     if (iec_err) {
@@ -1836,22 +1865,21 @@ scsiSmartDisable(scsi_device * device)
 static void
 scsiPrintTemp(scsi_device * device)
 {
-    UINT8 temp = 0;
-    UINT8 trip = 0;
+    UINT8 temp = 255;
+    UINT8 trip = 255;
 
     if (scsiGetTemp(device, &temp, &trip))
         return;
 
-    if (temp) {
-        if (255 != temp)
-            pout("Current Drive Temperature:     %d C\n", temp);
-        else
-            pout("Current Drive Temperature:     <not available>\n");
-    }
-    if (trip)
+    if (255 == temp)
+        pout("Current Drive Temperature:     <not available>\n");
+    else
+        pout("Current Drive Temperature:     %d C\n", temp);
+    if (255 == trip)
+        pout("Drive Trip Temperature:        <not available>\n");
+    else
         pout("Drive Trip Temperature:        %d C\n", trip);
-    if (temp || trip)
-        pout("\n");
+    pout("\n");
 }
 
 /* Main entry point used by smartctl command. Return 0 for success */
@@ -1863,6 +1891,8 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
     int returnval = 0;
     int res, durationSec;
     struct scsi_sense_disect sense_info;
+    bool is_disk;
+    bool is_tape;
 
     bool any_output = options.drive_info;
 
@@ -1880,115 +1910,108 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
             failuretest(MANDATORY_CMD, returnval |= FAILID);
         any_output = true;
     }
+    is_disk = (SCSI_PT_DIRECT_ACCESS == peripheral_type);
+    is_tape = ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
+               (SCSI_PT_MEDIUM_CHANGER == peripheral_type));
+
+    short int wce = -1, rcd = -1;
+    // Print read look-ahead status for disks
+    if (options.get_rcd || options.get_wce) {
+        if (is_disk) {
+            res = scsiGetSetCache(device, modese_len, &wce, &rcd);
+            if (options.get_rcd)
+                pout("Read Cache is:        %s\n",
+                     res ? "Unavailable" : // error
+                     rcd ? "Disabled" : "Enabled");
+            if (options.get_wce)
+                pout("Writeback Cache is:   %s\n",
+                     res ? "Unavailable" : // error
+                     !wce ? "Disabled" : "Enabled");
+        }
+    } else
+        any_output = true;
 
-  // Print read look-ahead status for disks
-  short int wce = -1, rcd = -1;
-  if (options.get_rcd || options.get_wce) {
-    if (SCSI_PT_DIRECT_ACCESS == peripheral_type)
-       res = scsiGetSetCache(device, modese_len, &wce, &rcd);
-    else
-       res = -1; // fetch for disks only
-    any_output = true;
-  }
-
-  if (options.get_rcd) {
-    pout("Read Cache is:        %s\n",
-      res ? "Unavailable" : // error
-      rcd ? "Disabled" : "Enabled");
-   }
-
-  if (options.get_wce) {
-    pout("Writeback Cache is:   %s\n",
-      res ? "Unavailable" : // error
-      !wce ? "Disabled" : "Enabled");
-   }
-   if (options.drive_info)
-     pout("\n");
-
-  // START OF THE ENABLE/DISABLE SECTION OF THE CODE
-  if (   options.smart_disable           || options.smart_enable
-      || options.smart_auto_save_disable || options.smart_auto_save_enable)
-    pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
+    if (options.drive_info)
+        pout("\n");
+
+    // START OF THE ENABLE/DISABLE SECTION OF THE CODE
+    if (options.smart_disable           || options.smart_enable ||
+        options.smart_auto_save_disable || options.smart_auto_save_enable)
+        pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
 
     if (options.smart_enable) {
         if (scsiSmartEnable(device))
             failuretest(MANDATORY_CMD, returnval |= FAILSMART);
-        any_output = true;
+            any_output = true;
     }
 
     if (options.smart_disable) {
         if (scsiSmartDisable(device))
             failuretest(MANDATORY_CMD,returnval |= FAILSMART);
-        any_output = true;
+            any_output = true;
     }
 
     if (options.smart_auto_save_enable) {
-      if (scsiSetControlGLTSD(device, 0, modese_len)) {
-        pout("Enable autosave (clear GLTSD bit) failed\n");
-        failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
-      }
-      else {
-         pout("Autosave enabled (GLTSD bit set).\n");
-      }
-      any_output = true;
+        if (scsiSetControlGLTSD(device, 0, modese_len)) {
+            pout("Enable autosave (clear GLTSD bit) failed\n");
+            failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+        } else
+            pout("Autosave enabled (GLTSD bit cleared).\n");
+        any_output = true;
     }
 
     // Enable/Disable write cache
-    if (options.set_wce && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
-      short int enable = wce = (options.set_wce > 0);
-      rcd = -1;
-      if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
-          pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"),
-               device->get_errmsg());
-          failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
-      }
-      else
-        pout("Write cache %sabled\n", (enable ? "en" : "dis"));
-      any_output = true;
+    if (options.set_wce && is_disk) {
+        short int enable = wce = (options.set_wce > 0);
+
+        rcd = -1;
+        if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
+            pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"),
+                 device->get_errmsg());
+            failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+        } else
+            pout("Write cache %sabled\n", (enable ? "en" : "dis"));
+        any_output = true;
     }
 
     // Enable/Disable read cache
-    if (options.set_rcd && SCSI_PT_DIRECT_ACCESS == peripheral_type) {
-      short int enable =  (options.set_rcd > 0);
-      rcd = !enable;
-      wce = -1;
-      if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
-          pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"),
+    if (options.set_rcd && is_disk) {
+        short int enable =  (options.set_rcd > 0);
+
+        rcd = !enable;
+        wce = -1;
+        if (scsiGetSetCache(device, modese_len, &wce, &rcd)) {
+            pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"),
                 device->get_errmsg());
-          failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
-      }
-      else
-        pout("Read cache %sabled\n", (enable ? "en" : "dis"));
-      any_output = true;
+            failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+        } else
+            pout("Read cache %sabled\n", (enable ? "en" : "dis"));
+        any_output = true;
     }
 
     if (options.smart_auto_save_disable) {
-      if (scsiSetControlGLTSD(device, 1, modese_len)) {
-        pout("Disable autosave (set GLTSD bit) failed\n");
-        failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
-      }
-      else {
-         pout("Autosave disabled (GLTSD bit cleared).\n");
-      }
-      any_output = true;
-    }
-  if (   options.smart_disable           || options.smart_enable
-      || options.smart_auto_save_disable || options.smart_auto_save_enable)
-    pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE
+        if (scsiSetControlGLTSD(device, 1, modese_len)) {
+            pout("Disable autosave (set GLTSD bit) failed\n");
+            failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+        } else
+            pout("Autosave disabled (GLTSD bit set).\n");
+        any_output = true;
+    }
+    if (options.smart_disable           || options.smart_enable ||
+        options.smart_auto_save_disable || options.smart_auto_save_enable)
+        pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE
 
     // START OF READ-ONLY OPTIONS APART FROM -V and -i
-    if (    options.smart_check_status  || options.smart_ss_media_log
-           || options.smart_vendor_attrib || options.smart_error_log
-           || options.smart_selftest_log  || options.smart_vendor_attrib
-           || options.smart_background_log || options.sasphy
-         )
-    pout("=== START OF READ SMART DATA SECTION ===\n");
+    if (options.smart_check_status  || options.smart_ss_media_log ||
+        options.smart_vendor_attrib || options.smart_error_log ||
+        options.smart_selftest_log  || options.smart_background_log ||
+        options.sasphy)
+        pout("=== START OF READ SMART DATA SECTION ===\n");
 
     if (options.smart_check_status) {
         scsiGetSupportedLogPages(device);
         checkedSupportedLogPages = 1;
-        if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
-            (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
+        if (is_tape) {
             if (gTapeAlertsLPage) {
                 if (options.drive_info)
                     pout("TapeAlert Supported\n");
@@ -2008,7 +2031,7 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
         any_output = true;
     }
 
-    if (options.smart_ss_media_log) {
+    if (is_disk && options.smart_ss_media_log) {
         if (! checkedSupportedLogPages)
             scsiGetSupportedLogPages(device);
         res = 0;
@@ -2021,12 +2044,11 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
     if (options.smart_vendor_attrib) {
         if (! checkedSupportedLogPages)
             scsiGetSupportedLogPages(device);
-        if (gTempLPage) {
+        if (gTempLPage)
             scsiPrintTemp(device);
-        }
         if (gStartStopLPage)
             scsiGetStartStopData(device);
-        if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
+        if (is_disk) {
             scsiPrintGrownDefectListLen(device);
             if (gSeagateCacheLPage)
                 scsiPrintSeagateCacheLPage(device);
@@ -2058,7 +2080,7 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
             failuretest(OPTIONAL_CMD, returnval|=res);
         any_output = true;
     }
-    if (options.smart_background_log) {
+    if (options.smart_background_log && is_disk) {
         if (! checkedSupportedLogPages)
             scsiGetSupportedLogPages(device);
         res = 0;
@@ -2086,21 +2108,19 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
     }
     // check if another test is running
     if (options.smart_short_selftest || options.smart_extend_selftest) {
-      if (!scsiRequestSense(device, &sense_info) &&
+        if (!scsiRequestSense(device, &sense_info) &&
             (sense_info.asc == 0x04 && sense_info.ascq == 0x09)) {
-         if (!options.smart_selftest_force) {
-           pout("Can't start self-test without aborting current test");
-           if (sense_info.progress != -1) {
-             pout(" (%d%% remaining)",
-                  100 - sense_info.progress * 100 / 65535);
-           }
-           pout(",\nadd '-t force' option to override, or run 'smartctl -X' "
-                "to abort test.\n");
-            return -1;
-         }
-         else
-            scsiSmartSelfTestAbort(device);
-       }
+            if (!options.smart_selftest_force) {
+                pout("Can't start self-test without aborting current test");
+                if (sense_info.progress != -1)
+                    pout(" (%d%% remaining)",
+                         100 - sense_info.progress * 100 / 65535);
+                pout(",\nadd '-t force' option to override, or run "
+                     "'smartctl -X' to abort test.\n");
+                return -1;
+            } else
+                scsiSmartSelfTestAbort(device);
+        }
     }
     if (options.smart_short_selftest) {
         if (scsiSmartShortSelfTest(device))
@@ -2143,8 +2163,8 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
     }
 
     if (!any_output)
-      pout("SCSI device successfully opened\n\n"
-           "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n");
+        pout("SCSI device successfully opened\n\nUse 'smartctl -a' (or '-x') "
+             "to print SMART (and more) information\n\n");
 
     return returnval;
 }
index fad594de9c197f48430f03b99cee14013642ffee..29632ada9daa7df63359d708948bee26f0cf68a3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * scsiprint.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
@@ -29,7 +29,7 @@
 #ifndef SCSI_PRINT_H_
 #define SCSI_PRINT_H_
 
-#define SCSIPRINT_H_CVSID "$Id: scsiprint.h 3776 2013-02-17 04:25:42Z dpgilbert $\n"
+#define SCSIPRINT_H_CVSID "$Id: scsiprint.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 // Options for scsiPrintMain
 struct scsi_print_options
index aa79757402535d354ba834426fd3ca24a95dd0a2..943b6221d7b045730947d156903af031a0285a34 100644 (file)
@@ -1,8 +1,8 @@
 .ig
-Copyright (C) 2002-10 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-Copyright (C) 2004-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+Copyright (C) 2002-10 Bruce Allen
+Copyright (C) 2004-16 Christian Franke
 
-$Id: smartctl.8.in 3965 2014-07-20 14:46:41Z chrfranke $
+$Id: smartctl.8.in 4311 2016-04-27 21:03:01Z chrfranke $
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -18,16 +18,13 @@ Research Center), Jack Baskin School of Engineering, University of
 California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 
 ..
-.TH SMARTCTL 8 CURRENT_SVN_DATE CURRENT_SVN_VERSION CURRENT_SVN_DATE
+.TH SMARTCTL 8 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools"
 .SH NAME
 \fBsmartctl\fP \- Control and Monitor Utility for SMART Disks
 
 .SH SYNOPSIS
 .B smartctl [options] device
 
-.SH PACKAGE VERSION
-CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
-
 .SH DESCRIPTION
 .\" %IF NOT OS ALL
 .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools.
@@ -55,11 +52,9 @@ option (for more information see the section on "ATA, SCSI command sets
 and SAT" below). Device paths are as follows:
 .\" %IF OS Linux
 .IP \fBLINUX\fP: 9
-Use the forms \fB"/dev/hd[a\-t]"\fP for IDE/ATA devices, and
-\fB"/dev/sd[a\-z]"\fP for SCSI devices. For SCSI Tape Drives and
-Changers with TapeAlert support use the devices \fB"/dev/nst*"\fP and
-\fB"/dev/sg*"\fP.  For SATA disks accessed with libata, use
-\fB"/dev/sd[a\-z]"\fP and append \fB"\-d ata"\fP. For disks behind
+Use the forms \fB"/dev/sd[a\-z]"\fP for ATA/SATA and SCSI/SAS devices.
+For SCSI Tape Drives and Changers with TapeAlert support use the
+devices \fB"/dev/nst*"\fP and \fB"/dev/sg*"\fP.  For disks behind
 3ware controllers you may need \fB"/dev/sd[a\-z]"\fP or
 \fB"/dev/twe[0\-9]"\fP, \fB"/dev/twa[0\-9]"\fP or \fB"/dev/twl[0\-9]"\fP: see details
 below. For disks behind HighPoint RocketRAID controllers you may need
@@ -74,6 +69,8 @@ the hpahcisr and hpsa drivers, the device nodes you need are \fB"/dev/sg[0\-9]*"
 ("lsscsi \-g" is helpful in determining which scsi generic device node corresponds
 to which device.)  Use the nodes corresponding to the RAID controllers,
 not the nodes corresponding to logical drives.  See the \fB\-d\fP option below, as well.
+Use the forms \fB"/dev/nvme[0\-9]"\fP (broadcast namespace) or
+\fB"/dev/nvme[0\-9]n[1\-9]"\fP (specific namespace 1\-9) for NVMe devices.
 .\" %ENDIF OS Linux
 .\" %IF OS Darwin
 .IP \fBDARWIN\fP: 9
@@ -135,8 +132,15 @@ For disks behind an Intel ICHxR controller with RST driver use
 \fB"/dev/csmi[0\-9],N"\fP where N specifies the port behind the logical
 scsi controller "\\\\.\\Scsi[0\-9]:".
 
-[NEW EXPERIMENTAL SMARTCTL FEATURE] For SATA or SAS disks behind an Areca
-controller use \fB"/dev/arcmsr[0\-9]"\fP, see \'\-d areca,N[/E]\' below.
+For SATA or SAS disks behind an Areca controller use
+\fB"/dev/arcmsr[0\-9]"\fP, see \'\-d areca,N[/E]\' below.
+
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+Use the forms \fB"/dev/nvme[0\-9]"\fP (broadcast namespace) or
+\fB"/dev/nvme[0\-9]n[1\-9]"\fP (specific namespace 1\-9) for first,
+second, ..., NVMe device.
+Alternatively use the forms \fB"/dev/nvmes[0\-9][n[1\-9]]"\fP for NVMe devices
+behind the logical scsi controller "\\\\.\\Scsi[0\-9]:".
 
 The prefix \fB"/dev/"\fP is optional.
 .\" %ENDIF OS Windows Cygwin
@@ -186,6 +190,13 @@ than the potential maximum drive capacity.)  Indicates if the drive is
 in the smartmontools database (see \'\-v\' options below).  If so, the
 drive model family may also be printed. If \'\-n\' (see below) is
 specified, the power mode of the drive is printed.
+.\" %IF OS FreeBSD Linux Windows Cygwin
+
+[NVMe] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+For NVMe devices the information is obtained from the Identify Controller
+and the Identify Namespace data structure.
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .TP
 .B \-\-identify[=[w][nvb]]
 [ATA only] Prints an annotated table of the IDENTIFY DEVICE data.
@@ -211,6 +222,12 @@ and for SCSI, this is equivalent to
 .nf
 \'\-H \-i \-A \-l error \-l selftest\'.
 .fi
+.\" %IF OS FreeBSD Linux Windows Cygwin
+For NVMe, this is equivalent to
+.nf
+\'\-H \-i \-c \-A \-l error'.
+.fi
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 Note that for ATA disks this does \fBnot\fP enable the non-SMART options
 and the SMART options which require support for 48-bit ATA commands.
 .TP
@@ -225,6 +242,12 @@ and for SCSI, this is equivalent to
 .nf
 \'\-H \-i \-A \-l error \-l selftest \-l background \-l sasphy\'.
 .fi
+.\" %IF OS FreeBSD Linux Windows Cygwin
+For NVMe, this is equivalent to
+.nf
+\'\-H \-i \-c \-A \-l error'.
+.fi
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .TP
 .B \-\-scan
 Scans for devices and prints each device name, device type and protocol
@@ -244,6 +267,10 @@ For example:
 .nf
 smartctl \-\-scan\-open \-\- \-a \-W 4,45,50 \-m admin@work > smartd.conf
 .fi
+
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+Multiple \'\-d TYPE\' options may be specified with \'\-\-scan[\-open]\'
+to combine the scan results of more than one TYPE.
 .TP
 .B \-g NAME, \-\-get=NAME
 Get non-SMART device settings.  See \'\-s, \-\-set\' below for further info.
@@ -266,7 +293,7 @@ which failed either now or in the past.
 
 .I silent
 \- print no output.  The only way to learn about what was found is to
-use the exit status of \fBsmartctl\fP (see RETURN VALUES below).
+use the exit status of \fBsmartctl\fP (see EXIT STATUS below).
 
 .I noserial
 \- Do not print the serial number of the device.
@@ -297,6 +324,19 @@ from issuing SCSI commands to an ATA device.
 \fBsmartctl\fP
 from issuing ATA commands to a SCSI device.
 
+.\" %ENDIF NOT OS Darwin
+.\" %IF OS FreeBSD Linux Windows Cygwin
+.I nvme[,NSID]
+\- [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+the device type is NVM Express (NVMe).
+The optional parameter NSID specifies the namespace id (in hex) passed
+to the driver.
+Use 0xffffffff for the broadcast namespace id.
+The default for NSID is the namespace id addressed by the device name.
+
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
+.\" %IF NOT OS Darwin
 .I sat[,auto][,N]
 \- the device type is SCSI to ATA Translation (SAT).
 This is for ATA disks that have a SCSI to ATA Translation (SAT) Layer
@@ -331,13 +371,17 @@ CAUTION: Specifying \',x\' for a device which does not support it results
 in I/O errors and may disconnect the drive.  The same applies if the specified
 PORT does not exist or is not connected to a disk.
 
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
 The Prolific PL2507/3507 USB bridges with older firmware support a pass-through
 command similar to JMicron and work with \'\-d usbjmicron,0\'.
 Newer Prolific firmware requires a modified command which can be selected by
 \'\-d usbjmicron,p\'.
 Note that this does not yet support the SMART status command.
 
+.I usbprolific
+\- [NEW EXPERIMENTAL SMARTCTL FEATURE]
+this device type is for SATA disks that are behind a Prolific PL2571/2771/2773/2775
+USB to SATA bridge.
+
 .I usbsunplus
 \- this device type is for SATA disks that are behind a SunplusIT USB to SATA
 bridge.
@@ -372,24 +416,32 @@ For PERC2/3/4 controllers: \fBmegadevN\fP
 .br
 For PERC5/6 controllers: \fBmegaraid_sas_ioctlN\fP
 
+.\" %ENDIF OS Linux
+.\" %IF OS Linux Windows Cygwin
 .I aacraid,H,L,ID
-\- [Linux only] [NEW EXPERIMENTAL SMARTCTL FEATURE]
+\- [Linux, Windows and Cygwin only] [NEW EXPERIMENTAL SMARTCTL FEATURE]
 the device consists of one or more SCSI/SAS disks connected to an AacRaid controller.
 The non-negative integers H,L,ID (Host number, Lun, ID) denote which disk
 on the controller is monitored.
 Use syntax such as:
 .nf
-\fBsmartctl \-a \-d aacraid,0,0,66 /dev/sda\fP
+\fBsmartctl \-a \-d aacraid,0,0,2 /dev/sda\fP
 .fi
 .nf
-\fBsmartctl \-a \-d aacraid,0,0,66 /dev/sdb\fP
+\fBsmartctl \-a \-d aacraid,1,0,4 /dev/sdb\fP
 .fi
-The L and ID numbers of a disk can be found in /proc/scsi/scsi
 
-The following entry in /proc/devices must exist: \fBaac\fP.
+.\" %ENDIF OS Linux Windows Cygwin
+.\" %IF OS Linux
+On Linux, the following entry in /proc/devices must exist: \fBaac\fP.
 Character device nodes /dev/aacH (H=Host number) are created if required.
 
 .\" %ENDIF OS Linux
+.\" %IF OS Windows Cygwin
+On Windows, the device name parameter /dev/sdX is ignored if \'-d aacraid\'
+is specified.
+
+.\" %ENDIF OS Windows Cygwin
 .\" %IF OS FreeBSD Linux
 .I 3ware,N
 \- [FreeBSD and Linux only] the device consists of one or more ATA disks
@@ -495,7 +547,7 @@ On FreeBSD use syntax such as:
 .fi
 .\" %ENDIF OS FreeBSD
 .\" %IF OS Windows Cygwin
-[NEW EXPERIMENTAL SMARTCTL FEATURE] On Windows and Cygwin use syntax such as:
+On Windows and Cygwin use syntax such as:
 .nf
 \fBsmartctl \-a \-d areca,2 /dev/arcmsr0\fP
 .fi
@@ -526,8 +578,8 @@ later.  Lower-numbered firmware versions will give (harmless) SCSI
 error messages and no SMART information.
 
 .I areca,N/E
-\- [FreeBSD, Linux, Windows and Cygwin only] [NEW EXPERIMENTAL SMARTCTL FEATURE] the
-device consists of one or more SATA or SAS disks connected to an Areca SAS RAID controller.
+\- [FreeBSD, Linux, Windows and Cygwin only] the device consists of one
+or more SATA or SAS disks connected to an Areca SAS RAID controller.
 The integer N (range 1 to 128) denotes the channel (slot) and E (range
 1 to 8) denotes the enclosure.
 Important: This requires Areca SAS controller firmware version 1.51 or later.
@@ -670,6 +722,13 @@ shows the SCSI commands in hex and the corresponding status. Invoking
 it a second time adds a hex listing of the first 64 bytes of data send to, 
 or received from the device.
 
+.\" %IF OS FreeBSD Linux Windows Cygwin
+.I nvmeioctl
+\- [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+report only ioctl() transactions with NVMe devices.
+
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 Any argument may include a positive integer to specify the level of detail
 that should be reported.  The argument should be followed by a comma then
 the integer with no spaces.  For example, 
@@ -689,7 +748,7 @@ behaviour. This is does not work for SCSI devices yet.
 checks when the device is in a low-power mode. It may be used to prevent
 a disk from being spun-up by \fBsmartctl\fP. The power mode is ignored by
 default.  A nonzero exit status is returned if the device is in one of the
-specified low-power modes (see RETURN VALUES below).
+specified low-power modes (see EXIT STATUS below).
 
 Note: If this option is used it may also be necessary to specify the device
 type with the \'\-d\' option.  Otherwise the device may spin up due to
@@ -885,13 +944,11 @@ the drive.  The setting of the standby timer is not affected.
 The write cache is usually enabled by default.
 
 .I wcache[,on|off]
-\- [SCSI] [NEW EXPERIMENTAL SMARTCTL FEATURE]
-Gets/sets the \'Write Cache Enable\' (WCE) bit (if supported).
+\- [SCSI] Gets/sets the \'Write Cache Enable\' (WCE) bit (if supported).
 The write cache is usually enabled by default.
 
 .I wcreorder[,on|off]
-\- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE]
-Gets/sets Write Cache Reordering.
+\- [ATA only] Gets/sets Write Cache Reordering.
 If it is disabled (off), disk write scheduling is executed on a 
 first-in-first-out (FIFO) basis. If Write Cache Reordering is enabled (on),
 then disk write scheduling may be reordered by the drive. If write cache is
@@ -901,21 +958,15 @@ The state of Write Cache Reordering has no effect on either NCQ or LCQ queued
 commands.
 
 .I rcache[,on|off]
-\- [SCSI only] [NEW EXPERIMENTAL SMARTCTL FEATURE]
-Gets/sets the \'Read Cache Disable\' (RCE) bit. \'Off\' value disables read cache
-(if supported).
+\- [SCSI only] Gets/sets the \'Read Cache Disable\' (RCE) bit.
+\'Off\' value disables read cache (if supported).
 The read cache is usually enabled by default.
 
 .TP
 .B SMART READ AND DISPLAY DATA OPTIONS:
 .TP
 .B \-H, \-\-health
-Check: Ask the device to report its SMART health status or pending
-TapeAlert messages.  SMART status is based on
-information that it has gathered from online and offline
-tests, which were used to determine/update its
-SMART vendor-specific Attribute values. TapeAlert status is obtained
-by reading the TapeAlert log page.
+Prints the health status of the device or pending TapeAlert messages.
 
 If the device reports failing health status, this means
 .B either
@@ -924,9 +975,36 @@ that the device has already failed,
 that it is predicting its own failure within the next 24 hours.  If
 this happens, use the \'\-a\' option to get more information, and
 .B get your data off the disk and to someplace safe as soon as you can.
+
+[ATA] Health status is obtained by checking the (boolean) result returned
+by the SMART RETURN STATUS command.
+The return value of this ATA command may be unknown due to limitations or
+bugs in some layer (e.g. RAID controller or USB bridge firmware) between
+disk and operating system.
+In this case, \fBsmartctl\fP prints a warning and checks whether any
+Prefailure SMART Attribute value is less than or equal to its threshold
+(see \'\-A\' below).
+
+[SCSI] Health status is obtained by checking the Additional Sense Code
+(ASC) and Additional Sense Code Qualifier (ASCQ) from Informal Exceptions
+(IE) log page (if supported) and/or from SCSI sense data.
+
+[SCSI tape drive or changer] TapeAlert status is obtained by reading the
+TapeAlert log page.
+Please note that the TapeAlert log page flags are cleared for the initiator
+when the page is read.
+This means that each alert condition is reported only once by \fBsmartctl\fP
+for each initiator for each activation of the condition.
+.\" %IF OS FreeBSD Linux Windows Cygwin
+
+[NVMe] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+NVMe status is obtained by reading the "Critical Warning" byte from
+the SMART/Health Information log.
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .TP
 .B \-c, \-\-capabilities
-[ATA only] Prints only the generic SMART capabilities.  These
+[ATA] Prints only the generic SMART capabilities.  These
 show what SMART features are implemented and how the device will
 respond to some of the different SMART commands.  For example it
 shows if the device logs errors, if it supports offline surface
@@ -941,6 +1019,13 @@ then the time may jump to a larger value and then count down as the
 Immediate Offline Test is carried out.  Please see REFERENCES below
 for further information about the the flags and capabilities described
 by this option.
+.\" %IF OS FreeBSD Linux Windows Cygwin
+
+[NVMe] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+Prints various NVMe device capabilities obtained from the Identify Controller
+and the Identify Namespace data structure.
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .TP
 .B \-A, \-\-attributes
 [ATA] Prints only the vendor specific SMART Attributes.  The Attributes
@@ -1033,6 +1118,13 @@ the drive is already in the smartmontools drive database.
 and start-stop cycle counter log pages. Certain vendor specific
 attributes are listed if recognised. The attributes are output in a
 relatively free format (compared with ATA disk attributes).
+.\" %IF OS FreeBSD Linux Windows Cygwin
+
+[NVMe] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+For NVMe devices the attributes are obtained from the SMART/Health
+Information log.
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .TP
 .B \-f FORMAT, \-\-format=FORMAT
 [ATA only] Selects the output format of the attributes:
@@ -1139,6 +1231,17 @@ receives a command which is not implemented or is not valid.
 \- [SCSI] prints the error counter log pages for reads, write and verifies.
 The verify row is only output if it has an element other than zero.
 
+.\" %IF OS FreeBSD Linux Windows Cygwin
+.I error[,NUM]
+\- [NVMe] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+prints the NVMe Error Information log.
+Only the 16 most recent log entries are printed by default.
+This number can be changed by the optional parameter NUM.
+The maximum number of log entries is vendor specific
+(in the range from 1 to 256 inclusive).
+
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .I xerror[,NUM][,error]
 \- [ATA only] prints the Extended Comprehensive SMART error log
 (General Purpose Log address 0x03).  Unlike the Summary SMART error
@@ -1326,6 +1429,18 @@ This command:
 writes a binary representation of the one sector log 0x11
 (SATA Phy Event Counters) to file log.bin.
 
+.\" %IF OS FreeBSD Linux Windows Cygwin
+.I nvmelog,PAGE,SIZE
+\- [NVMe only] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+prints a hex dump of the first SIZE bytes from the NVMe log with
+identifier PAGE.
+PAGE is a hexadecimal number in the range from 0x1 to 0xff.
+SIZE is a hexadecimal number in the range from 0x4 to 0x4000 (16 KiB).
+\fBWARNING: Do not specify the identifier of an unknown log page.
+Reading a log page may have undesirable side effects.\fP
+
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .I ssd
 \- [ATA] prints the Solid State Device Statistics log page.
 This has the same effect as \'\-l devstat,7\', see above.
@@ -1658,6 +1773,7 @@ If
 .\"! \fBEXEDIR/drivedb.h\fP
 .\" %ENDIF OS Windows
 is present, the contents of this file is used instead of the built in table.
+.\" %IF ENABLE_UPDATE_SMART_DRIVEDB
 
 Run
 .\" %IF NOT OS Windows
@@ -1670,6 +1786,7 @@ Run
 .\"! \fBEXEDIR/update-smart-drivedb.exe\fP
 .\" %ENDIF OS Windows
 to update this file from the smartmontools SVN repository.
+.\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB
 .\" %ENDIF ENABLE_DRIVEDB
 
 The database files use the same C/C++ syntax that is used to initialize
@@ -1791,25 +1908,25 @@ a disk can be specified by N\-\fBmax\fP.
 
 For example the commands:
 .nf
-  smartctl \-t select,10\-20 /dev/hda
-  smartctl \-t select,10+11 /dev/hda
+  smartctl \-t select,10\-20 /dev/sda
+  smartctl \-t select,10+11 /dev/sda
 .fi
 both runs a self test on one span consisting of LBAs ten to twenty
 (inclusive). The command:
 .nf
-  smartctl \-t select,100000000\-max /dev/hda
+  smartctl \-t select,100000000\-max /dev/sda
 .fi
 run a self test from LBA 100000000 up to the end of the disk.
 The \'\-t\' option can be given up to five times, to test
 up to five spans.  For example the command:
 .nf
-  smartctl \-t select,0\-100 \-t select,1000\-2000 /dev/hda
+  smartctl \-t select,0\-100 \-t select,1000\-2000 /dev/sda
 .fi
 runs a self test on two spans.  The first span consists of 101 LBAs
 and the second span consists of 1001 LBAs.  Note that the spans can
 overlap partially or completely, for example:
 .nf
-  smartctl \-t select,0\-10 \-t select,5\-15 \-t select,10\-20 /dev/hda
+  smartctl \-t select,0\-10 \-t select,5\-15 \-t select,10\-20 /dev/sda
 .fi
 The results of the selective self-test can be obtained (both during
 and after the test) by printing the SMART self-test log, using the
@@ -1836,15 +1953,15 @@ argument.
 
 For example the commands:
 .nf
-  smartctl \-t select,10\-20 /dev/hda
-  smartctl \-t select,redo /dev/hda
-  smartctl \-t select,redo+20 /dev/hda
+  smartctl \-t select,10\-20 /dev/sda
+  smartctl \-t select,redo /dev/sda
+  smartctl \-t select,redo+20 /dev/sda
 .fi
 have the same effect as:
 .nf
-  smartctl \-t select,10\-20 /dev/hda
-  smartctl \-t select,10\-20 /dev/hda
-  smartctl \-t select,10\-29 /dev/hda
+  smartctl \-t select,10\-20 /dev/sda
+  smartctl \-t select,10\-20 /dev/sda
+  smartctl \-t select,10\-29 /dev/sda
 .fi
 
 .I select,next[+SIZE]
@@ -1855,15 +1972,15 @@ optional +SIZE argument.
 
 For example the commands:
 .nf
-  smartctl \-t select,0\-999 /dev/hda
-  smartctl \-t select,next /dev/hda
-  smartctl \-t select,next+2000 /dev/hda
+  smartctl \-t select,0\-999 /dev/sda
+  smartctl \-t select,next /dev/sda
+  smartctl \-t select,next+2000 /dev/sda
 .fi
 have the same effect as:
 .nf
-  smartctl \-t select,0\-999 /dev/hda
-  smartctl \-t select,1000\-1999 /dev/hda
-  smartctl \-t select,2000\-3999 /dev/hda
+  smartctl \-t select,0\-999 /dev/sda
+  smartctl \-t select,1000\-1999 /dev/sda
+  smartctl \-t select,2000\-3999 /dev/sda
 .fi
 
 If the last test ended at the last LBA of the disk, the new range starts
@@ -1986,62 +2103,55 @@ browser.
 
 .SH EXAMPLES
 .nf
-.B smartctl \-a /dev/hda
+.B smartctl \-a /dev/sda
 .fi
-Print a large amount of SMART information for drive /dev/hda which is
-typically an ATA (IDE) or SATA disk in Linux.
+Print a large amount of SMART information for drive /dev/sda .
 .PP
 .nf
-.B smartctl \-a /dev/sdb
+.B smartctl \-s off /dev/sdd
 .fi
-Print a large amount of SMART information for drive /dev/sdb . This may
-be a SCSI disk or an ATA (SATA) disk.
+Disable SMART monitoring and data log collection on drive /dev/sdd .
 .PP
 .nf
-.B smartctl \-s off /dev/hdd
+.B smartctl \-\-smart=on \-\-offlineauto=on \-\-saveauto=on /dev/sda
 .fi
-Disable SMART monitoring and data log collection on drive /dev/hdd .
-.PP
-.nf
-.B smartctl \-\-smart=on \-\-offlineauto=on \-\-saveauto=on /dev/hda
-.fi
-Enable SMART on drive /dev/hda, enable automatic offline
+Enable SMART on drive /dev/sda, enable automatic offline
 testing every four hours, and enable autosaving of
 SMART Attributes.  This is a good start-up line for your system\'s
 init files.  You can issue this command on a running system.
 .PP
 .nf
-.B smartctl \-t long /dev/hdc
+.B smartctl \-t long /dev/sdc
 .fi
-Begin an extended self-test of drive /dev/hdc.  You can issue this
+Begin an extended self-test of drive /dev/sdc.  You can issue this
 command on a running system.  The results can be seen in the self-test
 log visible with the \'\-l selftest\' option after it has completed.
 .PP
 .nf
-.B smartctl \-s on \-t offline /dev/hda
+.B smartctl \-s on \-t offline /dev/sda
 .fi
 Enable SMART on the disk, and begin an immediate offline test of
-drive /dev/hda.  You can issue this command on a running system.  The
+drive /dev/sda.  You can issue this command on a running system.  The
 results are only used to update the SMART Attributes, visible
 with the \'\-A\' option.  If any device errors occur, they are logged to
 the SMART error log, which can be seen with the \'\-l error\' option.
 .PP
 .nf
-.B smartctl \-A \-v 9,minutes /dev/hda
+.B smartctl \-A \-v 9,minutes /dev/sda
 .fi
 Shows the vendor Attributes, when the disk stores its power-on time
 internally in minutes rather than hours.
 .PP
 .nf
-.B smartctl \-q errorsonly \-H \-l selftest /dev/hda
+.B smartctl \-q errorsonly \-H \-l selftest /dev/sda
 .fi
 Produces output only if the device returns failing SMART status,
 or if some of the logged self-tests ended with errors.
 .PP
 .nf
-.B smartctl \-q silent \-a /dev/hda
+.B smartctl \-q silent \-a /dev/sda
 .fi
-Examine all SMART data for device /dev/hda, but produce no
+Examine all SMART data for device /dev/sda, but produce no
 printed output.  You must use the exit status (the
 .B $?
 shell variable) to learn if any Attributes are out of bound, if the
@@ -2100,7 +2210,7 @@ Start a short self-test on the (S)ATA disk connected to second pmport on the
 first channel of the first HighPoint RocketRAID controller card.
 .PP
 .nf
-.B smartctl \-t select,10\-100 \-t select,30\-300 \-t afterselect,on \-t pending,45 /dev/hda
+.B smartctl \-t select,10\-100 \-t select,30\-300 \-t afterselect,on \-t pending,45 /dev/sda
 .fi
 Run a selective self-test on LBAs 10 to 100 and 30 to 300.  After the
 these LBAs have been tested, read-scan the remainder of the disk.  If the disk is
@@ -2113,13 +2223,13 @@ device is restored.
 Examine all SMART data for the first SCSI disk connected to a cciss
 RAID controller card.
 
-.SH RETURN VALUES
-The return values of \fBsmartctl\fP are defined by a bitmask.  If all
-is well with the disk, the return value (exit status) of
+.SH EXIT STATUS
+The exit statuses of \fBsmartctl\fP are defined by a bitmask.
+If all is well with the disk, the exit status (return value) of
 \fBsmartctl\fP is 0 (all bits turned off).  If a problem occurs, or an
 error, potential error, or fault is detected, then a non-zero status
-is returned.  In this case, the eight different bits in the return
-value have the following meanings for ATA disks; some of these values
+is returned.  In this case, the eight different bits in the exit status
+have the following meanings for ATA disks; some of these values
 may also be returned for SCSI disks.
 .TP
 .B Bit 0:
@@ -2188,68 +2298,44 @@ drive database (see \'\-B\' option).
 optional local drive database (see \'\-B\' option).
 
 .\" %ENDIF NOT OS Windows
-.SH NOTES
-The TapeAlert log page flags are cleared for the initiator when the
-page is read. This means that each alert condition is reported only
-once by \fBsmartctl\fP for each initiator for each activation of the
-condition.
-
 .SH AUTHORS
-\fBBruce Allen\fP
+\fBBruce Allen\fP (project initiator),
 .br
-University of Wisconsin \- Milwaukee Physics Department
+\fBChristian Franke\fP (project manager, Windows port and all sort of things),
 .br
-\fBChristian Franke\fP (Windows interface, C++ redesign, most enhancements
-since 2009)
+\fBDouglas Gilbert\fP (SCSI subsystem),
 .br
-\fBsmartmontools\-support@lists.sourceforge.net\fP
-
-The following have made large contributions to smartmontools:
+\fBVolker Kuhlmann\fP (moderator of support and database mailing list),
 .br
-\fBCasper Dik\fP (Solaris SCSI interface)
+\fBGabriele Pohl\fP (wiki & development team support),
 .br
-\fBDouglas Gilbert\fP (SCSI subsystem)
-.br
-\fBGuido Guenther\fP (Autoconf/Automake packaging)
-.br
-\fBGeoffrey Keating\fP (Darwin ATA interface)
-.br
-\fBEduard Martinescu\fP (FreeBSD interface)
-.br
-\fBFr\['e]d\['e]ric L. W. Meunier\fP (Web site and Mailing list)
-.br
-\fBGabriele Pohl\fP (Web site and Wiki, conversion from CVS to SVN)
-.br
-\fBKeiji Sawada\fP (Solaris ATA interface)
-.br
-\fBManfred Schwarb\fP (Drive database)
-.br
-\fBSergey Svishchev\fP (NetBSD interface)
-.br
-\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface)
-.br
-\fBPhil Williams\fP (User interface and drive database)
+\fBAlex Samorukov\fP (FreeBSD port and more, new Trac wiki).
+
+Many other individuals have made contributions and corrections,
+see AUTHORS, ChangeLog and repository files.
+
+The first smartmontools code was derived from the smartsuite package,
+written by Michael Cornwell and Andre Hedrick.
+
+.SH REPORTING BUGS
+To submit a bug report, create a ticket in smartmontools wiki:
 .br
-\fBYuri Dario\fP (OS/2, eComStation interface)
+<\fBhttp://www.smartmontools.org/\fP>.
 .br
-\fBShengfeng Zhou\fP (Linux/FreeBSD HighPoint RocketRAID interface)
+Alternatively send the info to the smartmontools support mailing list:
 .br
-Many other individuals have made smaller contributions and corrections.
-
-The first smartmontools code was derived from the smartsuite package,
-written by Michael Cornwell, and from the previous UCSC smartsuite package.
-This code was originally developed as a
-Senior Thesis by Michael Cornwell at the Concurrent Systems Laboratory
-(now part of the Storage Systems Research Center), Jack Baskin School
-of Engineering, University of California, Santa
-Cruz. \fBhttp://ssrc.soe.ucsc.edu/\fP .
+<\fBhttps://lists.sourceforge.net/lists/listinfo/smartmontools-support\fB>.
 
 .SH SEE ALSO
-\fBsmartd\fP(8), \fBupdate-smart-drivedb\fP(8).
+\fBsmartd\fP(8).
+.\" %IF ENABLE_UPDATE_SMART_DRIVEDB
+.br
+\fBupdate-smart-drivedb\fP(8).
+.\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB
 
 .SH REFERENCES
 Please see the following web site for more info:
-\fBhttp://smartmontools.sourceforge.net/\fP
+\fBhttp://www.smartmontools.org/\fP
 
 An introductory article about smartmontools is \fIMonitoring Hard
 Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004,
@@ -2269,5 +2355,7 @@ publications of the Small Form Factors (SFF) Committee.
 Links to these and other documents may be found on the Links page of the
 \fBsmartmontools\fP Wiki at \fBhttp://www.smartmontools.org/wiki/Links\fP .
 
-.SH SVN ID OF THIS PAGE
-$Id: smartctl.8.in 3965 2014-07-20 14:46:41Z chrfranke $
+.SH PACKAGE VERSION
+CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
+.br
+$Id: smartctl.8.in 4311 2016-04-27 21:03:01Z chrfranke $
index bbaec43859f8f5d9bcb0ccb0b3acbc250d37fa90..3c2e40569486cf097411416947cd4c8f03f258a9 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * smartctl.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
+ * Copyright (C) 2008-16 Christian Franke
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
  *
  * This program is free software; you can redistribute it and/or modify
 #include "knowndrives.h"
 #include "scsicmds.h"
 #include "scsiprint.h"
+#include "nvmeprint.h"
 #include "smartctl.h"
 #include "utility.h"
 
-const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 3936 2014-07-05 17:16:23Z chrfranke $"
+const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4311 2016-04-27 21:03:01Z chrfranke $"
   CONFIG_H_CVSID SMARTCTL_H_CVSID;
 
 // Globals to control printing
@@ -128,7 +129,7 @@ static void Usage()
 "======================================= READ AND DISPLAY DATA OPTIONS =====\n\n"
 "  -H, --health\n"
 "        Show device SMART health status\n\n"
-"  -c, --capabilities                                                  (ATA)\n"
+"  -c, --capabilities                                            (ATA, NVMe)\n"
 "        Show device SMART capabilities\n\n"
 "  -A, --attributes\n"
 "        Show device SMART vendor-specific Attributes and values\n\n"
@@ -140,7 +141,8 @@ static void Usage()
 "                               background, sasphy[,reset], sataphy[,reset],\n"
 "                               scttemp[sts,hist], scttempint,N[,p],\n"
 "                               scterc[,N,M], devstat[,N], ssd,\n"
-"                               gplog,N[,RANGE], smartlog,N[,RANGE]\n\n"
+"                               gplog,N[,RANGE], smartlog,N[,RANGE],\n"
+"                               nvmelog,N,SIZE\n\n"
 "  -v N,OPTION , --vendorattribute=N,OPTION                            (ATA)\n"
 "        Set display OPTION for vendor Attribute N (see man page)\n\n"
 "  -F TYPE, --firmwarebug=TYPE                                         (ATA)\n"
@@ -194,7 +196,7 @@ static std::string getvalidarglist(int opt)
   case 'b':
     return "warn, exit, ignore";
   case 'r':
-    return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
+    return "ioctl[,N], ataioctl[,N], scsiioctl[,N], nvmeioctl[,N]";
   case opt_smart:
   case 'o':
   case 'S':
@@ -205,8 +207,8 @@ static std::string getvalidarglist(int opt)
            "background, sasphy[,reset], sataphy[,reset], "
            "scttemp[sts,hist], scttempint,N[,p], "
            "scterc[,N,M], devstat[,N], ssd, "
-           "gplog,N[,RANGE], smartlog,N[,RANGE]";
-
+           "gplog,N[,RANGE], smartlog,N[,RANGE], "
+           "nvmelog,N,SIZE";
   case 'P':
     return "use, ignore, show, showall";
   case 't':
@@ -259,13 +261,13 @@ enum checksum_err_mode_t {
 
 static checksum_err_mode_t checksum_err_mode = CHECKSUM_ERR_WARN;
 
-static void scan_devices(const char * type, bool with_open, char ** argv);
+static void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv);
 
 
 /*      Takes command options and sets features to be run */    
 static const char * parse_options(int argc, char** argv,
   ata_print_options & ataopts, scsi_print_options & scsiopts,
-  bool & print_type_only)
+  nvme_print_options & nvmeopts, bool & print_type_only)
 {
   // Please update getvalidarglist() if you edit shortopts
   const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:f:g:";
@@ -313,7 +315,8 @@ static const char * parse_options(int argc, char** argv,
   opterr=optopt=0;
 
   const char * type = 0; // set to -d optarg
-  bool no_defaultdb = false; // set true on '-B FILE'
+  smart_devtype_list scan_types; // multiple -d TYPE options for --scan
+  bool use_default_db = true; // set false on '-B FILE'
   bool output_format_set = false; // set true on '-f FORMAT'
   int scan = 0; // set by --scan, --scan-open
   bool badarg = false, captive = false;
@@ -345,8 +348,14 @@ static const char * parse_options(int argc, char** argv,
     case 'd':
       if (!strcmp(optarg, "test"))
         print_type_only = true;
-      else
-        type = (strcmp(optarg, "auto") ? optarg : (char *)0);
+      else if (!strcmp(optarg, "auto")) {
+        type = 0;
+        scan_types.clear();
+      }
+      else {
+        type = optarg;
+        scan_types.push_back(optarg);
+      }
       break;
     case 'T':
       if (!strcmp(optarg,"normal")) {
@@ -376,26 +385,22 @@ static const char * parse_options(int argc, char** argv,
       break;
     case 'r':
       {
-        int i;
-        char *s;
-
-        // split_report_arg() may modify its first argument string, so use a
-        // copy of optarg in case we want optarg for an error message.
-        if (!(s = strdup(optarg))) {
-          throw std::bad_alloc();
-        }
-        if (split_report_arg(s, &i)) {
+        int n1 = -1, n2 = -1, len = strlen(optarg);
+        char s[9+1]; unsigned i = 1;
+        sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2);
+        if (!((n1 == len || n2 == len) && 1 <= i && i <= 4)) {
           badarg = true;
         } else if (!strcmp(s,"ioctl")) {
-          ata_debugmode  = scsi_debugmode = i;
+          ata_debugmode = scsi_debugmode = nvme_debugmode = i;
         } else if (!strcmp(s,"ataioctl")) {
           ata_debugmode = i;
         } else if (!strcmp(s,"scsiioctl")) {
           scsi_debugmode = i;
+        } else if (!strcmp(s,"nvmeioctl")) {
+          nvme_debugmode = i;
         } else {
           badarg = true;
         }
-        free(s);
       }
       break;
 
@@ -437,7 +442,7 @@ static const char * parse_options(int argc, char** argv,
       }
       break;
     case 'H':
-      ataopts.smart_check_status = scsiopts.smart_check_status = true;
+      ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true;
       scsiopts.smart_ss_media_log = true;
       break;
     case 'F':
@@ -447,14 +452,23 @@ static const char * parse_options(int argc, char** argv,
         badarg = true;
       break;
     case 'c':
-      ataopts.smart_general_values = true;
+      ataopts.smart_general_values = nvmeopts.drive_capabilities = true;
       break;
     case 'A':
-      ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true;
+      ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true;
       break;
     case 'l':
-      if (!strcmp(optarg,"error")) {
+      if (str_starts_with(optarg, "error")) {
+        int n1 = -1, n2 = -1, len = strlen(optarg);
+        unsigned val = ~0;
+        sscanf(optarg, "error%n,%u%n", &n1, &val, &n2);
         ataopts.smart_error_log = scsiopts.smart_error_log = true;
+        if (n1 == len)
+          nvmeopts.error_log_entries = 16;
+        else if (n2 == len && val > 0)
+          nvmeopts.error_log_entries = val;
+        else
+          badarg = true;
       } else if (!strcmp(optarg,"selftest")) {
         ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true;
       } else if (!strcmp(optarg, "selective")) {
@@ -503,10 +517,14 @@ static const char * parse_options(int argc, char** argv,
         sscanf(optarg, "devstat%n,%u%n", &n1, &val, &n2);
         if (n1 == len)
           ataopts.devstat_all_pages = true;
-        else if (n2 == len && val <= 255)
-          ataopts.devstat_pages.push_back(val);
-        else
-          badarg = true;
+        else {
+            if (n2 != len) // retry with hex
+              sscanf(optarg, "devstat,0x%x%n", &val, &n2);
+            if (n2 == len && val <= 0xff)
+              ataopts.devstat_pages.push_back(val);
+            else
+              badarg = true;
+        }
 
       } else if (!strncmp(optarg, "xerror", sizeof("xerror")-1)) {
         int n1 = -1, n2 = -1, len = strlen(optarg);
@@ -579,12 +597,25 @@ static const char * parse_options(int argc, char** argv,
           req.nsectors = (sign == '-' ? nsectors-page+1 : nsectors);
           ataopts.log_requests.push_back(req);
         }
-      } else {
+      }
+
+      else if (str_starts_with(optarg, "nvmelog,")) {
+        int n = -1, len = strlen(optarg);
+        unsigned page = 0, size = 0;
+        sscanf(optarg, "nvmelog,0x%x,0x%x%n", &page, &size, &n);
+        if (n == len && page <= 0xff && 0 < size && size <= 0x4000) {
+          nvmeopts.log_page = page; nvmeopts.log_page_size = size;
+        }
+        else
+          badarg = true;
+      }
+
+      else {
         badarg = true;
       }
       break;
     case 'i':
-      ataopts.drive_info = scsiopts.drive_info = true;
+      ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true;
       break;
 
     case opt_identify:
@@ -603,23 +634,25 @@ static const char * parse_options(int argc, char** argv,
       break;
 
     case 'a':
-      ataopts.drive_info           = scsiopts.drive_info          = true;
-      ataopts.smart_check_status   = scsiopts.smart_check_status  = true;
-      ataopts.smart_general_values = true;
-      ataopts.smart_vendor_attrib  = scsiopts.smart_vendor_attrib = true;
+      ataopts.drive_info           = scsiopts.drive_info          = nvmeopts.drive_info          = true;
+      ataopts.smart_check_status   = scsiopts.smart_check_status  = nvmeopts.smart_check_status  = true;
+      ataopts.smart_general_values =                                nvmeopts.drive_capabilities  = true;
+      ataopts.smart_vendor_attrib  = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true;
       ataopts.smart_error_log      = scsiopts.smart_error_log     = true;
+      nvmeopts.error_log_entries   = 16;
       ataopts.smart_selftest_log   = scsiopts.smart_selftest_log  = true;
       ataopts.smart_selective_selftest_log = true;
       /* scsiopts.smart_background_log = true; */
       scsiopts.smart_ss_media_log = true;
       break;
     case 'x':
-      ataopts.drive_info           = scsiopts.drive_info          = true;
-      ataopts.smart_check_status   = scsiopts.smart_check_status  = true;
-      ataopts.smart_general_values = true;
-      ataopts.smart_vendor_attrib  = scsiopts.smart_vendor_attrib = true;
+      ataopts.drive_info           = scsiopts.drive_info          = nvmeopts.drive_info          = true;
+      ataopts.smart_check_status   = scsiopts.smart_check_status  = nvmeopts.smart_check_status  = true;
+      ataopts.smart_general_values =                                nvmeopts.drive_capabilities  = true;
+      ataopts.smart_vendor_attrib  = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true;
       ataopts.smart_ext_error_log  = 8;
       ataopts.retry_error_log      = true;
+      nvmeopts.error_log_entries   = 16;
       ataopts.smart_ext_selftest_log = 25;
       ataopts.retry_selftest_log   = true;
       scsiopts.smart_error_log     = scsiopts.smart_selftest_log    = true;
@@ -661,7 +694,7 @@ static const char * parse_options(int argc, char** argv,
       } else if (!strcmp(optarg, "show")) {
         ataopts.show_presets = true;
       } else if (!strcmp(optarg, "showall")) {
-        if (!no_defaultdb && !read_default_drive_databases())
+        if (!init_drive_database(use_default_db))
           EXIT(FAILCMD);
         if (optind < argc) { // -P showall MODEL [FIRMWARE]
           int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL));
@@ -801,7 +834,7 @@ static const char * parse_options(int argc, char** argv,
         if (*path == '+' && path[1])
           path++;
         else
-          no_defaultdb = true;
+          use_default_db = false;
         if (!read_drive_database(path))
           EXIT(FAILCMD);
       }
@@ -997,9 +1030,9 @@ static const char * parse_options(int argc, char** argv,
   // Special handling of --scan, --scanopen
   if (scan) {
     // Read or init drive database to allow USB ID check.
-    if (!no_defaultdb && !read_default_drive_databases())
+    if (!init_drive_database(use_default_db))
       EXIT(FAILCMD);
-    scan_devices(type, (scan == opt_scan_open), argv + optind);
+    scan_devices(scan_types, (scan == opt_scan_open), argv + optind);
     EXIT(0);
   }
 
@@ -1009,6 +1042,15 @@ static const char * parse_options(int argc, char** argv,
   if (printing_is_switchable)
     printing_is_off = true;
 
+  // Check for multiple -d TYPE options
+  if (scan_types.size() > 1) {
+    printing_is_off = false;
+    printslogan();
+    pout("ERROR: multiple -d TYPE options are only allowed with --scan\n");
+    UsageSummary();
+    EXIT(FAILCMD);
+  }
+
   // error message if user has asked for more than one test
   if (testcnt > 1) {
     printing_is_off = false;
@@ -1075,7 +1117,7 @@ static const char * parse_options(int argc, char** argv,
   }
 
   // Read or init drive database
-  if (!no_defaultdb && !read_default_drive_databases())
+  if (!init_drive_database(use_default_db))
     EXIT(FAILCMD);
 
   return type;
@@ -1151,19 +1193,22 @@ void checksumwarning(const char * string)
 // Return info string about device protocol
 static const char * get_protocol_info(const smart_device * dev)
 {
-  switch ((int)dev->is_ata() | ((int)dev->is_scsi() << 1)) {
+  switch (   (int)dev->is_ata()
+          | ((int)dev->is_scsi() << 1)
+          | ((int)dev->is_nvme() << 2)) {
     case 0x1: return "ATA";
     case 0x2: return "SCSI";
     case 0x3: return "ATA+SCSI";
+    case 0x4: return "NVMe";
     default:  return "Unknown";
   }
 }
 
 // Device scan
 // smartctl [-d type] --scan[-open] -- [PATTERN] [smartd directive ...]
-void scan_devices(const char * type, bool with_open, char ** argv)
+void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv)
 {
-  bool dont_print = !(ata_debugmode || scsi_debugmode);
+  bool dont_print = !(ata_debugmode || scsi_debugmode || nvme_debugmode);
 
   const char * pattern = 0;
   int ai = 0;
@@ -1172,7 +1217,7 @@ void scan_devices(const char * type, bool with_open, char ** argv)
 
   smart_device_list devlist;
   printing_is_off = dont_print;
-  bool ok = smi()->scan_smart_devices(devlist, type , pattern);
+  bool ok = smi()->scan_smart_devices(devlist, types, pattern);
   printing_is_off = false;
 
   if (!ok) {
@@ -1224,8 +1269,9 @@ static int main_worker(int argc, char **argv)
   // Parse input arguments
   ata_print_options ataopts;
   scsi_print_options scsiopts;
+  nvme_print_options nvmeopts;
   bool print_type_only = false;
-  const char * type = parse_options(argc, argv, ataopts, scsiopts, print_type_only);
+  const char * type = parse_options(argc, argv, ataopts, scsiopts, nvmeopts, print_type_only);
 
   const char * name = argv[argc-1];
 
@@ -1258,6 +1304,11 @@ static int main_worker(int argc, char **argv)
     pout("%s: Device of type '%s' [%s] detected\n",
          dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get()));
 
+  if (dev->is_ata() && ataopts.powermode>=2 && dev->is_powered_down()) {
+    pout( "%s: Device is in %s mode, exit(%d)\n", dev->get_info_name(), "STANDBY (OS)", FAILPOWER );
+    return FAILPOWER;
+  }
+
   // Open device
   {
     // Save old info
@@ -1267,7 +1318,8 @@ static int main_worker(int argc, char **argv)
     dev.replace( dev->autodetect_open() );
 
     // Report if type has changed
-    if ((type || print_type_only) && oldinfo.dev_type != dev->get_dev_type())
+    if (   (ata_debugmode || scsi_debugmode || nvme_debugmode || print_type_only)
+        && oldinfo.dev_type != dev->get_dev_type()                               )
       pout("%s: Device open changed type from '%s' to '%s'\n",
         dev->get_info_name(), oldinfo.dev_type.c_str(), dev->get_dev_type());
   }
@@ -1285,9 +1337,11 @@ static int main_worker(int argc, char **argv)
     retval = ataPrintMain(dev->to_ata(), ataopts);
   else if (dev->is_scsi())
     retval = scsiPrintMain(dev->to_scsi(), scsiopts);
+  else if (dev->is_nvme())
+    retval = nvmePrintMain(dev->to_nvme(), nvmeopts);
   else
     // we should never fall into this branch!
-    pout("%s: Neither ATA nor SCSI device\n", dev->get_info_name());
+    pout("%s: Neither ATA, SCSI nor NVMe device\n", dev->get_info_name());
 
   dev->close();
   return retval;
@@ -1298,6 +1352,8 @@ static int main_worker(int argc, char **argv)
 int main(int argc, char **argv)
 {
   int status;
+  bool badcode = false;
+
   try {
     // Do the real work ...
     status = main_worker(argc, argv);
@@ -1314,8 +1370,21 @@ int main(int argc, char **argv)
   catch (const std::exception & ex) {
     // Other fatal errors
     printf("Smartctl: Exception: %s\n", ex.what());
+    badcode = true;
     status = FAILCMD;
   }
+
+  // Check for remaining device objects
+  if (smart_device::get_num_objects() != 0) {
+    printf("Smartctl: Internal Error: %d device object(s) left at exit.\n",
+           smart_device::get_num_objects());
+    badcode = true;
+    status = FAILCMD;
+  }
+
+  if (badcode)
+     printf("Please inform " PACKAGE_BUGREPORT ", including output of smartctl -V.\n");
+
   return status;
 }
 
index 8820287c6c5f1cdcb8aac09a058aa96edd818f25..fee2b66091a5fd77f00c5451ef366485a639fa47 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * smartctl.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-10 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2008-10 Christian Franke <smartmontools-support@lists.sourceforge.net>
@@ -26,7 +26,7 @@
 #ifndef SMARTCTL_H_
 #define SMARTCTL_H_
 
-#define SMARTCTL_H_CVSID "$Id: smartctl.h 3727 2012-12-13 17:23:06Z samm2 $\n"
+#define SMARTCTL_H_CVSID "$Id: smartctl.h 4120 2015-08-27 16:12:21Z samm2 $\n"
 
 // Return codes (bitmask)
 
index c6f1568e3257bd2171d5b4323ff04f8be0fbadbe..3ea06283bff1246c97adaa5f01cd3ef861922da5 100644 (file)
@@ -1,8 +1,8 @@
 .ig
-Copyright (C) 2002-10 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-Copyright (C) 2004-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+Copyright (C) 2002-10 Bruce Allen
+Copyright (C) 2004-16 Christian Franke
 
-$Id: smartd.8.in 3965 2014-07-20 14:46:41Z chrfranke $
+$Id: smartd.8.in 4299 2016-04-16 19:45:57Z chrfranke $
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -18,16 +18,13 @@ Research Center), Jack Baskin School of Engineering, University of
 California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 
 ..
-.TH SMARTD 8 CURRENT_SVN_DATE CURRENT_SVN_VERSION CURRENT_SVN_DATE
+.TH SMARTD 8 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools"
 .SH NAME
 \fBsmartd\fP \- SMART Disk Monitoring Daemon
 
 .SH SYNOPSIS
 .B smartd [options]
 
-.SH PACKAGE VERSION
-CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
-
 .SH DESCRIPTION
 .\" %IF NOT OS ALL
 .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools.
@@ -101,8 +98,15 @@ devices that support SMART.  The scanning is done as follows:
 .IP \fBLINUX:\fP 9
 Examine all entries \fB"/dev/hd[a-t]"\fP for IDE/ATA
 devices, and \fB"/dev/sd[a-z]"\fP, \fB"/dev/sd[a-c][a-z]"\fP
-for SCSI or SATA devices.
+for ATA/SATA or SCSI/SAS devices.
 Disks behind RAID controllers are not included.
+
+[NEW EXPERIMENTAL SMARTD FEATURE]
+If directive \'\-d nvme\'
+.\" %IF ENABLE_NVME_DEVICESCAN
+or no \'\-d\' directive
+.\" %ENDIF ENABLE_NVME_DEVICESCAN
+is specified, examine all entries \fB"/dev/nvme[0-99]"\fP for NVMe devices.
 .\" %ENDIF OS Linux
 .\" %IF OS FreeBSD
 .IP \fBFREEBSD:\fP 9
@@ -139,6 +143,13 @@ examine all entries \fB"/dev/csmi[0\-9],N"\fP for drives behind an Intel
 ICHxR controller with RST driver.
 
 Disks behind Areca RAID controllers are not included.
+
+[NEW EXPERIMENTAL SMARTD FEATURE]
+If directive \'\-d nvme\'
+.\" %IF ENABLE_NVME_DEVICESCAN
+or no \'\-d\' directive
+.\" %ENDIF ENABLE_NVME_DEVICESCAN
+is specified, examine all entries \fB"/dev/nvme[0-9]"\fP for NVMe devices.
 .\" %ENDIF OS Windows Cygwin
 .PP
 \fBsmartd\fP then monitors
@@ -186,13 +197,14 @@ can be used to verify the existence of the default configuration file.
 By using \'\-\' for FILE, the configuration is read from standard
 input. This is useful for commands like:
 .nf
-.B echo /dev/hdb \-m user@home \-M test | smartd \-c \- \-q onecheck
+.B echo /dev/sdb \-m user@home \-M test | smartd \-c \- \-q onecheck
 .fi
 to perform quick and simple checks without a configuration file.
 .\" %IF ENABLE_CAPABILITIES
 .TP
 .B \-C, \-\-capabilities
-Use \fBcapabilities\fP(7).
+[Linux only] Use libcap-ng to drop unneeded Linux process \fBcapabilities\fP(7).
+The following capabilities are kept: CAP_SYS_ADMIN, CAP_SYS_RAWIO, CAP_MKNOD.
 
 Warning: Mail notification does not work when used.
 .\" %ENDIF ENABLE_CAPABILITIES
@@ -283,7 +295,7 @@ log output is redirected as follows:
 .TP
 .B \-n, \-\-no\-fork
 Do not fork into background; this is useful when executed from modern
-init methods like initng, minit or supervise.
+init methods like initng, minit, supervise or systemd.
 .\" %IF OS Cygwin
 
 On Cygwin, this allows running \fBsmartd\fP as service via cygrunsrv,
@@ -379,6 +391,13 @@ this option are:
 .I scsiioctl
 \- report only ioctl() transactions with SCSI devices.
 
+.\" %IF OS FreeBSD Linux Windows Cygwin
+.I nvmeioctl
+\- [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTD FEATURE]
+report only ioctl() transactions with NVMe devices.
+
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 Any argument may include a positive integer to specify the level of
 detail that should be reported.  The argument should be followed by a
 comma then the integer with no spaces.  For example, \fIataioctl,2\fP
@@ -416,7 +435,6 @@ forced by SIGUSR1. After a normal check cycle, a file is only rewritten if
 an important change (which usually results in a SYSLOG output) occurred.
 .TP
 .B \-w PATH, \-\-warnexec=PATH
-[NEW EXPERIMENTAL SMARTD FEATURE]
 Run the executable PATH instead of the default script when smartd
 needs to send warning messages.  PATH must point to an executable binary
 file or script.
@@ -464,6 +482,7 @@ once. The exit status (the shell
 variable) will be zero if all went well, and nonzero if no devices
 were detected or some other problem was encountered.
 
+.\" %IF ENABLE_INITSCRIPT
 Note that \fBsmartmontools\fP provides a start-up script in
 \fB/usr/local/etc/rc.d/init.d/smartd\fP which is responsible for starting and
 stopping the daemon via the normal init interface.  Using this script,
@@ -476,6 +495,7 @@ and stop it by using the command:
 .B /usr/local/etc/rc.d/init.d/smartd stop
 .fi
 
+.\" %ENDIF ENABLE_INITSCRIPT
 .SH CONFIGURATION
 The syntax of the \fBsmartd.conf\fP(5) file is discussed separately.
 
@@ -489,7 +509,7 @@ or
 .B \'\-u\'
 Directives. For example:
 .nf
-.B \'Device: /dev/hda, SMART Attribute: 194 Temperature_Celsius changed from 94 to 93\'
+.B \'Device: /dev/sda, SMART Attribute: 194 Temperature_Celsius changed from 94 to 93\'
 .fi
 Note that in this message, the value given is the \'Normalized\' not the \'Raw\' 
 Attribute value (the disk temperature in this case is about 22
@@ -500,7 +520,7 @@ and
 Directives modify this behavior, so that the information is printed
 with the Raw values as well, for example:
 .nf
-.B \'Device: /dev/hda, SMART Attribute: 194 Temperature_Celsius changed from 94 [Raw 22] to 93 [Raw 23]\'
+.B \'Device: /dev/sda, SMART Attribute: 194 Temperature_Celsius changed from 94 [Raw 22] to 93 [Raw 23]\'
 .fi
 Here the Raw values are the actual disk temperatures in Celsius.  The
 way in which the Raw values are printed, and the names under which the
@@ -518,7 +538,7 @@ will make log entries at loglevel
 .B LOG_CRIT
 if a SMART Attribute has failed, for example:
 .nf
-.B \'Device: /dev/hdc, Failed SMART Attribute: 5 Reallocated_Sector_Ct\'
+.B \'Device: /dev/sdc, Failed SMART Attribute: 5 Reallocated_Sector_Ct\'
 .fi
  This loglevel is used for reporting enabled by the
 .B \'\-H\', \-f\', \'\-l\ selftest\',
@@ -549,14 +569,17 @@ see the \fBsmartd\fP '\-l' command-line option described above.
 .\" %ENDIF OS Solaris
 .\" %IF OS Cygwin
 The Cygwin Version of \fBsmartd\fP can be run as a service via the
-cygrunsrv tool. The start-up script provides Cygwin-specific commands
-to install and remove the service:
+cygrunsrv tool.
+.\" %IF ENABLE_INITSCRIPT
+The start-up script provides Cygwin-specific commands to install and
+remove the service:
 .nf
 .B /usr/local/etc/rc.d/init.d/smartd install [options]
 .B /usr/local/etc/rc.d/init.d/smartd remove
 .fi
 The service can be started and stopped by the start-up script as usual
 (see \fBEXAMPLES\fP above).
+.\" %ENDIF ENABLE_INITSCRIPT
 
 .\" %ENDIF OS Cygwin
 .\" %IF OS Windows
@@ -624,10 +647,8 @@ set using \fB/etc/localtime\fP. The work-around \fIfails\fP if the
 time-zone is set using the \'\fBTZ\fP\' variable (or a file that it
 points to).
 
-.SH RETURN VALUES
-The return value (exit status) of 
-\fBsmartd\fP
-can have the following values:
+.SH EXIT STATUS
+The exit status (return value) of \fBsmartd\fP can have the following values:
 .TP
 .B 0:
 Daemon startup successful, or \fBsmartd\fP was killed by a SIGTERM (or in debug mode, a SIGQUIT).
@@ -654,16 +675,11 @@ Config file exists, but cannot be read.
 \fBsmartd\fP
 ran out of memory during startup.
 .TP
-.B 9:
-A compile time constant of\fB smartd\fP was too small.  This can be caused by an
-excessive number of disks, or by lines in \fB /usr/local/etc/smartd.conf\fP that are too long.
-Please report this problem to \fB smartmontools-support@lists.sourceforge.net\fP.
-.TP
 .B 10:
 An inconsistency was found in \fBsmartd\fP\'s internal data
 structures. This should never happen.  It must be due to either a
 coding or compiler bug.  \fIPlease\fP report such failures to
-smartmontools-support@lists.sourceforge.net.
+smartmontools developers, see REPORTING BUGS below.
 .TP
 .B 16:
 A device explicitly listed in
@@ -718,59 +734,43 @@ optional local drive database (see \'\-B\' option).
 
 .\" %ENDIF NOT OS Windows
 .SH AUTHORS
-\fBBruce Allen\fP
-.br
-University of Wisconsin \- Milwaukee Physics Department
-.br
-\fBChristian Franke\fP (Windows interface, C++ redesign, most enhancements
-since 2009)
-.br
-\fBsmartmontools\-support@lists.sourceforge.net\fP
-
-The following have made large contributions to smartmontools:
+\fBBruce Allen\fP (project initiator),
 .br
-\fBCasper Dik\fP (Solaris SCSI interface)
+\fBChristian Franke\fP (project manager, Windows port and all sort of things),
 .br
-\fBDouglas Gilbert\fP (SCSI subsystem)
+\fBDouglas Gilbert\fP (SCSI subsystem),
 .br
-\fBGuido Guenther\fP (Autoconf/Automake packaging)
+\fBVolker Kuhlmann\fP (moderator of support and database mailing list),
 .br
-\fBGeoffrey Keating\fP (Darwin ATA interface)
+\fBGabriele Pohl\fP (wiki & development team support),
 .br
-\fBEduard Martinescu\fP (FreeBSD interface)
-.br
-\fBFr\['e]d\['e]ric L. W. Meunier\fP (Web site and Mailing list)
-.br
-\fBGabriele Pohl\fP (Web site and Wiki, conversion from CVS to SVN)
-.br
-\fBKeiji Sawada\fP (Solaris ATA interface)
-.br
-\fBManfred Schwarb\fP (Drive database)
-.br
-\fBSergey Svishchev\fP (NetBSD interface)
-.br
-\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface)
+\fBAlex Samorukov\fP (FreeBSD port and more, new Trac wiki).
+
+Many other individuals have made contributions and corrections,
+see AUTHORS, ChangeLog and repository files.
+
+The first smartmontools code was derived from the smartsuite package,
+written by Michael Cornwell and Andre Hedrick.
+
+.SH REPORTING BUGS
+To submit a bug report, create a ticket in smartmontools wiki:
 .br
-\fBPhil Williams\fP (User interface and drive database)
+<\fBhttp://www.smartmontools.org/\fP>.
 .br
-\fBShengfeng Zhou\fP (Linux/FreeBSD HighPoint RocketRAID interface)
+Alternatively send the info to the smartmontools support mailing list:
 .br
-Many other individuals have made smaller contributions and corrections.
-
-The first smartmontools code was derived from the smartsuite package,
-written by Michael Cornwell, and from the previous UCSC smartsuite package.
-This code was originally developed as a
-Senior Thesis by Michael Cornwell at the Concurrent Systems Laboratory
-(now part of the Storage Systems Research Center), Jack Baskin School
-of Engineering, University of California, Santa
-Cruz. \fBhttp://ssrc.soe.ucsc.edu/\fP .
+<\fBhttps://lists.sourceforge.net/lists/listinfo/smartmontools-support\fB>.
 
 .SH SEE ALSO
-\fBsmartd.conf\fP(5), \fBsmartctl\fP(8), \fBupdate-smart-drivedb\fP(8).
+\fBsmartd.conf\fP(5), \fBsmartctl\fP(8).
+.\" %IF ENABLE_UPDATE_SMART_DRIVEDB
+.br
+\fBupdate-smart-drivedb\fP(8).
+.\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB
 
 .SH REFERENCES
 Please see the following web site for more info:
-\fBhttp://smartmontools.sourceforge.net/\fP
+\fBhttp://www.smartmontools.org/\fP
 
 An introductory article about smartmontools is \fIMonitoring Hard
 Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004,
@@ -790,5 +790,7 @@ publications of the Small Form Factors (SFF) Committee.
 Links to these and other documents may be found on the Links page of the
 \fBsmartmontools\fP Wiki at \fBhttp://www.smartmontools.org/wiki/Links\fP .
 
-.SH SVN ID OF THIS PAGE
-$Id: smartd.8.in 3965 2014-07-20 14:46:41Z chrfranke $
+.SH PACKAGE VERSION
+CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
+.br
+$Id: smartd.8.in 4299 2016-04-16 19:45:57Z chrfranke $
index 5ef85b1bae0157361fdd2b19fbc47217e9c3c1b9..d4a0d10adb944d5defb70dd876aaf3bb17f7353b 100644 (file)
@@ -1,8 +1,8 @@
 # Sample configuration file for smartd.  See man smartd.conf.
 
-# Home page is: http://smartmontools.sourceforge.net
+# Home page is: http://www.smartmontools.org
 
-# $Id: smartd.conf 3651 2012-10-18 15:11:36Z samm2 $
+# $Id: smartd.conf 4120 2015-08-27 16:12:21Z samm2 $
 
 # smartd will re-read the configuration file if it receives a HUP
 # signal
@@ -33,21 +33,21 @@ DEVICESCAN
 # and min/max temperatures.
 #DEVICESCAN -I 194 -I 231 -I 9 -W 5
 
-# First (primary) ATA/IDE hard disk.  Monitor all attributes, enable
+# First ATA/SATA or SCSI/SAS disk.  Monitor all attributes, enable
 # automatic online data collection, automatic Attribute autosave, and
 # start a short self-test every day between 2-3am, and a long self test
 # Saturdays between 3-4am.
-#/dev/hda -a -o on -S on -s (S/../.././02|L/../../6/03)
+#/dev/sda -a -o on -S on -s (S/../.././02|L/../../6/03)
 
 # Monitor SMART status, ATA Error Log, Self-test log, and track
 # changes in all attributes except for attribute 194
-#/dev/hdb -H -l error -l selftest -t -I 194 
+#/dev/sdb -H -l error -l selftest -t -I 194
 
 # Monitor all attributes except normalized Temperature (usually 194),
 # but track Temperature changes >= 4 Celsius, report Temperatures
 # >= 45 Celsius and changes in Raw value of Reallocated_Sector_Ct (5).
 # Send mail on SMART failures or when Temperature is >= 55 Celsius.
-#/dev/hdc -a -I 194 -W 4,45,55 -R 5 -m admin@example.com
+#/dev/sdc -a -I 194 -W 4,45,55 -R 5 -m admin@example.com
 
 # An ATA disk may appear as a SCSI device to the OS. If a SCSI to
 # ATA Translation (SAT) layer is between the OS and the device then
@@ -57,7 +57,7 @@ DEVICESCAN
 
 # A very silent check.  Only report SMART health status if it fails
 # But send an email in this case
-#/dev/hdc -H -C 0 -U 0 -m admin@example.com
+#/dev/sdc -H -C 0 -U 0 -m admin@example.com
 
 # First two SCSI disks.  This will monitor everything that smartd can
 # monitor.  Start extended self-tests Wednesdays between 6-7pm and
@@ -141,5 +141,5 @@ DEVICESCAN
 # All but -d, -m and -M Directives are only implemented for ATA devices
 #
 # If the test string DEVICESCAN is the first uncommented text
-# then smartd will scan for devices /dev/hd[a-l] and /dev/sd[a-z]
+# then smartd will scan for devices.
 # DEVICESCAN may be followed by any desired Directives.
index 0acec7d17f62176b866289a1296067e317c345e8..7c38e649a0300a43f65ed720b87b341be9cc1453 100644 (file)
@@ -1,8 +1,8 @@
 .ig
-Copyright (C) 2002-10 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-Copyright (C) 2004-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+Copyright (C) 2002-10 Bruce Allen
+Copyright (C) 2004-16 Christian Franke
 
-$Id: smartd.conf.5.in 3965 2014-07-20 14:46:41Z chrfranke $
+$Id: smartd.conf.5.in 4307 2016-04-24 12:37:31Z chrfranke $
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -18,13 +18,10 @@ Research Center), Jack Baskin School of Engineering, University of
 California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 
 ..
-.TH SMARTD.CONF 5 CURRENT_SVN_DATE CURRENT_SVN_VERSION CURRENT_SVN_DATE
+.TH SMARTD.CONF 5 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools"
 .SH NAME
 \fBsmartd.conf\fP \- SMART Disk Monitoring Daemon Configuration File\fP
 
-.SH PACKAGE VERSION
-CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
-
 .SH DESCRIPTION
 .\" %IF NOT OS ALL
 .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools.
@@ -90,27 +87,17 @@ Section below!
 .nf
 .B ################################################
 .B # This is an example smartd startup config file
-.B # /usr/local/etc/smartd.conf for monitoring three
-.B # ATA disks, three SCSI disks, six ATA disks
-.B # behind two 3ware controllers, three SATA disks
-.B # directly connected to the HighPoint Rocket-
-.B # RAID controller, two SATA disks connected to
-.B # the HighPoint RocketRAID controller via a pmport
-.B # device, four SATA disks connected to an Areca
-.B # RAID controller, and one SATA disk.
+.B # /usr/local/etc/smartd.conf
 .B #
-.B # First ATA disk on two different interfaces. On
-.B # the second disk, start a long self-test every
+.B # On the second disk, start a long self-test every
 .B # Sunday between 3 and 4 am.
 .B #
-.B \ \ /dev/hda -a -m admin@example.com,root@localhost 
-.B \ \ /dev/hdc -a -I 194 -I 5 -i 12 -s L/../../7/03
+.B \ \ /dev/sda -a -m admin@example.com,root@localhost
+.B \ \ /dev/sdb -a -I 194 -I 5 -i 12 -s L/../../7/03
 .B #
-.B # SCSI disks.  Send a TEST warning email to admin on
-.B # startup.
+.B # Send a TEST warning email to admin on startup.
 .B #
-.B \ \ /dev/sda
-.B \ \ /dev/sdb -m admin@example.com -M test
+.B \ \ /dev/sdc -m admin@example.com -M test
 .B #
 .B # Strange device.  It\'s SCSI. Start a scheduled
 .B # long self test between 5 and 6 am Monday/Thursday
@@ -220,7 +207,7 @@ Section below!
 .B # and Usage Attributes, apart from Attributes
 .B # 9, 194, and 231, and shows  continued lines:
 .B #
-.B \ \ /dev/hdd\ -l\ error\ \e
+.B \ \ /dev/sdd\ -l\ error\ \e
 .B \ \ \ \ \ \ \ \ \ \ \ -l\ selftest\ \e
 .B \ \ \ \ \ \ \ \ \ \ \ -t\ \e\ \ \ \ \ \ # Attributes not tracked:
 .B \ \ \ \ \ \ \ \ \ \ \ -I\ 194\ \e\ \ # temperature
@@ -335,6 +322,18 @@ from issuing SCSI commands to an ATA device.
 \fBsmartd\fP
 from issuing ATA commands to a SCSI device.
 
+.\" %ENDIF NOT OS Darwin
+.\" %IF OS FreeBSD Linux Windows Cygwin
+.I nvme[,NSID]
+\- [FreeBSD, Linux, Windows and Cygwin only] [NEW EXPERIMENTAL SMARTD FEATURE]
+the device type is NVM Express (NVMe).
+The optional parameter NSID specifies the namespace id (in hex) passed
+to the driver.
+Use 0xffffffff for the broadcast namespace id.
+The default for NSID is the namespace id addressed by the device name.
+
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
+.\" %IF NOT OS Darwin
 .I sat[,auto][,N]
 \- the device type is SCSI to ATA Translation (SAT).
 This is for ATA disks that have a SCSI to ATA Translation (SAT) Layer
@@ -369,13 +368,17 @@ CAUTION: Specifying \',x\' for a device which does not support it results
 in I/O errors and may disconnect the drive.  The same applies if the specified
 PORT does not exist or is not connected to a disk.
 
-[NEW EXPERIMENTAL SMARTD FEATURE]
 The Prolific PL2507/3507 USB bridges with older firmware support a pass-through
 command similar to JMicron and work with \'\-d usbjmicron,0\'.
 Newer Prolific firmware requires a modified command which can be selected by
 \'\-d usbjmicron,p\'.
 Note that this does not yet support the SMART status command.
 
+.I usbprolific
+\- [NEW EXPERIMENTAL SMARTD FEATURE]
+this device type is for SATA disks that are behind a Prolific PL2571/2771/2773/2775
+USB to SATA bridge.
+
 .I usbsunplus
 \- this device type is for SATA disks that are behind a SunplusIT USB to SATA
 bridge.
@@ -397,15 +400,17 @@ It is possible to set RAID device name as /dev/bus/N, where N is a SCSI bus
 number.
 Please see the \fBsmartctl\fP(8) man page for further details.
 
+.\" %ENDIF OS Linux
+.\" %IF OS Linux Windows Cygwin
 .I aacraid,H,L,ID
-\- [Linux only] [NEW EXPERIMENTAL SMARTD FEATURE]
+\- [Linux, Windows and Cygwin only] [NEW EXPERIMENTAL SMARTD FEATURE]
 the device consists of one or more SCSI/SAS disks connected to an AacRaid controller.
 The non-negative integers H,L,ID (Host number, Lun, ID) denote which disk
 on the controller is monitored.
 In log files and email messages this disk will be identified as aacraid_disk_HH_LL_ID.
 Please see the \fBsmartctl\fP(8) man page for further details.
 
-.\" %ENDIF OS Linux
+.\" %ENDIF OS Linux Windows Cygwin
 .\" %IF OS FreeBSD Linux
 .I 3ware,N
 \- [FreeBSD and Linux only] the device consists of one or more ATA disks
@@ -432,8 +437,8 @@ areca_disk_XX with XX in the range from 01 to 24 inclusive.
 Please see the \fBsmartctl\fP(8) man page for further details.
 
 .I areca,N/E
-\- [FreeBSD, Linux, Windows and Cygwin only] [NEW EXPERIMENTAL SMARTD FEATURE] the
-device consists of one or more SATA or SAS disks connected to an Areca SAS RAID controller.
+\- [FreeBSD, Linux, Windows and Cygwin only] the device consists of one
+or more SATA or SAS disks connected to an Areca SAS RAID controller.
 The integer N (range 1 to 128) denotes the channel (slot) and E (range
 1 to 8) denotes the enclosure.
 Important: This requires Areca SAS controller firmware version 1.51 or later.
@@ -463,8 +468,7 @@ Please see the \fBsmartctl\fP(8) man page for further details.
 
 .\" %ENDIF OS FreeBSD Linux
 .I ignore
-\- [NEW EXPERIMENTAL SMARTD FEATURE]
-the device specified by this configuration entry should be ignored.
+\- the device specified by this configuration entry should be ignored.
 This allows to ignore specific devices which are detected by a following
 DEVICESCAN configuration line.
 It may also be used to temporary disable longer multi-line configuration entries.
@@ -574,24 +578,41 @@ Directive are \fIon\fP and \fIoff\fP.  Also affects SCSI devices.
 [Please see the \fBsmartctl \-S\fP command-line option.]
 .TP
 .B \-H
-[ATA only] Check the SMART health status of the disk.  If any Prefailure
-Attributes are less than or equal to their threshold values, then disk
+[ATA] Check the health status of the disk with the SMART RETURN
+STATUS command.
+If this command reports a failing health status, then disk
 failure is predicted in less than 24 hours, and a message at loglevel
 .B \'LOG_CRIT\'
 will be logged to syslog.  [Please see the
 .B smartctl \-H
 command-line option.]
+.\" %IF OS FreeBSD Linux Windows Cygwin
+
+[NVMe] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTD FEATURE]
+Checks the "Critical Warning" byte from the SMART/Health Information log.
+If any warning bit is set, a message at loglevel \fB\'LOG_CRIT\'\fP
+will be logged to syslog.
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .TP
 .B \-l TYPE
 Reports increases in the number of errors in one of three SMART logs.  The
 valid arguments to this Directive are:
 
 .I error
-\- [ATA only] report if the number of ATA errors reported in the Summary SMART
+\- [ATA] report if the number of ATA errors reported in the Summary SMART
 error log has increased since the last check.
 
+.\" %IF OS FreeBSD Linux Windows Cygwin
+.I error
+\- [NVMe] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTD FEATURE]
+report if the "Number of Error Information Log Entries" from the
+SMART/Health Information log has increased since the last check.
+
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .I xerror
-\- [ATA only] report if the number of ATA errors reported in the Extended
+\- [ATA] report if the number of ATA errors reported in the Extended
 Comprehensive SMART error log has increased since the last check.
 
 If both \'\-l error\' and \'\-l xerror\' are specified, smartd checks
@@ -599,6 +620,13 @@ the maximum of both values.
 
 [Please see the \fBsmartctl \-l xerror\fP command-line option.]
 
+.\" %IF OS FreeBSD Linux Windows Cygwin
+.I xerror
+\- [NVMe] [FreeBSD, Linux, Windows and Cygwin only]
+[NEW EXPERIMENTAL SMARTD FEATURE]
+same as \'-l error\'.
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
+
 .I selftest
 \- report if the number of failed tests reported in the SMART
 Self-Test Log has increased since the last check, or if the timestamp
@@ -868,7 +896,6 @@ below.
 .\" %IF ENABLE_SMARTDPLUGINDIR
 .\" %IF NOT OS Windows
 
-[NEW EXPERIMENTAL SMARTD FEATURE]
 If a word of the comma separated list has the form \'@plugin\', a custom
 script /usr/local/etc/smartd_warning.d/plugin is run and the word is
 removed from the list before sending mail. The string \'plugin\' may be any
@@ -881,9 +908,8 @@ This is handled by the script /usr/local/etc/smartd_warning.sh
 .\" %ENDIF ENABLE_SMARTDPLUGINDIR
 .\" %IF OS Windows
 
-[Windows only] [NEW EXPERIMENTAL SMARTD FEATURE]
-If one of the following words are used as the first address in the
-comma separated list, warning messages are sent via WTSSendMessage().
+[Windows only] If one of the following words are used as the first address
+in the comma separated list, warning messages are sent via WTSSendMessage().
 This displays message boxes on the desktops of the selected sessions.
 Address \'\fBconsole\fP\' specifies the console session only,
 \'\fBactive\fP\' specifies the console session and all active remote
@@ -891,8 +917,6 @@ sessions, and \'\fBconnected\fP\' specifies the console session and
 all connected (active or waiting for login) remote sessions.
 This is handled by the script EXEDIR/smartd_warning.cmd which runs
 the tool EXEDIR/wtssendmsg.exe (see also \'\-M exec\' below).
-The addresses \'\fBmsgbox\fP\' and \'\fBsysmsgbox\fP\' are now
-deprecated and have the same effect as \'\fBconsole\fP\'.
 .\" %ENDIF OS Windows
 .TP
 .B \-M TYPE
@@ -978,7 +1002,7 @@ exported by \fBsmartd\fP are:
 is set to the argument of \-M exec, if present or else to \'mail\'
 (examples: /usr/local/bin/mail, mail).
 .IP \fBSMARTD_DEVICE\fP 4
-is set to the device path (examples: /dev/hda, /dev/sdb).
+is set to the device path (example: /dev/sda).
 .IP \fBSMARTD_DEVICETYPE\fP 4
 is set to the device type specified by \'-d\' directive or
 \'auto\' if none.
@@ -1082,10 +1106,6 @@ It it set to empty on \'\-M once\' and set to \'1\' on \'\-M daily\'.
 .\" They define a non-existent option; useful because man2html can't correctly reset the margins.
 .TP
 .B \&
-The shell which is used to run PATH is system-dependent. For vanilla
-Linux/glibc it\'s bash. For other systems, the man page for
-\fBpopen\fP(3) should say what shell is used.
-
 If the \'\-m ADD\' Directive is given with a normal address argument,
 then the executable pointed to by PATH will be run in a shell with
 STDIN receiving the body of the email message, and with the same
@@ -1130,7 +1150,7 @@ Some sample scripts are also included in
 /usr/local/share/doc/smartmontools/examplescripts/.
 .\" %ENDIF NOT OS Windows
 
-[NEW EXPERIMENTAL SMARTD FEATURE] The executable is run by the script
+The executable is run by the script
 .\" %IF NOT OS Windows
 /usr/local/etc/smartd_warning.sh.
 .\" %ENDIF NOT OS Windows
@@ -1339,6 +1359,12 @@ To combine all of the above reports, use:
 For ATA devices, smartd interprets Attribute 194 or 190 as Temperature Celsius
 by default. This can be changed to Attribute 9 or 220 by the drive
 database or by the \'\-v 9,temp\' or \'\-v 220,temp\' directive.
+.\" %IF OS FreeBSD Linux Windows Cygwin
+
+[NEW EXPERIMENTAL SMARTD FEATURE]
+For NVMe devices, smartd checks the maximum of the Composite Temperature value
+and all Temperature Sensor values reported by SMART/Health Information log.
+.\" %ENDIF OS FreeBSD Linux Windows Cygwin
 .TP
 .B \-F TYPE
 [ATA only] Modifies the behavior of \fBsmartd\fP to compensate for some
@@ -1492,8 +1518,11 @@ will do the same, but restricts the scan to ATA devices only.
 will do the same, but only monitors the SMART health status of the
 devices, (rather than the default \-a, which monitors all SMART
 properties).
-
+.br
 [NEW EXPERIMENTAL SMARTD FEATURE]
+Multiple \'\-d TYPE\' options may be specified with DEVICESCAN
+to combine the scan results of more than one TYPE.
+
 Configuration entries for specific devices may precede the \fBDEVICESCAN\fP entry.
 For example
 .nf
@@ -1581,5 +1610,7 @@ full path of this file.
 \fBsmartd\fP(8), \fBsmartctl\fP(8),
 \fBmail\fP(1), \fBregex\fP(7).
 
-.SH SVN ID OF THIS PAGE
-$Id: smartd.conf.5.in 3965 2014-07-20 14:46:41Z chrfranke $
+.SH PACKAGE VERSION
+CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
+.br
+$Id: smartd.conf.5.in 4307 2016-04-24 12:37:31Z chrfranke $
index 7013e60f9c294b3d2213a62734543788448bef56..99e0c23762a3cc8daaf686a28fae584a0cf9dacb 100644 (file)
@@ -1,10 +1,10 @@
 /*
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
+ * Copyright (C) 2008-16 Christian Franke
  * Copyright (C) 2000    Michael Cornwell <cornwell@acm.org>
  * Copyright (C) 2008    Oliver Bock <brevilo@users.sourceforge.net>
- * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -51,9 +51,6 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
 
 #ifdef _WIN32
 #ifdef _MSC_VER
@@ -78,6 +75,7 @@ typedef int pid_t;
 #include "dev_interface.h"
 #include "knowndrives.h"
 #include "scsicmds.h"
+#include "nvmecmds.h"
 #include "utility.h"
 
 // This is for solaris, where signal() resets the handler to SIG_DFL
@@ -102,13 +100,11 @@ typedef int pid_t;
 #define SIGQUIT_KEYNAME "CONTROL-\\"
 #endif // _WIN32
 
-#if defined (__SVR4) && defined (__sun)
-extern "C" int getdomainname(char *, int); // no declaration in header files!
-#endif
-
-const char * smartd_cpp_cvsid = "$Id: smartd.cpp 3948 2014-07-13 16:53:30Z chrfranke $"
+const char * smartd_cpp_cvsid = "$Id: smartd.cpp 4308 2016-04-24 13:36:10Z chrfranke $"
   CONFIG_H_CVSID;
 
+using namespace smartmontools;
+
 // smartd exit codes
 #define EXIT_BADCMD    1   // command line did not parse
 #define EXIT_BADCONF   2   // syntax error in config file
@@ -385,19 +381,24 @@ struct persistent_dev_state
   
   // SCSI ONLY
 
-  struct scsi_error_counter {
+  struct scsi_error_counter_t {
     struct scsiErrorCounter errCounter;
     unsigned char found;
-    scsi_error_counter() : found(0) { }
+    scsi_error_counter_t() : found(0)
+      { memset(&errCounter, 0, sizeof(errCounter)); }
   };
-  scsi_error_counter scsi_error_counters[3];
+  scsi_error_counter_t scsi_error_counters[3];
 
-  struct scsi_nonmedium_error {
+  struct scsi_nonmedium_error_t {
     struct scsiNonMediumError nme;
     unsigned char found;
-    scsi_nonmedium_error() : found(0) { }
+    scsi_nonmedium_error_t() : found(0)
+      { memset(&nme, 0, sizeof(nme)); }
   };
-  scsi_nonmedium_error scsi_nonmedium_error;
+  scsi_nonmedium_error_t scsi_nonmedium_error;
+
+  // NVMe only
+  uint64_t nvme_err_log_entries;
 
   persistent_dev_state();
 };
@@ -409,7 +410,8 @@ persistent_dev_state::persistent_dev_state()
   scheduled_test_next_check(0),
   selective_test_last_start(0),
   selective_test_last_end(0),
-  ataerrorcount(0)
+  ataerrorcount(0),
+  nvme_err_log_entries(0)
 {
 }
 
@@ -429,6 +431,7 @@ struct temp_dev_state
 
   bool powermodefail;                     // true if power mode check failed
   int powerskipcnt;                       // Number of checks skipped due to idle or standby mode
+  int lastpowermodeskipped;               // the last power mode that was skipped
 
   // SCSI ONLY
   unsigned char SmartPageSupported;       // has log sense IE page (0x2f)
@@ -461,6 +464,7 @@ temp_dev_state::temp_dev_state()
   tempmin_delay(0),
   powermodefail(false),
   powerskipcnt(0),
+  lastpowermodeskipped(0),
   SmartPageSupported(false),
   TempPageSupported(false),
   ReadECounterPageSupported(false),
@@ -566,12 +570,13 @@ static bool parse_dev_state_line(const char * line, persistent_dev_state & state
        "|(resvd)" // (23)
        ")" // 18)
       ")" // 16)
+     "|(nvme-err-log-entries)" // (24)
      ")" // 1)
-     " *= *([0-9]+)[ \n]*$", // (24)
+     " *= *([0-9]+)[ \n]*$", // (25)
     REG_EXTENDED
   );
 
-  const int nmatch = 1+24;
+  const int nmatch = 1+25;
   regmatch_t match[nmatch];
   if (!regex.execute(line, nmatch, match))
     return false;
@@ -629,6 +634,8 @@ static bool parse_dev_state_line(const char * line, persistent_dev_state & state
     else
       return false;
   }
+  else if (match[m+7].rm_so >= 0)
+    state.nvme_err_log_entries = val;
   else
     return false;
   return true;
@@ -734,6 +741,9 @@ static bool write_dev_state(const char * path, const persistent_dev_state & stat
     write_dev_state_line(f, "ata-smart-attribute", i, "resvd", pa.resvd);
   }
 
+  // NVMe only
+  write_dev_state_line(f, "nvme-err-log-entries", state.nvme_err_log_entries);
+
   return true;
 }
 
@@ -884,10 +894,6 @@ static int Goodbye(int status)
   // delete PID file, if one was created
   RemovePidFile();
 
-  // if we are exiting because of a code bug, tell user
-  if (status==EXIT_BADCODE)
-        PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
-
   // and this should be the final output from smartd before it exits
   PrintOut(status?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", status);
 
@@ -1056,13 +1062,13 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co
   env[11].set("SMARTD_NEXTDAYS", dates);
 
   // now construct a command to send this as EMAIL
-  char command[2048];
   if (!*executable)
     executable = "<mail>";
   const char * newadd = (!address.empty()? address.c_str() : "<nomailer>");
   const char * newwarn = (which? "Warning via" : "Test of");
 
 #ifndef _WIN32
+  char command[2048];
   snprintf(command, sizeof(command), "%s 2>&1", warning_script.c_str());
   
   // tell SYSLOG what we are about to do...
@@ -1109,12 +1115,9 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co
               errno?strerror(errno):"");
     else {
       // mail process apparently succeeded. Check and report exit status
-      int status8;
-
       if (WIFEXITED(status)) {
        // exited 'normally' (but perhaps with nonzero status)
-       status8=WEXITSTATUS(status);
-       
+        int status8 = WEXITSTATUS(status);
        if (status8>128)  
          PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", 
                   newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128));
@@ -1140,6 +1143,7 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co
   
 #else // _WIN32
   {
+    char command[2048];
     snprintf(command, sizeof(command), "cmd /c \"%s\"", warning_script.c_str());
 
     char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog()
@@ -1472,7 +1476,7 @@ static const char *GetValidArgList(char opt)
   case 'q':
     return "nodev, errors, nodevstartup, never, onecheck, showtests";
   case 'r':
-    return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
+    return "ioctl[,N], ataioctl[,N], scsiioctl[,N], nvmeioctl[,N]";
   case 'B':
   case 'p':
   case 'w':
@@ -1507,7 +1511,7 @@ static void Usage()
   PrintOut(LOG_INFO,"        [default is %s]\n\n", configfile);
 #ifdef HAVE_LIBCAP_NG
   PrintOut(LOG_INFO,"  -C, --capabilities\n");
-  PrintOut(LOG_INFO,"        Use capabilities.\n"
+  PrintOut(LOG_INFO,"        Drop unneeded Linux process capabilities.\n"
                     "        Warning: Mail notification does not work when used.\n\n");
 #endif
   PrintOut(LOG_INFO,"  -d, --debug\n");
@@ -1591,7 +1595,7 @@ static int read_ata_error_count(ata_device * device, const char * name,
   }
   else {
     ata_smart_exterrlog logx;
-    if (!ataReadExtErrorLog(device, &logx, 1 /*first sector only*/, firmwarebugs)) {
+    if (!ataReadExtErrorLog(device, &logx, 0, 1 /*first sector only*/, firmwarebugs)) {
       PrintOut(LOG_INFO,"Device: %s, Read Extended Comprehensive SMART Error Log failed\n",name);
       return -1;
     }
@@ -1706,7 +1710,7 @@ static bool check_pending_id(const dev_config & cfg, const dev_state & state,
   return true;
 }
 
-// Called by ATA/SCSIDeviceScan() after successful device check
+// Called by ATA/SCSI/NVMeDeviceScan() after successful device check
 static void finish_device_scan(dev_config & cfg, dev_state & state)
 {
   // Set cfg.emailfreq if user hasn't set it
@@ -1808,6 +1812,12 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade
     }
   }
 
+  // Check for ATA Security LOCK
+  unsigned short word128 = drive.words088_255[128-88];
+  bool locked = ((word128 & 0x0007) == 0x0007); // LOCKED|ENABLED|SUPPORTED
+  if (locked)
+    PrintOut(LOG_INFO, "Device: %s, ATA Security is **LOCKED**\n", name);
+
   // Set default '-C 197[+]' if no '-C ID' is specified.
   if (!cfg.curr_pending_set)
     cfg.curr_pending_id = get_unc_attr_id(false, cfg.attribute_defs, cfg.curr_pending_incr);
@@ -1849,8 +1859,12 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade
   if (ataEnableSmart(atadev)) {
     // Enable SMART command has failed
     PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
-    CloseDevice(atadev, name);
-    return 2; 
+
+    if (ataIsSmartEnabled(&drive) <= 0) {
+      CloseDevice(atadev, name);
+      return 2;
+    }
+    PrintOut(LOG_INFO, "Device: %s, proceeding since SMART is already enabled\n", name);
   }
   
   // disable device attribute autosave...
@@ -2060,7 +2074,10 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade
       PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
       cfg.powermode=0;
     } 
-    else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) {
+    else if (powermode!=0x00 && powermode!=0x01
+        && powermode!=0x40 && powermode!=0x41
+        && powermode!=0x80 && powermode!=0x81 && powermode!=0x82 && powermode!=0x83
+        && powermode!=0xff) {
       PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
               name, powermode);
       cfg.powermode=0;
@@ -2106,6 +2123,9 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade
     if (!isSCTErrorRecoveryControlCapable(&drive))
       PrintOut(LOG_INFO, "Device: %s, no SCT Error Recovery Control support, ignoring -l scterc\n",
                name);
+    else if (locked)
+      PrintOut(LOG_INFO, "Device: %s, no SCT support if ATA Security is LOCKED, ignoring -l scterc\n",
+               name);
     else if (   ataSetSCTErrorRecoveryControltime(atadev, 1, cfg.sct_erc_readtime )
              || ataSetSCTErrorRecoveryControltime(atadev, 2, cfg.sct_erc_writetime))
       PrintOut(LOG_INFO, "Device: %s, set of SCT Error Recovery Control failed\n", name);
@@ -2156,7 +2176,7 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade
 // please.
 static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scsidev)
 {
-  int k, err, req_len, avail_len, version, len;
+  int err, req_len, avail_len, version, len;
   const char *device = cfg.name.c_str();
   struct scsi_iec_mode_page iec;
   UINT8  tBuf[64];
@@ -2223,7 +2243,7 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
   uint64_t capacity = scsiGetSize(scsidev, &lb_size, NULL);
 
   if (capacity)
-    format_capacity(si_str, sizeof(si_str), capacity);
+    format_capacity(si_str, sizeof(si_str), capacity, ".");
   else
     si_str[0] = '\0';
 
@@ -2284,8 +2304,11 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
   
   // Flag that certain log pages are supported (information may be
   // available from other sources).
-  if (0 == scsiLogSense(scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0)) {
-    for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
+  if (0 == scsiLogSense(scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0) ||
+      0 == scsiLogSense(scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 68))
+      /* workaround for the bug #678 on ST8000NM0075/E001. Up to 64 pages + 4b header */
+  {
+    for (int k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
       switch (tBuf[k]) { 
       case TEMPERATURE_LPAGE:
         state.TempPageSupported = 1;
@@ -2394,6 +2417,119 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
   return 0;
 }
 
+// Convert 128 bit LE integer to uint64_t or its max value on overflow.
+static uint64_t le128_to_uint64(const unsigned char (& val)[16])
+{
+  for (int i = 8; i < 16; i++) {
+    if (val[i])
+      return ~(uint64_t)0;
+  }
+  uint64_t lo = val[7];
+  for (int i = 7-1; i >= 0; i--) {
+    lo <<= 8; lo += val[i];
+  }
+  return lo;
+}
+
+// Get max temperature in Kelvin reported in NVMe SMART/Health log.
+static int nvme_get_max_temp_kelvin(const nvme_smart_log & smart_log)
+{
+  int k = (smart_log.temperature[1] << 8) | smart_log.temperature[0];
+  for (int i = 0; i < 8; i++) {
+    if (smart_log.temp_sensor[i] > k)
+      k = smart_log.temp_sensor[i];
+  }
+  return k;
+}
+
+static int NVMeDeviceScan(dev_config & cfg, dev_state & state, nvme_device * nvmedev)
+{
+  const char *name = cfg.name.c_str();
+
+  // Device must be open
+
+  // Get ID Controller
+  nvme_id_ctrl id_ctrl;
+  if (!nvme_read_id_ctrl(nvmedev, id_ctrl)) {
+    PrintOut(LOG_INFO, "Device: %s, NVMe Identify Controller failed\n", name);
+    CloseDevice(nvmedev, name);
+    return 2;
+  }
+
+  // Get drive identity
+  char model[40+1], serial[20+1], firmware[8+1];
+  format_char_array(model, id_ctrl.mn);
+  format_char_array(serial, id_ctrl.sn);
+  format_char_array(firmware, id_ctrl.fr);
+
+  // Format device id string for warning emails
+  char nsstr[32] = "", capstr[32] = "";
+  unsigned nsid = nvmedev->get_nsid();
+  if (nsid != 0xffffffff)
+    snprintf(nsstr, sizeof(nsstr), ", NSID:%u", nsid);
+  uint64_t capacity = le128_to_uint64(id_ctrl.tnvmcap);
+  if (capacity)
+    format_capacity(capstr, sizeof(capstr), capacity, ".");
+  cfg.dev_idinfo = strprintf("%s, S/N:%s, FW:%s%s%s%s", model, serial, firmware,
+                             nsstr, (capstr[0] ? ", " : ""), capstr);
+
+  PrintOut(LOG_INFO, "Device: %s, %s\n", name, cfg.dev_idinfo.c_str());
+
+  // Read SMART/Health log
+  nvme_smart_log smart_log;
+  if (!nvme_read_smart_log(nvmedev, smart_log)) {
+    PrintOut(LOG_INFO, "Device: %s, failed to read NVMe SMART/Health Information\n", name);
+    CloseDevice(nvmedev, name);
+    return 2;
+  }
+
+  // Check temperature sensor support
+  if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) {
+    if (!nvme_get_max_temp_kelvin(smart_log)) {
+      PrintOut(LOG_INFO, "Device: %s, no Temperature sensors, ignoring -W %d,%d,%d\n",
+               name, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit);
+      cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
+    }
+  }
+
+  // Init total error count
+  if (cfg.errorlog || cfg.xerrorlog) {
+    state.nvme_err_log_entries = le128_to_uint64(smart_log.num_err_log_entries);
+  }
+
+  // If no supported tests selected, return
+  if (!(   cfg.smartcheck || cfg.errorlog || cfg.xerrorlog
+        || cfg.tempdiff   || cfg.tempinfo || cfg.tempcrit )) {
+    CloseDevice(nvmedev, name);
+    return 3;
+  }
+
+  // Tell user we are registering device
+  PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n", name);
+
+  // Make sure that init_standby_check() ignores NVMe devices
+  cfg.offlinests_ns = cfg.selfteststs_ns = false;
+
+  CloseDevice(nvmedev, name);
+
+  if (!state_path_prefix.empty()) {
+    // Build file name for state file
+    std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_');
+    std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_');
+    nsstr[0] = 0;
+    if (nsid != 0xffffffff)
+      snprintf(nsstr, sizeof(nsstr), "-n%u", nsid);
+    cfg.state_file = strprintf("%s%s-%s%s.nvme.state", state_path_prefix.c_str(), model, serial, nsstr);
+    // Read previous state
+    if (read_dev_state(cfg.state_file.c_str(), state))
+      PrintOut(LOG_INFO, "Device: %s, state read from %s\n", name, cfg.state_file.c_str());
+  }
+
+  finish_device_scan(cfg, state);
+
+  return 0;
+}
+
 // If the self-test log has got more self-test errors (or more recent
 // self-test errors) recorded, then notify user.
 static void CheckSelfTestLogs(const dev_config & cfg, dev_state & state, int newi)
@@ -2999,6 +3135,28 @@ static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device
   if (cfg.emailtest)
     MailWarning(cfg, state, 0, "TEST EMAIL from smartd for device: %s", name);
 
+  // User may have requested (with the -n Directive) to leave the disk
+  // alone if it is in idle or standby mode.  In this case check the
+  // power mode first before opening the device for full access,
+  // and exit without check if disk is reported in standby.
+  if (cfg.powermode && !state.powermodefail) {
+    // Note that 'is_powered_down()' handles opening the device itself, and
+    // can be used before calling 'open()' (that's the whole point of 'is_powered_down()'!).
+    if (atadev->is_powered_down())
+    {
+      // skip at most powerskipmax checks
+      if (!cfg.powerskipmax || state.powerskipcnt<cfg.powerskipmax) {
+        // report first only except if state has changed, avoid waking up system disk
+        if ((!state.powerskipcnt || state.lastpowermodeskipped != -1) && !cfg.powerquiet) {
+          PrintOut(LOG_INFO, "Device: %s, is in %s mode, suspending checks\n", name, "STANDBY (OS)");
+          state.lastpowermodeskipped = -1;
+        }
+        state.powerskipcnt++;
+        return 0;
+      }
+    }
+  }
+
   // if we can't open device, fail gracefully rather than hard --
   // perhaps the next time around we'll be able to open it.  ATAPI
   // cd/dvd devices will hang awaiting media if O_NONBLOCK is not
@@ -3035,20 +3193,48 @@ static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device
       if (cfg.powermode>=1)
         dontcheck=1;
       break;
-    case 0:
+    case 0x00:
       // STANDBY
       mode="STANDBY";
       if (cfg.powermode>=2)
         dontcheck=1;
       break;
+    case 0x01:
+      // STANDBY_Y
+      mode="STANDBY_Y";
+      if (cfg.powermode>=2)
+        dontcheck=1;
+      break;
     case 0x80:
       // IDLE
       mode="IDLE";
       if (cfg.powermode>=3)
         dontcheck=1;
       break;
+    case 0x81:
+      // IDLE_A
+      mode="IDLE_A";
+      if (cfg.powermode>=3)
+        dontcheck=1;
+      break;
+    case 0x82:
+      // IDLE_B
+      mode="IDLE_B";
+      if (cfg.powermode>=3)
+        dontcheck=1;
+      break;
+    case 0x83:
+      // IDLE_C
+      mode="IDLE_C";
+      if (cfg.powermode>=3)
+        dontcheck=1;
+      break;
     case 0xff:
       // ACTIVE/IDLE
+    case 0x40:
+      // ACTIVE
+    case 0x41:
+      // ACTIVE
       mode="ACTIVE or IDLE";
       break;
     default:
@@ -3064,8 +3250,11 @@ static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device
       // skip at most powerskipmax checks
       if (!cfg.powerskipmax || state.powerskipcnt<cfg.powerskipmax) {
         CloseDevice(atadev, name);
-        if (!state.powerskipcnt && !cfg.powerquiet) // report first only and avoid waking up system disk
+        // report first only except if state has changed, avoid waking up system disk
+        if ((!state.powerskipcnt || state.lastpowermodeskipped != powermode) && !cfg.powerquiet) {
           PrintOut(LOG_INFO, "Device: %s, is in %s mode, suspending checks\n", name, mode);
+          state.lastpowermodeskipped = powermode;
+        }
         state.powerskipcnt++;
         return 0;
       }
@@ -3218,12 +3407,7 @@ static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device
 
 static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_device * scsidev, bool allow_selftests)
 {
-    UINT8 asc, ascq;
-    UINT8 currenttemp;
-    UINT8 triptemp;
-    UINT8  tBuf[252];
     const char * name = cfg.name.c_str();
-    const char *cp;
 
     // If the user has asked for it, test the email warning system
     if (cfg.emailtest)
@@ -3238,9 +3422,9 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic
     } else if (debugmode)
         PrintOut(LOG_INFO,"Device: %s, opened SCSI device\n", name);
     reset_warning_mail(cfg, state, 9, "open device worked again");
-    currenttemp = 0;
-    asc = 0;
-    ascq = 0;
+
+    UINT8 asc = 0, ascq = 0;
+    UINT8 currenttemp = 0, triptemp = 0;
     if (!state.SuppressReport) {
         if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported,
                         &asc, &ascq, &currenttemp, &triptemp)) {
@@ -3251,7 +3435,7 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic
         }
     }
     if (asc > 0) {
-        cp = scsiGetIEString(asc, ascq);
+        const char * cp = scsiGetIEString(asc, ascq);
         if (cp) {
             PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp);
             MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp);
@@ -3278,6 +3462,7 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic
     }
     if (!cfg.attrlog_file.empty()){
       // saving error counters to state
+      UINT8 tBuf[252];
       if (state.ReadECounterPageSupported && (0 == scsiLogSense(scsidev,
           READ_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
           scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[0].errCounter);
@@ -3303,6 +3488,89 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic
     return 0;
 }
 
+static int NVMeCheckDevice(const dev_config & cfg, dev_state & state, nvme_device * nvmedev)
+{
+  const char * name = cfg.name.c_str();
+
+  // TODO: Use common open function for ATA/SCSI/NVMe
+  // If user has asked, test the email warning system
+  if (cfg.emailtest)
+    MailWarning(cfg, state, 0, "TEST EMAIL from smartd for device: %s", name);
+
+  if (!nvmedev->open()) {
+    PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, nvmedev->get_errmsg());
+    MailWarning(cfg, state, 9, "Device: %s, unable to open device", name);
+    return 1;
+  }
+  if (debugmode)
+    PrintOut(LOG_INFO,"Device: %s, opened NVMe device\n", name);
+  reset_warning_mail(cfg, state, 9, "open device worked again");
+
+  // Read SMART/Health log
+  nvme_smart_log smart_log;
+  if (!nvme_read_smart_log(nvmedev, smart_log)) {
+      PrintOut(LOG_INFO, "Device: %s, failed to read NVMe SMART/Health Information\n", name);
+      MailWarning(cfg, state, 6, "Device: %s, failed to read NVMe SMART/Health Information", name);
+      state.must_write = true;
+      return 0;
+  }
+
+  // Check Critical Warning bits
+  if (cfg.smartcheck && smart_log.critical_warning) {
+    unsigned char w = smart_log.critical_warning;
+    std::string msg;
+    static const char * const wnames[] =
+      {"LowSpare", "Temperature", "Reliability", "R/O", "VolMemBackup"};
+
+    for (unsigned b = 0, cnt = 0; b < 8 ; b++) {
+      if (!(w & (1 << b)))
+        continue;
+      if (cnt)
+        msg += ", ";
+      if (++cnt > 3) {
+        msg += "..."; break;
+      }
+      if (b >= sizeof(wnames)/sizeof(wnames[0])) {
+        msg += "*Unknown*"; break;
+      }
+      msg += wnames[b];
+    }
+
+    PrintOut(LOG_CRIT, "Device: %s, Critical Warning (0x%02x): %s\n", name, w, msg.c_str());
+    MailWarning(cfg, state, 1, "Device: %s, Critical Warning (0x%02x): %s", name, w, msg.c_str());
+    state.must_write = true;
+  }
+
+  // Check temperature limits
+  if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) {
+    int k = nvme_get_max_temp_kelvin(smart_log);
+    // Convert Kelvin to positive Celsius (TODO: Allow negative temperatures)
+    int c = k - 273;
+    if (c < 1)
+      c = 1;
+    else if (c > 0xff)
+      c = 0xff;
+    CheckTemperature(cfg, state, c, 0);
+  }
+
+  // Check if number of errors has increased
+  if (cfg.errorlog || cfg.xerrorlog) {
+    uint64_t oldcnt = state.nvme_err_log_entries;
+    uint64_t newcnt = le128_to_uint64(smart_log.num_err_log_entries);
+    if (newcnt > oldcnt) {
+      PrintOut(LOG_CRIT, "Device: %s, number of Error Log entries increased from %" PRIu64 " to %" PRIu64 "\n",
+               name, oldcnt, newcnt);
+      MailWarning(cfg, state, 4, "Device: %s, number of Error Log entries increased from %" PRIu64 " to %" PRIu64,
+                  name, oldcnt, newcnt);
+      state.must_write = true;
+    }
+    state.nvme_err_log_entries = newcnt;
+  }
+
+  CloseDevice(nvmedev, name);
+  return 0;
+}
+
 // 0=not used, 1=not disabled, 2=disable rejected by OS, 3=disabled
 static int standby_disable_state = 0;
 
@@ -3393,6 +3661,8 @@ static void CheckDevicesOnce(const dev_config_vector & configs, dev_state_vector
       ATACheckDevice(cfg, state, dev->to_ata(), firstpass, allow_selftests);
     else if (dev->is_scsi())
       SCSICheckDevice(cfg, state, dev->to_scsi(), allow_selftests);
+    else if (dev->is_nvme())
+      NVMeCheckDevice(cfg, state, dev->to_nvme());
   }
 
   do_disable_standby_check(configs, states);
@@ -3657,7 +3927,7 @@ static const char * strtok_dequote(const char * delimiters)
 // This function returns 1 if it has correctly parsed one token (and
 // any arguments), else zero if no tokens remain.  It returns -1 if an
 // error was encountered.
-static int ParseToken(char * token, dev_config & cfg)
+static int ParseToken(char * token, dev_config & cfg, smart_devtype_list & scan_types)
 {
   char sym;
   const char * name = cfg.name.c_str();
@@ -3729,8 +3999,10 @@ static int ParseToken(char * token, dev_config & cfg)
       cfg.removable = true;
     } else if (!strcmp(arg, "auto")) {
       cfg.dev_type = "";
+      scan_types.clear();
     } else {
       cfg.dev_type = arg;
+      scan_types.push_back(arg);
     }
     break;
   case 'F':
@@ -3898,13 +4170,13 @@ static int ParseToken(char * token, dev_config & cfg)
                  configfile, lineno, name, arg, cfg.test_regex.get_errmsg());
         return -1;
       }
+      // Do a bit of sanity checking and warn user if we think that
+      // their regexp is "strange". User probably confused about shell
+      // glob(3) syntax versus regular expression syntax regexp(7).
+      if (arg[(val = strspn(arg, "0123456789/.-+*|()?^$[]SLCOcnr"))])
+        PrintOut(LOG_INFO,  "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n",
+                 configfile, lineno, name, val+1, arg[val], arg);
     }
-    // Do a bit of sanity checking and warn user if we think that
-    // their regexp is "strange". User probably confused about shell
-    // glob(3) syntax versus regular expression syntax regexp(7).
-    if (arg[(val = strspn(arg, "0123456789/.-+*|()?^$[]SLCOcnr"))])
-      PrintOut(LOG_INFO,  "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n",
-               configfile, lineno, name, val+1, arg[val], arg);
     break;
   case 'm':
     // send email to address that follows
@@ -3914,17 +4186,13 @@ static int ParseToken(char * token, dev_config & cfg)
       if (!cfg.emailaddress.empty())
         PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n",
                  configfile, lineno, name, cfg.emailaddress.c_str());
-#ifdef _WIN32
+#ifdef _WIN32 // TODO: Remove after smartmontools 6.5
       if (   !strcmp(arg, "msgbox")          || !strcmp(arg, "sysmsgbox")
           || str_starts_with(arg, "msgbox,") || str_starts_with(arg, "sysmsgbox,")) {
-        cfg.emailaddress = "console";
-        const char * arg2 = strchr(arg, ',');
-        if (arg2)
-          cfg.emailaddress += arg2;
-        PrintOut(LOG_INFO, "File %s line %d (drive %s): Deprecated -m %s changed to -m %s\n",
-                 configfile, lineno, name, arg, cfg.emailaddress.c_str());
+        PrintOut(LOG_CRIT, "File %s line %d (drive %s): -m %s is no longer supported, use -m console[,...] instead\n",
+                 configfile, lineno, name, arg);
+        return -1;
       }
-      else
 #endif
       cfg.emailaddress = arg;
     }
@@ -3998,8 +4266,8 @@ static int ParseToken(char * token, dev_config & cfg)
     break;
   case 'W':
     // track Temperature
-    if ((val=Get3Integers(arg=strtok(NULL,delim), name, token, lineno, configfile,
-                          &cfg.tempdiff, &cfg.tempinfo, &cfg.tempcrit))<0)
+    if (Get3Integers(arg=strtok(NULL, delim), name, token, lineno, configfile,
+                     &cfg.tempdiff, &cfg.tempinfo, &cfg.tempcrit) < 0)
       return -1;
     break;
   case 'v':
@@ -4130,7 +4398,8 @@ static int ParseToken(char * token, dev_config & cfg)
 // -2: found an error
 //
 // Note: this routine modifies *line from the caller!
-static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & default_conf, int lineno, /*const*/ char * line)
+static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & default_conf,
+  smart_devtype_list & scan_types, int lineno, /*const*/ char * line)
 {
   const char *delim = " \n\t";
 
@@ -4159,7 +4428,7 @@ static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & defaul
 
   // parse tokens one at a time from the file.
   while (char * token = strtok(0, delim)) {
-    int rc = ParseToken(token, cfg);
+    int rc = ParseToken(token, cfg, scan_types);
     if (rc < 0)
       // error found on the line
       return -2;
@@ -4171,6 +4440,13 @@ static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & defaul
     // PrintOut(LOG_INFO,"Parsed token %s\n",token);
   }
 
+  // Check for multiple -d TYPE directives
+  if (retval != -1 && scan_types.size() > 1) {
+    PrintOut(LOG_CRIT, "Drive: %s, invalid multiple -d TYPE Directives on line %d of file %s\n",
+             cfg.name.c_str(), cfg.lineno, configfile);
+    return -2;
+  }
+
   // Don't perform checks below for DEFAULT entries
   if (retval == 0)
     return retval;
@@ -4227,7 +4503,7 @@ static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & defaul
 // Empty configuration file ==> conf_entries.empty()
 // No configuration file    ==> conf_entries[0].lineno == 0
 // SCANDIRECTIVE found      ==> conf_entries.back().lineno != 0 (size >= 1)
-static int ParseConfigFile(dev_config_vector & conf_entries)
+static int ParseConfigFile(dev_config_vector & conf_entries, smart_devtype_list & scan_types)
 {
   // maximum line length in configuration file
   const int MAXLINELEN = 256;
@@ -4256,7 +4532,7 @@ static int ParseConfigFile(dev_config_vector & conf_entries)
   if (!f) {
     char fakeconfig[] = SCANDIRECTIVE " -a"; // TODO: Remove this hack, build cfg_entry.
 
-    if (ParseConfigLine(conf_entries, default_conf, 0, fakeconfig) != -1)
+    if (ParseConfigLine(conf_entries, default_conf, scan_types, 0, fakeconfig) != -1)
       throw std::logic_error("Internal error parsing " SCANDIRECTIVE);
     return 0;
   }
@@ -4288,7 +4564,7 @@ static int ParseConfigFile(dev_config_vector & conf_entries)
     // are we at the end of the file?
     if (!code){
       if (cont) {
-        scandevice = ParseConfigLine(conf_entries, default_conf, contlineno, fullline);
+        scandevice = ParseConfigLine(conf_entries, default_conf, scan_types, contlineno, fullline);
         // See if we found a SCANDIRECTIVE directive
         if (scandevice==-1)
           return 0;
@@ -4296,7 +4572,6 @@ static int ParseConfigFile(dev_config_vector & conf_entries)
         if (scandevice==-2)
           return -1;
         // the final line is part of a continuation line
-        cont=0;
         entry+=scandevice;
       }
       break;
@@ -4342,7 +4617,8 @@ static int ParseConfigFile(dev_config_vector & conf_entries)
     }
 
     // Not a continuation line. Parse it
-    scandevice = ParseConfigLine(conf_entries, default_conf, contlineno, fullline);
+    scan_types.clear();
+    scandevice = ParseConfigLine(conf_entries, default_conf, scan_types, contlineno, fullline);
 
     // did we find a scandevice directive?
     if (scandevice==-1)
@@ -4442,7 +4718,7 @@ static void ParseOpts(int argc, char **argv)
 
   opterr=optopt=0;
   bool badarg = false;
-  bool no_defaultdb = false; // set true on '-B FILE'
+  bool use_default_db = true; // set false on '-B FILE'
 
   // Parse input options.
   int optchar;
@@ -4529,33 +4805,22 @@ static void ParseOpts(int argc, char **argv)
     case 'r':
       // report IOCTL transactions
       {
-        int i;
-        char *s;
-
-        // split_report_arg() may modify its first argument string, so use a
-        // copy of optarg in case we want optarg for an error message.
-        if (!(s = strdup(optarg))) {
-          PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n");
-          EXIT(EXIT_NOMEM);
-        }
-        if (split_report_arg(s, &i)) {
+        int n1 = -1, n2 = -1, len = strlen(optarg);
+        char s[9+1]; unsigned i = 1;
+        sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2);
+        if (!((n1 == len || n2 == len) && 1 <= i && i <= 4)) {
           badarg = true;
-        } else if (i<1 || i>3) {
-          debugmode=1;
-          PrintHead();
-          PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg);
-          PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n");
-          EXIT(EXIT_BADCMD);
         } else if (!strcmp(s,"ioctl")) {
-          ata_debugmode = scsi_debugmode = i;
+          ata_debugmode = scsi_debugmode = nvme_debugmode = i;
         } else if (!strcmp(s,"ataioctl")) {
           ata_debugmode = i;
         } else if (!strcmp(s,"scsiioctl")) {
           scsi_debugmode = i;
+        } else if (!strcmp(s,"nvmeioctl")) {
+          nvme_debugmode = i;
         } else {
           badarg = true;
         }
-        free(s);  // TODO: use std::string
       }
       break;
     case 'c':
@@ -4583,7 +4848,7 @@ static void ParseOpts(int argc, char **argv)
         if (*path == '+' && path[1])
           path++;
         else
-          no_defaultdb = true;
+          use_default_db = false;
         unsigned char savedebug = debugmode; debugmode = 1;
         if (!read_drive_database(path))
           EXIT(EXIT_BADCMD);
@@ -4688,9 +4953,9 @@ static void ParseOpts(int argc, char **argv)
 #endif
 
   // Read or init drive database
-  if (!no_defaultdb) {
+  {
     unsigned char savedebug = debugmode; debugmode = 1;
-    if (!read_default_drive_databases())
+    if (!init_drive_database(use_default_db))
         EXIT(EXIT_BADCMD);
     debugmode = savedebug;
   }
@@ -4703,14 +4968,17 @@ static void ParseOpts(int argc, char **argv)
 // SCANDIRECTIVE Directive was found.  It makes entries for device
 // names returned by scan_smart_devices() in os_OSNAME.cpp
 static int MakeConfigEntries(const dev_config & base_cfg,
-  dev_config_vector & conf_entries, smart_device_list & scanned_devs, const char * type)
+  dev_config_vector & conf_entries, smart_device_list & scanned_devs,
+  const smart_devtype_list & types)
 {
   // make list of devices
   smart_device_list devlist;
-  if (!smi()->scan_smart_devices(devlist, (*type ? type : 0)))
-    PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
+  if (!smi()->scan_smart_devices(devlist, types)) {
+    PrintOut(LOG_CRIT, "DEVICESCAN failed: %s\n", smi()->get_errmsg());
+    return 0;
+  }
   
-  // if no devices, or error constructing list, return
+  // if no devices, return
   if (devlist.size() <= 0)
     return 0;
 
@@ -4729,7 +4997,7 @@ static int MakeConfigEntries(const dev_config & base_cfg,
     dev_config & cfg = conf_entries.back();
     cfg.name = dev->get_info().info_name;
     cfg.dev_name = dev->get_info().dev_name;
-    cfg.dev_type = type;
+    cfg.dev_type = dev->get_info().dev_type;
   }
   
   return devlist.size();
@@ -4754,7 +5022,8 @@ static void CanNotRegister(const char *name, const char *type, int line, bool sc
 static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_device_list & scanned_devs)
 {
   // parse configuration file configfile (normally /etc/smartd.conf)  
-  int entries = ParseConfigFile(conf_entries);
+  smart_devtype_list scan_types;
+  int entries = ParseConfigFile(conf_entries, scan_types);
 
   if (entries < 0) {
     // There was an error reading the configuration file.
@@ -4782,14 +5051,14 @@ static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_devic
       PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile);
     
     // make config list of devices to search for
-    MakeConfigEntries(first, conf_entries, scanned_devs, first.dev_type.c_str());
+    MakeConfigEntries(first, conf_entries, scanned_devs, scan_types);
 
     // warn user if scan table found no devices
     if (conf_entries.empty())
       PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n");
   } 
   else
-    PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile);
+    PrintOut(LOG_CRIT, "Configuration file %s parsed but has no entries\n", configfile);
   
   return conf_entries.size();
 }
@@ -4930,8 +5199,15 @@ static void RegisterDevices(const dev_config_vector & conf_entries, smart_device
         dev.reset();
       }
     }
+    // or register NVMe devices
+    else if (dev->is_nvme()) {
+      if (NVMeDeviceScan(cfg, state, dev->to_nvme())) {
+        CanNotRegister(cfg.name.c_str(), "NVMe", cfg.lineno, scanning);
+        dev.reset();
+      }
+    }
     else {
-      PrintOut(LOG_INFO, "Device: %s, neither ATA nor SCSI device\n", cfg.name.c_str());
+      PrintOut(LOG_INFO, "Device: %s, neither ATA, SCSI nor NVMe device\n", cfg.name.c_str());
       dev.reset();
     }
 
@@ -5057,13 +5333,16 @@ static int main_worker(int argc, char **argv)
 
       // Log number of devices we are monitoring...
       if (devices.size() > 0 || quit==2 || (quit==1 && !firstpass)) {
-        int numata = 0;
+        int numata = 0, numscsi = 0;
         for (unsigned i = 0; i < devices.size(); i++) {
-          if (devices.at(i)->is_ata())
+          const smart_device * dev = devices.at(i);
+          if (dev->is_ata())
             numata++;
+          else if (dev->is_scsi())
+            numscsi++;
         }
-        PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n",
-                 numata, devices.size() - numata);
+        PrintOut(LOG_INFO,"Monitoring %d ATA/SATA, %d SCSI/SAS and %d NVMe devices\n",
+                 numata, numscsi, (int)devices.size() - numata - numscsi);
       }
       else {
         PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
@@ -5159,6 +5438,16 @@ static int smartd_main(int argc, char **argv)
     status = EXIT_BADCODE;
   }
 
+  // Check for remaining device objects
+  if (smart_device::get_num_objects() != 0) {
+    PrintOut(LOG_CRIT, "Smartd: Internal Error: %d device object(s) left at exit.\n",
+             smart_device::get_num_objects());
+    status = EXIT_BADCODE;
+  }
+
+  if (status == EXIT_BADCODE)
+    PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
+
   if (is_initialized)
     status = Goodbye(status);
 
index 0c1e52c6de9df214d9455c650a3ee03137621eba..afc67db08750da3291a81ff84b8e8775f01ddcce 100644 (file)
@@ -2,7 +2,7 @@
 
 # smartmontools init file for smartd
 # Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-# $Id: smartd.initd.in 3727 2012-12-13 17:23:06Z samm2 $
+# $Id: smartd.initd.in 4120 2015-08-27 16:12:21Z samm2 $
 
 # For RedHat and cousins:
 # chkconfig: 2345 40 40
@@ -540,7 +540,7 @@ elif uname | grep -i CYGWIN > /dev/null 2>&1 ; then
 Controls and monitors storage devices using the Self-Monitoring \
 Analysis and Reporting Technology System (S.M.A.R.T.) \
 built into ATA and SCSI Hard Drives. \
-http://smartmontools.sourceforge.net/"
+http://www.smartmontools.org/"
 
     # Source configuration file.  This should define the shell variable smartd_opts.
     # Email smartmontools-support@lists.sourceforge.net if there is a better choice
index 24e6afc1c1b8be82c006dc431c60b7f7f6d4bf75..1517be5dcc08e7f8bc9831c63f1858b5a2cc5d54 100644 (file)
@@ -1,8 +1,8 @@
 .ig
 Copyright (C) 2013 Hannes von Haugwitz <hannes@vonhaugwitz.com>
-Copyright (C) 2014 Christian Franke <smartmontools-support@lists.sourceforge.net>
+Copyright (C) 2014-16 Christian Franke
 
-$Id: update-smart-drivedb.8.in 3961 2014-07-19 16:44:10Z chrfranke $
+$Id: update-smart-drivedb.8.in 4223 2016-02-26 20:18:40Z chrfranke $
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -13,19 +13,21 @@ You should have received a copy of the GNU General Public License
 (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
 
 ..
-.TH UPDATE-SMART-DRIVEDB 8 CURRENT_SVN_DATE CURRENT_SVN_VERSION CURRENT_SVN_DATE
+.TH UPDATE-SMART-DRIVEDB 8 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools"
 .SH NAME
 update-smart-drivedb \- update smartmontools drive database
 
 .SH "SYNOPSIS"
 .B update-smart-drivedb
-.RB [ -v ]
+.RI [ OPTIONS ]
 .RI [ DESTFILE ]
 
-.SH PACKAGE VERSION
-CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
-
 .SH "DESCRIPTION"
+.\" %IF NOT OS ALL
+.\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools.
+.\"! It does not contain info specific to other platforms.]
+.\"! .PP
+.\" %ENDIF NOT OS ALL
 .B update-smart-drivedb
 updates
 .B /usr/local/share/smartmontools/drivedb.h
@@ -36,18 +38,18 @@ from smartmontools SVN repository.
 It tries to download first from the current branch and then from
 trunk. The tools used for downloading are either
 .BR curl (1),
-.BR wget "(1) or"
-.BR lynx (1).
+.BR wget (1),
+.BR lynx (1),
 .\" %IF OS FreeBSD
-On FreeBSD,
 .BR fetch (1)
-is used as a fallback.
+[FreeBSD only],
 .\" %ENDIF OS FreeBSD
 .\" %IF OS OpenBSD
-On OpenBSD,
 .BR ftp (1)
-is used as a fallback.
+[OpenBSD only],
 .\" %ENDIF OS OpenBSD
+or
+.BR svn (1).
 
 The old file is kept if the downloaded file is identical (ignoring
 the differences in Id string) otherwise it is moved to
@@ -55,8 +57,60 @@ the differences in Id string) otherwise it is moved to
 
 .SH "OPTIONS"
 .TP
-\-v
-verbose output
+.B \-s SMARTCTL
+Use the
+.BR smartctl (8)
+executable at path SMARTCTL for drive database syntax check.
+The form \'\-s \-\' disables the syntax check.
+The default is
+.BR /usr/local/sbin/smartctl .
+.TP
+.B \-t TOOL
+Use TOOL for download.
+TOOL is one of:
+.I curl wget lynx
+.\" %IF OS FreeBSD
+.I fetch
+.\" %ENDIF OS FreeBSD
+.\" %IF OS OpenBSD
+.I ftp
+.\" %ENDIF OS OpenBSD
+.IR svn .
+The default is the first one found in PATH.
+.TP
+.B \-u LOCATION
+Use URL of LOCATION for download. LOCATION is one of:
+.br
+.I sf
+(Sourceforge code browser via HTTPS),
+.br
+.I svn
+(SVN repository via HTTPS),
+.br
+.I svni
+(SVN repository via HTTP),
+.br
+.I trac
+(Trac code browser via HTTPS).
+.br
+The default is
+.IR svn .
+.TP
+.B \-\-cacert FILE
+Use CA certificates from FILE to verify the peer.
+.TP
+.B \-\-capath DIR
+Use CA certificate files from DIR to verify the peer.
+.TP
+.B \-\-insecure
+Don't abort download if certificate verification fails.
+This option is also required if a HTTP URL is selected with \'-u\' option.
+.TP
+.B \-\-dryrun
+Print download commands only.
+.TP
+.B \-v
+Verbose output.
 
 .SH "EXAMPLES"
 .nf
@@ -83,22 +137,30 @@ current drive database.
 previous drive database.
 .TP
 .B /usr/local/share/smartmontools/drivedb.h.error
-new drive database rejected due to syntax errors.
+new drive database if rejected due to syntax errors.
 .TP
 .B /usr/local/share/smartmontools/drivedb.h.lastcheck
 empty file created if downloaded file was identical.
 
-.SH "SEE ALSO"
-.BR smartctl (8),
-.BR smartd (8).
-
 .SH AUTHORS
-\fBChristian Franke\fP
-.br
-\fBsmartmontools\-support@lists.sourceforge.net\fP
+\fBChristian Franke\fP.
 .br
 This manual page was originally written by
 .BR "Hannes von Haugwitz <hannes@vonhaugwitz.com>" .
 
-.SH SVN ID OF THIS PAGE
-$Id: update-smart-drivedb.8.in 3961 2014-07-19 16:44:10Z chrfranke $
+.SH REPORTING BUGS
+To submit a bug report, create a ticket in smartmontools wiki:
+.br
+<\fBhttp://www.smartmontools.org/\fP>.
+.br
+Alternatively send the info to the smartmontools support mailing list:
+.br
+<\fBhttps://lists.sourceforge.net/lists/listinfo/smartmontools-support\fB>.
+
+.SH SEE ALSO
+\fBsmartctl\fP(8), \fBsmartd\fP(8).
+
+.SH PACKAGE VERSION
+CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
+.br
+$Id: update-smart-drivedb.8.in 4223 2016-02-26 20:18:40Z chrfranke $
index 1bd6395b67b7c4c700cf43e09fff897e94760197..98434446c3004195f9c2cc24338df299f33b7215 100644 (file)
@@ -2,7 +2,7 @@
 #
 # smartmontools drive database update script
 #
-# Copyright (C) 2010-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
+# Copyright (C) 2010-16 Christian Franke
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -12,7 +12,7 @@
 # You should have received a copy of the GNU General Public License
 # (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
 #
-# $Id: update-smart-drivedb.in 3814 2013-06-04 19:38:25Z chrfranke $
+# $Id: update-smart-drivedb.in 4224 2016-02-26 20:29:24Z chrfranke $
 #
 
 set -e
@@ -34,104 +34,343 @@ os_dltools="@os_dltools@"
 BRANCH="@DRIVEDB_BRANCH@"
 
 # Default drivedb location
-DEST="$drivedbdir/drivedb.h"
+DRIVEDB="$drivedbdir/drivedb.h"
 
 # Smartctl used for syntax check
 SMARTCTL="$sbindir/smartctl"
 
-# Download URL for sourceforge code browser
-SRCEXPR='http://sourceforge.net/p/smartmontools/code/HEAD/tree/$location/smartmontools/drivedb.h?format=raw'
+myname=$0
 
-# Parse options
-q="-q "
-case "$1" in
-  -v) q=; shift ;;
-esac
-
-case "$*" in
-  -*|*\ *)
-    cat <<EOF
+usage()
+{
+  cat <<EOF
 smartmontools $VERSION drive database update script
 
-Usage: $0 [-v] [DESTFILE]
+Usage: $myname [OPTIONS] [DESTFILE]
 
-  -v    verbose output
+  -s SMARTCTL     Use SMARTCTL for syntax check ('-s -' to disable)
+                  [default: $SMARTCTL]
+  -t TOOL         Use TOOL for download: $os_dltools
+                  [default: first one found in PATH]
+  -u LOCATION     Use URL of LOCATION for download:
+                    sf (Sourceforge code browser via HTTPS)
+                    svn (SVN repository via HTTPS) [default]
+                    svni (SVN repository via HTTP)
+                    trac (Trac code browser via HTTPS)
+  --cacert FILE   Use CA certificates from FILE to verify the peer
+  --capath DIR    Use CA certificate files from DIR to verify the peer
+  --insecure      Don't abort download if certificate verification fails
+  --dryrun        Print download commands only
+  -v              Verbose output
 
-Updates $DEST
+Updates $DRIVEDB
 or DESTFILE from smartmontools SVN repository.
 Tries to download first from branch $BRANCH
 and then from trunk.
 EOF
-    exit 1
-    ;;
+  exit 1
+}
 
-  "") ;;
-  *)  DEST="$1" ;;
-esac
+error()
+{
+  echo "$myname: $*" >&2
+  exit 1
+}
 
-# Abort if 'which' is not available
-which which >/dev/null || exit 1
-
-# Find download tool
-DOWNLOAD=
-for t in $os_dltools; do
-  if which $t >/dev/null 2>/dev/null; then
-    case $t in
-      curl)  DOWNLOAD="curl ${q:+-s }"'-f -o "$DEST.new" "$SRC"' ;;
-      lynx)  DOWNLOAD='lynx -source "$SRC" >"$DEST.new"' ;;
-      wget)  DOWNLOAD="wget $q"'-O "$DEST.new" "$SRC"' ;;
-      fetch) DOWNLOAD='fetch -o "$DEST.new" "$SRC"' ;; # FreeBSD
-      ftp)   DOWNLOAD='ftp -o "$DEST.new" "$SRC"' ;; # OpenBSD
-    esac
+warning()
+{
+  echo "$myname: (Warning) $*" >&2
+}
+
+selecturl()
+{
+  case $1 in
+    sf)   url='https://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/drivedb.h?format=raw' ;;
+    svn)  url='https://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools/drivedb.h' ;;
+    svni) url='http://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools/drivedb.h' ;;
+    trac) url='https://www.smartmontools.org/export/HEAD/trunk/smartmontools/drivedb.h' ;;
+    *) usage ;;
+  esac
+}
+
+inpath()
+{
+  local d rc save
+  rc=1
+  save=$IFS
+  IFS=':'
+  for d in $PATH; do
+    test -f "$d/$1" || continue
+    test -x "$d/$1" || continue
+    rc=0
     break
+  done
+  IFS=$save
+  return $rc
+}
+
+vecho()
+{
+  test -n "$q" || echo "$*"
+}
+
+# vrun COMMAND ARGS...
+vrun()
+{
+  if [ -n "$dryrun" ]; then
+    echo "$*"
+  elif [ -n "$q" ]; then
+    "$@" 2>/dev/null
+  else
+    echo "$*"
+    "$@"
   fi
-done
-if [ -z "$DOWNLOAD" ]; then
-  echo "$0: found none of: $os_dltools" >&2; exit 1
+}
+
+# vrun2 OUTFILE COMMAND ARGS...
+vrun2()
+{
+  local f err rc
+  f=$1; shift
+  rc=0
+  if [ -n "$dryrun" ]; then
+    echo "$* > $f"
+  else
+    vecho "$* > $f"
+    err=`"$@" 2>&1 > $f` || rc=$?
+    if [ -n "$err" ]; then
+      vecho "$err" >&2
+      test $rc != 0 || rc=42
+    fi
+  fi
+  return $rc
+}
+
+# download URL FILE
+download()
+{
+  local f u se rc
+  u=$1; f=$2
+  rc=0
+
+  case $tool in
+    curl)
+      vrun curl ${q:+-s} -f --max-redirs 0 \
+        ${cacert:+--cacert "$cacert"} \
+        ${capath:+--capath "$capath"} \
+        ${insecure:+--insecure} \
+        -o "$f" "$u" || rc=$?
+      ;;
+
+    wget)
+      vrun wget $q --max-redirect=0 \
+        ${cacert:+--ca-certificate="$cacert"} \
+        ${capath:+--ca-directory="$capath"} \
+        ${insecure:+--no-check-certificate} \
+        -O "$f" "$u" || rc=$?
+      ;;
+
+    lynx)
+      test -z "$cacert" || vrun export SSL_CERT_FILE="$cacert"
+      test -z "$capath" || vrun export SSL_CERT_DIR="$capath"
+      # Check also stderr as lynx does not return != 0 on HTTP error
+      vrun2 "$f" lynx -stderr -noredir -source "$u" || rc=$?
+      ;;
+
+    svn)
+      vrun svn $q export \
+        --non-interactive --no-auth-cache \
+        ${cacert:+--config-option "servers:global:ssl-trust-default-ca=no"} \
+        ${cacert:+--config-option "servers:global:ssl-authority-files=$cacert"} \
+        ${insecure:+--trust-server-cert} \
+        "$u" "$f" || rc=$?
+      ;;
+
+    fetch) # FreeBSD
+      vrun fetch $q --no-redirect \
+        ${cacert:+--ca-cert "$cacert"} \
+        ${capath:+--ca-path "$capath"} \
+        ${insecure:+--no-verify-hostname} \
+        -o "$f" "$u" || rc=$?
+      ;;
+
+    ftp) # OpenBSD
+      vrun ftp \
+        ${cacert:+-S cafile="$cacert"} \
+        ${capath:+-S capath="$capath"} \
+        ${insecure:+-S dont} \
+        -o "$f" "$u" || rc=$?
+      ;;
+
+    *) error "$tool: unknown (internal error)" ;;
+  esac
+  return $rc
+}
+
+# Parse options
+smtctl=$SMARTCTL
+tool=
+url=
+q="-q"
+dryrun=
+cacert=
+capath=
+insecure=
+
+while true; do case $1 in
+  -s)
+    shift; test -n "$1" || usage
+    smtctl=$1 ;;
+
+  -t)
+    shift
+    case $1 in *\ *) usage ;; esac
+    case " $os_dltools " in *\ $1\ *) ;; *) usage ;; esac
+    tool=$1 ;;
+
+  -u)
+    shift; selecturl "$1" ;;
+
+  -v)
+    q= ;;
+
+  --dryrun)
+    dryrun=t ;;
+
+  --cacert)
+    shift; test -n "$1" || usage
+    cacert=$1 ;;
+
+  --capath)
+    shift; test -n "$1" || usage
+    capath=$1 ;;
+
+  --insecure)
+    insecure=t ;;
+
+  -*)
+    usage ;;
+
+  *)
+    break ;;
+esac; shift; done
+
+case $# in
+  0) DEST=$DRIVEDB ;;
+  1) DEST=$1 ;;
+  *) usage ;;
+esac
+
+if [ -z "$tool" ]; then
+  # Find download tool in PATH
+  for t in $os_dltools; do
+    if inpath "$t"; then
+      tool=$t
+      break
+    fi
+  done
+  test -n "$tool" || error "found none of: $os_dltools"
 fi
 
+test -n "$url" || selecturl "svn"
+
+# Check option compatibility
+case "$tool:$url" in
+  svn:http*://svn.code.sf.net*) ;;
+  svn:*) error "'-t svn' requires '-u svn' or '-u svni'" ;;
+esac
+case "$tool:${capath:+set}" in
+  svn:set) warning "'--capath' is ignored if '-t svn' is used" ;;
+esac
+case "${insecure:-f}:$url" in
+  t:http:*) insecure= ;;
+  ?:https:*) ;;
+  *) error "'-u svni' requires '--insecure'" ;;
+esac
+case "$tool:$insecure" in
+  lynx:t) warning "'--insecure' is ignored if '-t lynx' is used" ;;
+esac
+
 # Try possible branch first, then trunk
+errmsg=
+errmsg2=
 for location in "branches/$BRANCH" "trunk"; do
-  test -n "$q" || echo "Download from $location"
+  test -z "$errmsg" || errmsg2=$errmsg
+  vecho "Download from $location with $tool"
 
-  errmsg=
-  rm -f "$DEST.new"
-  SRC="`eval echo "$SRCEXPR"`"
+  # Adjust URL
+  case $location in
+    trunk) src=$url ;;
+    *)     src=`echo "$url" | sed "s,/trunk/,/$location/,"` ;;
+  esac
+
+  # Download
+  test -n "$dryrun" || rm -f "$DEST.new" || exit 1
+  rc=0
+  download "$src" "$DEST.new" || rc=$?
+  test -z "$dryrun" || continue
 
-  if (eval $DOWNLOAD); then :; else
-    errmsg="download from $location failed (HTTP error)"
+  errmsg=
+  if [ $rc != 0 ]; then
+    errmsg="download from $location failed ($tool: exit $rc)"
     continue
   fi
-  if grep -i '<title>.*Error has Occurred' "$DEST.new" >/dev/null; then
-    errmsg="download from $location failed (SF code browser error)"
+
+  # Check file contents
+  case `sed 1q "$DEST.new"` in
+    /*) ;;
+    \<*)
+      errmsg="download from $location failed (HTML error message)"
+      continue ;;
+    *)
+      errmsg="download from $location failed (Unknown file contents)"
+      continue ;;
+  esac
+
+  # Check file size
+  size=`wc -c < "$DEST.new"`
+  if [ "$size" -lt 10000 ]; then
+    errmsg="download from $location failed (too small file size $size bytes)"
     continue
   fi
+  if [ "$size" -gt 1000000 ]; then
+    errmsg="download from $location failed (too large file size $size bytes)"
+    break
+  fi
 
   break
 done
 
+test -z "$dryrun" || exit 0
+
 if [ -n "$errmsg" ]; then
   rm -f "$DEST.new"
-  echo "$0: $errmsg" >&2
-  exit 1
+  test -z "$errmsg2" || echo "$myname: $errmsg2" >&2
+  error "$errmsg"
 fi
 
 # Adjust timestamp and permissions
 touch "$DEST.new"
 chmod 0644 "$DEST.new"
 
-# Check syntax
-rm -f "$DEST.error"
-if $SMARTCTL -B "$DEST.new" -P showall >/dev/null; then :; else
-  mv "$DEST.new" "$DEST.error"
-  echo "$DEST.error: rejected by $SMARTCTL, probably no longer compatible" >&2
-  exit 1
+if [ "$smtctl" != "-" ]; then
+  # Check syntax
+  rm -f "$DEST.error"
+  if "$smtctl" -B "$DEST.new" -P showall >/dev/null; then
+    test -n "$q" || echo "$smtctl: syntax OK"
+  else
+    mv "$DEST.new" "$DEST.error"
+    echo "$DEST.error: rejected by $smtctl, probably no longer compatible" >&2
+    exit 1
+  fi
 fi
 
-# Keep old file if identical
+# Keep old file if identical, ignore missing Id keyword expansion in new file
 rm -f "$DEST.lastcheck"
 if [ -f "$DEST" ]; then
-  if cmp "$DEST" "$DEST.new" >/dev/null 2>/dev/null; then
+  if    cmp "$DEST" "$DEST.new" >/dev/null 2>/dev/null \
+     || cat "$DEST" | sed 's|\$''Id''[^$]*\$|$''Id''$|' \
+        | cmp - "$DEST.new" >/dev/null 2>/dev/null; then
     rm -f "$DEST.new"
     touch "$DEST.lastcheck"
     echo "$DEST is already up to date"
index 09a4f1f1bb45c86afe9c0b1b37113e0533df4354..40fb2bdc09fc074da1a6a6863eb5dcb8ef1b2a5b 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * utility.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-12 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-12 Bruce Allen
+ * Copyright (C) 2008-16 Christian Franke
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -52,7 +52,7 @@
 #include "atacmds.h"
 #include "dev_interface.h"
 
-const char * utility_cpp_cvsid = "$Id: utility.cpp 3937 2014-07-05 17:51:21Z chrfranke $"
+const char * utility_cpp_cvsid = "$Id: utility.cpp 4309 2016-04-24 14:59:15Z chrfranke $"
                                  UTILITY_H_CVSID INT64_H_CVSID;
 
 const char * packet_types[] = {
@@ -90,23 +90,20 @@ std::string format_version_info(const char * prog_name, bool full /*= false*/)
       "(build date " __DATE__ ")" // checkout without expansion of Id keywords
 #endif
       " [%s] " BUILD_INFO "\n"
-    "Copyright (C) 2002-14, Bruce Allen, Christian Franke, www.smartmontools.org\n",
+    "Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org\n",
     prog_name, smi()->get_os_version_str().c_str()
   );
   if (!full)
     return info;
 
-  info += strprintf(
-    "\n"
-    "%s comes with ABSOLUTELY NO WARRANTY. This is free\n"
+  info += "\n";
+  info += prog_name;
+  info += " comes with ABSOLUTELY NO WARRANTY. This is free\n"
     "software, and you are welcome to redistribute it under\n"
     "the terms of the GNU General Public License; either\n"
     "version 2, or (at your option) any later version.\n"
     "See http://www.gnu.org for further details.\n"
-    "\n",
-    prog_name
-  );
-  info +=
+    "\n"
     "smartmontools release " PACKAGE_VERSION
       " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n"
 #ifdef SMARTMONTOOLS_SVN_REV
@@ -116,13 +113,24 @@ std::string format_version_info(const char * prog_name, bool full /*= false*/)
     "smartmontools SVN rev is unknown\n"
 #endif
     "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n"
+    "smartmontools build with: "
+#if   __cplusplus > 201402
+                               "C++17"
+#elif __cplusplus > 201103
+                               "C++14"
+#elif __cplusplus > 199711
+                               "C++11"
+#else
+                               "C++98"
+#endif
 #if defined(__GNUC__) && defined(__VERSION__) // works also with CLang
-    "smartmontools build with: GCC " __VERSION__ "\n"
+                                     ", GCC " __VERSION__
 #endif
-    "smartmontools configure arguments: "
+                                                          "\n"
+    "smartmontools configure arguments:"
   ;
   info += (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ?
-           SMARTMONTOOLS_CONFIGURE_ARGS : "[no arguments given]");
+           SMARTMONTOOLS_CONFIGURE_ARGS : " [no arguments given]");
   info += '\n';
 
   return info;
@@ -291,9 +299,6 @@ void dateandtimezoneepoch(char *buffer, time_t tval){
   const char *timezonename;
   char datebuffer[DATEANDEPOCHLEN];
   int lenm1;
-#ifdef _WIN32
-  char tzfixbuf[6+1];
-#endif
 
   FixGlibcTimeZoneBug();
   
@@ -323,6 +328,8 @@ void dateandtimezoneepoch(char *buffer, time_t tval){
 
 #ifdef _WIN32
   // Fix long non-ascii timezone names
+    // cppcheck-suppress variableScope
+  char tzfixbuf[6+1] = "";
   if (!getenv("TZ"))
     timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename);
 #endif
@@ -511,33 +518,6 @@ bool regular_expression::compile()
   return true;
 }
 
-// Splits an argument to the -r option into a name part and an (optional) 
-// positive integer part.  s is a pointer to a string containing the
-// argument.  After the call, s will point to the name part and *i the
-// integer part if there is one or 1 otherwise.  Note that the string s may
-// be changed by this function.  Returns zero if successful and non-zero
-// otherwise.
-int split_report_arg(char *s, int *i)
-{
-  if ((s = strchr(s, ','))) {
-    // Looks like there's a name part and an integer part.
-    char *tailptr;
-
-    *s++ = '\0';
-    if (*s == '0' || !isdigit((int)*s))  // The integer part must be positive
-      return 1;
-    errno = 0;
-    *i = (int) strtol(s, &tailptr, 10);
-    if (errno || *tailptr != '\0')
-      return 1;
-  } else {
-    // There's no integer part.
-    *i = 1;
-  }
-
-  return 0;
-}
-
 #ifndef HAVE_STRTOULL
 // Replacement for missing strtoull() (Linux with libc < 6, MSVC)
 // Functionality reduced to requirements of smartd and split_selective_arg().
@@ -644,73 +624,6 @@ int split_selective_arg(char *s, uint64_t *start,
   return 0;
 }
 
-#ifdef OLD_INTERFACE
-
-int64_t bytes = 0;
-
-// Helps debugging.  If the second argument is non-negative, then
-// decrement bytes by that amount.  Else decrement bytes by (one plus)
-// length of null terminated string.
-void *FreeNonZero1(void *address, int size, int line, const char* file){
-  if (address) {
-    if (size<0)
-      bytes-=1+strlen((char*)address);
-    else
-      bytes-=size;
-    return CheckFree1(address, line, file);
-  }
-  return NULL;
-}
-
-// To help with memory checking.  Use when it is known that address is
-// NOT null.
-void *CheckFree1(void *address, int /*whatline*/, const char* /*file*/){
-  if (address){
-    free(address);
-    return NULL;
-  }
-  throw std::runtime_error("Internal error in CheckFree()");
-}
-
-// A custom version of calloc() that tracks memory use
-void *Calloc(size_t nmemb, size_t size) { 
-  void *ptr=calloc(nmemb, size);
-  
-  if (ptr)
-    bytes+=nmemb*size;
-
-  return ptr;
-}
-
-// A custom version of strdup() that keeps track of how much memory is
-// being allocated. If mustexist is set, it also throws an error if we
-// try to duplicate a NULL string.
-char *CustomStrDup(const char *ptr, int mustexist, int /*whatline*/, const char* /*file*/){
-  char *tmp;
-
-  // report error if ptr is NULL and mustexist is set
-  if (ptr==NULL){
-    if (mustexist)
-      throw std::runtime_error("Internal error in CustomStrDup()");
-    else
-      return NULL;
-  }
-
-  // make a copy of the string...
-  tmp=strdup(ptr);
-  
-  if (!tmp)
-    throw std::bad_alloc();
-  
-  // and track memory usage
-  bytes+=1+strlen(ptr);
-  
-  return tmp;
-}
-
-#endif // OLD_INTERFACE
-
-
 // Returns true if region of memory contains non-zero entries
 bool nonempty(const void * data, int size)
 {
@@ -720,6 +633,31 @@ bool nonempty(const void * data, int size)
   return false;
 }
 
+// Copy not null terminated char array to null terminated string.
+// Replace non-ascii characters.  Remove leading and trailing blanks.
+const char * format_char_array(char * str, int strsize, const char * chr, int chrsize)
+{
+  int b = 0;
+  while (b < chrsize && chr[b] == ' ')
+    b++;
+  int n = 0;
+  while (b+n < chrsize && chr[b+n])
+    n++;
+  while (n > 0 && chr[b+n-1] == ' ')
+    n--;
+
+  if (n >= strsize)
+    n = strsize-1;
+
+  for (int i = 0; i < n; i++) {
+    char c = chr[b+i];
+    str[i] = (' ' <= c && c <= '~' ? c : '?');
+  }
+
+  str[n] = 0;
+  return str;
+}
+
 // Format integer with thousands separator
 const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
                                        const char * thousands_sep /* = 0 */)
index b172029d03436e4eaadf48d47114925e08386acc..bd56a24933aae4efd55ca2bdf522714d0dbcd874 100644 (file)
--- a/utility.h
+++ b/utility.h
@@ -1,10 +1,10 @@
 /*
  * utility.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2008-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
+ * Copyright (C) 2008-16 Christian Franke
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,7 @@
 #ifndef UTILITY_H_
 #define UTILITY_H_
 
-#define UTILITY_H_CVSID "$Id: utility.h 3936 2014-07-05 17:16:23Z chrfranke $"
+#define UTILITY_H_CVSID "$Id: utility.h 4309 2016-04-24 14:59:15Z chrfranke $"
 
 #include <time.h>
 #include <sys/types.h> // for regex.h (according to POSIX)
@@ -92,58 +92,13 @@ void pout(const char *fmt, ...)
 // replacement for perror() with redirected output.
 void syserror(const char *message);
 
-// Function for processing -r option in smartctl and smartd
-int split_report_arg(char *s, int *i);
-
 // Function for processing -t selective... option in smartctl
 int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode);
 
-
-// Guess device type (ata or scsi) based on device name 
-// Guessing will now use Controller Type defines below
-
-// Moved to C++ interface
-//int guess_device_type(const char * dev_name);
-
-// Create and return the list of devices to probe automatically
-// if the DEVICESCAN option is in the smartd config file
-// Moved to C++ interface
-//int make_device_names (char ***devlist, const char* name);
-
 // Replacement for exit(status)
 // (exit is not compatible with C++ destructors)
 #define EXIT(status) { throw (int)(status); }
 
-
-#ifdef OLD_INTERFACE
-
-// replacement for calloc() that tracks memory usage
-void *Calloc(size_t nmemb, size_t size);
-
-// Utility function to free memory
-void *FreeNonZero1(void* address, int size, int whatline, const char* file);
-
-// Typesafe version of above
-template <class T>
-inline T * FreeNonZero(T * address, int size, int whatline, const char* file)
-  { return (T *)FreeNonZero1((void *)address, size, whatline, file); }
-
-// A custom version of strdup() that keeps track of how much memory is
-// being allocated. If mustexist is set, it also throws an error if we
-// try to duplicate a NULL string.
-char *CustomStrDup(const char *ptr, int mustexist, int whatline, const char* file);
-
-// To help with memory checking.  Use when it is known that address is
-// NOT null.
-void *CheckFree1(void *address, int whatline, const char* file);
-
-// Typesafe version of above
-template <class T>
-inline T * CheckFree(T * address, int whatline, const char* file)
-  { return (T *)CheckFree1((void *)address, whatline, file); }
-
-#endif // OLD_INTERFACE
-
 // Compile time check of byte ordering
 // (inline const function allows compiler to remove dead code)
 inline bool isbigendian()
@@ -163,23 +118,21 @@ void check_config();
 // the ATA standard for packet devices to define the device type.
 const char *packetdevicetype(int type);
 
-// Moved to C++ interface
-//int deviceopen(const char *pathname, char *type);
-
-//int deviceclose(int fd);
-
-// Optional functions of os_*.c
-#ifdef HAVE_GET_OS_VERSION_STR
-// Return build host and OS version as static string
-//const char * get_os_version_str(void);
-#endif
-
 // returns true if any of the n bytes are nonzero, else zero.
 bool nonempty(const void * data, int size);
 
 // needed to fix glibc bug
 void FixGlibcTimeZoneBug();
 
+// Copy not null terminated char array to null terminated string.
+// Replace non-ascii characters.  Remove leading and trailing blanks.
+const char * format_char_array(char * str, int strsize, const char * chr, int chrsize);
+
+// Version for fixed size buffers.
+template<size_t STRSIZE, size_t CHRSIZE>
+inline const char * format_char_array(char (& str)[STRSIZE], const char (& chr)[CHRSIZE])
+  { return format_char_array(str, (int)STRSIZE, chr, (int)CHRSIZE); }
+
 // Format integer with thousands separator
 const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
                                        const char * thousands_sep = 0);
@@ -234,6 +187,8 @@ public:
 
   bool open(const char * name, const char * mode)
     {
+      if (m_file && m_owner)
+        fclose(m_file);
       m_file = fopen(name, mode);
       m_owner = true;
       return !!m_file;
@@ -241,6 +196,8 @@ public:
 
   void open(FILE * f, bool owner = false)
     {
+      if (m_file && m_owner)
+        fclose(m_file);
       m_file = f;
       m_owner = owner;
     }