/*
* 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>
- *
- * 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/>.
+ * Copyright (C) 2008-18 Christian Franke
*
+ * SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
-#include "int64.h"
+
#include "dev_interface.h"
+#include "dev_intelliprop.h"
#include "dev_tunnelled.h"
#include "atacmds.h" // ATA_SMART_CMD/STATUS
+#include "scsicmds.h" // scsi_cmnd_io
#include "utility.h"
#include <errno.h>
#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 4848 2018-12-05 18:30:46Z 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
return this;
}
+bool smart_device::is_powered_down()
+{
+ return false;
+}
+
bool smart_device::owns(const smart_device * /*dev*/) const
{
return false;
return false;
}
+/////////////////////////////////////////////////////////////////////////////
+// scsi_device
+
+bool scsi_device::scsi_pass_through_and_check(scsi_cmnd_io * iop,
+ const char * msg)
+{
+ // Provide sense buffer
+ unsigned char sense[32] = {0, };
+ iop->sensep = sense;
+ iop->max_sense_len = sizeof(sense);
+ iop->timeout = SCSI_TIMEOUT_DEFAULT;
+
+ // Run cmd
+ if (!scsi_pass_through(iop)) {
+ if (scsi_debugmode > 0)
+ pout("%sscsi_pass_through() failed, errno=%d [%s]\n",
+ msg, get_errno(), get_errmsg());
+ return false;
+ }
+
+ // Check sense
+ scsi_sense_disect sinfo;
+ scsi_do_sense_disect(iop, &sinfo);
+ int err = scsiSimpleSenseFilter(&sinfo);
+ if (err) {
+ if (scsi_debugmode > 0)
+ pout("%sscsi error: %s\n", msg, scsiErrString(err));
+ return set_err(EIO, "scsi error %s", scsiErrString(err));
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// 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
{
// default
std::string s =
- "ata, scsi, sat[,auto][,N][+TYPE], usbcypress[,X], usbjmicron[,p][,x][,N], usbsunplus";
+ "ata, scsi[+TYPE], nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], "
+ "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, sntjmicron[,NSID], "
+ "intelliprop,N[+TYPE]";
// append custom
std::string s2 = get_valid_custom_dev_types_str();
if (!s2.empty()) {
else if (!strcmp(type, "scsi"))
dev = get_scsi_device(name, type);
- else if ( ((!strncmp(type, "sat", 3) && (!type[3] || strchr(",+", type[3])))
- || (!strncmp(type, "usb", 3)))) {
+ 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 ( (str_starts_with(type, "sat") && (!type[3] || strchr(",+", type[3])))
+ || str_starts_with(type, "scsi+")
+ || str_starts_with(type, "usb") ) {
// Split "sat...+base..." -> ("sat...", "base...")
unsigned satlen = strcspn(type, "+");
std::string sattype(type, satlen);
return 0;
}
// Attach SAT tunnel
- ata_device * satdev = get_sat_device(sattype.c_str(), basedev->to_scsi());
- if (!satdev)
+ return get_sat_device(sattype.c_str(), basedev.release()->to_scsi());
+ }
+
+ else if (str_starts_with(type, "snt")) {
+ smart_device_auto_ptr basedev( get_smart_device(name, "scsi") );
+ if (!basedev) {
+ set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
+ return 0;
+ }
+
+ return get_snt_device(type, basedev.release()->to_scsi());
+ }
+
+ else if (str_starts_with(type, "intelliprop")) {
+ // Parse "intelliprop,N[+base...]"
+ unsigned phydrive = ~0; int n = -1; char c = 0;
+ sscanf(type, "intelliprop,%u%n%c", &phydrive, &n, &c);
+ if (!((n == (int)strlen(type) || c == '+') && phydrive <= 3)) {
+ set_err(EINVAL, "Option '-d intelliprop,N' requires N between 0 and 3");
+ return 0;
+ }
+ const char * basetype = (type[n] ? type + n + 1 : "");
+ // Recurse to allocate base device, default is standard ATA
+ if (!*basetype)
+ basetype = "ata";
+ smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
+ if (!basedev) {
+ set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
return 0;
- basedev.release();
- return satdev;
+ }
+ // Result must be ATA
+ if (!basedev->is_ata()) {
+ set_err(EINVAL, "Type '%s': Device type '%s' is not ATA", type, basetype);
+ return 0;
+ }
+ return get_intelliprop_device(this, phydrive, basedev.release()->to_ata());
}
else {
return dev;
}
+bool smart_interface::scan_smart_devices(smart_device_list & /*devlist*/,
+ const char * /*type*/, const char * /*pattern*/ /* = 0 */)
+{
+ return set_err(ENOSYS);
+}
+
+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;
{
return "";
}
+
+smart_device * smart_interface::get_scsi_passthrough_device(const char * type, scsi_device * scsidev)
+{
+ if (!strncmp(type, "snt", 3)) {
+ return get_snt_device(type, scsidev);
+ }
+
+ return get_sat_device(type, scsidev);
+}