+/////////////////////////////////////////////////////////////////////////////
+/// NVMe support
+
+class linux_nvme_device
+: public /*implements*/ nvme_device,
+ public /*extends*/ linux_smart_device
+{
+public:
+ linux_nvme_device(smart_interface * intf, const char * dev_name,
+ const char * req_type, unsigned nsid);
+
+ virtual bool open();
+
+ virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
+};
+
+linux_nvme_device::linux_nvme_device(smart_interface * intf, const char * dev_name,
+ const char * req_type, unsigned nsid)
+: smart_device(intf, dev_name, "nvme", req_type),
+ nvme_device(nsid),
+ linux_smart_device(O_RDONLY | O_NONBLOCK)
+{
+}
+
+bool linux_nvme_device::open()
+{
+ if (!linux_smart_device::open())
+ return false;
+
+ if (!get_nsid()) {
+ // Use actual NSID (/dev/nvmeXnN) if available,
+ // else use broadcast namespace (/dev/nvmeX)
+ int nsid = ioctl(get_fd(), NVME_IOCTL_ID, (void*)0);
+ set_nsid(nsid);
+ }
+
+ return true;
+}
+
+bool linux_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
+{
+ nvme_passthru_cmd pt;
+ memset(&pt, 0, sizeof(pt));
+
+ pt.opcode = in.opcode;
+ pt.nsid = in.nsid;
+ pt.addr = (uint64_t)in.buffer;
+ pt.data_len = in.size;
+ pt.cdw10 = in.cdw10;
+ pt.cdw11 = in.cdw11;
+ pt.cdw12 = in.cdw12;
+ pt.cdw13 = in.cdw13;
+ pt.cdw14 = in.cdw14;
+ pt.cdw15 = in.cdw15;
+ // Kernel default for NVMe admin commands is 60 seconds
+ // pt.timeout_ms = 60 * 1000;
+
+ int status = ioctl(get_fd(), NVME_IOCTL_ADMIN_CMD, &pt);
+
+ if (status < 0)
+ return set_err(errno, "NVME_IOCTL_ADMIN_CMD: %s", strerror(errno));
+
+ if (status > 0)
+ return set_nvme_err(out, status);
+
+ out.result = pt.result;
+ return true;
+}
+
+