4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2003-8 Sergey Svishchev
7 * Copyright (C) 2016 Kimihiro Nonaka
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)
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.
25 #include "os_netbsd.h"
27 #include <sys/drvctlio.h>
28 #include <sys/utsname.h>
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
35 const char * os_netbsd_cpp_cvsid
= "$Id: os_netbsd.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
42 /* Utility function for printing warnings */
44 printwarning(int msgNo
, const char *extra
)
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",
52 if (msgNo
>= 0 && msgNo
<= MAX_MSG
) {
53 if (!printed
[msgNo
]) {
55 pout("%s", message
[msgNo
]);
63 #define ARGUSED(x) ((void)(x))
65 /////////////////////////////////////////////////////////////////////////////
67 namespace os_netbsd
{ // No need to publish anything, name provided for Doxygen
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";
76 /////////////////////////////////////////////////////////////////////////////
77 /// Implement shared open/close routines with old functions.
79 class netbsd_smart_device
80 : virtual public /*implements*/ smart_device
83 explicit netbsd_smart_device()
84 : smart_device(never_called
),
87 virtual ~netbsd_smart_device() throw();
89 virtual bool is_open() const;
96 /// Return filedesc for derived classes.
104 int m_fd
; ///< filedesc, -1 if not open.
107 netbsd_smart_device::~netbsd_smart_device() throw()
110 os_netbsd::netbsd_smart_device::close();
113 bool netbsd_smart_device::is_open() const
119 bool netbsd_smart_device::open()
121 const char *dev
= get_dev_name();
125 fd
= ::open(dev
,O_RDWR
|O_NONBLOCK
);
126 if (fd
< 0 && errno
== EROFS
)
127 fd
= ::open(dev
,O_RDONLY
|O_NONBLOCK
);
132 } else if (is_ata() || is_nvme()) {
133 if ((fd
= ::open(dev
,O_RDWR
|O_NONBLOCK
))<0) {
144 bool netbsd_smart_device::close()
147 // close device, if open
149 failed
=::close(get_fd());
153 if(failed
) return false;
157 /////////////////////////////////////////////////////////////////////////////
158 /// Implement standard ATA support
160 class netbsd_ata_device
161 : public /*implements*/ ata_device
,
162 public /*extends*/ netbsd_smart_device
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
);
169 virtual int do_cmd(struct atareq
* request
, bool is_48bit_cmd
);
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()
178 int netbsd_ata_device::do_cmd( struct atareq
* request
, bool is_48bit_cmd
)
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
);
187 bool netbsd_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
189 bool ata_48bit
= false; // no ata_48bit_support via ATAIOCCOMMAND
191 if (!ata_cmd_is_ok(in
,
192 true, // data_out_support
193 true, // multi_sector_support
196 set_err(ENOSYS
, "48-bit ATA commands not implemented");
201 memset(&req
, 0, sizeof(req
));
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));
211 switch (in
.direction
) {
212 case ata_cmd_in::no_data
:
213 req
.flags
= ATACMD_READREG
;
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
;
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
;
226 return set_err(ENOSYS
);
231 if (do_cmd(&req
, in
.in_regs
.is_48bit_cmd()))
233 if (req
.retsts
!= ATACMD_OK
)
234 return set_err(EIO
, "request failed, error code 0x%02x", req
.retsts
);
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
;
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
)
249 unsigned const char normal_lo
=0x4f, normal_hi
=0xc2;
250 unsigned const char failed_lo
=0xf4, failed_hi
=0x2c;
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
))
258 // We haven't gotten output that makes sense; print out some debugging info
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",
266 (int)(le16toh(req
.cylinder
) & 0xff),
267 (int)((le16toh(req
.cylinder
) >> 8) & 0xff),
269 printwarning(BAD_SMART
,buf
);
270 out
.out_regs
.lba_high
= failed_hi
;
271 out
.out_regs
.lba_mid
= failed_lo
;
278 /////////////////////////////////////////////////////////////////////////////
281 class netbsd_nvme_device
282 : public /*implements*/ nvme_device
,
283 public /*extends*/ netbsd_smart_device
286 netbsd_nvme_device(smart_interface
* intf
, const char * dev_name
,
287 const char * req_type
, unsigned nsid
);
291 virtual bool nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
);
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
),
298 netbsd_smart_device()
302 bool netbsd_nvme_device::open()
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'",
311 int nsid
= -1, ctrlid
= -1;
314 if(sscanf(dev
, NVME_PREFIX
"%d%c", &ctrlid
, &tmp
) == 1)
317 set_err(EINVAL
, "Invalid NVMe controller number");
320 nsid
= 0xFFFFFFFF; // broadcast id
322 else if (sscanf(dev
, NVME_PREFIX
"%d"NVME_NS_PREFIX
"%d%c",
323 &ctrlid
, &nsid
, &tmp
) == 2)
325 if(ctrlid
< 0 || nsid
<= 0) {
326 set_err(EINVAL
, "Invalid NVMe controller/namespace number");
331 set_err(EINVAL
, "Invalid NVMe controller/namespace syntax");
335 // we should always open controller, not namespace device
337 snprintf(full_path
, sizeof(full_path
), NVME_PREFIX
"%d", ctrlid
);
340 if ((fd
= ::open(full_path
, O_RDWR
))<0) {
353 bool netbsd_nvme_device::nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
)
355 struct nvme_pt_command pt
;
356 memset(&pt
, 0, sizeof(pt
));
358 pt
.cmd
.opcode
= in
.opcode
;
359 pt
.cmd
.nsid
= in
.nsid
;
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()?
370 int status
= ioctl(get_fd(), NVME_PASSTHROUGH_CMD
, &pt
);
373 return set_err(errno
, "NVME_PASSTHROUGH_CMD: %s", strerror(errno
));
375 out
.result
=pt
.cpl
.cdw0
; // Command specific result (DW0)
377 if (nvme_completion_is_error(&pt
.cpl
))
378 return set_nvme_err(out
, nvme_completion_is_error(&pt
.cpl
));
383 /////////////////////////////////////////////////////////////////////////////
384 /// Standard SCSI support
386 class netbsd_scsi_device
387 : public /*implements*/ scsi_device
,
388 public /*extends*/ netbsd_smart_device
391 netbsd_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
, bool scanning
= false);
393 virtual smart_device
* autodetect_open();
395 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
398 bool m_scanning
; ///< true if created within scan_smart_devices
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(),
409 bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io
* iop
)
414 if (scsi_debugmode
) {
416 const unsigned char * ucp
= iop
->cmnd
;
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;
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);
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
);
443 (iop
->dxfer_dir
== DXFER_NONE
? SCCMD_READ
:
444 (iop
->dxfer_dir
== DXFER_FROM_DEVICE
? SCCMD_READ
: SCCMD_WRITE
));
446 if (ioctl(fd
, SCIOCCOMMAND
, &sc
) < 0) {
447 if (scsi_debugmode
) {
448 pout(" error sending SCSI ccb\n");
452 iop
->resid
= sc
.datalen
- sc
.datalen_used
;
453 iop
->scsi_status
= sc
.status
;
455 memcpy(iop
->sensep
, sc
.sense
, sc
.senselen_used
);
456 iop
->resp_sense_len
= sc
.senselen_used
;
458 if (scsi_debugmode
) {
462 trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
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);
472 return set_err(ETIMEDOUT
);
474 return set_err(EBUSY
);
482 /////////////////////////////////////////////////////////////////////////////
483 ///// SCSI open with autodetection support
485 smart_device
* netbsd_scsi_device::autodetect_open()
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")))
500 // The code below is based on smartd.cpp:SCSIFilterKnown()
503 unsigned char req_buff
[64] = {0, };
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
509 if (scsiStdInquiry(this, req_buff
, req_len
)) {
510 // device doesn't like INQUIRY commands
512 set_err(EIO
, "INQUIRY failed");
517 int avail_len
= req_buff
[4] + 5;
518 int len
= (avail_len
< req_len
? avail_len
: req_len
);
522 set_err(EIO
, "INQUIRY too short for SAT");
527 // Use INQUIRY to detect type
529 // SAT or USB, skip MFI controllers because of bugs
531 smart_device
* newdev
= smi()->autodetect_sat_device(this, req_buff
, len
);
533 // NOTE: 'this' is now owned by '*newdev'
538 // Nothing special found
542 set_err(EIO
, "Not a SAT device");
547 /////////////////////////////////////////////////////////////////////////////
548 /// Implement platform interface with old functions.
550 class netbsd_smart_interface
551 : public /*implements*/ smart_interface
554 virtual std::string
get_os_version_str();
556 virtual std::string
get_app_examples(const char * appname
);
558 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
559 const char * pattern
= 0);
562 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
564 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
566 virtual nvme_device
* get_nvme_device(const char * name
, const char * type
,
569 virtual smart_device
* autodetect_smart_device(const char * name
);
571 virtual smart_device
* get_custom_smart_device(const char * name
, const char * type
);
573 virtual std::string
get_valid_custom_dev_types_str();
576 int get_dev_names(char ***, const char *);
578 bool get_nvme_devlist(smart_device_list
& devlist
, const char * type
);
582 //////////////////////////////////////////////////////////////////////
584 std::string
netbsd_smart_interface::get_os_version_str()
586 struct utsname osname
;
588 return strprintf("%s %s %s", osname
.sysname
, osname
.release
, osname
.machine
);
591 std::string
netbsd_smart_interface::get_app_examples(const char * appname
)
593 if (!strcmp(appname
, "smartctl")) {
596 p
= 'a' + getrawpartition();
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"
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"
618 ata_device
* netbsd_smart_interface::get_ata_device(const char * name
, const char * type
)
620 return new netbsd_ata_device(this, name
, type
);
623 scsi_device
* netbsd_smart_interface::get_scsi_device(const char * name
, const char * type
)
625 return new netbsd_scsi_device(this, name
, type
);
628 nvme_device
* netbsd_smart_interface::get_nvme_device(const char * name
, const char * type
, unsigned nsid
)
630 return new netbsd_nvme_device(this, name
, type
, nsid
);
633 int netbsd_smart_interface::get_dev_names(char ***names
, const char *prefix
)
635 char *disknames
, *p
, **mp
;
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");
648 if (!(disknames
= (char *)malloc(sysctl_len
))) {
649 pout("Out of memory constructing scan device list\n");
652 if (-1 == sysctl(sysctl_mib
, 2, disknames
, &sysctl_len
, NULL
, 0)) {
653 pout("Failed to get value of sysctl `hw.disknames'\n");
656 if (!(mp
= (char **) calloc(strlen(disknames
) / 2, sizeof(char *)))) {
657 pout("Out of memory constructing scan device list\n");
660 for (p
= strtok(disknames
, " "); p
; p
= strtok(NULL
, " ")) {
661 if (strncmp(p
, prefix
, strlen(prefix
))) {
664 mp
[n
] = (char *)malloc(strlen(net_dev_raw_prefix
) + strlen(p
) + 2);
666 pout("Out of memory constructing scan device list\n");
669 sprintf(mp
[n
], "%s%s%c", net_dev_raw_prefix
, p
, 'a' + getrawpartition());
673 char ** tmp
= (char **)realloc(mp
, n
* (sizeof(char *)));
675 pout("Out of memory constructing scan device list\n");
685 bool netbsd_smart_interface::get_nvme_devlist(smart_device_list
& devlist
,
688 char ctrlpath
[64], nspath
[64];
690 struct devlistargs laa
;
691 nvme_device
* nvmedev
;
693 int drvfd
= ::open(DRVCTLDEV
, O_RDONLY
, 0);
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
))
704 snprintf(laa
.l_devname
, sizeof(laa
.l_devname
), "%s%d", net_dev_nvme_ctrl
,
706 laa
.l_childname
= NULL
;
708 if (ioctl(drvfd
, DRVLISTDEV
, &laa
) == -1) {
714 nvmedev
= get_nvme_device(ctrlpath
, type
, 0);
716 devlist
.push_back(nvmedev
);
719 for (int nsid
= 1; n
< laa
.l_children
; nsid
++) {
720 snprintf(nspath
, sizeof(nspath
), NVME_PREFIX
"%d"NVME_NS_PREFIX
"%d",
722 if (stat(nspath
, &sb
) == -1 || !S_ISCHR(sb
.st_mode
))
724 int nsfd
= ::open(nspath
, O_RDONLY
, 0);
730 nvmedev
= get_nvme_device(nspath
, type
, nsid
);
732 devlist
.push_back(nvmedev
);
740 bool netbsd_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
741 const char * type
, const char * pattern
/*= 0*/)
744 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
751 bool scan_ata
= !*type
|| !strcmp(type
, "ata");
752 bool scan_scsi
= !*type
|| !strcmp(type
, "scsi") || !strcmp(type
, "sat");
754 #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
755 bool scan_nvme
= !*type
|| !strcmp(type
, "nvme");
757 bool scan_nvme
= !strcmp(type
, "nvme");
761 char * * atanames
= 0; int numata
= 0;
763 numata
= get_dev_names(&atanames
, net_dev_ata_disk
);
770 char * * scsinames
= 0; int numscsi
= 0;
771 char * * scsitapenames
= 0; int numscsitape
= 0;
773 numscsi
= get_dev_names(&scsinames
, net_dev_scsi_disk
);
778 numscsitape
= get_dev_names(&scsitapenames
, net_dev_scsi_tape
);
779 if (numscsitape
< 0) {
787 for (i
= 0; i
< numata
; i
++) {
788 ata_device
* atadev
= get_ata_device(atanames
[i
], type
);
790 devlist
.push_back(atadev
);
793 if(numata
) free(atanames
);
795 for (i
= 0; i
< numscsi
; i
++) {
796 scsi_device
* scsidev
= new netbsd_scsi_device(this, scsinames
[i
], type
, true /*scanning*/);
798 devlist
.push_back(scsidev
);
801 if(numscsi
) free(scsinames
);
803 for (i
= 0; i
< numscsitape
; i
++) {
804 scsi_device
* scsidev
= get_scsi_device(scsitapenames
[i
], type
);
806 devlist
.push_back(scsidev
);
807 free(scsitapenames
[i
]);
809 if(numscsitape
) free(scsitapenames
);
812 get_nvme_devlist(devlist
, type
);
817 smart_device
* netbsd_smart_interface::autodetect_smart_device(const char * name
)
819 const char * test_name
= name
;
821 // if dev_name null, or string length zero
825 // Dereference symlinks
828 if (!lstat(name
, &st
) && S_ISLNK(st
.st_mode
)) {
829 char * p
= realpath(name
, (char *)0);
833 test_name
= pathbuf
.c_str();
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().
846 // No USB bridge found, assume regular SCSI device
847 return get_scsi_device(test_name
, "scsi");
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 */);
856 // device type unknown
860 smart_device
* netbsd_smart_interface::get_custom_smart_device(const char * name
, const char * type
)
867 std::string
netbsd_smart_interface::get_valid_custom_dev_types_str()
874 /////////////////////////////////////////////////////////////////////////////
875 /// Initialize platform interface and register with smi()
877 void smart_interface::init()
879 static os_netbsd::netbsd_smart_interface the_interface
;
880 smart_interface::set(&the_interface
);
883 /* vim: set ts=2 sw=2 et ff=unix : */