smartmontools  SVN Rev 4347
Utility to control and monitor storage systems with "S.M.A.R.T."
os_netbsd.cpp
Go to the documentation of this file.
1 /*
2  * os_netbsd.cpp
3  *
4  * Home page of code is: http://www.smartmontools.org
5  *
6  * Copyright (C) 2003-8 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
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 4343 2016-09-25 16:46:49Z chrfranke $"
37 
38 enum warnings {
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 
80 : virtual public /*implements*/ smart_device
81 {
82 public:
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 
108 {
109  if (m_fd >= 0)
111 }
112 
114 {
115  return (m_fd >= 0);
116 }
117 
118 
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 
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 
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),
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 
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;
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
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 
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 
295  const char * req_type, unsigned nsid)
296 : smart_device(intf, dev_name, "nvme", req_type),
297  nvme_device(nsid),
299 {
300 }
301 
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 
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 
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 
402  const char * dev_name, const char * req_type, bool scanning /* = false */)
403 : smart_device(intf, dev_name, "scsi", req_type),
405  m_scanning(scanning)
406 {
407 }
408 
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 
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 
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 
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 
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 
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  bool scan_nvme = !*type || !strcmp(type, "nvme");
754 
755  // Make namelists
756  char * * atanames = 0; int numata = 0;
757  if (scan_ata) {
758  numata = get_dev_names(&atanames, net_dev_ata_disk);
759  if (numata < 0) {
760  set_err(ENOMEM);
761  return false;
762  }
763  }
764 
765  char * * scsinames = 0; int numscsi = 0;
766  char * * scsitapenames = 0; int numscsitape = 0;
767  if (scan_scsi) {
768  numscsi = get_dev_names(&scsinames, net_dev_scsi_disk);
769  if (numscsi < 0) {
770  set_err(ENOMEM);
771  return false;
772  }
773  numscsitape = get_dev_names(&scsitapenames, net_dev_scsi_tape);
774  if (numscsitape < 0) {
775  set_err(ENOMEM);
776  return false;
777  }
778  }
779 
780  // Add to devlist
781  int i;
782  for (i = 0; i < numata; i++) {
783  ata_device * atadev = get_ata_device(atanames[i], type);
784  if (atadev)
785  devlist.push_back(atadev);
786  free(atanames[i]);
787  }
788  if(numata) free(atanames);
789 
790  for (i = 0; i < numscsi; i++) {
791  scsi_device * scsidev = new netbsd_scsi_device(this, scsinames[i], type, true /*scanning*/);
792  if (scsidev)
793  devlist.push_back(scsidev);
794  free(scsinames[i]);
795  }
796  if(numscsi) free(scsinames);
797 
798  for (i = 0; i < numscsitape; i++) {
799  scsi_device * scsidev = get_scsi_device(scsitapenames[i], type);
800  if (scsidev)
801  devlist.push_back(scsidev);
802  free(scsitapenames[i]);
803  }
804  if(numscsitape) free(scsitapenames);
805 
806  if (scan_nvme)
807  get_nvme_devlist(devlist, type);
808 
809  return true;
810 }
811 
813 {
814  const char * test_name = name;
815 
816  // if dev_name null, or string length zero
817  if (!name || !*name)
818  return 0;
819 
820  // Dereference symlinks
821  struct stat st;
822  std::string pathbuf;
823  if (!lstat(name, &st) && S_ISLNK(st.st_mode)) {
824  char * p = realpath(name, (char *)0);
825  if (p) {
826  pathbuf = p;
827  free(p);
828  test_name = pathbuf.c_str();
829  }
830  }
831 
832  if (str_starts_with(test_name, net_dev_raw_prefix)) {
833  test_name += strlen(net_dev_raw_prefix);
834  if (!strncmp(net_dev_ata_disk, test_name, strlen(net_dev_ata_disk)))
835  return get_ata_device(test_name, "ata");
836  if (!strncmp(net_dev_scsi_disk, test_name, strlen(net_dev_scsi_disk))) {
837  // XXX Try to detect possible USB->(S)ATA bridge
838  // XXX get USB vendor ID, product ID and version from sd(4)/umass(4).
839  // XXX check sat device via get_usb_dev_type_by_id().
840 
841  // No USB bridge found, assume regular SCSI device
842  return get_scsi_device(test_name, "scsi");
843  }
844  if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape)))
845  return get_scsi_device(test_name, "scsi");
846  } else if (str_starts_with(test_name, net_dev_prefix)) {
847  if (!strncmp(NVME_PREFIX, test_name, strlen(NVME_PREFIX)))
848  return get_nvme_device(test_name, "nvme", 0 /* use default nsid */);
849  }
850 
851  // device type unknown
852  return 0;
853 }
854 
855 smart_device * netbsd_smart_interface::get_custom_smart_device(const char * name, const char * type)
856 {
857  ARGUSED(name);
858  ARGUSED(type);
859  return 0;
860 }
861 
863 {
864  return "";
865 }
866 
867 } // namespace
868 
869 /////////////////////////////////////////////////////////////////////////////
870 /// Initialize platform interface and register with smi()
871 
873 {
874  static os_netbsd::netbsd_smart_interface the_interface;
875  smart_interface::set(&the_interface);
876 }
877 
878 /* vim: set ts=2 sw=2 et ff=unix : */
struct nvme_completion cpl
virtual ata_device * autodetect_sat_device(scsi_device *scsidev, const unsigned char *inqdata, unsigned inqsize)
Try to detect a SAT device behind a SCSI interface.
Definition: scsiata.cpp:1564
ata_out_regs_flags out_needed
True if output register value needed.
virtual std::string get_os_version_str()
Return info string about build host and/or OS version.
Definition: os_netbsd.cpp:584
UINT8 * sensep
Definition: scsicmds.h:122
warnings
Definition: os_netbsd.cpp:38
ata_register error
#define ATA_SMART_CMD
Definition: atacmds.h:75
virtual scsi_device * get_scsi_device(const char *name, const char *type)
Return standard SCSI device.
Definition: os_netbsd.cpp:623
static const char * net_dev_prefix
Definition: os_netbsd.cpp:69
ata_register lba_mid
#define DXFER_FROM_DEVICE
Definition: scsicmds.h:111
netbsd_scsi_device(smart_interface *intf, const char *dev_name, const char *req_type, bool scanning=false)
Definition: os_netbsd.cpp:401
Implement standard ATA support.
Definition: os_netbsd.cpp:160
#define ARGUSED(x)
Definition: os_netbsd.cpp:63
bool str_starts_with(const char *str, const char *prefix)
Definition: utility.h:57
bool set_nvme_err(nvme_cmd_out &out, unsigned status, const char *msg=0)
Set last error number and message if pass-through returns NVMe error status.
ata_register device
const char * scsi_get_opcode_name(UINT8 opcode)
Definition: scsicmds.cpp:177
ata_register sector_count
unsigned cdw11
unsigned timeout
Definition: scsicmds.h:125
bool get_nvme_devlist(smart_device_list &devlist, const char *type)
Definition: os_netbsd.cpp:685
static const char * net_dev_scsi_tape
Definition: os_netbsd.cpp:73
virtual ata_device * get_ata_device(const char *name, const char *type)
Return standard ATA device.
Definition: os_netbsd.cpp:618
NVMe pass through input parameters.
#define NVME_PREFIX
Implement platform interface with old functions.
Definition: os_netbsd.cpp:550
int get_dev_names(char ***, const char *)
Definition: os_netbsd.cpp:633
#define snprintf
Definition: utility.h:68
virtual nvme_device * get_nvme_device(const char *name, const char *type, unsigned nsid)
Return standard NVMe device.
Definition: os_netbsd.cpp:628
bool is_nvme() const
Return true if NVMe device.
Definition: dev_interface.h:99
void * buffer
Pointer to data buffer.
NVMe pass through output parameters.
size_t resp_sense_len
Definition: scsicmds.h:126
virtual smart_device * get_custom_smart_device(const char *name, const char *type)
Return device for platform specific 'type'.
Definition: os_netbsd.cpp:855
#define OS_NETBSD_H_CVSID
Definition: os_netbsd.h:27
void push_back(smart_device *dev)
static const char * net_dev_raw_prefix
Definition: os_netbsd.cpp:70
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out)
ATA pass through.
Definition: os_netbsd.cpp:187
ata_register lba_mid
uint32_t nsid
virtual bool open()
Open device, return false on error.
Definition: os_netbsd.cpp:119
ata_in_regs_48bit in_regs
Input registers.
UINT8 * dxferp
Definition: scsicmds.h:120
ata_out_regs_48bit out_regs
Output registers.
UINT8 * cmnd
Definition: scsicmds.h:116
unsigned result
Command specific result (DW0)
List of devices for DEVICESCAN.
enum ata_cmd_in::@27 direction
I/O direction.
netbsd_nvme_device(smart_interface *intf, const char *dev_name, const char *req_type, unsigned nsid)
Definition: os_netbsd.cpp:294
static void init()
Initialize platform interface and register with smi().
Definition: dev_legacy.cpp:341
The platform interface abstraction.
struct nvme_command cmd
NVMe device access.
static void set(smart_interface *intf)
Set interface to use, must be called from init().
void * buffer
Pointer to data buffer.
unsigned get_nsid() const
Get namespace id.
#define NVME_NS_PREFIX
void printwarning(int msgNo, const char *extra)
Definition: os_netbsd.cpp:44
void dStrHex(const char *str, int len, int no_ascii)
Definition: scsicmds.cpp:87
virtual bool scan_smart_devices(smart_device_list &devlist, const char *type, const char *pattern=0)
Fill 'devlist' with devices of some 'type' with device names specified by some optional 'pattern'...
Definition: os_netbsd.cpp:740
unsigned char opcode
Opcode (CDW0 07:00)
unsigned char scsi_debugmode
Definition: scsicmds.cpp:54
int get_fd() const
Return filedesc for derived classes.
Definition: os_netbsd.cpp:97
bool is_ata() const
Return true if ATA device.
Definition: dev_interface.h:93
Standard SCSI support.
Definition: os_netbsd.cpp:386
void pout(const char *fmt,...)
Definition: smartctl.cpp:1170
size_t max_sense_len
Definition: scsicmds.h:124
unsigned cdw12
ata_register status
int dxfer_dir
Definition: scsicmds.h:118
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
static const char * net_dev_ata_disk
Definition: os_netbsd.cpp:71
virtual std::string get_valid_custom_dev_types_str()
Return valid 'type' args accepted by above.
Definition: os_netbsd.cpp:862
unsigned size
Size of buffer.
ata_register lba_low
unsigned cdw15
Cmd specific.
SCSI device access.
unsigned nsid
Namespace ID.
ata_register features
Implement shared open/close routines with old functions.
Definition: os_netbsd.cpp:79
#define DXFER_NONE
Definition: scsicmds.h:110
bool m_scanning
true if created within scan_smart_devices
Definition: os_netbsd.cpp:398
virtual bool is_open() const
Return true if device is open.
Definition: os_netbsd.cpp:113
bool ata_cmd_is_ok(const ata_cmd_in &in, bool data_out_support=false, bool multi_sector_support=false, bool ata_48bit_support=false)
Check command input parameters (old version).
#define DXFER_TO_DEVICE
Definition: scsicmds.h:112
bool is_scsi() const
Return true if SCSI device.
Definition: dev_interface.h:96
bool is_48bit_cmd() const
Return true if 48-bit command.
virtual int do_cmd(struct atareq *request, bool is_48bit_cmd)
Definition: os_netbsd.cpp:178
ata_register device
Base class for all devices.
Definition: dev_interface.h:39
virtual smart_device * autodetect_open()
Open device with autodetection support.
Definition: os_netbsd.cpp:485
ata_register lba_high
static const char * net_dev_nvme_ctrl
Definition: os_netbsd.cpp:74
virtual smart_device * autodetect_smart_device(const char *name)
Autodetect device if no device type specified.
Definition: os_netbsd.cpp:812
size_t cmnd_len
Definition: scsicmds.h:117
ATA device access.
ata_register sector_count
virtual std::string get_app_examples(const char *appname)
Return example string for program 'appname'.
Definition: os_netbsd.cpp:591
static const char * net_dev_scsi_disk
Definition: os_netbsd.cpp:72
virtual bool nvme_pass_through(const nvme_cmd_in &in, nvme_cmd_out &out)
NVMe pass through.
Definition: os_netbsd.cpp:353
unsigned size
Size of buffer.
virtual bool close()
Close device, return false on error.
Definition: os_netbsd.cpp:144
const char * os_netbsd_cpp_cvsid
Definition: os_netbsd.cpp:35
virtual bool open()
Open device, return false on error.
Definition: os_netbsd.cpp:302
unsigned cdw13
ata_register command
virtual bool scsi_pass_through(scsi_cmnd_io *iop)
SCSI pass through.
Definition: os_netbsd.cpp:409
UINT8 scsi_status
Definition: scsicmds.h:127
#define NVME_PASSTHROUGH_CMD
ATA pass through input parameters.
unsigned cdw10
int m_fd
filedesc, -1 if not open.
Definition: os_netbsd.cpp:104
int scsiStdInquiry(scsi_device *device, UINT8 *pBuf, int bufLen)
Definition: scsicmds.cpp:774
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:750
ata_register lba_low
void set_nsid(unsigned nsid)
Set namespace id.
ata_register lba_high
smart_interface * smi()
Get interface which produced this object.
size_t dxfer_len
Definition: scsicmds.h:121
void clear_err()
Clear last error info.
const char * get_dev_name() const
Get device (path)name.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
const char * get_req_type() const
Get type requested by user, empty if none.
netbsd_ata_device(smart_interface *intf, const char *dev_name, const char *req_type)
Definition: os_netbsd.cpp:172
ATA pass through output parameters.
#define nvme_completion_is_error(cpl)
unsigned cdw14
#define ATA_SMART_STATUS
Definition: atacmds.h:105