Last active
September 10, 2016 02:09
-
-
Save nonakap/7ae015d475c187bf43cdcc224f8edf9b to your computer and use it in GitHub Desktop.
pkgsrc/sysutils/smartmontools: rewrite os_netbsd.cpp for new world order.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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