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