aboutsummaryrefslogtreecommitdiff
path: root/include/sg_pt_nvme.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/sg_pt_nvme.h')
-rw-r--r--include/sg_pt_nvme.h224
1 files changed, 224 insertions, 0 deletions
diff --git a/include/sg_pt_nvme.h b/include/sg_pt_nvme.h
new file mode 100644
index 00000000..06f8f0f4
--- /dev/null
+++ b/include/sg_pt_nvme.h
@@ -0,0 +1,224 @@
+#ifndef SG_PT_NVME_H
+#define SG_PT_NVME_H
+
+/*
+ * Copyright (c) 2017-2019 Douglas Gilbert.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* structures copied and slightly modified from <linux/nvme_ioctl.h> which
+ * is Copyright (c) 2011-2014, Intel Corporation. */
+
+
+/* Note that the command input structure is in (packed) "cpu" format. That
+ * means, for example, if the CPU is little endian (most are) then so is the
+ * structure. However what comes out in the data-in buffer (e.g. for the
+ * Admin Identify command response) is almost all little endian following ATA
+ * (but no SCSI and IP which are big endian) and Intel's preference. There
+ * are exceptions, for example the EUI-64 identifiers in the Admin Identify
+ * response are big endian.
+ *
+ * Code online (e.g. nvme-cli at github.com) seems to favour packed
+ * structures, while the author prefers byte offset plus a range of unaligned
+ * integer builders such as those in sg_unaligned.h .
+ */
+
+#ifdef __GNUC__
+#ifndef __clang__
+ struct __attribute__((__packed__)) sg_nvme_user_io
+#else
+ struct sg_nvme_user_io
+#endif
+#else
+struct sg_nvme_user_io
+#endif
+{
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t control;
+ uint16_t nblocks;
+ uint16_t rsvd;
+ uint64_t metadata;
+ uint64_t addr;
+ uint64_t slba;
+ uint32_t dsmgmt;
+ uint32_t reftag;
+ uint16_t apptag;
+ uint16_t appmask;
+}
+#ifdef SG_LIB_FREEBSD
+__packed;
+#else
+;
+#endif
+
+/* Using byte offsets and unaligned be/le copies safer than packed
+ * structures. These are for sg_nvme_user_io . */
+#define SG_NVME_IO_OPCODE 0
+#define SG_NVME_IO_FLAGS 1
+#define SG_NVME_IO_CONTROL 2
+#define SG_NVME_IO_NBLOCKS 4
+#define SG_NVME_IO_RSVD 6
+#define SG_NVME_IO_METADATA 8
+#define SG_NVME_IO_ADDR 16
+#define SG_NVME_IO_SLBA 24
+#define SG_NVME_IO_DSMGMT 32
+#define SG_NVME_IO_REFTAG 36
+#define SG_NVME_IO_APPTAG 40
+#define SG_NVME_IO_APPMASK 42
+
+#ifdef __GNUC__
+#ifndef __clang__
+ struct __attribute__((__packed__)) sg_nvme_passthru_cmd
+#else
+ struct sg_nvme_passthru_cmd
+#endif
+#else
+struct sg_nvme_passthru_cmd
+#endif
+{
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t rsvd1;
+ uint32_t nsid;
+ uint32_t cdw2;
+ uint32_t cdw3;
+ uint64_t metadata;
+ uint64_t addr;
+ uint32_t metadata_len;
+ uint32_t data_len;
+ uint32_t cdw10;
+ uint32_t cdw11;
+ uint32_t cdw12;
+ uint32_t cdw13;
+ uint32_t cdw14;
+ uint32_t cdw15;
+#ifdef SG_LIB_LINUX
+ uint32_t timeout_ms;
+ uint32_t result; /* out: DWord(0) from completion queue */
+#endif
+}
+#ifdef SG_LIB_FREEBSD
+__packed;
+#else
+;
+#endif
+
+struct sg_sntl_dev_state_t {
+ uint8_t scsi_dsense;
+ uint8_t enclosure_override; /* ENC_OV in sdparm */
+ uint8_t pdt; /* 6 bit value in INQUIRY response */
+ uint8_t enc_serv; /* single bit in INQUIRY response */
+ uint8_t id_ctl253; /* NVMSR field of Identify controller (byte 253) */
+ bool wce; /* Write Cache Enable (WCE) setting */
+ bool wce_changed; /* WCE setting has been changed */
+};
+
+struct sg_sntl_result_t {
+ uint8_t sstatus;
+ uint8_t sk;
+ uint8_t asc;
+ uint8_t ascq;
+ uint8_t in_byte;
+ uint8_t in_bit; /* use 255 for 'no bit position given' */
+};
+
+struct sg_opcode_info_t {
+ uint8_t opcode;
+ uint16_t sa; /* service action, 0 for none */
+ uint32_t flags; /* OR-ed set of F_* flags */
+ uint8_t len_mask[16]; /* len=len_mask[0], then mask for cdb[1]... */
+ /* ignore cdb bytes after position 15 */
+};
+
+/* Using byte offsets and unaligned be/le copies safer than packed
+ * structures. These are for sg_nvme_passthru_cmd . */
+#define SG_NVME_PT_OPCODE 0 /* length: 1 byte */
+#define SG_NVME_PT_FLAGS 1 /* length: 1 byte */
+#define SG_NVME_PT_RSVD1 2 /* length: 2 bytes */
+#define SG_NVME_PT_NSID 4 /* length: 4 bytes */
+#define SG_NVME_PT_CDW2 8 /* length: 4 bytes */
+#define SG_NVME_PT_CDW3 12 /* length: 4 bytes */
+#define SG_NVME_PT_METADATA 16 /* length: 8 bytes */
+#define SG_NVME_PT_ADDR 24 /* length: 8 bytes */
+#define SG_NVME_PT_METADATA_LEN 32 /* length: 4 bytes */
+#define SG_NVME_PT_DATA_LEN 36 /* length: 4 bytes */
+#define SG_NVME_PT_CDW10 40 /* length: 4 bytes */
+#define SG_NVME_PT_CDW11 44 /* length: 4 bytes */
+#define SG_NVME_PT_CDW12 48 /* length: 4 bytes */
+#define SG_NVME_PT_CDW13 52 /* length: 4 bytes */
+#define SG_NVME_PT_CDW14 56 /* length: 4 bytes */
+#define SG_NVME_PT_CDW15 60 /* length: 4 bytes */
+
+#ifdef SG_LIB_LINUX
+/* General references state that "all NVMe commands are 64 bytes long". If
+ * so then the following are add-ons by Linux, go to the OS and not the
+ * the NVMe device. */
+#define SG_NVME_PT_TIMEOUT_MS 64 /* length: 4 bytes */
+#define SG_NVME_PT_RESULT 68 /* length: 4 bytes */
+#endif
+
+/* Byte offset of Result and Status (plus phase bit) in CQ */
+#define SG_NVME_PT_CQ_RESULT 0 /* CDW0, length: 4 bytes */
+#define SG_NVME_PT_CQ_DW0 0 /* CDW0, length: 4 bytes */
+#define SG_NVME_PT_CQ_DW1 4 /* CDW1, length: 4 bytes */
+#define SG_NVME_PT_CQ_DW2 8 /* CDW2, length: 4 bytes */
+#define SG_NVME_PT_CQ_DW3 12 /* CDW3, length: 4 bytes */
+#define SG_NVME_PT_CQ_STATUS_P 14 /* CDW3 31:16, length: 2 bytes */
+
+
+/* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */
+#define SG_NVME_BROADCAST_NSID 0xffffffff /* all namespaces */
+#define SG_NVME_CTL_NSID 0x0 /* the "controller's" namespace */
+
+/* Vendor specific (sg3_utils) VPD pages */
+#define SG_NVME_VPD_NICR 0xde /* NVME Identify controller response */
+
+
+/* Given the NVMe Identify Controller response and optionally the NVMe
+ * Identify Namespace response (NULL otherwise), generate the SCSI VPD
+ * page 0x83 (device identification) descriptor(s) in dop. Return the
+ * number of bytes written which will not exceed max_do_len. Probably use
+ * Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport
+ * protocol (tproto) should be -1 if not known, else SCSI value.
+ * N.B. Does not write total VPD page length into dop[2:3] . */
+int sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p,
+ const uint8_t * nvme_id_ns_p, int pdt,
+ int tproto, uint8_t * dop, int max_do_len);
+
+/* Initialize dev_stat pointed to by dsp */
+void sntl_init_dev_stat(struct sg_sntl_dev_state_t * dsp);
+
+/* Internal function (common to all OSes) to support the SNTL SCSI MODE
+ * SENSE(10) command. Has a vendor specific Unit Attention mpage which
+ * has only one field currently: ENC_OV (enclosure override) */
+int sntl_resp_mode_sense10(const struct sg_sntl_dev_state_t * dsp,
+ const uint8_t * cdbp, uint8_t * dip, int mx_di_len,
+ struct sg_sntl_result_t * resp);
+
+/* Internal function (common to all OSes) to support the SNTL SCSI MODE
+ * SELECT(10) command. */
+int sntl_resp_mode_select10(struct sg_sntl_dev_state_t * dsp,
+ const uint8_t * cdbp, const uint8_t * dop,
+ int do_len, struct sg_sntl_result_t * resp);
+
+/* Returns pointer to array of struct sg_opcode_info_t of SCSI commands
+ * translated to NVMe. */
+const struct sg_opcode_info_t * sg_get_opcode_translation(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SG_PT_NVME_H */