Skip to content

Instantly share code, notes, and snippets.

@nonakap
Last active September 10, 2016 02:09
Show Gist options
  • Save nonakap/7ae015d475c187bf43cdcc224f8edf9b to your computer and use it in GitHub Desktop.
Save nonakap/7ae015d475c187bf43cdcc224f8edf9b to your computer and use it in GitHub Desktop.
pkgsrc/sysutils/smartmontools: rewrite os_netbsd.cpp for new world order.
Index: Makefile
===================================================================
RCS file: /cvsroot/pkgsrc/sysutils/smartmontools/Makefile,v
retrieving revision 1.29
diff -u -p -r1.29 Makefile
--- Makefile 8 May 2016 11:23:15 -0000 1.29
+++ Makefile 10 Sep 2016 02:08:00 -0000
@@ -1,6 +1,7 @@
# $NetBSD: Makefile,v 1.29 2016/05/08 11:23:15 nonaka Exp $
DISTNAME= smartmontools-6.5
+PKGREVISION= 1
CATEGORIES= sysutils
MASTER_SITES= ${MASTER_SITE_SOURCEFORGE:=smartmontools/}
Index: distinfo
===================================================================
RCS file: /cvsroot/pkgsrc/sysutils/smartmontools/distinfo,v
retrieving revision 1.27
diff -u -p -r1.27 distinfo
--- distinfo 8 May 2016 11:23:15 -0000 1.27
+++ distinfo 10 Sep 2016 02:08:00 -0000
@@ -4,6 +4,8 @@ SHA1 (smartmontools-6.5.tar.gz) = 7e788b
RMD160 (smartmontools-6.5.tar.gz) = e62d15d406a92b75fa239603180c6b587f9d2aa8
SHA512 (smartmontools-6.5.tar.gz) = dca7a6363ab7280ee4197155025f624c978886dcc94fc47b524f6f849138b62c471e966c0d4bf59c7bba50519dc122264618e5ded80a406863ddd10b43d928d3
Size (smartmontools-6.5.tar.gz) = 855642 bytes
+SHA1 (patch-Makefile.am) = 4a500dc27d4c852aaa58ada7367f2e4b6e09a3b9
SHA1 (patch-aa) = bd08aff267f0e9e9307ebc57858e61e35144bf16
SHA1 (patch-ag) = 01b0b35d89d6d6a11b13b3c05a141c46a0c1590b
-SHA1 (patch-os__netbsd.cpp) = 28f78c08d7c7287165cfa38f8ee3467b3b03cb44
+SHA1 (patch-netbsd_nvme_ioctl.h) = 03c603917f970da0e5747bbe74d5606e376b5cad
+SHA1 (patch-os__netbsd.cpp) = aefe8628f69589274cae31b5d98cd8612a7d891d
Index: patches/patch-Makefile.am
===================================================================
RCS file: patches/patch-Makefile.am
diff -N patches/patch-Makefile.am
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-Makefile.am 10 Sep 2016 02:08:00 -0000
@@ -0,0 +1,10 @@
+--- Makefile.am.orig 2016-04-17 04:45:57.000000000 +0900
++++ Makefile.am 2016-05-13 16:51:49.000000000 +0900
+@@ -174,6 +174,7 @@
+ dev_legacy.cpp \
+ linux_nvme_ioctl.h \
+ freebsd_nvme_ioctl.h \
++ netbsd_nvme_ioctl.h \
+ megaraid.h
+
+ if OS_WIN32_MINGW
Index: patches/patch-netbsd_nvme_ioctl.h
===================================================================
RCS file: patches/patch-netbsd_nvme_ioctl.h
diff -N patches/patch-netbsd_nvme_ioctl.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-netbsd_nvme_ioctl.h 10 Sep 2016 02:08:00 -0000
@@ -0,0 +1,186 @@
+--- /dev/null
++++ netbsd_nvme_ioctl.h 2016-05-13 16:56:31.000000000 +0900
+@@ -0,0 +1,183 @@
++/* $NetBSD: nvmereg.h,v 1.1 2016/05/01 10:21:02 nonaka Exp $ */
++/* $OpenBSD: nvmereg.h,v 1.10 2016/04/14 11:18:32 dlg Exp $ */
++
++/*
++ * Copyright (c) 2014 David Gwynne <dlg@openbsd.org>
++ *
++ * Permission to use, copy, modify, and distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <sys/param.h>
++
++struct nvme_sge {
++ uint8_t id;
++ uint8_t _reserved[15];
++} __packed __aligned(8);
++
++struct nvme_sqe {
++ uint8_t opcode;
++ uint8_t flags;
++ uint16_t cid;
++
++ uint32_t nsid;
++
++ uint8_t _reserved[8];
++
++ uint64_t mptr;
++
++ union {
++ uint64_t prp[2];
++ struct nvme_sge sge;
++ } __packed entry;
++
++ uint32_t cdw10;
++ uint32_t cdw11;
++ uint32_t cdw12;
++ uint32_t cdw13;
++ uint32_t cdw14;
++ uint32_t cdw15;
++} __packed __aligned(8);
++
++struct nvme_cqe {
++ uint32_t cdw0;
++
++ uint32_t _reserved;
++
++ uint16_t sqhd; /* SQ Head Pointer */
++ uint16_t sqid; /* SQ Identifier */
++
++ uint16_t cid; /* Command Identifier */
++ uint16_t flags;
++#define NVME_CQE_DNR __BIT(15)
++#define NVME_CQE_M __BIT(14)
++#define NVME_CQE_SCT(_f) ((_f) & (0x07 << 8))
++#define NVME_CQE_SCT_GENERIC (0x00 << 8)
++#define NVME_CQE_SCT_COMMAND (0x01 << 8)
++#define NVME_CQE_SCT_MEDIAERR (0x02 << 8)
++#define NVME_CQE_SCT_VENDOR (0x07 << 8)
++#define NVME_CQE_SC(_f) ((_f) & (0x7f << 1))
++#define NVME_CQE_SC_SUCCESS (0x00 << 1)
++#define NVME_CQE_SC_INVALID_OPCODE (0x01 << 1)
++#define NVME_CQE_SC_INVALID_FIELD (0x02 << 1)
++#define NVME_CQE_SC_CID_CONFLICT (0x03 << 1)
++#define NVME_CQE_SC_DATA_XFER_ERR (0x04 << 1)
++#define NVME_CQE_SC_ABRT_BY_NO_PWR (0x05 << 1)
++#define NVME_CQE_SC_INTERNAL_DEV_ERR (0x06 << 1)
++#define NVME_CQE_SC_CMD_ABRT_REQD (0x07 << 1)
++#define NVME_CQE_SC_CMD_ABDR_SQ_DEL (0x08 << 1)
++#define NVME_CQE_SC_CMD_ABDR_FUSE_ERR (0x09 << 1)
++#define NVME_CQE_SC_CMD_ABDR_FUSE_MISS (0x0a << 1)
++#define NVME_CQE_SC_INVALID_NS (0x0b << 1)
++#define NVME_CQE_SC_CMD_SEQ_ERR (0x0c << 1)
++#define NVME_CQE_SC_INVALID_LAST_SGL (0x0d << 1)
++#define NVME_CQE_SC_INVALID_NUM_SGL (0x0e << 1)
++#define NVME_CQE_SC_DATA_SGL_LEN (0x0f << 1)
++#define NVME_CQE_SC_MDATA_SGL_LEN (0x10 << 1)
++#define NVME_CQE_SC_SGL_TYPE_INVALID (0x11 << 1)
++#define NVME_CQE_SC_LBA_RANGE (0x80 << 1)
++#define NVME_CQE_SC_CAP_EXCEEDED (0x81 << 1)
++#define NVME_CQE_NS_NOT_RDY (0x82 << 1)
++#define NVME_CQE_RSV_CONFLICT (0x83 << 1)
++#define NVME_CQE_PHASE __BIT(0)
++} __packed __aligned(8);
++
++/*-
++ * 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$
++ */
++
++#define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command)
++
++#define nvme_completion_is_error(cpl) \
++ ((NVME_CQE_SC((cpl)->flags) != NVME_CQE_SC_SUCCESS) \
++ || (NVME_CQE_SCT((cpl)->flags) != NVME_CQE_SCT_GENERIC))
++
++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:
++ * * opcode
++ * * nsid (namespace id) - for admin commands only
++ * * cdw10-cdw15
++ *
++ * Remaining fields must be set to 0 by the caller.
++ */
++ struct nvme_sqe 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
++ * * flags (except for phase)
++ *
++ * Remaining fields will be set to 0 by the driver.
++ */
++ struct nvme_cqe 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;
++
++ /*
++ * timeout (unit: ms)
++ *
++ * 0: use default timeout value
++ */
++ uint32_t timeout;
++};
++
++#define NVME_PREFIX "/dev/nvme"
++#define NVME_NS_PREFIX "ns"
Index: patches/patch-os__netbsd.cpp
===================================================================
RCS file: /cvsroot/pkgsrc/sysutils/smartmontools/patches/patch-os__netbsd.cpp,v
retrieving revision 1.3
diff -u -p -r1.3 patch-os__netbsd.cpp
--- patches/patch-os__netbsd.cpp 8 May 2016 11:23:15 -0000 1.3
+++ patches/patch-os__netbsd.cpp 10 Sep 2016 02:08:00 -0000
@@ -1,24 +1,1138 @@
$NetBSD: patch-os__netbsd.cpp,v 1.3 2016/05/08 11:23:15 nonaka Exp $
-Use a raw disk device file on NetBSD.
+Rewrite for new world order.
--- os_netbsd.cpp.orig 2016-03-26 19:47:47.000000000 +0000
-+++ os_netbsd.cpp 2016-05-08 06:45:25.000000000 +0000
-@@ -54,7 +54,7 @@ printwarning(int msgNo, const char *extr
++++ os_netbsd.cpp 2016-09-10 01:55:51.063649520 +0000
+@@ -4,6 +4,7 @@
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2003-8 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
++ * Copyright (C) 2016 Kimihiro Nonaka
+ *
+ * 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,16 +24,21 @@
+ #include "utility.h"
+ #include "os_netbsd.h"
+
++#include <sys/drvctlio.h>
++#include <sys/utsname.h>
+ #include <errno.h>
+ #include <unistd.h>
+
++// based on "sys/dev/ic/nvmeio.h" from NetBSD kernel sources
++#include "netbsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error
++
+ const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 4253 2016-03-26 19:47:47Z chrfranke $"
+ OS_NETBSD_H_CVSID;
+
+ enum warnings {
+ BAD_SMART, MAX_MSG
+ };
+-
++
+ /* Utility function for printing warnings */
+ void
+ printwarning(int msgNo, const char *extra)
+@@ -48,323 +54,384 @@ printwarning(int msgNo, const char *extr
+ printed[msgNo] = 1;
+ pout("%s", message[msgNo]);
+ if (extra)
+- pout("%s", extra);
++ pout("%s", extra);
+ }
+ }
return;
}
--static const char *net_dev_prefix = "/dev/";
-+static const char *net_dev_prefix = "/dev/r";
++#define ARGUSED(x) ((void)(x))
++
++/////////////////////////////////////////////////////////////////////////////
++
++namespace os_netbsd { // No need to publish anything, name provided for Doxygen
++
+ static const char *net_dev_prefix = "/dev/";
++static const char *net_dev_raw_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,7 +128,7 @@ get_dev_names(char ***names, const char
- n++;
- }
++static const char *net_dev_nvme_ctrl = "nvme";
+
+-/* Guess device type (ATA or SCSI) based on device name */
+-int
+-guess_device_type(const char *dev_name)
++/////////////////////////////////////////////////////////////////////////////
++/// Implement shared open/close routines with old functions.
++
++class netbsd_smart_device
++: virtual public /*implements*/ smart_device
+ {
+- int len;
+- int dev_prefix_len = strlen(net_dev_prefix);
++public:
++ explicit netbsd_smart_device()
++ : smart_device(never_called),
++ m_fd(-1) { }
+
+- if (!dev_name || !(len = strlen(dev_name)))
+- return CONTROLLER_UNKNOWN;
++ virtual ~netbsd_smart_device() throw();
+
+- if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
+- if (len <= dev_prefix_len)
+- return CONTROLLER_UNKNOWN;
+- else
+- dev_name += dev_prefix_len;
+- }
+- if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
+- return CONTROLLER_ATA;
++ virtual bool is_open() const;
++
++ virtual bool open();
++
++ virtual bool close();
+
+- if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
+- return CONTROLLER_SCSI;
++protected:
++ /// Return filedesc for derived classes.
++ int get_fd() const
++ { return m_fd; }
+
+- if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
+- return CONTROLLER_SCSI;
++ void set_fd(int fd)
++ { m_fd = fd; }
+
+- return CONTROLLER_UNKNOWN;
++private:
++ int m_fd; ///< filedesc, -1 if not open.
++};
++
++netbsd_smart_device::~netbsd_smart_device() throw()
++{
++ if (m_fd >= 0)
++ os_netbsd::netbsd_smart_device::close();
+ }
+
+-int
+-get_dev_names(char ***names, const char *prefix)
++bool netbsd_smart_device::is_open() const
+ {
+- char *disknames, *p, **mp;
+- int n = 0;
+- int sysctl_mib[2];
+- size_t sysctl_len;
++ return (m_fd >= 0);
++}
+
+- *names = NULL;
+
+- sysctl_mib[0] = CTL_HW;
+- sysctl_mib[1] = HW_DISKNAMES;
+- if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
+- pout("Failed to get value of sysctl `hw.disknames'\n");
+- return -1;
+- }
+- if (!(disknames = (char *)malloc(sysctl_len))) {
+- pout("Out of memory constructing scan device list\n");
+- return -1;
+- }
+- if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
+- pout("Failed to get value of sysctl `hw.disknames'\n");
+- return -1;
+- }
+- if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
+- pout("Out of memory constructing scan device list\n");
+- return -1;
+- }
+- for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
+- if (strncmp(p, prefix, strlen(prefix))) {
+- continue;
++bool netbsd_smart_device::open()
++{
++ const char *dev = get_dev_name();
++ int fd;
++
++ if (is_scsi()) {
++ fd = ::open(dev,O_RDWR|O_NONBLOCK);
++ if (fd < 0 && errno == EROFS)
++ fd = ::open(dev,O_RDONLY|O_NONBLOCK);
++ if (fd < 0) {
++ set_err(errno);
++ return false;
+ }
+- mp[n] = (char *)malloc(strlen(net_dev_prefix) + strlen(p) + 2);
+- if (!mp[n]) {
+- pout("Out of memory constructing scan device list\n");
+- return -1;
++ } else if (is_ata() || is_nvme()) {
++ if ((fd = ::open(dev,O_RDWR|O_NONBLOCK))<0) {
++ set_err(errno);
++ return false;
+ }
+- sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
+- n++;
+- }
++ } else
++ return false;
- void * 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;
++ set_fd(fd);
++ return true;
+ }
+
+-int
+-make_device_names(char ***devlist, const char *name)
++bool netbsd_smart_device::close()
+ {
+- if (!strcmp(name, "SCSI"))
+- return get_dev_names(devlist, net_dev_scsi_disk);
+- else if (!strcmp(name, "ATA"))
+- return get_dev_names(devlist, net_dev_ata_disk);
+- else
+- return 0;
++ int failed = 0;
++ // close device, if open
++ if (is_open())
++ failed=::close(get_fd());
++
++ set_fd(-1);
++
++ if(failed) return false;
++ else return true;
+ }
+
+-int
+-deviceopen(const char *pathname, char *type)
++/////////////////////////////////////////////////////////////////////////////
++/// Implement standard ATA support
++
++class netbsd_ata_device
++: public /*implements*/ ata_device,
++ public /*extends*/ netbsd_smart_device
++{
++public:
++ netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
++ virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
++
++protected:
++ virtual int do_cmd(struct atareq* request, bool is_48bit_cmd);
++};
++
++netbsd_ata_device::netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
++: smart_device(intf, dev_name, "ata", req_type),
++ netbsd_smart_device()
+ {
+- if (!strcmp(type, "SCSI")) {
+- int fd = open(pathname, O_RDWR | O_NONBLOCK);
+- if (fd < 0 && errno == EROFS)
+- fd = open(pathname, O_RDONLY | O_NONBLOCK);
+- return fd;
+- } else if (!strcmp(type, "ATA"))
+- return open(pathname, O_RDWR | O_NONBLOCK);
+- else
+- return -1;
+ }
+
+-int
+-deviceclose(int fd)
++int netbsd_ata_device::do_cmd( struct atareq* request, bool is_48bit_cmd)
+ {
+- return close(fd);
++ int fd = get_fd(), ret;
++ ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the ATAIOCCOMMAND
++ ret = ioctl(fd, ATAIOCCOMMAND, request);
++ if (ret) set_err(errno);
++ return ret;
+ }
+
+-int
+-ata_command_interface(int fd, smart_command_set command, int select, char *data)
++bool netbsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+ {
+- struct atareq req;
+- unsigned char inbuf[DEV_BSIZE];
+- int retval, copydata = 0;
++ bool ata_48bit = false; // no ata_48bit_support via ATAIOCCOMMAND
+
++ if (!ata_cmd_is_ok(in,
++ true, // data_out_support
++ true, // multi_sector_support
++ ata_48bit)
++ ) {
++ set_err(ENOSYS, "48-bit ATA commands not implemented");
++ return false;
++ }
++
++ struct atareq req;
+ memset(&req, 0, sizeof(req));
+- req.timeout = 1000;
+
+- memset(&inbuf, 0, sizeof(inbuf));
++ req.timeout = 1000;
++ req.command = in.in_regs.command;
++ req.features = in.in_regs.features;
++ req.sec_count = in.in_regs.sector_count;
++ req.sec_num = in.in_regs.lba_low;
++ req.head = in.in_regs.device;
++ req.cylinder = le16toh(in.in_regs.lba_mid | (in.in_regs.lba_high << 8));
++
++ switch (in.direction) {
++ case ata_cmd_in::no_data:
++ req.flags = ATACMD_READREG;
++ break;
++ case ata_cmd_in::data_in:
++ req.flags = ATACMD_READ | ATACMD_READREG;
++ req.databuf = (char *)in.buffer;
++ req.datalen = in.size;
++ break;
++ case ata_cmd_in::data_out:
++ req.flags = ATACMD_WRITE | ATACMD_READREG;
++ req.databuf = (char *)in.buffer;
++ req.datalen = in.size;
++ break;
++ default:
++ return set_err(ENOSYS);
++ }
+
+- switch (command) {
+- case READ_VALUES:
+- req.flags = ATACMD_READ;
+- req.features = WDSM_RD_DATA;
+- req.command = WDCC_SMART;
+- req.databuf = (char *)inbuf;
+- req.datalen = sizeof(inbuf);
+- req.cylinder = WDSMART_CYL;
+- copydata = 1;
+- break;
+- case READ_THRESHOLDS:
+- req.flags = ATACMD_READ;
+- req.features = WDSM_RD_THRESHOLDS;
+- req.command = WDCC_SMART;
+- req.databuf = (char *)inbuf;
+- req.datalen = sizeof(inbuf);
+- req.cylinder = WDSMART_CYL;
+- copydata = 1;
+- break;
+- case READ_LOG:
+- req.flags = ATACMD_READ;
+- req.features = ATA_SMART_READ_LOG_SECTOR; /* XXX missing from wdcreg.h */
+- req.command = WDCC_SMART;
+- req.databuf = (char *)inbuf;
+- req.datalen = sizeof(inbuf);
+- req.cylinder = WDSMART_CYL;
+- req.sec_num = select;
+- req.sec_count = 1;
+- copydata = 1;
+- break;
+- case WRITE_LOG:
+- memcpy(inbuf, data, 512);
+- req.flags = ATACMD_WRITE;
+- req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */
+- req.command = WDCC_SMART;
+- req.databuf = (char *)inbuf;
+- req.datalen = sizeof(inbuf);
+- req.cylinder = WDSMART_CYL;
+- req.sec_num = select;
+- req.sec_count = 1;
+- break;
+- case IDENTIFY:
+- req.flags = ATACMD_READ;
+- req.command = WDCC_IDENTIFY;
+- req.databuf = (char *)inbuf;
+- req.datalen = sizeof(inbuf);
+- copydata = 1;
+- break;
+- case PIDENTIFY:
+- req.flags = ATACMD_READ;
+- req.command = ATAPI_IDENTIFY_DEVICE;
+- req.databuf = (char *)inbuf;
+- req.datalen = sizeof(inbuf);
+- copydata = 1;
+- break;
+- case ENABLE:
+- req.flags = ATACMD_READREG;
+- req.features = WDSM_ENABLE_OPS;
+- req.command = WDCC_SMART;
+- req.cylinder = WDSMART_CYL;
+- break;
+- case DISABLE:
+- req.flags = ATACMD_READREG;
+- req.features = WDSM_DISABLE_OPS;
+- req.command = WDCC_SMART;
+- req.cylinder = WDSMART_CYL;
+- break;
+- case AUTO_OFFLINE:
+- /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+- req.flags = ATACMD_READREG;
+- req.features = ATA_SMART_AUTO_OFFLINE; /* XXX missing from wdcreg.h */
+- req.command = WDCC_SMART;
+- req.cylinder = WDSMART_CYL;
+- req.sec_count = select;
+- break;
+- case AUTOSAVE:
+- req.flags = ATACMD_READREG;
+- req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */
+- req.command = WDCC_SMART;
+- req.cylinder = WDSMART_CYL;
+- req.sec_count = select;
+- break;
+- case IMMEDIATE_OFFLINE:
+- /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+- req.flags = ATACMD_READREG;
+- req.features = ATA_SMART_IMMEDIATE_OFFLINE; /* XXX missing from wdcreg.h */
+- req.command = WDCC_SMART;
+- req.cylinder = WDSMART_CYL;
+- req.sec_num = select;
+- req.sec_count = 1;
+- break;
+- case STATUS: /* should return 0 if SMART is enabled at all */
+- case STATUS_CHECK: /* should return 0 if disk's health is ok */
+- req.flags = ATACMD_READREG;
+- req.features = WDSM_STATUS;
+- req.command = WDCC_SMART;
+- req.cylinder = WDSMART_CYL;
+- break;
+- case CHECK_POWER_MODE:
+- req.flags = ATACMD_READREG;
+- req.command = WDCC_CHECK_PWR;
+- break;
+- default:
+- pout("Unrecognized command %d in ata_command_interface()\n", command);
+- errno = ENOSYS;
+- return -1;
++ clear_err();
++ errno = 0;
++ if (do_cmd(&req, in.in_regs.is_48bit_cmd()))
++ return false;
++ if (req.retsts != ATACMD_OK)
++ return set_err(EIO, "request failed, error code 0x%02x", req.retsts);
++
++ out.out_regs.error = req.error;
++ out.out_regs.sector_count = req.sec_count;
++ out.out_regs.lba_low = req.sec_num;
++ out.out_regs.device = req.head;
++ out.out_regs.lba_mid = le16toh(req.cylinder);
++ out.out_regs.lba_high = le16toh(req.cylinder) >> 8;
++ out.out_regs.status = req.command;
++
++ // Command specific processing
++ if (in.in_regs.command == ATA_SMART_CMD
++ && in.in_regs.features == ATA_SMART_STATUS
++ && in.out_needed.lba_high)
++ {
++ unsigned const char normal_lo=0x4f, normal_hi=0xc2;
++ unsigned const char failed_lo=0xf4, failed_hi=0x2c;
++
++ // Cyl low and Cyl high unchanged means "Good SMART status"
++ if (!(out.out_regs.lba_mid==normal_lo && out.out_regs.lba_high==normal_hi)
++ // These values mean "Bad SMART status"
++ && !(out.out_regs.lba_mid==failed_lo && out.out_regs.lba_high==failed_hi))
++
++ {
++ // We haven't gotten output that makes sense; print out some debugging info
++ char buf[512];
++ snprintf(buf, sizeof(buf),
++ "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
++ (int)req.command,
++ (int)req.features,
++ (int)req.sec_count,
++ (int)req.sec_num,
++ (int)(le16toh(req.cylinder) & 0xff),
++ (int)((le16toh(req.cylinder) >> 8) & 0xff),
++ (int)req.error);
++ printwarning(BAD_SMART,buf);
++ out.out_regs.lba_high = failed_hi;
++ out.out_regs.lba_mid = failed_lo;
++ }
+ }
+
+- if (command == STATUS_CHECK || command == AUTOSAVE || command == AUTO_OFFLINE) {
+- char buf[512];
++ return true;
++}
+
+- unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
++/////////////////////////////////////////////////////////////////////////////
++/// NVMe support
+
+- if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+- perror("Failed command");
+- return -1;
+- }
+- if (req.retsts != ATACMD_OK) {
+- return -1;
++class netbsd_nvme_device
++: public /*implements*/ nvme_device,
++ public /*extends*/ netbsd_smart_device
++{
++public:
++ netbsd_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);
++};
++
++netbsd_nvme_device::netbsd_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),
++ netbsd_smart_device()
++{
++}
++
++bool netbsd_nvme_device::open()
++{
++ const char *dev = get_dev_name();
++ if (strncmp(dev, NVME_PREFIX, strlen(NVME_PREFIX))) {
++ set_err(EINVAL, "NVMe controller controller/namespace ids must begin with '%s'",
++ NVME_PREFIX);
++ return false;
++ }
++
++ int nsid = -1, ctrlid = -1;
++ char tmp;
++
++ if(sscanf(dev, NVME_PREFIX"%d%c", &ctrlid, &tmp) == 1)
++ {
++ if(ctrlid < 0) {
++ set_err(EINVAL, "Invalid NVMe controller number");
++ return false;
+ }
+- /* Cyl low and Cyl high unchanged means "Good SMART status" */
+- if (req.cylinder == normal)
+- return 0;
+-
+- /* These values mean "Bad SMART status" */
+- if (req.cylinder == failed)
+- return 1;
+-
+- /* We haven't gotten output that makes sense;
+- * print out some debugging info */
+- snprintf(buf, sizeof(buf),
+- "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
+- (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
+- (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff),
+- (int) req.error);
+- printwarning(BAD_SMART, buf);
+- return 0;
++ nsid = 0xFFFFFFFF; // broadcast id
+ }
+-
+- retval = ioctl(fd, ATAIOCCOMMAND, &req);
+- if (retval < 0) {
+- perror("Failed command");
+- return -1;
++ else if (sscanf(dev, NVME_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;
++ }
+ }
+- if (req.retsts != ATACMD_OK) {
+- return -1;
++ 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_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;
++}
+
+- if (command == CHECK_POWER_MODE)
+- data[0] = req.sec_count;
++bool netbsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
++{
++ struct nvme_pt_command pt;
++ memset(&pt, 0, sizeof(pt));
+
+- if (copydata)
+- memcpy(data, inbuf, 512);
++ pt.cmd.opcode = 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));
+
+- return 0;
++ 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;
+ }
+
+-int
+-do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
++/////////////////////////////////////////////////////////////////////////////
++/// Standard SCSI support
++
++class netbsd_scsi_device
++: public /*implements*/ scsi_device,
++ public /*extends*/ netbsd_smart_device
+ {
+- struct scsireq sc;
++public:
++ netbsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type, bool scanning = false);
++
++ virtual smart_device * autodetect_open();
++
++ virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+- if (report > 0) {
+- size_t k;
++private:
++ bool m_scanning; ///< true if created within scan_smart_devices
++};
++
++netbsd_scsi_device::netbsd_scsi_device(smart_interface * intf,
++ const char * dev_name, const char * req_type, bool scanning /* = false */)
++: smart_device(intf, dev_name, "scsi", req_type),
++ netbsd_smart_device(),
++ m_scanning(scanning)
++{
++}
+
+- const unsigned char *ucp = iop->cmnd;
+- const char *np;
++bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
++{
++ struct scsireq sc;
++ int fd = get_fd();
++
++ if (scsi_debugmode) {
++ unsigned int k;
++ const unsigned char * ucp = iop->cmnd;
++ const char * np;
+
+ np = scsi_get_opcode_name(ucp[0]);
+ pout(" [%s: ", np ? np : "<unknown opcode>");
+ for (k = 0; k < iop->cmnd_len; ++k)
+ pout("%02x ", ucp[k]);
+- if ((report > 1) &&
++ if ((scsi_debugmode > 1) &&
+ (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+- int trunc = (iop->dxfer_len > 256) ? 1 : 0;
++ int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+- pout("]\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
+- pout("]");
++ pout("]\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
++ pout("]\n");
+ }
++
+ memset(&sc, 0, sizeof(sc));
+ memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
+ sc.cmdlen = iop->cmnd_len;
+@@ -377,8 +444,10 @@ do_scsi_cmnd_io(int fd, struct scsi_cmnd
+ (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
+
+ if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
+- warn("error sending SCSI ccb");
+- return -1;
++ if (scsi_debugmode) {
++ pout(" error sending SCSI ccb\n");
++ }
++ return set_err(EIO);
+ }
+ iop->resid = sc.datalen - sc.datalen_used;
+ iop->scsi_status = sc.status;
+@@ -386,7 +455,7 @@ do_scsi_cmnd_io(int fd, struct scsi_cmnd
+ memcpy(iop->sensep, sc.sense, sc.senselen_used);
+ iop->resp_sense_len = sc.senselen_used;
+ }
+- if (report > 0) {
++ if (scsi_debugmode) {
+ int trunc;
+
+ pout(" status=0\n");
+@@ -398,43 +467,412 @@ do_scsi_cmnd_io(int fd, struct scsi_cmnd
+ }
+ switch (sc.retsts) {
+ case SCCMD_OK:
+- return 0;
++ break;
+ case SCCMD_TIMEOUT:
+- return -ETIMEDOUT;
++ return set_err(ETIMEDOUT);
+ case SCCMD_BUSY:
+- return -EBUSY;
++ return set_err(EBUSY);
+ default:
+- return -EIO;
++ return set_err(EIO);
++ }
++
++ return true;
++}
++
++/////////////////////////////////////////////////////////////////////////////
++///// SCSI open with autodetection support
++
++smart_device * netbsd_scsi_device::autodetect_open()
++{
++ // Open device
++ if (!open())
++ return this;
++
++ // No Autodetection if device type was specified by user
++ bool sat_only = false;
++ if (*get_req_type()) {
++ // Detect SAT if device object was created by scan_smart_devices().
++ if (!(m_scanning && !strcmp(get_req_type(), "sat")))
++ return this;
++ sat_only = true;
++ }
++
++ // The code below is based on smartd.cpp:SCSIFilterKnown()
++
++ // Get INQUIRY
++ unsigned char req_buff[64] = {0, };
++ int req_len = 36;
++ if (scsiStdInquiry(this, req_buff, req_len)) {
++ // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices
++ // watch this spot ... other devices could lock up here
++ req_len = 64;
++ if (scsiStdInquiry(this, req_buff, req_len)) {
++ // device doesn't like INQUIRY commands
++ close();
++ set_err(EIO, "INQUIRY failed");
++ return this;
++ }
++ }
++
++ int avail_len = req_buff[4] + 5;
++ int len = (avail_len < req_len ? avail_len : req_len);
++ if (len < 36) {
++ if (sat_only) {
++ close();
++ set_err(EIO, "INQUIRY too short for SAT");
++ }
++ return this;
++ }
++
++ // Use INQUIRY to detect type
++
++ // SAT or USB, skip MFI controllers because of bugs
++ {
++ smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
++ if (newdev) {
++ // NOTE: 'this' is now owned by '*newdev'
++ return newdev;
++ }
+ }
++
++ // Nothing special found
++
++ if (sat_only) {
++ close();
++ set_err(EIO, "Not a SAT device");
++ }
++ return this;
+ }
+
+-/* print examples for smartctl */
+-void
+-print_smartctl_examples()
++/////////////////////////////////////////////////////////////////////////////
++/// Implement platform interface with old functions.
++
++class netbsd_smart_interface
++: public /*implements*/ smart_interface
+ {
+- char p;
++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,
++ const char * pattern = 0);
++
++protected:
++ virtual ata_device * get_ata_device(const char * name, const char * type);
++
++ 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);
++
++ virtual std::string get_valid_custom_dev_types_str();
++
++private:
++ int get_dev_names(char ***, const char *);
++
++ bool get_nvme_devlist(smart_device_list & devlist, const char * type);
++};
++
++
++//////////////////////////////////////////////////////////////////////
+
+- p = 'a' + getrawpartition();
+- printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
++std::string netbsd_smart_interface::get_os_version_str()
++{
++ struct utsname osname;
++ uname(&osname);
++ return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine);
++}
++
++std::string netbsd_smart_interface::get_app_examples(const char * appname)
++{
++ if (!strcmp(appname, "smartctl")) {
++ char p;
++
++ p = 'a' + getrawpartition();
++ return strprintf(
++ "=================================================== SMARTCTL EXAMPLES =====\n\n"
+ #ifdef HAVE_GETOPT_LONG
+- printf(
+- " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n"
+- " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
+- " (Enables SMART on first disk)\n\n"
+- " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n"
+- " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
+- " (Prints Self-Test & Attribute errors)\n",
+- p, p, p, p
+- );
++ " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n"
++ " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
++ " (Enables SMART on first disk)\n\n"
++ " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n"
++ " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
++ " (Prints Self-Test & Attribute errors)\n"
+ #else
+- printf(
+- " smartctl -a /dev/wd0%c (Prints all SMART information)\n"
+- " smartctl -s on -o on -S on /dev/wd0%c (Enables SMART on first disk)\n"
+- " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n"
+- " smartctl -A -l selftest -q errorsonly /dev/wd0%c"
+- " (Prints Self-Test & Attribute errors)\n",
+- p, p, p, p
+- );
++ " smartctl -a /dev/wd0%c (Prints all SMART information)\n"
++ " smartctl -s on -o on -S on /dev/wd0%c (Enables SMART on first disk)\n"
++ " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n"
++ " smartctl -A -l selftest -q errorsonly /dev/wd0%c"
++ " (Prints Self-Test & Attribute errors)\n"
+ #endif
+- return;
++ , p, p, p, p);
++ }
++ return "";
++}
++
++ata_device * netbsd_smart_interface::get_ata_device(const char * name, const char * type)
++{
++ return new netbsd_ata_device(this, name, type);
++}
++
++scsi_device * netbsd_smart_interface::get_scsi_device(const char * name, const char * type)
++{
++ return new netbsd_scsi_device(this, name, type);
++}
++
++nvme_device * netbsd_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid)
++{
++ return new netbsd_nvme_device(this, name, type, nsid);
+ }
++
++int netbsd_smart_interface::get_dev_names(char ***names, const char *prefix)
++{
++ char *disknames, *p, **mp;
++ int n = 0;
++ int sysctl_mib[2];
++ size_t sysctl_len;
++
++ *names = NULL;
++
++ sysctl_mib[0] = CTL_HW;
++ sysctl_mib[1] = HW_DISKNAMES;
++ if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
++ pout("Failed to get value of sysctl `hw.disknames'\n");
++ return -1;
++ }
++ if (!(disknames = (char *)malloc(sysctl_len))) {
++ pout("Out of memory constructing scan device list\n");
++ return -1;
++ }
++ if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
++ pout("Failed to get value of sysctl `hw.disknames'\n");
++ return -1;
++ }
++ if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
++ pout("Out of memory constructing scan device list\n");
++ return -1;
++ }
++ for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
++ if (strncmp(p, prefix, strlen(prefix))) {
++ continue;
++ }
++ mp[n] = (char *)malloc(strlen(net_dev_raw_prefix) + strlen(p) + 2);
++ if (!mp[n]) {
++ pout("Out of memory constructing scan device list\n");
++ return -1;
++ }
++ sprintf(mp[n], "%s%s%c", net_dev_raw_prefix, p, 'a' + getrawpartition());
++ n++;
++ }
++
+ char ** tmp = (char **)realloc(mp, n * (sizeof(char *)));
- if (NULL == tmp) {
- pout("Out of memory constructing scan device list\n");
- free(mp);
++ if (NULL == tmp) {
++ pout("Out of memory constructing scan device list\n");
++ free(mp);
++ return -1;
++ }
++ else
++ mp = tmp;
++ *names = mp;
++ return n;
++}
++
++bool netbsd_smart_interface::get_nvme_devlist(smart_device_list & devlist,
++ const char * type)
++{
++ char ctrlpath[64], nspath[64];
++ struct stat sb;
++ struct devlistargs laa;
++ nvme_device * nvmedev;
++
++ int drvfd = ::open(DRVCTLDEV, O_RDONLY, 0);
++ if (drvfd < 0) {
++ set_err(errno);
++ return false;
++ }
++
++ for (int ctrl = 0;; ctrl++) {
++ snprintf(ctrlpath, sizeof(ctrlpath), NVME_PREFIX"%d", ctrl);
++ if (stat(ctrlpath, &sb) == -1 || !S_ISCHR(sb.st_mode))
++ break;
++
++ snprintf(laa.l_devname, sizeof(laa.l_devname), "%s%d", net_dev_nvme_ctrl,
++ ctrl);
++ laa.l_childname = NULL;
++ laa.l_children = 0;
++ if (ioctl(drvfd, DRVLISTDEV, &laa) == -1) {
++ if (errno == ENXIO)
++ continue;
++ break;
++ }
++
++ nvmedev = get_nvme_device(ctrlpath, type, 0);
++ if (nvmedev)
++ devlist.push_back(nvmedev);
++
++ uint32_t n = 0;
++ for (int nsid = 1; n < laa.l_children; nsid++) {
++ snprintf(nspath, sizeof(nspath), NVME_PREFIX"%d"NVME_NS_PREFIX"%d",
++ ctrl, nsid);
++ if (stat(nspath, &sb) == -1 || !S_ISCHR(sb.st_mode))
++ break;
++ int nsfd = ::open(nspath, O_RDONLY, 0);
++ if (nsfd < 0)
++ continue;
++ ::close(nsfd);
++
++ n++;
++ nvmedev = get_nvme_device(nspath, type, nsid);
++ if (nvmedev)
++ devlist.push_back(nvmedev);
++ }
++ }
++
++ ::close(drvfd);
++ return true;
++}
++
++bool netbsd_smart_interface::scan_smart_devices(smart_device_list & devlist,
++ const char * type, const char * pattern /*= 0*/)
++{
++ if (pattern) {
++ set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
++ return false;
++ }
++
++ if (type == NULL)
++ type = "";
++
++ bool scan_ata = !*type || !strcmp(type, "ata");
++ bool scan_scsi = !*type || !strcmp(type, "scsi") || !strcmp(type, "sat");
++ bool scan_nvme = !*type || !strcmp(type, "nvme");
++
++ // Make namelists
++ char * * atanames = 0; int numata = 0;
++ if (scan_ata) {
++ numata = get_dev_names(&atanames, net_dev_ata_disk);
++ if (numata < 0) {
++ set_err(ENOMEM);
++ return false;
++ }
++ }
++
++ char * * scsinames = 0; int numscsi = 0;
++ char * * scsitapenames = 0; int numscsitape = 0;
++ if (scan_scsi) {
++ numscsi = get_dev_names(&scsinames, net_dev_scsi_disk);
++ if (numscsi < 0) {
++ set_err(ENOMEM);
++ return false;
++ }
++ numscsitape = get_dev_names(&scsitapenames, net_dev_scsi_tape);
++ if (numscsitape < 0) {
++ set_err(ENOMEM);
++ return false;
++ }
++ }
++
++ // Add to devlist
++ int i;
++ for (i = 0; i < numata; i++) {
++ ata_device * atadev = get_ata_device(atanames[i], type);
++ if (atadev)
++ devlist.push_back(atadev);
++ free(atanames[i]);
++ }
++ if(numata) free(atanames);
++
++ for (i = 0; i < numscsi; i++) {
++ scsi_device * scsidev = new netbsd_scsi_device(this, scsinames[i], type, true /*scanning*/);
++ if (scsidev)
++ devlist.push_back(scsidev);
++ free(scsinames[i]);
++ }
++ if(numscsi) free(scsinames);
++
++ for (i = 0; i < numscsitape; i++) {
++ scsi_device * scsidev = get_scsi_device(scsitapenames[i], type);
++ if (scsidev)
++ devlist.push_back(scsidev);
++ free(scsitapenames[i]);
++ }
++ if(numscsitape) free(scsitapenames);
++
++ if (scan_nvme)
++ get_nvme_devlist(devlist, type);
++
++ return true;
++}
++
++smart_device * netbsd_smart_interface::autodetect_smart_device(const char * name)
++{
++ const char * test_name = name;
++
++ // if dev_name null, or string length zero
++ if (!name || !*name)
++ return 0;
++
++ // Dereference symlinks
++ struct stat st;
++ std::string pathbuf;
++ if (!lstat(name, &st) && S_ISLNK(st.st_mode)) {
++ char * p = realpath(name, (char *)0);
++ if (p) {
++ pathbuf = p;
++ free(p);
++ test_name = pathbuf.c_str();
++ }
++ }
++
++ if (str_starts_with(test_name, net_dev_raw_prefix)) {
++ test_name += strlen(net_dev_raw_prefix);
++ if (!strncmp(net_dev_ata_disk, test_name, strlen(net_dev_ata_disk)))
++ return get_ata_device(test_name, "ata");
++ if (!strncmp(net_dev_scsi_disk, test_name, strlen(net_dev_scsi_disk))) {
++ // XXX Try to detect possible USB->(S)ATA bridge
++ // XXX get USB vendor ID, product ID and version from sd(4)/umass(4).
++ // XXX check sat device via get_usb_dev_type_by_id().
++
++ // No USB bridge found, assume regular SCSI device
++ return get_scsi_device(test_name, "scsi");
++ }
++ if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape)))
++ return get_scsi_device(test_name, "scsi");
++ } else if (str_starts_with(test_name, net_dev_prefix)) {
++ if (!strncmp(NVME_PREFIX, test_name, strlen(NVME_PREFIX)))
++ return get_nvme_device(test_name, "nvme", 0 /* use default nsid */);
++ }
++
++ // device type unknown
++ return 0;
++}
++
++smart_device * netbsd_smart_interface::get_custom_smart_device(const char * name, const char * type)
++{
++ ARGUSED(name);
++ ARGUSED(type);
++ return 0;
++}
++
++std::string netbsd_smart_interface::get_valid_custom_dev_types_str()
++{
++ return "";
++}
++
++} // namespace
++
++/////////////////////////////////////////////////////////////////////////////
++/// Initialize platform interface and register with smi()
++
++void smart_interface::init()
++{
++ static os_netbsd::netbsd_smart_interface the_interface;
++ smart_interface::set(&the_interface);
++}
++
++/* vim: set ts=2 sw=2 et ff=unix : */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment