]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - os_netbsd.cpp
New upstream version 6.6
[mirror_smartmontools-debian.git] / os_netbsd.cpp
1 /*
2 * os_netbsd.cpp
3 *
4 * Home page of code is: http://www.smartmontools.org
5 *
6 * Copyright (C) 2003-8 Sergey Svishchev
7 * Copyright (C) 2016 Kimihiro Nonaka
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
15 * (for example COPYING); if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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"
26
27 #include <sys/drvctlio.h>
28 #include <sys/utsname.h>
29 #include <errno.h>
30 #include <unistd.h>
31
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
35 const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
36 OS_NETBSD_H_CVSID;
37
38 enum warnings {
39 BAD_SMART, MAX_MSG
40 };
41
42 /* Utility function for printing warnings */
43 void
44 printwarning(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",
49 PACKAGE_STRING " does not currently support twe(4) and twa(4) devices (3ware Escalade, Apache)\n",
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)
57 pout("%s", extra);
58 }
59 }
60 return;
61 }
62
63 #define ARGUSED(x) ((void)(x))
64
65 /////////////////////////////////////////////////////////////////////////////
66
67 namespace os_netbsd { // No need to publish anything, name provided for Doxygen
68
69 static const char *net_dev_prefix = "/dev/";
70 static const char *net_dev_raw_prefix = "/dev/r";
71 static const char *net_dev_ata_disk = "wd";
72 static const char *net_dev_scsi_disk = "sd";
73 static const char *net_dev_scsi_tape = "enrst";
74 static const char *net_dev_nvme_ctrl = "nvme";
75
76 /////////////////////////////////////////////////////////////////////////////
77 /// Implement shared open/close routines with old functions.
78
79 class netbsd_smart_device
80 : virtual public /*implements*/ smart_device
81 {
82 public:
83 explicit netbsd_smart_device()
84 : smart_device(never_called),
85 m_fd(-1) { }
86
87 virtual ~netbsd_smart_device() throw();
88
89 virtual bool is_open() const;
90
91 virtual bool open();
92
93 virtual bool close();
94
95 protected:
96 /// Return filedesc for derived classes.
97 int get_fd() const
98 { return m_fd; }
99
100 void set_fd(int fd)
101 { m_fd = fd; }
102
103 private:
104 int m_fd; ///< filedesc, -1 if not open.
105 };
106
107 netbsd_smart_device::~netbsd_smart_device() throw()
108 {
109 if (m_fd >= 0)
110 os_netbsd::netbsd_smart_device::close();
111 }
112
113 bool netbsd_smart_device::is_open() const
114 {
115 return (m_fd >= 0);
116 }
117
118
119 bool 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;
131 }
132 } else if (is_ata() || is_nvme()) {
133 if ((fd = ::open(dev,O_RDWR|O_NONBLOCK))<0) {
134 set_err(errno);
135 return false;
136 }
137 } else
138 return false;
139
140 set_fd(fd);
141 return true;
142 }
143
144 bool netbsd_smart_device::close()
145 {
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;
155 }
156
157 /////////////////////////////////////////////////////////////////////////////
158 /// Implement standard ATA support
159
160 class netbsd_ata_device
161 : public /*implements*/ ata_device,
162 public /*extends*/ netbsd_smart_device
163 {
164 public:
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
168 protected:
169 virtual int do_cmd(struct atareq* request, bool is_48bit_cmd);
170 };
171
172 netbsd_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()
175 {
176 }
177
178 int netbsd_ata_device::do_cmd( struct atareq* request, bool is_48bit_cmd)
179 {
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;
185 }
186
187 bool netbsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
188 {
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 }
199
200 struct atareq req;
201 memset(&req, 0, sizeof(req));
202
203 req.timeout = 1000;
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 }
228
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 }
273 }
274
275 return true;
276 }
277
278 /////////////////////////////////////////////////////////////////////////////
279 /// NVMe support
280
281 class netbsd_nvme_device
282 : public /*implements*/ nvme_device,
283 public /*extends*/ netbsd_smart_device
284 {
285 public:
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
294 netbsd_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
302 bool 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;
319 }
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;
328 }
329 }
330 else {
331 set_err(EINVAL, "Invalid NVMe controller/namespace syntax");
332 return false;
333 }
334
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;
343 }
344 set_fd(fd);
345
346 if (!get_nsid()) {
347 set_nsid(nsid);
348 }
349
350 return true;
351 }
352
353 bool 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));
357
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;
381 }
382
383 /////////////////////////////////////////////////////////////////////////////
384 /// Standard SCSI support
385
386 class netbsd_scsi_device
387 : public /*implements*/ scsi_device,
388 public /*extends*/ netbsd_smart_device
389 {
390 public:
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
397 private:
398 bool m_scanning; ///< true if created within scan_smart_devices
399 };
400
401 netbsd_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 }
408
409 bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
410 {
411 struct scsireq sc;
412 int fd = get_fd();
413
414 if (scsi_debugmode) {
415 unsigned int k;
416 const unsigned char * ucp = iop->cmnd;
417 const char * np;
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]);
423 if ((scsi_debugmode > 1) &&
424 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
425 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
426
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");
433 }
434
435 memset(&sc, 0, sizeof(sc));
436 memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
437 sc.cmdlen = iop->cmnd_len;
438 sc.databuf = (char *)iop->dxferp;
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 =
443 (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :
444 (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
445
446 if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
447 if (scsi_debugmode) {
448 pout(" error sending SCSI ccb\n");
449 }
450 return set_err(EIO);
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 }
458 if (scsi_debugmode) {
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 }
468 switch (sc.retsts) {
469 case SCCMD_OK:
470 break;
471 case SCCMD_TIMEOUT:
472 return set_err(ETIMEDOUT);
473 case SCCMD_BUSY:
474 return set_err(EBUSY);
475 default:
476 return set_err(EIO);
477 }
478
479 return true;
480 }
481
482 /////////////////////////////////////////////////////////////////////////////
483 ///// SCSI open with autodetection support
484
485 smart_device * netbsd_scsi_device::autodetect_open()
486 {
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 }
499
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
550 class netbsd_smart_interface
551 : public /*implements*/ smart_interface
552 {
553 public:
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
561 protected:
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
575 private:
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
584 std::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
591 std::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"
599 #ifdef HAVE_GETOPT_LONG
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"
606 #else
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"
612 #endif
613 , p, p, p, p);
614 }
615 return "";
616 }
617
618 ata_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
623 scsi_device * netbsd_smart_interface::get_scsi_device(const char * name, const char * type)
624 {
625 return new netbsd_scsi_device(this, name, type);
626 }
627
628 nvme_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
633 int 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
685 bool 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
740 bool 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
817 smart_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
860 smart_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
867 std::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
877 void 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 : */