]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_netbsd.cpp
New upstream version 6.6
[mirror_smartmontools-debian.git] / os_netbsd.cpp
CommitLineData
832b75ed 1/*
4d59bff9 2 * os_netbsd.cpp
832b75ed 3 *
a86ec89e 4 * Home page of code is: http://www.smartmontools.org
832b75ed 5 *
f9e10201
JD
6 * Copyright (C) 2003-8 Sergey Svishchev
7 * Copyright (C) 2016 Kimihiro Nonaka
832b75ed
GG
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * You should have received a copy of the GNU General Public License
ee38a438
GI
15 * (for example COPYING); if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
832b75ed
GG
17 *
18 */
19
20#include "config.h"
21#include "int64.h"
22#include "atacmds.h"
23#include "scsicmds.h"
24#include "utility.h"
25#include "os_netbsd.h"
cfbba5b9 26
f9e10201
JD
27#include <sys/drvctlio.h>
28#include <sys/utsname.h>
cfbba5b9 29#include <errno.h>
832b75ed
GG
30#include <unistd.h>
31
f9e10201
JD
32// based on "sys/dev/ic/nvmeio.h" from NetBSD kernel sources
33#include "netbsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error
34
35const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
cfbba5b9 36 OS_NETBSD_H_CVSID;
832b75ed 37
832b75ed 38enum warnings {
a86ec89e 39 BAD_SMART, MAX_MSG
832b75ed
GG
40};
41
42/* Utility function for printing warnings */
43void
44printwarning(int msgNo, const char *extra)
45{
46 static int printed[] = {0, 0};
47 static const char *message[] = {
48 "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
34ad0c5f 49 PACKAGE_STRING " does not currently support twe(4) and twa(4) devices (3ware Escalade, Apache)\n",
832b75ed
GG
50 };
51
52 if (msgNo >= 0 && msgNo <= MAX_MSG) {
53 if (!printed[msgNo]) {
54 printed[msgNo] = 1;
55 pout("%s", message[msgNo]);
56 if (extra)
f9e10201 57 pout("%s", extra);
832b75ed
GG
58 }
59 }
60 return;
61}
62
f9e10201
JD
63#define ARGUSED(x) ((void)(x))
64
65/////////////////////////////////////////////////////////////////////////////
66
67namespace os_netbsd { // No need to publish anything, name provided for Doxygen
68
69static const char *net_dev_prefix = "/dev/";
70static const char *net_dev_raw_prefix = "/dev/r";
832b75ed
GG
71static const char *net_dev_ata_disk = "wd";
72static const char *net_dev_scsi_disk = "sd";
73static const char *net_dev_scsi_tape = "enrst";
f9e10201
JD
74static const char *net_dev_nvme_ctrl = "nvme";
75
76/////////////////////////////////////////////////////////////////////////////
77/// Implement shared open/close routines with old functions.
832b75ed 78
f9e10201
JD
79class netbsd_smart_device
80: virtual public /*implements*/ smart_device
832b75ed 81{
f9e10201
JD
82public:
83 explicit netbsd_smart_device()
84 : smart_device(never_called),
85 m_fd(-1) { }
832b75ed 86
f9e10201 87 virtual ~netbsd_smart_device() throw();
832b75ed 88
f9e10201
JD
89 virtual bool is_open() const;
90
91 virtual bool open();
92
93 virtual bool close();
94
95protected:
96 /// Return filedesc for derived classes.
97 int get_fd() const
98 { return m_fd; }
832b75ed 99
f9e10201
JD
100 void set_fd(int fd)
101 { m_fd = fd; }
832b75ed 102
f9e10201
JD
103private:
104 int m_fd; ///< filedesc, -1 if not open.
105};
832b75ed 106
f9e10201
JD
107netbsd_smart_device::~netbsd_smart_device() throw()
108{
109 if (m_fd >= 0)
110 os_netbsd::netbsd_smart_device::close();
832b75ed
GG
111}
112
f9e10201 113bool netbsd_smart_device::is_open() const
832b75ed 114{
f9e10201
JD
115 return (m_fd >= 0);
116}
832b75ed 117
832b75ed 118
f9e10201
JD
119bool netbsd_smart_device::open()
120{
121 const char *dev = get_dev_name();
122 int fd;
123
124 if (is_scsi()) {
125 fd = ::open(dev,O_RDWR|O_NONBLOCK);
126 if (fd < 0 && errno == EROFS)
127 fd = ::open(dev,O_RDONLY|O_NONBLOCK);
128 if (fd < 0) {
129 set_err(errno);
130 return false;
832b75ed 131 }
f9e10201
JD
132 } else if (is_ata() || is_nvme()) {
133 if ((fd = ::open(dev,O_RDWR|O_NONBLOCK))<0) {
134 set_err(errno);
135 return false;
832b75ed 136 }
f9e10201
JD
137 } else
138 return false;
832b75ed 139
f9e10201
JD
140 set_fd(fd);
141 return true;
832b75ed
GG
142}
143
f9e10201 144bool netbsd_smart_device::close()
832b75ed 145{
f9e10201
JD
146 int failed = 0;
147 // close device, if open
148 if (is_open())
149 failed=::close(get_fd());
150
151 set_fd(-1);
152
153 if(failed) return false;
154 else return true;
832b75ed
GG
155}
156
f9e10201
JD
157/////////////////////////////////////////////////////////////////////////////
158/// Implement standard ATA support
159
160class netbsd_ata_device
161: public /*implements*/ ata_device,
162 public /*extends*/ netbsd_smart_device
163{
164public:
165 netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
166 virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
167
168protected:
169 virtual int do_cmd(struct atareq* request, bool is_48bit_cmd);
170};
171
172netbsd_ata_device::netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
173: smart_device(intf, dev_name, "ata", req_type),
174 netbsd_smart_device()
832b75ed 175{
832b75ed
GG
176}
177
f9e10201 178int netbsd_ata_device::do_cmd( struct atareq* request, bool is_48bit_cmd)
832b75ed 179{
f9e10201
JD
180 int fd = get_fd(), ret;
181 ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the ATAIOCCOMMAND
182 ret = ioctl(fd, ATAIOCCOMMAND, request);
183 if (ret) set_err(errno);
184 return ret;
832b75ed
GG
185}
186
f9e10201 187bool netbsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
832b75ed 188{
f9e10201
JD
189 bool ata_48bit = false; // no ata_48bit_support via ATAIOCCOMMAND
190
191 if (!ata_cmd_is_ok(in,
192 true, // data_out_support
193 true, // multi_sector_support
194 ata_48bit)
195 ) {
196 set_err(ENOSYS, "48-bit ATA commands not implemented");
197 return false;
198 }
832b75ed 199
f9e10201 200 struct atareq req;
832b75ed 201 memset(&req, 0, sizeof(req));
f9e10201 202
34ad0c5f 203 req.timeout = 1000;
f9e10201
JD
204 req.command = in.in_regs.command;
205 req.features = in.in_regs.features;
206 req.sec_count = in.in_regs.sector_count;
207 req.sec_num = in.in_regs.lba_low;
208 req.head = in.in_regs.device;
209 req.cylinder = le16toh(in.in_regs.lba_mid | (in.in_regs.lba_high << 8));
210
211 switch (in.direction) {
212 case ata_cmd_in::no_data:
213 req.flags = ATACMD_READREG;
214 break;
215 case ata_cmd_in::data_in:
216 req.flags = ATACMD_READ | ATACMD_READREG;
217 req.databuf = (char *)in.buffer;
218 req.datalen = in.size;
219 break;
220 case ata_cmd_in::data_out:
221 req.flags = ATACMD_WRITE | ATACMD_READREG;
222 req.databuf = (char *)in.buffer;
223 req.datalen = in.size;
224 break;
225 default:
226 return set_err(ENOSYS);
227 }
34ad0c5f 228
f9e10201
JD
229 clear_err();
230 errno = 0;
231 if (do_cmd(&req, in.in_regs.is_48bit_cmd()))
232 return false;
233 if (req.retsts != ATACMD_OK)
234 return set_err(EIO, "request failed, error code 0x%02x", req.retsts);
235
236 out.out_regs.error = req.error;
237 out.out_regs.sector_count = req.sec_count;
238 out.out_regs.lba_low = req.sec_num;
239 out.out_regs.device = req.head;
240 out.out_regs.lba_mid = le16toh(req.cylinder);
241 out.out_regs.lba_high = le16toh(req.cylinder) >> 8;
242 out.out_regs.status = req.command;
243
244 // Command specific processing
245 if (in.in_regs.command == ATA_SMART_CMD
246 && in.in_regs.features == ATA_SMART_STATUS
247 && in.out_needed.lba_high)
248 {
249 unsigned const char normal_lo=0x4f, normal_hi=0xc2;
250 unsigned const char failed_lo=0xf4, failed_hi=0x2c;
251
252 // Cyl low and Cyl high unchanged means "Good SMART status"
253 if (!(out.out_regs.lba_mid==normal_lo && out.out_regs.lba_high==normal_hi)
254 // These values mean "Bad SMART status"
255 && !(out.out_regs.lba_mid==failed_lo && out.out_regs.lba_high==failed_hi))
256
257 {
258 // We haven't gotten output that makes sense; print out some debugging info
259 char buf[512];
260 snprintf(buf, sizeof(buf),
261 "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
262 (int)req.command,
263 (int)req.features,
264 (int)req.sec_count,
265 (int)req.sec_num,
266 (int)(le16toh(req.cylinder) & 0xff),
267 (int)((le16toh(req.cylinder) >> 8) & 0xff),
268 (int)req.error);
269 printwarning(BAD_SMART,buf);
270 out.out_regs.lba_high = failed_hi;
271 out.out_regs.lba_mid = failed_lo;
272 }
832b75ed
GG
273 }
274
f9e10201
JD
275 return true;
276}
832b75ed 277
f9e10201
JD
278/////////////////////////////////////////////////////////////////////////////
279/// NVMe support
832b75ed 280
f9e10201
JD
281class netbsd_nvme_device
282: public /*implements*/ nvme_device,
283 public /*extends*/ netbsd_smart_device
284{
285public:
286 netbsd_nvme_device(smart_interface * intf, const char * dev_name,
287 const char * req_type, unsigned nsid);
288
289 virtual bool open();
290
291 virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
292};
293
294netbsd_nvme_device::netbsd_nvme_device(smart_interface * intf, const char * dev_name,
295 const char * req_type, unsigned nsid)
296: smart_device(intf, dev_name, "nvme", req_type),
297 nvme_device(nsid),
298 netbsd_smart_device()
299{
300}
301
302bool netbsd_nvme_device::open()
303{
304 const char *dev = get_dev_name();
305 if (strncmp(dev, NVME_PREFIX, strlen(NVME_PREFIX))) {
306 set_err(EINVAL, "NVMe controller controller/namespace ids must begin with '%s'",
307 NVME_PREFIX);
308 return false;
309 }
310
311 int nsid = -1, ctrlid = -1;
312 char tmp;
313
314 if(sscanf(dev, NVME_PREFIX"%d%c", &ctrlid, &tmp) == 1)
315 {
316 if(ctrlid < 0) {
317 set_err(EINVAL, "Invalid NVMe controller number");
318 return false;
832b75ed 319 }
f9e10201
JD
320 nsid = 0xFFFFFFFF; // broadcast id
321 }
322 else if (sscanf(dev, NVME_PREFIX"%d"NVME_NS_PREFIX"%d%c",
323 &ctrlid, &nsid, &tmp) == 2)
324 {
325 if(ctrlid < 0 || nsid <= 0) {
326 set_err(EINVAL, "Invalid NVMe controller/namespace number");
327 return false;
34ad0c5f 328 }
f9e10201
JD
329 }
330 else {
331 set_err(EINVAL, "Invalid NVMe controller/namespace syntax");
332 return false;
832b75ed 333 }
34ad0c5f 334
f9e10201
JD
335 // we should always open controller, not namespace device
336 char full_path[64];
337 snprintf(full_path, sizeof(full_path), NVME_PREFIX"%d", ctrlid);
338
339 int fd;
340 if ((fd = ::open(full_path, O_RDWR))<0) {
341 set_err(errno);
342 return false;
832b75ed 343 }
f9e10201
JD
344 set_fd(fd);
345
346 if (!get_nsid()) {
347 set_nsid(nsid);
34ad0c5f
GG
348 }
349
f9e10201
JD
350 return true;
351}
832b75ed 352
f9e10201
JD
353bool netbsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
354{
355 struct nvme_pt_command pt;
356 memset(&pt, 0, sizeof(pt));
832b75ed 357
f9e10201
JD
358 pt.cmd.opcode = in.opcode;
359 pt.cmd.nsid = in.nsid;
360 pt.buf = in.buffer;
361 pt.len = in.size;
362 pt.cmd.cdw10 = in.cdw10;
363 pt.cmd.cdw11 = in.cdw11;
364 pt.cmd.cdw12 = in.cdw12;
365 pt.cmd.cdw13 = in.cdw13;
366 pt.cmd.cdw14 = in.cdw14;
367 pt.cmd.cdw15 = in.cdw15;
368 pt.is_read = 1; // should we use in.direction()?
369
370 int status = ioctl(get_fd(), NVME_PASSTHROUGH_CMD, &pt);
371
372 if (status < 0)
373 return set_err(errno, "NVME_PASSTHROUGH_CMD: %s", strerror(errno));
374
375 out.result=pt.cpl.cdw0; // Command specific result (DW0)
376
377 if (nvme_completion_is_error(&pt.cpl))
378 return set_nvme_err(out, nvme_completion_is_error(&pt.cpl));
379
380 return true;
832b75ed
GG
381}
382
f9e10201
JD
383/////////////////////////////////////////////////////////////////////////////
384/// Standard SCSI support
385
386class netbsd_scsi_device
387: public /*implements*/ scsi_device,
388 public /*extends*/ netbsd_smart_device
832b75ed 389{
f9e10201
JD
390public:
391 netbsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type, bool scanning = false);
392
393 virtual smart_device * autodetect_open();
394
395 virtual bool scsi_pass_through(scsi_cmnd_io * iop);
396
397private:
398 bool m_scanning; ///< true if created within scan_smart_devices
399};
400
401netbsd_scsi_device::netbsd_scsi_device(smart_interface * intf,
402 const char * dev_name, const char * req_type, bool scanning /* = false */)
403: smart_device(intf, dev_name, "scsi", req_type),
404 netbsd_smart_device(),
405 m_scanning(scanning)
406{
407}
832b75ed 408
f9e10201
JD
409bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
410{
411 struct scsireq sc;
412 int fd = get_fd();
832b75ed 413
f9e10201
JD
414 if (scsi_debugmode) {
415 unsigned int k;
416 const unsigned char * ucp = iop->cmnd;
417 const char * np;
832b75ed
GG
418
419 np = scsi_get_opcode_name(ucp[0]);
420 pout(" [%s: ", np ? np : "<unknown opcode>");
421 for (k = 0; k < iop->cmnd_len; ++k)
422 pout("%02x ", ucp[k]);
f9e10201 423 if ((scsi_debugmode > 1) &&
832b75ed 424 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
f9e10201 425 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
832b75ed 426
f9e10201
JD
427 pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
428 (trunc ? " [only first 256 bytes shown]" : ""));
429 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
430 }
431 else
432 pout("]\n");
832b75ed 433 }
f9e10201 434
832b75ed
GG
435 memset(&sc, 0, sizeof(sc));
436 memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
437 sc.cmdlen = iop->cmnd_len;
4d59bff9 438 sc.databuf = (char *)iop->dxferp;
832b75ed
GG
439 sc.datalen = iop->dxfer_len;
440 sc.senselen = iop->max_sense_len;
441 sc.timeout = iop->timeout == 0 ? 60000 : (1000 * iop->timeout);
442 sc.flags =
4d59bff9 443 (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :
832b75ed
GG
444 (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
445
446 if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
f9e10201
JD
447 if (scsi_debugmode) {
448 pout(" error sending SCSI ccb\n");
449 }
450 return set_err(EIO);
832b75ed
GG
451 }
452 iop->resid = sc.datalen - sc.datalen_used;
453 iop->scsi_status = sc.status;
454 if (iop->sensep) {
455 memcpy(iop->sensep, sc.sense, sc.senselen_used);
456 iop->resp_sense_len = sc.senselen_used;
457 }
f9e10201 458 if (scsi_debugmode) {
832b75ed
GG
459 int trunc;
460
461 pout(" status=0\n");
462 trunc = (iop->dxfer_len > 256) ? 1 : 0;
463
464 pout(" Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
465 (trunc ? " [only first 256 bytes shown]" : ""));
466 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
467 }
34ad0c5f
GG
468 switch (sc.retsts) {
469 case SCCMD_OK:
f9e10201 470 break;
34ad0c5f 471 case SCCMD_TIMEOUT:
f9e10201 472 return set_err(ETIMEDOUT);
34ad0c5f 473 case SCCMD_BUSY:
f9e10201 474 return set_err(EBUSY);
34ad0c5f 475 default:
f9e10201 476 return set_err(EIO);
34ad0c5f 477 }
f9e10201
JD
478
479 return true;
832b75ed
GG
480}
481
f9e10201
JD
482/////////////////////////////////////////////////////////////////////////////
483///// SCSI open with autodetection support
484
485smart_device * netbsd_scsi_device::autodetect_open()
832b75ed 486{
f9e10201
JD
487 // Open device
488 if (!open())
489 return this;
490
491 // No Autodetection if device type was specified by user
492 bool sat_only = false;
493 if (*get_req_type()) {
494 // Detect SAT if device object was created by scan_smart_devices().
495 if (!(m_scanning && !strcmp(get_req_type(), "sat")))
496 return this;
497 sat_only = true;
498 }
832b75ed 499
f9e10201
JD
500 // The code below is based on smartd.cpp:SCSIFilterKnown()
501
502 // Get INQUIRY
503 unsigned char req_buff[64] = {0, };
504 int req_len = 36;
505 if (scsiStdInquiry(this, req_buff, req_len)) {
506 // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices
507 // watch this spot ... other devices could lock up here
508 req_len = 64;
509 if (scsiStdInquiry(this, req_buff, req_len)) {
510 // device doesn't like INQUIRY commands
511 close();
512 set_err(EIO, "INQUIRY failed");
513 return this;
514 }
515 }
516
517 int avail_len = req_buff[4] + 5;
518 int len = (avail_len < req_len ? avail_len : req_len);
519 if (len < 36) {
520 if (sat_only) {
521 close();
522 set_err(EIO, "INQUIRY too short for SAT");
523 }
524 return this;
525 }
526
527 // Use INQUIRY to detect type
528
529 // SAT or USB, skip MFI controllers because of bugs
530 {
531 smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
532 if (newdev) {
533 // NOTE: 'this' is now owned by '*newdev'
534 return newdev;
535 }
536 }
537
538 // Nothing special found
539
540 if (sat_only) {
541 close();
542 set_err(EIO, "Not a SAT device");
543 }
544 return this;
545}
546
547/////////////////////////////////////////////////////////////////////////////
548/// Implement platform interface with old functions.
549
550class netbsd_smart_interface
551: public /*implements*/ smart_interface
552{
553public:
554 virtual std::string get_os_version_str();
555
556 virtual std::string get_app_examples(const char * appname);
557
558 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
559 const char * pattern = 0);
560
561protected:
562 virtual ata_device * get_ata_device(const char * name, const char * type);
563
564 virtual scsi_device * get_scsi_device(const char * name, const char * type);
565
566 virtual nvme_device * get_nvme_device(const char * name, const char * type,
567 unsigned nsid);
568
569 virtual smart_device * autodetect_smart_device(const char * name);
570
571 virtual smart_device * get_custom_smart_device(const char * name, const char * type);
572
573 virtual std::string get_valid_custom_dev_types_str();
574
575private:
576 int get_dev_names(char ***, const char *);
577
578 bool get_nvme_devlist(smart_device_list & devlist, const char * type);
579};
580
581
582//////////////////////////////////////////////////////////////////////
583
584std::string netbsd_smart_interface::get_os_version_str()
585{
586 struct utsname osname;
587 uname(&osname);
588 return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine);
589}
590
591std::string netbsd_smart_interface::get_app_examples(const char * appname)
592{
593 if (!strcmp(appname, "smartctl")) {
594 char p;
595
596 p = 'a' + getrawpartition();
597 return strprintf(
598 "=================================================== SMARTCTL EXAMPLES =====\n\n"
832b75ed 599#ifdef HAVE_GETOPT_LONG
f9e10201
JD
600 " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n"
601 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
602 " (Enables SMART on first disk)\n\n"
603 " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n"
604 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
605 " (Prints Self-Test & Attribute errors)\n"
832b75ed 606#else
f9e10201
JD
607 " smartctl -a /dev/wd0%c (Prints all SMART information)\n"
608 " smartctl -s on -o on -S on /dev/wd0%c (Enables SMART on first disk)\n"
609 " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n"
610 " smartctl -A -l selftest -q errorsonly /dev/wd0%c"
611 " (Prints Self-Test & Attribute errors)\n"
832b75ed 612#endif
f9e10201
JD
613 , p, p, p, p);
614 }
615 return "";
616}
617
618ata_device * netbsd_smart_interface::get_ata_device(const char * name, const char * type)
619{
620 return new netbsd_ata_device(this, name, type);
621}
622
623scsi_device * netbsd_smart_interface::get_scsi_device(const char * name, const char * type)
624{
625 return new netbsd_scsi_device(this, name, type);
832b75ed 626}
f9e10201
JD
627
628nvme_device * netbsd_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid)
629{
630 return new netbsd_nvme_device(this, name, type, nsid);
631}
632
633int netbsd_smart_interface::get_dev_names(char ***names, const char *prefix)
634{
635 char *disknames, *p, **mp;
636 int n = 0;
637 int sysctl_mib[2];
638 size_t sysctl_len;
639
640 *names = NULL;
641
642 sysctl_mib[0] = CTL_HW;
643 sysctl_mib[1] = HW_DISKNAMES;
644 if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
645 pout("Failed to get value of sysctl `hw.disknames'\n");
646 return -1;
647 }
648 if (!(disknames = (char *)malloc(sysctl_len))) {
649 pout("Out of memory constructing scan device list\n");
650 return -1;
651 }
652 if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
653 pout("Failed to get value of sysctl `hw.disknames'\n");
654 return -1;
655 }
656 if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
657 pout("Out of memory constructing scan device list\n");
658 return -1;
659 }
660 for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
661 if (strncmp(p, prefix, strlen(prefix))) {
662 continue;
663 }
664 mp[n] = (char *)malloc(strlen(net_dev_raw_prefix) + strlen(p) + 2);
665 if (!mp[n]) {
666 pout("Out of memory constructing scan device list\n");
667 return -1;
668 }
669 sprintf(mp[n], "%s%s%c", net_dev_raw_prefix, p, 'a' + getrawpartition());
670 n++;
671 }
672
673 char ** tmp = (char **)realloc(mp, n * (sizeof(char *)));
674 if (NULL == tmp) {
675 pout("Out of memory constructing scan device list\n");
676 free(mp);
677 return -1;
678 }
679 else
680 mp = tmp;
681 *names = mp;
682 return n;
683}
684
685bool netbsd_smart_interface::get_nvme_devlist(smart_device_list & devlist,
686 const char * type)
687{
688 char ctrlpath[64], nspath[64];
689 struct stat sb;
690 struct devlistargs laa;
691 nvme_device * nvmedev;
692
693 int drvfd = ::open(DRVCTLDEV, O_RDONLY, 0);
694 if (drvfd < 0) {
695 set_err(errno);
696 return false;
697 }
698
699 for (int ctrl = 0;; ctrl++) {
700 snprintf(ctrlpath, sizeof(ctrlpath), NVME_PREFIX"%d", ctrl);
701 if (stat(ctrlpath, &sb) == -1 || !S_ISCHR(sb.st_mode))
702 break;
703
704 snprintf(laa.l_devname, sizeof(laa.l_devname), "%s%d", net_dev_nvme_ctrl,
705 ctrl);
706 laa.l_childname = NULL;
707 laa.l_children = 0;
708 if (ioctl(drvfd, DRVLISTDEV, &laa) == -1) {
709 if (errno == ENXIO)
710 continue;
711 break;
712 }
713
714 nvmedev = get_nvme_device(ctrlpath, type, 0);
715 if (nvmedev)
716 devlist.push_back(nvmedev);
717
718 uint32_t n = 0;
719 for (int nsid = 1; n < laa.l_children; nsid++) {
720 snprintf(nspath, sizeof(nspath), NVME_PREFIX"%d"NVME_NS_PREFIX"%d",
721 ctrl, nsid);
722 if (stat(nspath, &sb) == -1 || !S_ISCHR(sb.st_mode))
723 break;
724 int nsfd = ::open(nspath, O_RDONLY, 0);
725 if (nsfd < 0)
726 continue;
727 ::close(nsfd);
728
729 n++;
730 nvmedev = get_nvme_device(nspath, type, nsid);
731 if (nvmedev)
732 devlist.push_back(nvmedev);
733 }
734 }
735
736 ::close(drvfd);
737 return true;
738}
739
740bool netbsd_smart_interface::scan_smart_devices(smart_device_list & devlist,
741 const char * type, const char * pattern /*= 0*/)
742{
743 if (pattern) {
744 set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
745 return false;
746 }
747
748 if (type == NULL)
749 type = "";
750
751 bool scan_ata = !*type || !strcmp(type, "ata");
752 bool scan_scsi = !*type || !strcmp(type, "scsi") || !strcmp(type, "sat");
753
754#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
755 bool scan_nvme = !*type || !strcmp(type, "nvme");
756#else
757 bool scan_nvme = !strcmp(type, "nvme");
758#endif
759
760 // Make namelists
761 char * * atanames = 0; int numata = 0;
762 if (scan_ata) {
763 numata = get_dev_names(&atanames, net_dev_ata_disk);
764 if (numata < 0) {
765 set_err(ENOMEM);
766 return false;
767 }
768 }
769
770 char * * scsinames = 0; int numscsi = 0;
771 char * * scsitapenames = 0; int numscsitape = 0;
772 if (scan_scsi) {
773 numscsi = get_dev_names(&scsinames, net_dev_scsi_disk);
774 if (numscsi < 0) {
775 set_err(ENOMEM);
776 return false;
777 }
778 numscsitape = get_dev_names(&scsitapenames, net_dev_scsi_tape);
779 if (numscsitape < 0) {
780 set_err(ENOMEM);
781 return false;
782 }
783 }
784
785 // Add to devlist
786 int i;
787 for (i = 0; i < numata; i++) {
788 ata_device * atadev = get_ata_device(atanames[i], type);
789 if (atadev)
790 devlist.push_back(atadev);
791 free(atanames[i]);
792 }
793 if(numata) free(atanames);
794
795 for (i = 0; i < numscsi; i++) {
796 scsi_device * scsidev = new netbsd_scsi_device(this, scsinames[i], type, true /*scanning*/);
797 if (scsidev)
798 devlist.push_back(scsidev);
799 free(scsinames[i]);
800 }
801 if(numscsi) free(scsinames);
802
803 for (i = 0; i < numscsitape; i++) {
804 scsi_device * scsidev = get_scsi_device(scsitapenames[i], type);
805 if (scsidev)
806 devlist.push_back(scsidev);
807 free(scsitapenames[i]);
808 }
809 if(numscsitape) free(scsitapenames);
810
811 if (scan_nvme)
812 get_nvme_devlist(devlist, type);
813
814 return true;
815}
816
817smart_device * netbsd_smart_interface::autodetect_smart_device(const char * name)
818{
819 const char * test_name = name;
820
821 // if dev_name null, or string length zero
822 if (!name || !*name)
823 return 0;
824
825 // Dereference symlinks
826 struct stat st;
827 std::string pathbuf;
828 if (!lstat(name, &st) && S_ISLNK(st.st_mode)) {
829 char * p = realpath(name, (char *)0);
830 if (p) {
831 pathbuf = p;
832 free(p);
833 test_name = pathbuf.c_str();
834 }
835 }
836
837 if (str_starts_with(test_name, net_dev_raw_prefix)) {
838 test_name += strlen(net_dev_raw_prefix);
839 if (!strncmp(net_dev_ata_disk, test_name, strlen(net_dev_ata_disk)))
840 return get_ata_device(test_name, "ata");
841 if (!strncmp(net_dev_scsi_disk, test_name, strlen(net_dev_scsi_disk))) {
842 // XXX Try to detect possible USB->(S)ATA bridge
843 // XXX get USB vendor ID, product ID and version from sd(4)/umass(4).
844 // XXX check sat device via get_usb_dev_type_by_id().
845
846 // No USB bridge found, assume regular SCSI device
847 return get_scsi_device(test_name, "scsi");
848 }
849 if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape)))
850 return get_scsi_device(test_name, "scsi");
851 } else if (str_starts_with(test_name, net_dev_prefix)) {
852 if (!strncmp(NVME_PREFIX, test_name, strlen(NVME_PREFIX)))
853 return get_nvme_device(test_name, "nvme", 0 /* use default nsid */);
854 }
855
856 // device type unknown
857 return 0;
858}
859
860smart_device * netbsd_smart_interface::get_custom_smart_device(const char * name, const char * type)
861{
862 ARGUSED(name);
863 ARGUSED(type);
864 return 0;
865}
866
867std::string netbsd_smart_interface::get_valid_custom_dev_types_str()
868{
869 return "";
870}
871
872} // namespace
873
874/////////////////////////////////////////////////////////////////////////////
875/// Initialize platform interface and register with smi()
876
877void smart_interface::init()
878{
879 static os_netbsd::netbsd_smart_interface the_interface;
880 smart_interface::set(&the_interface);
881}
882
883/* vim: set ts=2 sw=2 et ff=unix : */