summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 19:22:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 19:22:29 +0000
commit068a45420f2c98887e220b45e946cc7074da550e (patch)
treec5b54e8b4b235232b057a9c534d9a16d2208463d
parentInitial commit. (diff)
downloadlibnvme-upstream.tar.xz
libnvme-upstream.zip
Adding upstream version 1.8.upstream/1.8upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.checkpatch.conf24
-rw-r--r--.github/cross/ubuntu-cross-armhf.txt18
-rw-r--r--.github/cross/ubuntu-cross-ppc64le.txt18
-rw-r--r--.github/cross/ubuntu-cross-s390x.txt18
-rw-r--r--.github/dependabot.yml7
-rw-r--r--.github/workflows/build.yml109
-rw-r--r--.github/workflows/checkpatch.yml15
-rw-r--r--.github/workflows/coverage.yml22
-rw-r--r--.github/workflows/release-python.yml73
-rw-r--r--.github/workflows/release.yml20
-rw-r--r--.gitignore17
-rw-r--r--.readthedocs.yaml23
-rw-r--r--COPYING502
-rw-r--r--Makefile50
-rw-r--r--README.md188
l---------ccan/ccan/array_size/LICENSE1
-rw-r--r--ccan/ccan/array_size/_info46
-rw-r--r--ccan/ccan/array_size/array_size.h26
l---------ccan/ccan/build_assert/LICENSE1
-rw-r--r--ccan/ccan/build_assert/_info49
-rw-r--r--ccan/ccan/build_assert/build_assert.h40
l---------ccan/ccan/check_type/LICENSE1
-rw-r--r--ccan/ccan/check_type/_info33
-rw-r--r--ccan/ccan/check_type/check_type.h64
l---------ccan/ccan/container_of/LICENSE1
-rw-r--r--ccan/ccan/container_of/_info65
-rw-r--r--ccan/ccan/container_of/container_of.h145
l---------ccan/ccan/endian/LICENSE1
-rw-r--r--ccan/ccan/endian/_info55
-rw-r--r--ccan/ccan/endian/endian.h363
l---------ccan/ccan/list/LICENSE1
-rw-r--r--ccan/ccan/list/_info72
-rw-r--r--ccan/ccan/list/list.c43
-rw-r--r--ccan/ccan/list/list.h842
l---------ccan/ccan/minmax/LICENSE1
-rw-r--r--ccan/ccan/minmax/_info48
-rw-r--r--ccan/ccan/minmax/minmax.h65
l---------ccan/ccan/short_types/LICENSE1
-rw-r--r--ccan/ccan/short_types/short_types.h35
l---------ccan/ccan/str/LICENSE1
-rw-r--r--ccan/ccan/str/_info52
-rw-r--r--ccan/ccan/str/debug.c108
-rw-r--r--ccan/ccan/str/str.c13
-rw-r--r--ccan/ccan/str/str.h228
-rw-r--r--ccan/ccan/str/str_debug.h30
-rw-r--r--ccan/licenses/BSD-MIT17
-rw-r--r--ccan/licenses/CC028
-rw-r--r--ccan/meson.build26
-rw-r--r--codecov.yml6
-rw-r--r--doc/api.rst.in18
-rw-r--r--doc/conf.py32
-rw-r--r--doc/conf.py.in32
-rw-r--r--doc/config-schema.json189
-rw-r--r--doc/config-schema.json.in189
-rw-r--r--doc/index.rst23
-rw-r--r--doc/index.rst.in23
-rw-r--r--doc/installation.rst.in55
-rw-r--r--doc/man/nbft_control.2169
-rw-r--r--doc/man/nbft_control_flags.212
-rw-r--r--doc/man/nbft_desc_type.274
-rw-r--r--doc/man/nbft_discovery.271
-rw-r--r--doc/man/nbft_discovery_flags.214
-rw-r--r--doc/man/nbft_header.293
-rw-r--r--doc/man/nbft_heap_obj.220
-rw-r--r--doc/man/nbft_hfi.244
-rw-r--r--doc/man/nbft_hfi_flags.213
-rw-r--r--doc/man/nbft_hfi_info_tcp.2137
-rw-r--r--doc/man/nbft_hfi_info_tcp_flags.235
-rw-r--r--doc/man/nbft_host.236
-rw-r--r--doc/man/nbft_host_flags.275
-rw-r--r--doc/man/nbft_info.243
-rw-r--r--doc/man/nbft_info_discovery.234
-rw-r--r--doc/man/nbft_info_hfi.224
-rw-r--r--doc/man/nbft_info_hfi_info_tcp.283
-rw-r--r--doc/man/nbft_info_host.236
-rw-r--r--doc/man/nbft_info_nid_type.230
-rw-r--r--doc/man/nbft_info_primary_admin_host_flag.239
-rw-r--r--doc/man/nbft_info_security.216
-rw-r--r--doc/man/nbft_info_subsystem_ns.294
-rw-r--r--doc/man/nbft_security.298
-rw-r--r--doc/man/nbft_security_flags.2179
-rw-r--r--doc/man/nbft_security_secret_type.216
-rw-r--r--doc/man/nbft_ssns.2159
-rw-r--r--doc/man/nbft_ssns_ext_info.255
-rw-r--r--doc/man/nbft_ssns_ext_info_flags.225
-rw-r--r--doc/man/nbft_ssns_flags.2121
-rw-r--r--doc/man/nbft_ssns_trflags.244
-rw-r--r--doc/man/nbft_trtype.212
-rw-r--r--doc/man/nvme_admin_opcode.2216
-rw-r--r--doc/man/nvme_admin_passthru.271
-rw-r--r--doc/man/nvme_admin_passthru64.271
-rw-r--r--doc/man/nvme_ae_info_css_nvm.225
-rw-r--r--doc/man/nvme_ae_info_error.242
-rw-r--r--doc/man/nvme_ae_info_notice.254
-rw-r--r--doc/man/nvme_ae_info_smart.224
-rw-r--r--doc/man/nvme_ae_type.236
-rw-r--r--doc/man/nvme_aggregate_endurance_group_event.219
-rw-r--r--doc/man/nvme_aggregate_predictable_lat_event.219
-rw-r--r--doc/man/nvme_ana_group_desc.235
-rw-r--r--doc/man/nvme_ana_log.227
-rw-r--r--doc/man/nvme_ana_state.236
-rw-r--r--doc/man/nvme_apst_entry.230
-rw-r--r--doc/man/nvme_boot_partition.232
-rw-r--r--doc/man/nvme_capacity_config_desc.233
-rw-r--r--doc/man/nvme_capacity_mgmt.212
-rw-r--r--doc/man/nvme_change_ns_event.263
-rw-r--r--doc/man/nvme_channel_config_desc.224
-rw-r--r--doc/man/nvme_cmb_size.211
-rw-r--r--doc/man/nvme_cmd_effects.248
-rw-r--r--doc/man/nvme_cmd_effects_log.223
-rw-r--r--doc/man/nvme_cmd_format_mset.220
-rw-r--r--doc/man/nvme_cmd_format_pi.230
-rw-r--r--doc/man/nvme_cmd_format_pil.220
-rw-r--r--doc/man/nvme_cmd_format_ses.233
-rw-r--r--doc/man/nvme_cmd_get_log_lid.2186
-rw-r--r--doc/man/nvme_cmd_get_log_telemetry_host_lsp.218
-rw-r--r--doc/man/nvme_compare.212
-rw-r--r--doc/man/nvme_connect_err.2126
-rw-r--r--doc/man/nvme_constants.2199
-rw-r--r--doc/man/nvme_copy.212
-rw-r--r--doc/man/nvme_copy_range.240
-rw-r--r--doc/man/nvme_copy_range_f1.240
-rw-r--r--doc/man/nvme_copy_range_f2.248
-rw-r--r--doc/man/nvme_copy_range_f3.252
-rw-r--r--doc/man/nvme_copy_range_sopt.212
-rw-r--r--doc/man/nvme_create_ctrl.231
-rw-r--r--doc/man/nvme_create_root.214
-rw-r--r--doc/man/nvme_csi.224
-rw-r--r--doc/man/nvme_ctrl_config_match.233
-rw-r--r--doc/man/nvme_ctrl_find.237
-rw-r--r--doc/man/nvme_ctrl_first_ns.211
-rw-r--r--doc/man/nvme_ctrl_first_path.211
-rw-r--r--doc/man/nvme_ctrl_for_each_ns.212
-rw-r--r--doc/man/nvme_ctrl_for_each_ns_safe.215
-rw-r--r--doc/man/nvme_ctrl_for_each_path.212
-rw-r--r--doc/man/nvme_ctrl_for_each_path_safe.215
-rw-r--r--doc/man/nvme_ctrl_get_address.212
-rw-r--r--doc/man/nvme_ctrl_get_config.211
-rw-r--r--doc/man/nvme_ctrl_get_dhchap_host_key.211
-rw-r--r--doc/man/nvme_ctrl_get_dhchap_key.211
-rw-r--r--doc/man/nvme_ctrl_get_fd.218
-rw-r--r--doc/man/nvme_ctrl_get_firmware.211
-rw-r--r--doc/man/nvme_ctrl_get_host_iface.211
-rw-r--r--doc/man/nvme_ctrl_get_host_traddr.211
-rw-r--r--doc/man/nvme_ctrl_get_model.211
-rw-r--r--doc/man/nvme_ctrl_get_name.211
-rw-r--r--doc/man/nvme_ctrl_get_numa_node.211
-rw-r--r--doc/man/nvme_ctrl_get_phy_slot.212
-rw-r--r--doc/man/nvme_ctrl_get_queue_count.211
-rw-r--r--doc/man/nvme_ctrl_get_serial.211
-rw-r--r--doc/man/nvme_ctrl_get_sqsize.211
-rw-r--r--doc/man/nvme_ctrl_get_src_addr.217
-rw-r--r--doc/man/nvme_ctrl_get_state.211
-rw-r--r--doc/man/nvme_ctrl_get_subsysnqn.211
-rw-r--r--doc/man/nvme_ctrl_get_subsystem.211
-rw-r--r--doc/man/nvme_ctrl_get_sysfs_dir.211
-rw-r--r--doc/man/nvme_ctrl_get_traddr.211
-rw-r--r--doc/man/nvme_ctrl_get_transport.211
-rw-r--r--doc/man/nvme_ctrl_get_trsvcid.211
-rw-r--r--doc/man/nvme_ctrl_identify.217
-rw-r--r--doc/man/nvme_ctrl_is_discovered.211
-rw-r--r--doc/man/nvme_ctrl_is_discovery_ctrl.214
-rw-r--r--doc/man/nvme_ctrl_is_persistent.211
-rw-r--r--doc/man/nvme_ctrl_is_unique_discovery_ctrl.211
-rw-r--r--doc/man/nvme_ctrl_list.219
-rw-r--r--doc/man/nvme_ctrl_metadata_type.2108
-rw-r--r--doc/man/nvme_ctrl_next_ns.214
-rw-r--r--doc/man/nvme_ctrl_next_path.214
-rw-r--r--doc/man/nvme_ctrl_release_fd.29
-rw-r--r--doc/man/nvme_ctrl_reset.213
-rw-r--r--doc/man/nvme_ctrl_set_dhchap_host_key.212
-rw-r--r--doc/man/nvme_ctrl_set_dhchap_key.212
-rw-r--r--doc/man/nvme_ctrl_set_discovered.214
-rw-r--r--doc/man/nvme_ctrl_set_discovery_ctrl.215
-rw-r--r--doc/man/nvme_ctrl_set_persistent.214
-rw-r--r--doc/man/nvme_ctrl_set_unique_discovery_ctrl.215
-rw-r--r--doc/man/nvme_ctrls_filter.211
-rw-r--r--doc/man/nvme_data_tfr.230
-rw-r--r--doc/man/nvme_default_host.214
-rw-r--r--doc/man/nvme_describe_key_serial.215
-rw-r--r--doc/man/nvme_dev_self_test.223
-rw-r--r--doc/man/nvme_directive_dtype.218
-rw-r--r--doc/man/nvme_directive_receive_doper.226
-rw-r--r--doc/man/nvme_directive_recv.212
-rw-r--r--doc/man/nvme_directive_recv_identify_parameters.218
-rw-r--r--doc/man/nvme_directive_recv_stream_allocate.221
-rw-r--r--doc/man/nvme_directive_recv_stream_parameters.218
-rw-r--r--doc/man/nvme_directive_recv_stream_status.221
-rw-r--r--doc/man/nvme_directive_send.218
-rw-r--r--doc/man/nvme_directive_send_doper.221
-rw-r--r--doc/man/nvme_directive_send_id_endir.224
-rw-r--r--doc/man/nvme_directive_send_identify_endir.216
-rw-r--r--doc/man/nvme_directive_send_stream_release_identifier.218
-rw-r--r--doc/man/nvme_directive_send_stream_release_resource.215
-rw-r--r--doc/man/nvme_directive_types.224
-rw-r--r--doc/man/nvme_disconnect_ctrl.213
-rw-r--r--doc/man/nvme_dsm.218
-rw-r--r--doc/man/nvme_dsm_attributes.224
-rw-r--r--doc/man/nvme_dsm_range.223
-rw-r--r--doc/man/nvme_dst_stc.230
-rw-r--r--doc/man/nvme_dump_config.214
-rw-r--r--doc/man/nvme_dump_tree.214
-rw-r--r--doc/man/nvme_eg_critical_warning_flags.226
-rw-r--r--doc/man/nvme_eg_event_aggregate_log.219
-rw-r--r--doc/man/nvme_end_grp_chan_desc.220
-rw-r--r--doc/man/nvme_end_grp_config_desc.247
-rw-r--r--doc/man/nvme_endurance_group_log.287
-rw-r--r--doc/man/nvme_eom_lane_desc.263
-rw-r--r--doc/man/nvme_eom_optional_data.218
-rw-r--r--doc/man/nvme_errno_to_string.211
-rw-r--r--doc/man/nvme_error_log_page.2121
-rw-r--r--doc/man/nvme_fabrics_config.287
-rw-r--r--doc/man/nvme_fctype.242
-rw-r--r--doc/man/nvme_fdp_config_desc.255
-rw-r--r--doc/man/nvme_fdp_config_fdpa.242
-rw-r--r--doc/man/nvme_fdp_config_log.235
-rw-r--r--doc/man/nvme_fdp_event.251
-rw-r--r--doc/man/nvme_fdp_event_flags.224
-rw-r--r--doc/man/nvme_fdp_event_realloc.231
-rw-r--r--doc/man/nvme_fdp_event_realloc_flags.212
-rw-r--r--doc/man/nvme_fdp_event_type.242
-rw-r--r--doc/man/nvme_fdp_events_log.223
-rw-r--r--doc/man/nvme_fdp_reclaim_unit_handle_status.221
-rw-r--r--doc/man/nvme_fdp_reclaim_unit_handle_update.221
-rw-r--r--doc/man/nvme_fdp_ruh_desc.219
-rw-r--r--doc/man/nvme_fdp_ruh_status.223
-rw-r--r--doc/man/nvme_fdp_ruh_status_desc.231
-rw-r--r--doc/man/nvme_fdp_ruh_type.218
-rw-r--r--doc/man/nvme_fdp_ruha.230
-rw-r--r--doc/man/nvme_fdp_ruhu_desc.219
-rw-r--r--doc/man/nvme_fdp_ruhu_log.223
-rw-r--r--doc/man/nvme_fdp_stats_log.227
-rw-r--r--doc/man/nvme_fdp_supported_event_attributes.218
-rw-r--r--doc/man/nvme_fdp_supported_event_desc.219
-rw-r--r--doc/man/nvme_feat.2526
-rw-r--r--doc/man/nvme_feat_auto_pst.215
-rw-r--r--doc/man/nvme_feat_fdp_events_cdw11.223
-rw-r--r--doc/man/nvme_feat_host_behavior.235
-rw-r--r--doc/man/nvme_feat_nswpcfg_state.230
-rw-r--r--doc/man/nvme_feat_plm_window_select.218
-rw-r--r--doc/man/nvme_feat_resv_notify_flags.224
-rw-r--r--doc/man/nvme_feat_tmpthresh_thsel.218
-rw-r--r--doc/man/nvme_features_async_event_config_flags.276
-rw-r--r--doc/man/nvme_features_id.2222
-rw-r--r--doc/man/nvme_fid_supported_effects.290
-rw-r--r--doc/man/nvme_fid_supported_effects_log.215
-rw-r--r--doc/man/nvme_firmware_slot.227
-rw-r--r--doc/man/nvme_first_host.211
-rw-r--r--doc/man/nvme_first_subsystem.211
-rw-r--r--doc/man/nvme_flush.218
-rw-r--r--doc/man/nvme_for_each_host.212
-rw-r--r--doc/man/nvme_for_each_host_safe.215
-rw-r--r--doc/man/nvme_for_each_subsystem.212
-rw-r--r--doc/man/nvme_for_each_subsystem_safe.215
-rw-r--r--doc/man/nvme_format_nvm.217
-rw-r--r--doc/man/nvme_format_nvm_compln_event.231
-rw-r--r--doc/man/nvme_format_nvm_start_event.227
-rw-r--r--doc/man/nvme_free_ctrl.29
-rw-r--r--doc/man/nvme_free_host.29
-rw-r--r--doc/man/nvme_free_ns.29
-rw-r--r--doc/man/nvme_free_subsystem.211
-rw-r--r--doc/man/nvme_free_tree.211
-rw-r--r--doc/man/nvme_fw_commit.216
-rw-r--r--doc/man/nvme_fw_commit_ca.259
-rw-r--r--doc/man/nvme_fw_commit_event.239
-rw-r--r--doc/man/nvme_fw_download.225
-rw-r--r--doc/man/nvme_fw_download_seq.224
-rw-r--r--doc/man/nvme_gen_dhchap_key.224
-rw-r--r--doc/man/nvme_generate_tls_key_identity.230
-rw-r--r--doc/man/nvme_get_ana_log_len.215
-rw-r--r--doc/man/nvme_get_attr.215
-rw-r--r--doc/man/nvme_get_ctrl_attr.215
-rw-r--r--doc/man/nvme_get_ctrl_telemetry.227
-rw-r--r--doc/man/nvme_get_directive_receive_length.218
-rw-r--r--doc/man/nvme_get_discovery_args.235
-rw-r--r--doc/man/nvme_get_feature_length.219
-rw-r--r--doc/man/nvme_get_feature_length2.224
-rw-r--r--doc/man/nvme_get_features.212
-rw-r--r--doc/man/nvme_get_features_arbitration.218
-rw-r--r--doc/man/nvme_get_features_async_event.218
-rw-r--r--doc/man/nvme_get_features_auto_pst.220
-rw-r--r--doc/man/nvme_get_features_data.227
-rw-r--r--doc/man/nvme_get_features_endurance_event_cfg.221
-rw-r--r--doc/man/nvme_get_features_err_recovery.222
-rw-r--r--doc/man/nvme_get_features_err_recovery2.221
-rw-r--r--doc/man/nvme_get_features_hctm.218
-rw-r--r--doc/man/nvme_get_features_host_behavior.221
-rw-r--r--doc/man/nvme_get_features_host_id.224
-rw-r--r--doc/man/nvme_get_features_host_mem_buf.222
-rw-r--r--doc/man/nvme_get_features_host_mem_buf2.221
-rw-r--r--doc/man/nvme_get_features_iocs_profile.218
-rw-r--r--doc/man/nvme_get_features_irq_coalesce.218
-rw-r--r--doc/man/nvme_get_features_irq_config.220
-rw-r--r--doc/man/nvme_get_features_kato.218
-rw-r--r--doc/man/nvme_get_features_lba_range.225
-rw-r--r--doc/man/nvme_get_features_lba_range2.224
-rw-r--r--doc/man/nvme_get_features_lba_sts_interval.218
-rw-r--r--doc/man/nvme_get_features_nopsc.218
-rw-r--r--doc/man/nvme_get_features_num_queues.218
-rw-r--r--doc/man/nvme_get_features_plm_config.223
-rw-r--r--doc/man/nvme_get_features_plm_window.221
-rw-r--r--doc/man/nvme_get_features_power_mgmt.218
-rw-r--r--doc/man/nvme_get_features_resv_mask.222
-rw-r--r--doc/man/nvme_get_features_resv_mask2.221
-rw-r--r--doc/man/nvme_get_features_resv_persist.222
-rw-r--r--doc/man/nvme_get_features_resv_persist2.221
-rw-r--r--doc/man/nvme_get_features_rrl.218
-rw-r--r--doc/man/nvme_get_features_sanitize.218
-rw-r--r--doc/man/nvme_get_features_sel.230
-rw-r--r--doc/man/nvme_get_features_simple.221
-rw-r--r--doc/man/nvme_get_features_sw_progress.218
-rw-r--r--doc/man/nvme_get_features_temp_thresh.218
-rw-r--r--doc/man/nvme_get_features_timestamp.218
-rw-r--r--doc/man/nvme_get_features_volatile_wc.218
-rw-r--r--doc/man/nvme_get_features_write_atomic.218
-rw-r--r--doc/man/nvme_get_features_write_protect.221
-rw-r--r--doc/man/nvme_get_host_telemetry.224
-rw-r--r--doc/man/nvme_get_lba_status.215
-rw-r--r--doc/man/nvme_get_lba_status_log.218
-rw-r--r--doc/man/nvme_get_log.212
-rw-r--r--doc/man/nvme_get_log_ana.233
-rw-r--r--doc/man/nvme_get_log_ana_groups.223
-rw-r--r--doc/man/nvme_get_log_boot_partition.225
-rw-r--r--doc/man/nvme_get_log_changed_ns_list.222
-rw-r--r--doc/man/nvme_get_log_cmd_effects.221
-rw-r--r--doc/man/nvme_get_log_create_telemetry_host.215
-rw-r--r--doc/man/nvme_get_log_device_self_test.219
-rw-r--r--doc/man/nvme_get_log_discovery.227
-rw-r--r--doc/man/nvme_get_log_endurance_group.225
-rw-r--r--doc/man/nvme_get_log_endurance_grp_evt.224
-rw-r--r--doc/man/nvme_get_log_error.225
-rw-r--r--doc/man/nvme_get_log_fdp_configurations.221
-rw-r--r--doc/man/nvme_get_log_fdp_events.224
-rw-r--r--doc/man/nvme_get_log_fdp_stats.221
-rw-r--r--doc/man/nvme_get_log_fid_supported_effects.218
-rw-r--r--doc/man/nvme_get_log_fw_slot.222
-rw-r--r--doc/man/nvme_get_log_lba_status.224
-rw-r--r--doc/man/nvme_get_log_media_unit_stat.218
-rw-r--r--doc/man/nvme_get_log_mi_cmd_supported_effects.218
-rw-r--r--doc/man/nvme_get_log_page.218
-rw-r--r--doc/man/nvme_get_log_persistent_event.221
-rw-r--r--doc/man/nvme_get_log_phy_rx_eom.225
-rw-r--r--doc/man/nvme_get_log_predictable_lat_event.224
-rw-r--r--doc/man/nvme_get_log_predictable_lat_nvmset.218
-rw-r--r--doc/man/nvme_get_log_reclaim_unit_handle_usage.221
-rw-r--r--doc/man/nvme_get_log_reservation.218
-rw-r--r--doc/man/nvme_get_log_sanitize.221
-rw-r--r--doc/man/nvme_get_log_smart.228
-rw-r--r--doc/man/nvme_get_log_support_cap_config_list.218
-rw-r--r--doc/man/nvme_get_log_supported_log_pages.218
-rw-r--r--doc/man/nvme_get_log_telemetry_ctrl.227
-rw-r--r--doc/man/nvme_get_log_telemetry_host.224
-rw-r--r--doc/man/nvme_get_log_zns_changed_zones.223
-rw-r--r--doc/man/nvme_get_logical_block_size.218
-rw-r--r--doc/man/nvme_get_new_host_telemetry.224
-rw-r--r--doc/man/nvme_get_ns_attr.215
-rw-r--r--doc/man/nvme_get_nsid.219
-rw-r--r--doc/man/nvme_get_path_attr.215
-rw-r--r--doc/man/nvme_get_property.215
-rw-r--r--doc/man/nvme_get_subsys_attr.215
-rw-r--r--doc/man/nvme_get_telemetry_log.236
-rw-r--r--doc/man/nvme_get_telemetry_max.218
-rw-r--r--doc/man/nvme_hmac_alg.230
-rw-r--r--doc/man/nvme_host_behavior_support.212
-rw-r--r--doc/man/nvme_host_get_dhchap_key.211
-rw-r--r--doc/man/nvme_host_get_hostid.211
-rw-r--r--doc/man/nvme_host_get_hostnqn.211
-rw-r--r--doc/man/nvme_host_get_hostsymname.212
-rw-r--r--doc/man/nvme_host_get_root.211
-rw-r--r--doc/man/nvme_host_is_pdc_enabled.216
-rw-r--r--doc/man/nvme_host_mem_buf_attrs.231
-rw-r--r--doc/man/nvme_host_metadata.233
-rw-r--r--doc/man/nvme_host_release_fds.213
-rw-r--r--doc/man/nvme_host_set_dhchap_key.212
-rw-r--r--doc/man/nvme_host_set_hostsymname.212
-rw-r--r--doc/man/nvme_host_set_pdc_enabled.216
-rw-r--r--doc/man/nvme_id_ctrl.2511
-rw-r--r--doc/man/nvme_id_ctrl_anacap.259
-rw-r--r--doc/man/nvme_id_ctrl_apsta.213
-rw-r--r--doc/man/nvme_id_ctrl_avscc.214
-rw-r--r--doc/man/nvme_id_ctrl_cmic.243
-rw-r--r--doc/man/nvme_id_ctrl_cntrltype.224
-rw-r--r--doc/man/nvme_id_ctrl_cqes.220
-rw-r--r--doc/man/nvme_id_ctrl_ctratt.2110
-rw-r--r--doc/man/nvme_id_ctrl_dctype.224
-rw-r--r--doc/man/nvme_id_ctrl_dsto.213
-rw-r--r--doc/man/nvme_id_ctrl_fcatt.214
-rw-r--r--doc/man/nvme_id_ctrl_fna.248
-rw-r--r--doc/man/nvme_id_ctrl_frmw.234
-rw-r--r--doc/man/nvme_id_ctrl_fuses.213
-rw-r--r--doc/man/nvme_id_ctrl_hctm.215
-rw-r--r--doc/man/nvme_id_ctrl_lpa.265
-rw-r--r--doc/man/nvme_id_ctrl_mec.220
-rw-r--r--doc/man/nvme_id_ctrl_nvm.239
-rw-r--r--doc/man/nvme_id_ctrl_nvmsr.222
-rw-r--r--doc/man/nvme_id_ctrl_nvscc.213
-rw-r--r--doc/man/nvme_id_ctrl_nwpc.235
-rw-r--r--doc/man/nvme_id_ctrl_oacs.284
-rw-r--r--doc/man/nvme_id_ctrl_oaes.262
-rw-r--r--doc/man/nvme_id_ctrl_ofcs.214
-rw-r--r--doc/man/nvme_id_ctrl_oncs.290
-rw-r--r--doc/man/nvme_id_ctrl_rpmbs.230
-rw-r--r--doc/man/nvme_id_ctrl_sanicap.244
-rw-r--r--doc/man/nvme_id_ctrl_sgls.246
-rw-r--r--doc/man/nvme_id_ctrl_sqes.220
-rw-r--r--doc/man/nvme_id_ctrl_vwc.222
-rw-r--r--doc/man/nvme_id_ctrl_vwci.228
-rw-r--r--doc/man/nvme_id_directives.223
-rw-r--r--doc/man/nvme_id_domain_attr.235
-rw-r--r--doc/man/nvme_id_domain_list.223
-rw-r--r--doc/man/nvme_id_endurance_group_list.219
-rw-r--r--doc/man/nvme_id_independent_id_ns.256
-rw-r--r--doc/man/nvme_id_iocs.215
-rw-r--r--doc/man/nvme_id_ns.2233
-rw-r--r--doc/man/nvme_id_ns_attr.214
-rw-r--r--doc/man/nvme_id_ns_dlfeat.250
-rw-r--r--doc/man/nvme_id_ns_dpc.243
-rw-r--r--doc/man/nvme_id_ns_dps.244
-rw-r--r--doc/man/nvme_id_ns_flbas.235
-rw-r--r--doc/man/nvme_id_ns_granularity_desc.219
-rw-r--r--doc/man/nvme_id_ns_granularity_list.231
-rw-r--r--doc/man/nvme_id_ns_mc.221
-rw-r--r--doc/man/nvme_id_ns_nmic.213
-rw-r--r--doc/man/nvme_id_ns_rescap.262
-rw-r--r--doc/man/nvme_id_nsfeat.250
-rw-r--r--doc/man/nvme_id_nvmset_list.223
-rw-r--r--doc/man/nvme_id_psd.295
-rw-r--r--doc/man/nvme_id_uuid.226
-rw-r--r--doc/man/nvme_id_uuid_list.219
-rw-r--r--doc/man/nvme_id_uuid_list_entry.223
-rw-r--r--doc/man/nvme_identify.215
-rw-r--r--doc/man/nvme_identify_active_ns_list.224
-rw-r--r--doc/man/nvme_identify_active_ns_list_csi.228
-rw-r--r--doc/man/nvme_identify_allocated_ns.218
-rw-r--r--doc/man/nvme_identify_allocated_ns_list.224
-rw-r--r--doc/man/nvme_identify_allocated_ns_list_csi.228
-rw-r--r--doc/man/nvme_identify_cns.2163
-rw-r--r--doc/man/nvme_identify_ctrl.219
-rw-r--r--doc/man/nvme_identify_ctrl_csi.222
-rw-r--r--doc/man/nvme_identify_ctrl_list.224
-rw-r--r--doc/man/nvme_identify_domain_list.225
-rw-r--r--doc/man/nvme_identify_endurance_group_list.218
-rw-r--r--doc/man/nvme_identify_independent_identify_ns.222
-rw-r--r--doc/man/nvme_identify_iocs.221
-rw-r--r--doc/man/nvme_identify_iocs_ns_csi_user_data_format.228
-rw-r--r--doc/man/nvme_identify_ns.229
-rw-r--r--doc/man/nvme_identify_ns_csi.227
-rw-r--r--doc/man/nvme_identify_ns_csi_user_data_format.227
-rw-r--r--doc/man/nvme_identify_ns_descs.226
-rw-r--r--doc/man/nvme_identify_ns_granularity.222
-rw-r--r--doc/man/nvme_identify_nsid_ctrl_list.227
-rw-r--r--doc/man/nvme_identify_nvmset_list.225
-rw-r--r--doc/man/nvme_identify_primary_ctrl.220
-rw-r--r--doc/man/nvme_identify_secondary_ctrl_list.226
-rw-r--r--doc/man/nvme_identify_uuid.220
-rw-r--r--doc/man/nvme_init_copy_range.227
-rw-r--r--doc/man/nvme_init_copy_range_f1.227
-rw-r--r--doc/man/nvme_init_copy_range_f2.233
-rw-r--r--doc/man/nvme_init_copy_range_f3.233
-rw-r--r--doc/man/nvme_init_ctrl.217
-rw-r--r--doc/man/nvme_init_ctrl_list.218
-rw-r--r--doc/man/nvme_init_dsm_range.227
-rw-r--r--doc/man/nvme_init_logging.220
-rw-r--r--doc/man/nvme_insert_tls_key.233
-rw-r--r--doc/man/nvme_insert_tls_key_versioned.237
-rw-r--r--doc/man/nvme_io.215
-rw-r--r--doc/man/nvme_io_control_flags.266
-rw-r--r--doc/man/nvme_io_dsm_flags.296
-rw-r--r--doc/man/nvme_io_mgmt_recv.212
-rw-r--r--doc/man/nvme_io_mgmt_recv_mo.212
-rw-r--r--doc/man/nvme_io_mgmt_send.212
-rw-r--r--doc/man/nvme_io_mgmt_send_mo.212
-rw-r--r--doc/man/nvme_io_opcode.2114
-rw-r--r--doc/man/nvme_io_passthru.271
-rw-r--r--doc/man/nvme_io_passthru64.271
-rw-r--r--doc/man/nvme_is_64bit_reg.216
-rw-r--r--doc/man/nvme_lba_range_type.215
-rw-r--r--doc/man/nvme_lba_range_type_entry.239
-rw-r--r--doc/man/nvme_lba_rd.223
-rw-r--r--doc/man/nvme_lba_status.227
-rw-r--r--doc/man/nvme_lba_status_atype.219
-rw-r--r--doc/man/nvme_lba_status_desc.231
-rw-r--r--doc/man/nvme_lba_status_log.235
-rw-r--r--doc/man/nvme_lbaf.225
-rw-r--r--doc/man/nvme_lbaf_rp.237
-rw-r--r--doc/man/nvme_lbart.248
-rw-r--r--doc/man/nvme_lbas_ns_element.231
-rw-r--r--doc/man/nvme_lockdown.212
-rw-r--r--doc/man/nvme_log_ana_lsp.216
-rw-r--r--doc/man/nvme_log_phy_rx_eom_action.224
-rw-r--r--doc/man/nvme_log_phy_rx_eom_quality.224
-rw-r--r--doc/man/nvme_lookup_ctrl.235
-rw-r--r--doc/man/nvme_lookup_host.220
-rw-r--r--doc/man/nvme_lookup_key.218
-rw-r--r--doc/man/nvme_lookup_keyring.214
-rw-r--r--doc/man/nvme_lookup_subsystem.220
-rw-r--r--doc/man/nvme_media_unit_config_desc.223
-rw-r--r--doc/man/nvme_media_unit_stat_desc.247
-rw-r--r--doc/man/nvme_media_unit_stat_log.231
-rw-r--r--doc/man/nvme_metadata_element_desc.227
-rw-r--r--doc/man/nvme_mi_admin_admin_passthru.274
-rw-r--r--doc/man/nvme_mi_admin_format_nvm.218
-rw-r--r--doc/man/nvme_mi_admin_fw_commit.216
-rw-r--r--doc/man/nvme_mi_admin_fw_download.227
-rw-r--r--doc/man/nvme_mi_admin_get_features_data.230
-rw-r--r--doc/man/nvme_mi_admin_get_log.225
-rw-r--r--doc/man/nvme_mi_admin_get_log_ana.233
-rw-r--r--doc/man/nvme_mi_admin_get_log_ana_groups.223
-rw-r--r--doc/man/nvme_mi_admin_get_log_boot_partition.225
-rw-r--r--doc/man/nvme_mi_admin_get_log_changed_ns_list.222
-rw-r--r--doc/man/nvme_mi_admin_get_log_cmd_effects.221
-rw-r--r--doc/man/nvme_mi_admin_get_log_create_telemetry_host.215
-rw-r--r--doc/man/nvme_mi_admin_get_log_device_self_test.219
-rw-r--r--doc/man/nvme_mi_admin_get_log_discovery.227
-rw-r--r--doc/man/nvme_mi_admin_get_log_endurance_group.225
-rw-r--r--doc/man/nvme_mi_admin_get_log_endurance_grp_evt.224
-rw-r--r--doc/man/nvme_mi_admin_get_log_error.225
-rw-r--r--doc/man/nvme_mi_admin_get_log_fid_supported_effects.218
-rw-r--r--doc/man/nvme_mi_admin_get_log_fw_slot.222
-rw-r--r--doc/man/nvme_mi_admin_get_log_lba_status.224
-rw-r--r--doc/man/nvme_mi_admin_get_log_media_unit_stat.218
-rw-r--r--doc/man/nvme_mi_admin_get_log_mi_cmd_supported_effects.218
-rw-r--r--doc/man/nvme_mi_admin_get_log_page.228
-rw-r--r--doc/man/nvme_mi_admin_get_log_persistent_event.221
-rw-r--r--doc/man/nvme_mi_admin_get_log_phy_rx_eom.225
-rw-r--r--doc/man/nvme_mi_admin_get_log_predictable_lat_event.224
-rw-r--r--doc/man/nvme_mi_admin_get_log_predictable_lat_nvmset.218
-rw-r--r--doc/man/nvme_mi_admin_get_log_reservation.218
-rw-r--r--doc/man/nvme_mi_admin_get_log_sanitize.221
-rw-r--r--doc/man/nvme_mi_admin_get_log_simple.224
-rw-r--r--doc/man/nvme_mi_admin_get_log_smart.228
-rw-r--r--doc/man/nvme_mi_admin_get_log_support_cap_config_list.218
-rw-r--r--doc/man/nvme_mi_admin_get_log_supported_log_pages.218
-rw-r--r--doc/man/nvme_mi_admin_get_log_telemetry_ctrl.227
-rw-r--r--doc/man/nvme_mi_admin_get_log_telemetry_host.224
-rw-r--r--doc/man/nvme_mi_admin_get_log_zns_changed_zones.223
-rw-r--r--doc/man/nvme_mi_admin_get_nsid_log.232
-rw-r--r--doc/man/nvme_mi_admin_identify.224
-rw-r--r--doc/man/nvme_mi_admin_identify_active_ns_list.228
-rw-r--r--doc/man/nvme_mi_admin_identify_allocated_ns.221
-rw-r--r--doc/man/nvme_mi_admin_identify_allocated_ns_list.228
-rw-r--r--doc/man/nvme_mi_admin_identify_cns_nsid.230
-rw-r--r--doc/man/nvme_mi_admin_identify_ctrl.224
-rw-r--r--doc/man/nvme_mi_admin_identify_ctrl_list.227
-rw-r--r--doc/man/nvme_mi_admin_identify_ns.221
-rw-r--r--doc/man/nvme_mi_admin_identify_ns_descs.221
-rw-r--r--doc/man/nvme_mi_admin_identify_nsid_ctrl_list.230
-rw-r--r--doc/man/nvme_mi_admin_identify_partial.237
-rw-r--r--doc/man/nvme_mi_admin_identify_primary_ctrl.226
-rw-r--r--doc/man/nvme_mi_admin_identify_secondary_ctrl_list.227
-rw-r--r--doc/man/nvme_mi_admin_ns_attach.215
-rw-r--r--doc/man/nvme_mi_admin_ns_attach_ctrls.218
-rw-r--r--doc/man/nvme_mi_admin_ns_detach_ctrls.218
-rw-r--r--doc/man/nvme_mi_admin_req_hdr.272
-rw-r--r--doc/man/nvme_mi_admin_resp_hdr.234
-rw-r--r--doc/man/nvme_mi_admin_sanitize_nvm.225
-rw-r--r--doc/man/nvme_mi_admin_security_recv.225
-rw-r--r--doc/man/nvme_mi_admin_security_send.225
-rw-r--r--doc/man/nvme_mi_admin_xfer.242
-rw-r--r--doc/man/nvme_mi_ccs.278
-rw-r--r--doc/man/nvme_mi_close.29
-rw-r--r--doc/man/nvme_mi_close_ctrl.29
-rw-r--r--doc/man/nvme_mi_cmd_supported_effects.284
-rw-r--r--doc/man/nvme_mi_cmd_supported_effects_log.219
-rw-r--r--doc/man/nvme_mi_config_id.231
-rw-r--r--doc/man/nvme_mi_config_smbus_freq.227
-rw-r--r--doc/man/nvme_mi_create_root.220
-rw-r--r--doc/man/nvme_mi_csts.248
-rw-r--r--doc/man/nvme_mi_ctrl_health_status.239
-rw-r--r--doc/man/nvme_mi_ctrl_id.216
-rw-r--r--doc/man/nvme_mi_cwarn.236
-rw-r--r--doc/man/nvme_mi_dtyp.245
-rw-r--r--doc/man/nvme_mi_elem.248
-rw-r--r--doc/man/nvme_mi_free_root.29
-rw-r--r--doc/man/nvme_mi_init_ctrl.220
-rw-r--r--doc/man/nvme_mi_message_type.233
-rw-r--r--doc/man/nvme_mi_mi_opcode.230
-rw-r--r--doc/man/nvme_mi_mi_read_mi_data_ctrl.223
-rw-r--r--doc/man/nvme_mi_mi_read_mi_data_ctrl_list.223
-rw-r--r--doc/man/nvme_mi_mi_read_mi_data_port.224
-rw-r--r--doc/man/nvme_mi_mi_read_mi_data_subsys.218
-rw-r--r--doc/man/nvme_mi_mi_req_hdr.231
-rw-r--r--doc/man/nvme_mi_mi_resp_hdr.225
-rw-r--r--doc/man/nvme_mi_mi_subsystem_health_status_poll.224
-rw-r--r--doc/man/nvme_mi_msg_hdr.230
-rw-r--r--doc/man/nvme_mi_msg_resp.226
-rw-r--r--doc/man/nvme_mi_nvm_ss_health_status.235
-rw-r--r--doc/man/nvme_mi_open_mctp.222
-rw-r--r--doc/man/nvme_mi_osc.219
-rw-r--r--doc/man/nvme_mi_port_pcie.239
-rw-r--r--doc/man/nvme_mi_port_smb.235
-rw-r--r--doc/man/nvme_mi_read_ctrl_info.247
-rw-r--r--doc/man/nvme_mi_read_nvm_ss_info.227
-rw-r--r--doc/man/nvme_mi_read_port_info.241
-rw-r--r--doc/man/nvme_mi_read_sc_list.220
-rw-r--r--doc/man/nvme_mi_resp_status.2120
-rw-r--r--doc/man/nvme_mi_set_probe_enabled.216
-rw-r--r--doc/man/nvme_mi_status_to_string.217
-rw-r--r--doc/man/nvme_mi_vpd_hdr.247
-rw-r--r--doc/man/nvme_mi_vpd_mr_common.249
-rw-r--r--doc/man/nvme_mi_vpd_mra.275
-rw-r--r--doc/man/nvme_mi_vpd_ppmra.247
-rw-r--r--doc/man/nvme_mi_vpd_telem.228
-rw-r--r--doc/man/nvme_mi_vpd_tra.227
-rw-r--r--doc/man/nvme_namespace_attach_ctrls.221
-rw-r--r--doc/man/nvme_namespace_detach_ctrls.221
-rw-r--r--doc/man/nvme_namespace_filter.211
-rw-r--r--doc/man/nvme_namespace_first_path.211
-rw-r--r--doc/man/nvme_namespace_for_each_path.212
-rw-r--r--doc/man/nvme_namespace_for_each_path_safe.215
-rw-r--r--doc/man/nvme_namespace_next_path.214
-rw-r--r--doc/man/nvme_nbft_free.29
-rw-r--r--doc/man/nvme_nbft_read.217
-rw-r--r--doc/man/nvme_nd_ns_fpi.219
-rw-r--r--doc/man/nvme_next_host.214
-rw-r--r--doc/man/nvme_next_subsystem.214
-rw-r--r--doc/man/nvme_ns_attach.212
-rw-r--r--doc/man/nvme_ns_attach_ctrls.218
-rw-r--r--doc/man/nvme_ns_attach_sel.218
-rw-r--r--doc/man/nvme_ns_compare.220
-rw-r--r--doc/man/nvme_ns_detach_ctrls.218
-rw-r--r--doc/man/nvme_ns_flush.211
-rw-r--r--doc/man/nvme_ns_get_csi.211
-rw-r--r--doc/man/nvme_ns_get_ctrl.213
-rw-r--r--doc/man/nvme_ns_get_eui64.211
-rw-r--r--doc/man/nvme_ns_get_fd.218
-rw-r--r--doc/man/nvme_ns_get_firmware.211
-rw-r--r--doc/man/nvme_ns_get_generic_name.211
-rw-r--r--doc/man/nvme_ns_get_lba_count.211
-rw-r--r--doc/man/nvme_ns_get_lba_size.211
-rw-r--r--doc/man/nvme_ns_get_lba_util.211
-rw-r--r--doc/man/nvme_ns_get_meta_size.211
-rw-r--r--doc/man/nvme_ns_get_model.211
-rw-r--r--doc/man/nvme_ns_get_name.211
-rw-r--r--doc/man/nvme_ns_get_nguid.211
-rw-r--r--doc/man/nvme_ns_get_nsid.211
-rw-r--r--doc/man/nvme_ns_get_serial.211
-rw-r--r--doc/man/nvme_ns_get_subsystem.211
-rw-r--r--doc/man/nvme_ns_get_sysfs_dir.211
-rw-r--r--doc/man/nvme_ns_get_uuid.214
-rw-r--r--doc/man/nvme_ns_id_desc.230
-rw-r--r--doc/man/nvme_ns_id_desc_nidt.233
-rw-r--r--doc/man/nvme_ns_identify.217
-rw-r--r--doc/man/nvme_ns_identify_descs.217
-rw-r--r--doc/man/nvme_ns_list.215
-rw-r--r--doc/man/nvme_ns_metadata_type.234
-rw-r--r--doc/man/nvme_ns_mgmt.212
-rw-r--r--doc/man/nvme_ns_mgmt_create.232
-rw-r--r--doc/man/nvme_ns_mgmt_delete.219
-rw-r--r--doc/man/nvme_ns_mgmt_host_sw_specified.2122
-rw-r--r--doc/man/nvme_ns_mgmt_sel.218
-rw-r--r--doc/man/nvme_ns_read.220
-rw-r--r--doc/man/nvme_ns_release_fd.29
-rw-r--r--doc/man/nvme_ns_rescan.213
-rw-r--r--doc/man/nvme_ns_verify.217
-rw-r--r--doc/man/nvme_ns_write.220
-rw-r--r--doc/man/nvme_ns_write_protect_cfg.230
-rw-r--r--doc/man/nvme_ns_write_uncorrectable.217
-rw-r--r--doc/man/nvme_ns_write_zeros.217
-rw-r--r--doc/man/nvme_nss_hw_err_event.223
-rw-r--r--doc/man/nvme_nvm_id_ns.231
-rw-r--r--doc/man/nvme_nvm_id_ns_elbaf.220
-rw-r--r--doc/man/nvme_nvm_identify_ctrl.218
-rw-r--r--doc/man/nvme_nvmeset_pl_status.224
-rw-r--r--doc/man/nvme_nvmset_attr.246
-rw-r--r--doc/man/nvme_nvmset_pl_events.240
-rw-r--r--doc/man/nvme_nvmset_predictable_lat_log.267
-rw-r--r--doc/man/nvme_open.215
-rw-r--r--doc/man/nvme_passthru_cmd.283
-rw-r--r--doc/man/nvme_passthru_cmd64.287
-rw-r--r--doc/man/nvme_path_get_ana_state.211
-rw-r--r--doc/man/nvme_path_get_ctrl.211
-rw-r--r--doc/man/nvme_path_get_name.211
-rw-r--r--doc/man/nvme_path_get_ns.211
-rw-r--r--doc/man/nvme_path_get_sysfs_dir.211
-rw-r--r--doc/man/nvme_paths_filter.211
-rw-r--r--doc/man/nvme_persistent_event_entry.251
-rw-r--r--doc/man/nvme_persistent_event_log.287
-rw-r--r--doc/man/nvme_persistent_event_types.284
-rw-r--r--doc/man/nvme_pevent_log_action.224
-rw-r--r--doc/man/nvme_phy_rx_eom_log.299
-rw-r--r--doc/man/nvme_phy_rx_eom_progress.224
-rw-r--r--doc/man/nvme_plm_config.235
-rw-r--r--doc/man/nvme_pmr_size.211
-rw-r--r--doc/man/nvme_pmr_throughput.211
-rw-r--r--doc/man/nvme_power_on_reset_info_list.239
-rw-r--r--doc/man/nvme_primary_ctrl_cap.283
-rw-r--r--doc/man/nvme_psd_flags.226
-rw-r--r--doc/man/nvme_psd_power_scale.211
-rw-r--r--doc/man/nvme_psd_ps.224
-rw-r--r--doc/man/nvme_psd_workload.235
-rw-r--r--doc/man/nvme_read.212
-rw-r--r--doc/man/nvme_read_config.217
-rw-r--r--doc/man/nvme_refresh_topology.211
-rw-r--r--doc/man/nvme_register_offsets.2174
-rw-r--r--doc/man/nvme_registered_ctrl.231
-rw-r--r--doc/man/nvme_registered_ctrl_ext.235
-rw-r--r--doc/man/nvme_rescan_ctrl.29
-rw-r--r--doc/man/nvme_resv_acquire.216
-rw-r--r--doc/man/nvme_resv_cptpl.226
-rw-r--r--doc/man/nvme_resv_notification_log.235
-rw-r--r--doc/man/nvme_resv_notify_rnlpt.230
-rw-r--r--doc/man/nvme_resv_racqa.224
-rw-r--r--doc/man/nvme_resv_register.215
-rw-r--r--doc/man/nvme_resv_release.212
-rw-r--r--doc/man/nvme_resv_report.216
-rw-r--r--doc/man/nvme_resv_rrega.224
-rw-r--r--doc/man/nvme_resv_rrela.218
-rw-r--r--doc/man/nvme_resv_rtype.242
-rw-r--r--doc/man/nvme_resv_status.259
-rw-r--r--doc/man/nvme_sanitize_compln_event.227
-rw-r--r--doc/man/nvme_sanitize_log_page.2106
-rw-r--r--doc/man/nvme_sanitize_nvm.222
-rw-r--r--doc/man/nvme_sanitize_sanact.230
-rw-r--r--doc/man/nvme_sanitize_sstat.2105
-rw-r--r--doc/man/nvme_sanitize_start_event.223
-rw-r--r--doc/man/nvme_scan.211
-rw-r--r--doc/man/nvme_scan_ctrl.216
-rw-r--r--doc/man/nvme_scan_ctrl_namespace_paths.214
-rw-r--r--doc/man/nvme_scan_ctrl_namespaces.214
-rw-r--r--doc/man/nvme_scan_ctrls.211
-rw-r--r--doc/man/nvme_scan_namespace.211
-rw-r--r--doc/man/nvme_scan_subsystem_namespaces.214
-rw-r--r--doc/man/nvme_scan_subsystems.211
-rw-r--r--doc/man/nvme_scan_topology.220
-rw-r--r--doc/man/nvme_secondary_ctrl.243
-rw-r--r--doc/man/nvme_secondary_ctrl_list.223
-rw-r--r--doc/man/nvme_security_receive.212
-rw-r--r--doc/man/nvme_security_send.221
-rw-r--r--doc/man/nvme_self_test_log.243
-rw-r--r--doc/man/nvme_set_feature_event.219
-rw-r--r--doc/man/nvme_set_features.212
-rw-r--r--doc/man/nvme_set_features_arbitration.230
-rw-r--r--doc/man/nvme_set_features_async_event.221
-rw-r--r--doc/man/nvme_set_features_auto_pst.224
-rw-r--r--doc/man/nvme_set_features_data.233
-rw-r--r--doc/man/nvme_set_features_endurance_evt_cfg.224
-rw-r--r--doc/man/nvme_set_features_err_recovery.227
-rw-r--r--doc/man/nvme_set_features_hctm.224
-rw-r--r--doc/man/nvme_set_features_host_behavior.218
-rw-r--r--doc/man/nvme_set_features_host_id.221
-rw-r--r--doc/man/nvme_set_features_iocs_profile.218
-rw-r--r--doc/man/nvme_set_features_irq_coalesce.224
-rw-r--r--doc/man/nvme_set_features_irq_config.224
-rw-r--r--doc/man/nvme_set_features_lba_range.227
-rw-r--r--doc/man/nvme_set_features_lba_sts_interval.224
-rw-r--r--doc/man/nvme_set_features_nopsc.221
-rw-r--r--doc/man/nvme_set_features_plm_config.227
-rw-r--r--doc/man/nvme_set_features_plm_window.224
-rw-r--r--doc/man/nvme_set_features_power_mgmt.224
-rw-r--r--doc/man/nvme_set_features_resv_mask.225
-rw-r--r--doc/man/nvme_set_features_resv_mask2.224
-rw-r--r--doc/man/nvme_set_features_resv_persist.225
-rw-r--r--doc/man/nvme_set_features_resv_persist2.224
-rw-r--r--doc/man/nvme_set_features_rrl.224
-rw-r--r--doc/man/nvme_set_features_sanitize.221
-rw-r--r--doc/man/nvme_set_features_simple.227
-rw-r--r--doc/man/nvme_set_features_sw_progress.221
-rw-r--r--doc/man/nvme_set_features_temp_thresh.227
-rw-r--r--doc/man/nvme_set_features_timestamp.218
-rw-r--r--doc/man/nvme_set_features_volatile_wc.221
-rw-r--r--doc/man/nvme_set_features_write_atomic.221
-rw-r--r--doc/man/nvme_set_features_write_protect.225
-rw-r--r--doc/man/nvme_set_features_write_protect2.224
-rw-r--r--doc/man/nvme_set_keyring.215
-rw-r--r--doc/man/nvme_set_property.215
-rw-r--r--doc/man/nvme_set_root.216
-rw-r--r--doc/man/nvme_smart_crit.254
-rw-r--r--doc/man/nvme_smart_egcw.231
-rw-r--r--doc/man/nvme_smart_log.2235
-rw-r--r--doc/man/nvme_st_code.243
-rw-r--r--doc/man/nvme_st_curr_op.250
-rw-r--r--doc/man/nvme_st_result.277
-rw-r--r--doc/man/nvme_st_valid_diag_info.234
-rw-r--r--doc/man/nvme_status_code.212
-rw-r--r--doc/man/nvme_status_code_type.212
-rw-r--r--doc/man/nvme_status_equals.217
-rw-r--r--doc/man/nvme_status_field.21210
-rw-r--r--doc/man/nvme_status_get_type.211
-rw-r--r--doc/man/nvme_status_get_value.212
-rw-r--r--doc/man/nvme_status_result.287
-rw-r--r--doc/man/nvme_status_to_errno.215
-rw-r--r--doc/man/nvme_status_to_string.215
-rw-r--r--doc/man/nvme_status_type.240
-rw-r--r--doc/man/nvme_streams_directive_params.251
-rw-r--r--doc/man/nvme_streams_directive_status.219
-rw-r--r--doc/man/nvme_submit_admin_passthru.220
-rw-r--r--doc/man/nvme_submit_admin_passthru64.220
-rw-r--r--doc/man/nvme_submit_io_passthru.220
-rw-r--r--doc/man/nvme_submit_io_passthru64.220
-rw-r--r--doc/man/nvme_subsys_filter.211
-rw-r--r--doc/man/nvme_subsys_type.237
-rw-r--r--doc/man/nvme_subsystem_first_ctrl.211
-rw-r--r--doc/man/nvme_subsystem_first_ns.211
-rw-r--r--doc/man/nvme_subsystem_for_each_ctrl.212
-rw-r--r--doc/man/nvme_subsystem_for_each_ctrl_safe.215
-rw-r--r--doc/man/nvme_subsystem_for_each_ns.212
-rw-r--r--doc/man/nvme_subsystem_for_each_ns_safe.215
-rw-r--r--doc/man/nvme_subsystem_get_application.211
-rw-r--r--doc/man/nvme_subsystem_get_host.211
-rw-r--r--doc/man/nvme_subsystem_get_iopolicy.211
-rw-r--r--doc/man/nvme_subsystem_get_name.211
-rw-r--r--doc/man/nvme_subsystem_get_nqn.211
-rw-r--r--doc/man/nvme_subsystem_get_sysfs_dir.211
-rw-r--r--doc/man/nvme_subsystem_get_type.213
-rw-r--r--doc/man/nvme_subsystem_lookup_namespace.214
-rw-r--r--doc/man/nvme_subsystem_next_ctrl.214
-rw-r--r--doc/man/nvme_subsystem_next_ns.214
-rw-r--r--doc/man/nvme_subsystem_release_fds.213
-rw-r--r--doc/man/nvme_subsystem_reset.214
-rw-r--r--doc/man/nvme_subsystem_set_application.214
-rw-r--r--doc/man/nvme_supported_cap_config_list_log.224
-rw-r--r--doc/man/nvme_supported_log_pages.217
-rw-r--r--doc/man/nvme_telemetry_da.230
-rw-r--r--doc/man/nvme_telemetry_log.288
-rw-r--r--doc/man/nvme_thermal_exc_event.219
-rw-r--r--doc/man/nvme_time_stamp_change_event.219
-rw-r--r--doc/man/nvme_timestamp.223
-rw-r--r--doc/man/nvme_unlink_ctrl.29
-rw-r--r--doc/man/nvme_update_config.213
-rw-r--r--doc/man/nvme_uring_cmd.283
-rw-r--r--doc/man/nvme_verify.216
-rw-r--r--doc/man/nvme_version.218
-rw-r--r--doc/man/nvme_virt_mgmt_act.231
-rw-r--r--doc/man/nvme_virt_mgmt_rt.218
-rw-r--r--doc/man/nvme_virtual_mgmt.220
-rw-r--r--doc/man/nvme_write.212
-rw-r--r--doc/man/nvme_write_uncorrectable.217
-rw-r--r--doc/man/nvme_write_zeros.217
-rw-r--r--doc/man/nvme_zns_append.212
-rw-r--r--doc/man/nvme_zns_changed_zone_log.223
-rw-r--r--doc/man/nvme_zns_desc.247
-rw-r--r--doc/man/nvme_zns_id_ctrl.219
-rw-r--r--doc/man/nvme_zns_id_ns.287
-rw-r--r--doc/man/nvme_zns_identify_ctrl.215
-rw-r--r--doc/man/nvme_zns_identify_ns.218
-rw-r--r--doc/man/nvme_zns_lbafe.223
-rw-r--r--doc/man/nvme_zns_mgmt_recv.212
-rw-r--r--doc/man/nvme_zns_mgmt_send.212
-rw-r--r--doc/man/nvme_zns_recv_action.218
-rw-r--r--doc/man/nvme_zns_report_options.254
-rw-r--r--doc/man/nvme_zns_report_zones.239
-rw-r--r--doc/man/nvme_zns_send_action.248
-rw-r--r--doc/man/nvme_zns_za.235
-rw-r--r--doc/man/nvme_zns_zs.248
-rw-r--r--doc/man/nvme_zns_zt.212
-rw-r--r--doc/man/nvme_zone_report.223
-rw-r--r--doc/man/nvmf_add_ctrl.221
-rw-r--r--doc/man/nvmf_addr_family.243
-rw-r--r--doc/man/nvmf_adrfam_str.214
-rw-r--r--doc/man/nvmf_cms_str.214
-rw-r--r--doc/man/nvmf_connect_data.235
-rw-r--r--doc/man/nvmf_connect_disc_entry.220
-rw-r--r--doc/man/nvmf_default_config.211
-rw-r--r--doc/man/nvmf_dim_data.263
-rw-r--r--doc/man/nvmf_dim_entfmt.218
-rw-r--r--doc/man/nvmf_dim_etype.224
-rw-r--r--doc/man/nvmf_dim_tas.224
-rw-r--r--doc/man/nvmf_disc_eflags.246
-rw-r--r--doc/man/nvmf_disc_log_entry.296
-rw-r--r--doc/man/nvmf_discovery_log.238
-rw-r--r--doc/man/nvmf_eflags_str.214
-rw-r--r--doc/man/nvmf_exat_len.215
-rw-r--r--doc/man/nvmf_exattype.218
-rw-r--r--doc/man/nvmf_ext_attr.224
-rw-r--r--doc/man/nvmf_ext_die.279
-rw-r--r--doc/man/nvmf_get_discovery_log.222
-rw-r--r--doc/man/nvmf_get_discovery_wargs.220
-rw-r--r--doc/man/nvmf_hostid_from_file.213
-rw-r--r--doc/man/nvmf_hostnqn_from_file.213
-rw-r--r--doc/man/nvmf_hostnqn_generate.29
-rw-r--r--doc/man/nvmf_log_discovery_lid_support.230
-rw-r--r--doc/man/nvmf_log_discovery_lsp.230
-rw-r--r--doc/man/nvmf_prtype_str.214
-rw-r--r--doc/man/nvmf_qptype_str.214
-rw-r--r--doc/man/nvmf_rdma_cms.212
-rw-r--r--doc/man/nvmf_rdma_prtype.236
-rw-r--r--doc/man/nvmf_rdma_qptype.218
-rw-r--r--doc/man/nvmf_register_ctrl.222
-rw-r--r--doc/man/nvmf_sectype_str.214
-rw-r--r--doc/man/nvmf_subtype_str.214
-rw-r--r--doc/man/nvmf_tcp_sectype.226
-rw-r--r--doc/man/nvmf_treq.230
-rw-r--r--doc/man/nvmf_treq_str.214
-rw-r--r--doc/man/nvmf_trtype.243
-rw-r--r--doc/man/nvmf_trtype_str.214
-rw-r--r--doc/man/nvmf_update_config.215
-rw-r--r--doc/meson.build113
-rw-r--r--doc/mi.rst.in54
-rw-r--r--doc/quickstart.rst.in5
-rw-r--r--doc/rst/fabrics.rst545
-rw-r--r--doc/rst/filters.rst142
-rw-r--r--doc/rst/ioctl.rst5227
-rw-r--r--doc/rst/linux.rst580
-rw-r--r--doc/rst/log.rst49
-rw-r--r--doc/rst/meson.build36
-rw-r--r--doc/rst/mi.rst3239
-rw-r--r--doc/rst/nbft.rst1870
-rw-r--r--doc/rst/tree.rst2482
-rw-r--r--doc/rst/types.rst12335
-rw-r--r--doc/rst/util.rst734
-rw-r--r--examples/discover-loop.c92
-rw-r--r--examples/discover-loop.py65
-rw-r--r--examples/display-columnar.c121
-rw-r--r--examples/display-tree.c72
-rw-r--r--examples/meson.build50
-rw-r--r--examples/mi-conf.c225
-rw-r--r--examples/mi-mctp.c778
-rw-r--r--examples/telemetry-listen.c168
-rw-r--r--internal/meson.build30
-rw-r--r--libnvme.spec.in53
-rw-r--r--libnvme/.gitignore4
-rw-r--r--libnvme/README.md66
-rw-r--r--libnvme/__init__.py9
-rw-r--r--libnvme/meson.build82
-rw-r--r--libnvme/nvme.i1068
-rw-r--r--libnvme/tests/NBFTbin0 -> 1017 bytes
-rwxr-xr-xlibnvme/tests/create-ctrl-obj.py15
-rwxr-xr-xlibnvme/tests/gc.py41
-rwxr-xr-xlibnvme/tests/test-nbft.py93
-rw-r--r--meson.build309
-rw-r--r--meson_options.txt15
-rw-r--r--pyproject.toml25
-rwxr-xr-xscripts/build.sh214
-rwxr-xr-xscripts/collect-sysfs.sh22
-rwxr-xr-xscripts/kernel-doc2523
-rwxr-xr-xscripts/kernel-doc-check10
-rwxr-xr-xscripts/list-man-pages.sh16
-rwxr-xr-xscripts/list-pre-compiled.sh5
-rwxr-xr-xscripts/meson-vcs-tag.sh17
-rwxr-xr-xscripts/release.sh132
-rwxr-xr-xscripts/update-docs.sh49
-rw-r--r--src/libnvme-mi.h24
-rw-r--r--src/libnvme-mi.map61
-rw-r--r--src/libnvme.h31
-rw-r--r--src/libnvme.map356
-rw-r--r--src/meson.build136
-rw-r--r--src/nvme/api-types.h963
-rw-r--r--src/nvme/base64.c94
-rw-r--r--src/nvme/base64.h8
-rw-r--r--src/nvme/cleanup.h41
-rw-r--r--src/nvme/fabrics.c1736
-rw-r--r--src/nvme/fabrics.h327
-rw-r--r--src/nvme/filters.c163
-rw-r--r--src/nvme/filters.h96
-rw-r--r--src/nvme/ioctl.c2258
-rw-r--r--src/nvme/ioctl.h4062
-rw-r--r--src/nvme/json.c589
-rw-r--r--src/nvme/linux.c1297
-rw-r--r--src/nvme/linux.h338
-rw-r--r--src/nvme/log.c105
-rw-r--r--src/nvme/log.h51
-rw-r--r--src/nvme/mi-mctp.c824
-rw-r--r--src/nvme/mi.c1651
-rw-r--r--src/nvme/mi.h2677
-rw-r--r--src/nvme/nbft.c754
-rw-r--r--src/nvme/nbft.h1238
-rw-r--r--src/nvme/no-json.c26
-rw-r--r--src/nvme/private.h289
-rw-r--r--src/nvme/tree.c2713
-rw-r--r--src/nvme/tree.h1454
-rw-r--r--src/nvme/types.h7996
-rw-r--r--src/nvme/util.c1137
-rw-r--r--src/nvme/util.h714
-rw-r--r--subprojects/dbus.wrap4
-rw-r--r--subprojects/json-c.wrap13
-rw-r--r--subprojects/openssl.wrap15
-rw-r--r--test/cpp.cc66
-rw-r--r--test/ioctl/discovery.c428
-rw-r--r--test/ioctl/features.c1604
-rw-r--r--test/ioctl/identify.c572
-rw-r--r--test/ioctl/meson.build42
-rw-r--r--test/ioctl/mock.c174
-rw-r--r--test/ioctl/mock.h104
-rw-r--r--test/ioctl/util.c65
-rw-r--r--test/ioctl/util.h19
-rw-r--r--test/meson.build103
-rw-r--r--test/mi-mctp.c780
-rw-r--r--test/mi.c1937
-rw-r--r--test/mock-ifaddrs.c123
-rw-r--r--test/nbft/README17
-rw-r--r--test/nbft/diffs/NBFT-Dell.PowerEdge.R660-fw1.5.5-single54
-rw-r--r--test/nbft/diffs/NBFT-Dell.PowerEdge.R76060
-rw-r--r--test/nbft/diffs/NBFT-auto-ipv638
-rw-r--r--test/nbft/diffs/NBFT-dhcp-ipv443
-rw-r--r--test/nbft/diffs/NBFT-dhcp-ipv638
-rw-r--r--test/nbft/diffs/NBFT-rhpoc38
-rw-r--r--test/nbft/diffs/NBFT-static-ipv438
-rw-r--r--test/nbft/diffs/NBFT-static-ipv4-discovery43
-rw-r--r--test/nbft/diffs/NBFT-static-ipv638
-rwxr-xr-xtest/nbft/gen-nbft-diffs.sh.in5
-rw-r--r--test/nbft/meson.build85
-rwxr-xr-xtest/nbft/nbft-dump-diff.sh.in8
-rw-r--r--test/nbft/nbft-dump.c121
-rw-r--r--test/nbft/tables/NBFT-Dell.PowerEdge.R660-fw1.5.5-singlebin0 -> 930 bytes
l---------test/nbft/tables/NBFT-Dell.PowerEdge.R7601
-rw-r--r--test/nbft/tables/NBFT-auto-ipv6bin0 -> 721 bytes
-rw-r--r--test/nbft/tables/NBFT-dhcp-ipv4bin0 -> 825 bytes
-rw-r--r--test/nbft/tables/NBFT-dhcp-ipv6bin0 -> 725 bytes
-rw-r--r--test/nbft/tables/NBFT-rhpocbin0 -> 724 bytes
-rw-r--r--test/nbft/tables/NBFT-static-ipv4bin0 -> 725 bytes
-rw-r--r--test/nbft/tables/NBFT-static-ipv4-discoverybin0 -> 825 bytes
-rw-r--r--test/nbft/tables/NBFT-static-ipv6bin0 -> 721 bytes
-rw-r--r--test/nbft/tables_bad/NBFT-bad-oldspecbin0 -> 1103 bytes
-rw-r--r--test/nbft/tables_bad/NBFT-random-noisebin0 -> 512 bytes
-rw-r--r--test/register.c233
-rw-r--r--test/sysfs/data/nvme-sysfs-tw-carbon-6.8.0-rc1+.out32
-rw-r--r--test/sysfs/data/nvme-sysfs-tw-carbon-6.8.0-rc1+.tar.xzbin0 -> 19712 bytes
-rw-r--r--test/sysfs/meson.build29
-rwxr-xr-xtest/sysfs/setup.sh11
-rw-r--r--test/sysfs/sysfs.c89
-rw-r--r--test/test-util.c121
-rw-r--r--test/test.c437
-rw-r--r--test/tree.c1184
-rw-r--r--test/tree.py23
-rw-r--r--test/utils.c68
-rw-r--r--test/utils.h20
-rw-r--r--test/uuid.c120
-rw-r--r--test/zns.c90
1019 files changed, 107503 insertions, 0 deletions
diff --git a/.checkpatch.conf b/.checkpatch.conf
new file mode 100644
index 0000000..5541a56
--- /dev/null
+++ b/.checkpatch.conf
@@ -0,0 +1,24 @@
+# Checkpatch options.
+# REF: https://docs.kernel.org/dev-tools/checkpatch.html
+
+# This isn't actually a Linux kernel tree
+--no-tree
+
+--max-line-length=120
+
+--ignore EMAIL_SUBJECT
+
+# FILE_PATH_CHANGES reports this kind of message:
+# "added, moved or deleted file(s), does MAINTAINERS need updating?"
+--ignore FILE_PATH_CHANGES
+
+
+# Commit messages might contain a Gerrit Change-Id.
+--ignore GERRIT_CHANGE_ID
+
+# Do not check the format of commit messages, as Gerrit's merge commits do not
+# preserve it.
+--ignore GIT_COMMIT_ID
+
+# Avoid "Does not appear to be a unified-diff format patch" message
+--ignore NOT_UNIFIED_DIFF
diff --git a/.github/cross/ubuntu-cross-armhf.txt b/.github/cross/ubuntu-cross-armhf.txt
new file mode 100644
index 0000000..41c8328
--- /dev/null
+++ b/.github/cross/ubuntu-cross-armhf.txt
@@ -0,0 +1,18 @@
+[binaries]
+c = '/usr/bin/arm-linux-gnueabihf-gcc'
+ar = '/usr/arm-linux-gnueabihf/bin/ar'
+strip = '/usr/arm-linux-gnueabihf/bin/strip'
+pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config'
+ld = '/usr/bin/arm-linux/gnueabihf-ld'
+exe_wrapper = '/usr/bin/qemu-arm-static'
+
+[properties]
+root = '/usr/arm-linux-gnueabihf'
+has_function_printf = true
+skip_sanity_check = true
+
+[host_machine]
+system = 'linux'
+cpu_family = 'arm'
+cpu = 'armv7'
+endian = 'little'
diff --git a/.github/cross/ubuntu-cross-ppc64le.txt b/.github/cross/ubuntu-cross-ppc64le.txt
new file mode 100644
index 0000000..6baaefb
--- /dev/null
+++ b/.github/cross/ubuntu-cross-ppc64le.txt
@@ -0,0 +1,18 @@
+[binaries]
+c = '/usr/bin/powerpc64le-linux-gnu-gcc'
+ar = '/usr/powerpc64le-linux-gnu/bin/ar'
+strip = '/usr/powerpc64le-linux-gnu/bin/strip'
+pkgconfig = '/usr/bin/powerpc64le-linux-gnu-pkg-config'
+ld = '/usr/bin/powerpc64le-linux-gnu-ld'
+exe_wrapper = '/usr/bin/qemu-ppc64le-static'
+
+[properties]
+root = '/usr/powerpc64le-linux-gnu'
+has_function_printf = true
+skip_sanity_check = true
+
+[host_machine]
+system = 'linux'
+cpu_family = 'ppc64'
+cpu = ''
+endian = 'little'
diff --git a/.github/cross/ubuntu-cross-s390x.txt b/.github/cross/ubuntu-cross-s390x.txt
new file mode 100644
index 0000000..51a3511
--- /dev/null
+++ b/.github/cross/ubuntu-cross-s390x.txt
@@ -0,0 +1,18 @@
+[binaries]
+c = '/usr/bin/s390x-linux-gnu-gcc'
+ar = '/usr/s390x-linux-gnu/bin/ar'
+strip = '/usr/s390x-linux-gnu/bin/strip'
+pkgconfig = '/usr/bin/s390x-linux-gnu-pkg-config'
+ld = '/usr/bin/s390x-linux-gnu-ld'
+exe_wrapper = '/usr/bin/qemu-s390x-static'
+
+[properties]
+root = '/usr/s390x-linux-gnu'
+has_function_printf = true
+skip_sanity_check = true
+
+[host_machine]
+system = 'linux'
+cpu_family = 's390x'
+cpu = ''
+endian = 'big'
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..23c4cb3
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,7 @@
+---
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..df79f65
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,109 @@
+---
+name: build
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+ branches: [master]
+
+ workflow_dispatch:
+
+jobs:
+ default:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ compiler: [gcc, clang]
+ buildtype: [debug, release]
+ container:
+ image: ghcr.io/igaw/linux-nvme/debian.python:latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: build
+ run: |
+ scripts/build.sh -b ${{ matrix.buildtype }} -c ${{ matrix.compiler }}
+ - uses: actions/upload-artifact@v4
+ name: upload logs
+ if: failure()
+ with:
+ name: logs files
+ path: |
+ .build-ci/meson-logs/*.txt
+
+ cross:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - arch: armhf
+ - arch: s390x
+ - arch: ppc64le
+ steps:
+ - uses: actions/checkout@v4
+ - name: enable foreign arch
+ uses: dbhi/qus/action@main
+ - name: compile and run unit tests
+ uses: mosteo-actions/docker-run@v1
+ with:
+ image: ghcr.io/igaw/linux-nvme/ubuntu-cross-${{ matrix.arch }}:latest
+ guest-dir: /build
+ host-dir: ${{ github.workspace }}
+ command: |
+ scripts/build.sh -b release -c gcc -t ${{ matrix.arch }} cross
+ params: "--platform linux/amd64"
+ pull-params: "--platform linux/amd64"
+ - uses: actions/upload-artifact@v4
+ name: upload logs
+ if: failure()
+ with:
+ name: log files
+ path: |
+ .build-ci/meson-logs/*.txt
+
+ libdbus:
+ name: libdbus
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/igaw/linux-nvme/debian:latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: build
+ run: |
+ scripts/build.sh -b release -c gcc libdbus
+ - uses: actions/upload-artifact@v4
+ name: upload logs
+ if: failure()
+ with:
+ name: log files
+ path: |
+ .build-ci/meson-logs/*.txt
+
+ fallback-shared-libraries:
+ name: fallback shared libraries
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/igaw/linux-nvme/debian:latest
+ if: github.ref == 'refs/heads/master'
+ steps:
+ - uses: actions/checkout@v4
+ - name: build
+ run: |
+ scripts/build.sh -b release -c gcc fallback
+ - uses: actions/upload-artifact@v4
+ if: failure()
+ with:
+ name: log files
+ path: |
+ .build-ci/meson-logs/*.txt
+
+ build-muon:
+ name: muon minimal static
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/igaw/linux-nvme/debian:latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: build
+ run: |
+ scripts/build.sh -m muon
diff --git a/.github/workflows/checkpatch.yml b/.github/workflows/checkpatch.yml
new file mode 100644
index 0000000..c0a09b7
--- /dev/null
+++ b/.github/workflows/checkpatch.yml
@@ -0,0 +1,15 @@
+name: checkpatch review
+on: [pull_request]
+jobs:
+ checkpatch:
+ name: checkpatch review
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Calculate PR commits + 1'
+ run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> $GITHUB_ENV
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ fetch-depth: 0
+ - name: Run checkpatch review
+ uses: webispy/checkpatch-action@v9
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
new file mode 100644
index 0000000..97ff3f4
--- /dev/null
+++ b/.github/workflows/coverage.yml
@@ -0,0 +1,22 @@
+---
+name: coverage
+
+on:
+ push:
+ branches: [master]
+
+jobs:
+ code-coverage:
+ if: github.repository == 'linux-nvme/libnvme'
+ name: code coverage
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/igaw/linux-nvme/debian.python:latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: build
+ run: |
+ scripts/build.sh coverage
+ - uses: codecov/codecov-action@v4
+ with:
+ fail_ci_if_error: false
diff --git a/.github/workflows/release-python.yml b/.github/workflows/release-python.yml
new file mode 100644
index 0000000..3ca68a9
--- /dev/null
+++ b/.github/workflows/release-python.yml
@@ -0,0 +1,73 @@
+---
+name: release python
+
+on:
+ push:
+ branches: [master]
+ tags:
+ - '**'
+ pull_request:
+ branches: [master]
+
+ workflow_dispatch:
+
+jobs:
+ build_sdist:
+ name: Build source distribution
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/igaw/linux-nvme/debian.python:latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Allow workspace
+ run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
+
+ - name: Build sdist
+ run: pipx run build --sdist
+
+ - uses: actions/upload-artifact@v4
+ with:
+ path: dist/*.tar.gz
+ retention-days: 5
+
+ upload_test_pypi:
+ needs: [build_sdist]
+ runs-on: ubuntu-latest
+ if: startsWith(github.ref, 'refs/tags/v')
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ name: artifact
+ path: dist
+
+ - name: Publish package to TestPyPI
+ uses: pypa/gh-action-pypi-publish@release/v1.5
+ with:
+ user: __token__
+ password: ${{ secrets.TEST_PYPI_API_TOKEN }}
+ repository_url: https://test.pypi.org/legacy/
+
+ upload_pypi:
+ needs: [build_sdist]
+ runs-on: ubuntu-latest
+ if: startsWith(github.ref, 'refs/tags/v') && github.repository == 'linux-nvme/libnvme'
+ steps:
+ - name: Check if it is a release tag
+ id: check-tag
+ run: |
+ if [[ ${{ github.event.ref }} =~ ^refs/tags/v([0-9]+\.[0-9]+)(\.[0-9]+)?(-rc[0-9]+)?$ ]]; then
+ echo ::set-output name=match::true
+ fi
+ - name: Download artifiact
+ uses: actions/download-artifact@v4
+ if: steps.check-tag.outputs.match == 'true'
+ with:
+ name: artifact
+ path: dist
+ - name: Publish package to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1.5
+ if: steps.check-tag.outputs.match == 'true'
+ with:
+ user: __token__
+ password: ${{ secrets.PYPI_API_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..d4ef038
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,20 @@
+---
+name: release
+
+on:
+ push:
+ branches: [master]
+ tags:
+ - '**'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ if: startsWith(github.ref, 'refs/tags/v') && github.repository == 'linux-nvme/libnvme'
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ - uses: ncipollo/release-action@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..89e032e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+a.out
+*.o
+*.ol
+*.os
+*~
+*.swp
+*.a
+*.so.*
+*.tar.gz
+cscope.*
+compile_commands.json
+
+.build
+.cache
+
+subprojects/*
+!subprojects/*.wrap
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..3505101
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: Apache-2.0
+
+version: 2
+
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3"
+ apt_packages:
+ - meson
+ - python3-lxml
+ - docbook-xsl
+ - xsltproc
+ - pandoc
+ jobs:
+ post_install:
+ - pip3 install lxml
+ pre_build:
+ - meson .build -Ddocs=rst -Ddocs-build=true || cat .build/meson-logs/meson-log.txt
+ - ninja -C .build
+
+sphinx:
+ configuration: .build/doc/conf.py
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..768338a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2021 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+#
+NAME := libnvme
+.DEFAULT_GOAL := ${NAME}
+BUILD-DIR := .build
+
+${BUILD-DIR}:
+ meson $@
+ @echo "Configuration located in: $@"
+ @echo "-------------------------------------------------------"
+
+.PHONY: ${NAME}
+${NAME}: ${BUILD-DIR}
+ ninja -C ${BUILD-DIR}
+
+.PHONY: clean
+clean:
+ifneq ("$(wildcard ${BUILD-DIR})","")
+ ninja -C ${BUILD-DIR} -t $@
+endif
+
+.PHONY: purge
+purge:
+ifneq ("$(wildcard ${BUILD-DIR})","")
+ rm -rf ${BUILD-DIR}
+endif
+
+.PHONY: install dist
+install dist: ${BUILD-DIR}
+ cd ${BUILD-DIR} && meson $@
+
+.PHONY: uninstall
+uninstall:
+ cd ${BUILD-DIR} && meson --internal uninstall
+
+.PHONY: test
+test: ${BUILD-DIR}
+ ninja -C ${BUILD-DIR} $@
+
+.PHONY: rpm
+rpm: ${BUILD-DIR}
+ git archive --format=tar HEAD > libnvme.tar
+ tar rf libnvme.tar ${BUILD-DIR}/libnvme.spec
+ gzip -f -9 libnvme.tar
+ rpmbuild -ta libnvme.tar.gz -v
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..758d5d5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,188 @@
+# libnvme
+
+![MesonBuild](https://github.com/linux-nvme/libnvme/actions/workflows/build.yml/badge.svg)
+![PyBuild](https://github.com/linux-nvme/libnvme/actions/workflows/release-python.yml/badge.svg)
+[![PyPI](https://img.shields.io/pypi/v/libnvme)](https://pypi.org/project/libnvme/)
+[![PyPI - Wheel](https://img.shields.io/pypi/wheel/libnvme)](https://pypi.org/project/libnvme/)
+![GitHub](https://img.shields.io/github/license/linux-nvme/libnvme)
+[![codecov](https://codecov.io/gh/linux-nvme/libnvme/branch/master/graph/badge.svg)](https://codecov.io/gh/linux-nvme/libnvme)
+[![Read the Docs](https://img.shields.io/readthedocs/libnvme)](https://libnvme.readthedocs.io/en/latest/)
+
+This is the libnvme development C library. libnvme provides type
+definitions for NVMe specification structures, enumerations, and bit
+fields, helper functions to construct, dispatch, and decode commands
+and payloads, and utilities to connect, scan, and manage nvme devices
+on a Linux system.
+
+The public specification is the authority to resolve any protocol
+discrepancies with this library. For more info on NVM Express, please
+see:
+
+ https://nvmexpress.org
+
+Subscribe to linux-nvme@lists.infradead.org for linux-nvme related
+discussions and development for both kernel and userspace. The list is
+archived here:
+
+ https://lists.infradead.org/mailman/listinfo/linux-nvme
+
+# License
+
+Except where otherwise stated, all software contained within this repo
+is currently licensed LGPL-2.1-or-later, see COPYING for more
+information.
+
+Keith Busch 2020-02-06
+
+------
+
+# Dependency
+
+libnvme depends on minimum Linux kernel version v4.15, which
+introduced the /sys/class/nvme-subsystem.
+
+# Build from source
+## Prerequisite
+
+A minimal build depends on a set of build tools
+
+ - gcc
+ - ninja
+ - meson
+
+Not all feature will be present with such configuration, e.g.
+the fabrics part of the library wont support authentication or
+TLS over the nvme-tcp transport.
+
+To enable the optional features install following libraries
+
+`/etc/nvme/config.json`` support:
+ - json-c (recommend)
+
+Authentication and TLS over nvme-tcp:
+ - openssl
+ - keyutils
+
+End point discovery for MI
+ - libdbus
+
+Python bindings
+ - Python 3 interpreter
+ - Python 3 development libraries
+
+## Minimal on embedded builds
+
+The reference implemention of the Meson specification is in Python 3. Installing
+or porting this dependency is not really feasible for embedded project. Though
+there are two project which implement the Ninja and the Meson API in pure C99
+
+ - samurai: https://github.com/michaelforney/samurai.git
+ - muon: https://git.sr.ht/~lattis/muon
+
+The CI build helper script `scripts/build.sh` is able to setup and build this
+project in a minimal setup using samurai and muon and thus only depending on:
+- gcc
+- make
+- git
+
+`scripts/build.sh -m muon`
+
+## To compile libnvme
+
+To `configure` the project:
+
+```
+meson setup .build
+```
+
+Which will default to build a shared library. To configure for static libraries call
+
+```
+meson setup --default-library=static .build
+```
+
+One nice feature of meson is that it doesn't mix build artifacts
+(e.g. `*.o`, `*.so`, etc.) with source code. In the above example,
+"`.build`" is the name of the directory where the build configuration
+as well as all the build artifacts will be saved. This directory can
+be named anything as long as it's not an existing source directory. To
+completely "clean" all the build artifacts, one need only delete the
+`.build` directory.
+
+To compile:
+
+```
+meson compile -C .build
+```
+
+## To install libnvme
+
+To install `libnvme`:
+
+```
+meson install -C .build
+```
+
+## To run unit tests
+
+To run unit tests:
+
+```
+meson test -C .build
+```
+
+## To purge everything
+
+To completely clean all build artifacts, including the build configuration.
+
+```
+rm -rf .build
+```
+
+## Supported build options
+
+A few build options can be specified on the command line when invoking meson.
+
+| Option | Values [default] | Description |
+| ----------- | ------------------------- | ------------------------------------------------------------ |
+| version-tag | none | Overwrite the git version string in the binary |
+| htmldir | none | Installation directory for the HTML documentation |
+| rstdir | none | Installation directory for the RST documentation |
+| docs | [false], html, man, rst, all | Install documentation |
+| docs-build | [false], true | Enable build documentation |
+| python | [auto], enabled, disabled | Whether to build the Python bindings. When set to `auto`, the default, meson will check for the presence of the tools and libraries (e.g. `swig`) required to build the Python bindings. If found, meson will configure the project to build the Python bindings. If a tool or library is missing, then the Python bindings won't be built. Setting this to `enabled`, forces the Python bindings to be built. When set to `disabled`, meson will configure the project to not build the Python bindings.<br />Example: `meson setup .build -Dpython=disabled` |
+| openssl | [auto], enabled, disabled | Enables OpenSSL dependent features (e.g. TLS over TCP), adds build dependency on OpenSSL |
+| libdbus | auto, enabled, [disabled] | Enables D-Bus dependent features (libnvme-mi: End point discovery), adds build dependency on libdbus |
+| json-c | [auto], enabled, disabled | (recommended) Enables JSON-C dependend features (e.g. config.json parsing), adds build depdency on json-c |
+| keyutils | [auto], enabled, disabled | Enables keyutils dependent features (e.g. authentication), adds build dependency on keyutils |
+
+See the full configuration options with
+
+```bash
+meson configure .build
+```
+
+### Changing the build options from the command-line (i.e. w/o modifying any files)
+
+To configure a build for debugging purposes (i.e. optimization turned
+off and debug symbols enabled):
+
+```bash
+meson setup .build --buildtype=debug
+```
+
+To enable address sanitizer (advanced debugging of memory issues):
+
+```bash
+meson setup .build -Db_sanitize=address
+```
+
+This option adds `-fsanitize=address` to the gcc options. The tests can then be run normally (`meson test -C .build`).
+
+Note that when using the sanitize feature, the library `libasan.so` must be available and must be the very first library loaded when running an executable. If experiencing linking issues, you can ensure that `libasan.so` gets loaded first with the `LD_PRELOAD` environment variable as follows:
+
+```
+meson setup .build -Db_sanitize=address && LD_PRELOAD=/lib64/libasan.so.6 ninja -C .build test
+```
+
+It's also possible to enable the undefined behavior sanitizer with `-Db_sanitize=undefined`. To enable both, use `-Db_sanitize=address,undefined`.
diff --git a/ccan/ccan/array_size/LICENSE b/ccan/ccan/array_size/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ccan/array_size/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/ccan/array_size/_info b/ccan/ccan/array_size/_info
new file mode 100644
index 0000000..69570f3
--- /dev/null
+++ b/ccan/ccan/array_size/_info
@@ -0,0 +1,46 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * array_size - routine for safely deriving the size of a visible array.
+ *
+ * This provides a simple ARRAY_SIZE() macro, which (given a good compiler)
+ * will also break compile if you try to use it on a pointer.
+ *
+ * This can ensure your code is robust to changes, without needing a gratuitous
+ * macro or constant.
+ *
+ * Example:
+ * // Outputs "Initialized 32 values\n"
+ * #include <ccan/array_size/array_size.h>
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * // We currently use 32 random values.
+ * static unsigned int vals[32];
+ *
+ * int main(void)
+ * {
+ * unsigned int i;
+ * for (i = 0; i < ARRAY_SIZE(vals); i++)
+ * vals[i] = random();
+ * printf("Initialized %u values\n", i);
+ * return 0;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/build_assert\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/ccan/array_size/array_size.h b/ccan/ccan/array_size/array_size.h
new file mode 100644
index 0000000..0ca422a
--- /dev/null
+++ b/ccan/ccan/array_size/array_size.h
@@ -0,0 +1,26 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_ARRAY_SIZE_H
+#define CCAN_ARRAY_SIZE_H
+#include "config.h"
+#include <ccan/build_assert/build_assert.h>
+
+/**
+ * ARRAY_SIZE - get the number of elements in a visible array
+ * @arr: the array whose size you want.
+ *
+ * This does not work on pointers, or arrays declared as [], or
+ * function parameters. With correct compiler support, such usage
+ * will cause a build error (see build_assert).
+ */
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + _array_size_chk(arr))
+
+#if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF
+/* Two gcc extensions.
+ * &a[0] degrades to a pointer: a different type from an array */
+#define _array_size_chk(arr) \
+ BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(arr), \
+ typeof(&(arr)[0])))
+#else
+#define _array_size_chk(arr) 0
+#endif
+#endif /* CCAN_ALIGNOF_H */
diff --git a/ccan/ccan/build_assert/LICENSE b/ccan/ccan/build_assert/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ccan/build_assert/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/ccan/build_assert/_info b/ccan/ccan/build_assert/_info
new file mode 100644
index 0000000..97ebe6c
--- /dev/null
+++ b/ccan/ccan/build_assert/_info
@@ -0,0 +1,49 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * build_assert - routines for build-time assertions
+ *
+ * This code provides routines which will cause compilation to fail should some
+ * assertion be untrue: such failures are preferable to run-time assertions,
+ * but much more limited since they can only depends on compile-time constants.
+ *
+ * These assertions are most useful when two parts of the code must be kept in
+ * sync: it is better to avoid such cases if possible, but seconds best is to
+ * detect invalid changes at build time.
+ *
+ * For example, a tricky piece of code might rely on a certain element being at
+ * the start of the structure. To ensure that future changes don't break it,
+ * you would catch such changes in your code like so:
+ *
+ * Example:
+ * #include <stddef.h>
+ * #include <ccan/build_assert/build_assert.h>
+ *
+ * struct foo {
+ * char string[5];
+ * int x;
+ * };
+ *
+ * static char *foo_string(struct foo *foo)
+ * {
+ * // This trick requires that the string be first in the structure
+ * BUILD_ASSERT(offsetof(struct foo, string) == 0);
+ * return (char *)foo;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0)
+ /* Nothing. */
+ return 0;
+
+ return 1;
+}
diff --git a/ccan/ccan/build_assert/build_assert.h b/ccan/ccan/build_assert/build_assert.h
new file mode 100644
index 0000000..b9ecd84
--- /dev/null
+++ b/ccan/ccan/build_assert/build_assert.h
@@ -0,0 +1,40 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_BUILD_ASSERT_H
+#define CCAN_BUILD_ASSERT_H
+
+/**
+ * BUILD_ASSERT - assert a build-time dependency.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler. This can only be used within a function.
+ *
+ * Example:
+ * #include <stddef.h>
+ * ...
+ * static char *foo_to_char(struct foo *foo)
+ * {
+ * // This code needs string to be at start of foo.
+ * BUILD_ASSERT(offsetof(struct foo, string) == 0);
+ * return (char *)foo;
+ * }
+ */
+#define BUILD_ASSERT(cond) \
+ do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)
+
+/**
+ * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler. This can be used in an expression: its value is "0".
+ *
+ * Example:
+ * #define foo_to_char(foo) \
+ * ((char *)(foo) \
+ * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
+ */
+#define BUILD_ASSERT_OR_ZERO(cond) \
+ (sizeof(char [1 - 2*!(cond)]) - 1)
+
+#endif /* CCAN_BUILD_ASSERT_H */
diff --git a/ccan/ccan/check_type/LICENSE b/ccan/ccan/check_type/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ccan/check_type/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/ccan/check_type/_info b/ccan/ccan/check_type/_info
new file mode 100644
index 0000000..cc42673
--- /dev/null
+++ b/ccan/ccan/check_type/_info
@@ -0,0 +1,33 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * check_type - routines for compile time type checking
+ *
+ * C has fairly weak typing: ints get automatically converted to longs, signed
+ * to unsigned, etc. There are some cases where this is best avoided, and
+ * these macros provide methods for evoking warnings (or build errors) when
+ * a precise type isn't used.
+ *
+ * On compilers which don't support typeof() these routines are less effective,
+ * since they have to use sizeof() which can only distiguish between types of
+ * different size.
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+#if !HAVE_TYPEOF
+ printf("ccan/build_assert\n");
+#endif
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/ccan/check_type/check_type.h b/ccan/ccan/check_type/check_type.h
new file mode 100644
index 0000000..837aef7
--- /dev/null
+++ b/ccan/ccan/check_type/check_type.h
@@ -0,0 +1,64 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_CHECK_TYPE_H
+#define CCAN_CHECK_TYPE_H
+#include "config.h"
+
+/**
+ * check_type - issue a warning or build failure if type is not correct.
+ * @expr: the expression whose type we should check (not evaluated).
+ * @type: the exact type we expect the expression to be.
+ *
+ * This macro is usually used within other macros to try to ensure that a macro
+ * argument is of the expected type. No type promotion of the expression is
+ * done: an unsigned int is not the same as an int!
+ *
+ * check_type() always evaluates to 0.
+ *
+ * If your compiler does not support typeof, then the best we can do is fail
+ * to compile if the sizes of the types are unequal (a less complete check).
+ *
+ * Example:
+ * // They should always pass a 64-bit value to _set_some_value!
+ * #define set_some_value(expr) \
+ * _set_some_value((check_type((expr), uint64_t), (expr)))
+ */
+
+/**
+ * check_types_match - issue a warning or build failure if types are not same.
+ * @expr1: the first expression (not evaluated).
+ * @expr2: the second expression (not evaluated).
+ *
+ * This macro is usually used within other macros to try to ensure that
+ * arguments are of identical types. No type promotion of the expressions is
+ * done: an unsigned int is not the same as an int!
+ *
+ * check_types_match() always evaluates to 0.
+ *
+ * If your compiler does not support typeof, then the best we can do is fail
+ * to compile if the sizes of the types are unequal (a less complete check).
+ *
+ * Example:
+ * // Do subtraction to get to enclosing type, but make sure that
+ * // pointer is of correct type for that member.
+ * #define container_of(mbr_ptr, encl_type, mbr) \
+ * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \
+ * ((encl_type *) \
+ * ((char *)(mbr_ptr) - offsetof(encl_type, mbr))))
+ */
+#if HAVE_TYPEOF
+#define check_type(expr, type) \
+ ((typeof(expr) *)0 != (type *)0)
+
+#define check_types_match(expr1, expr2) \
+ ((typeof(expr1) *)0 != (typeof(expr2) *)0)
+#else
+#include <ccan/build_assert/build_assert.h>
+/* Without typeof, we can only test the sizes. */
+#define check_type(expr, type) \
+ BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type))
+
+#define check_types_match(expr1, expr2) \
+ BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2))
+#endif /* HAVE_TYPEOF */
+
+#endif /* CCAN_CHECK_TYPE_H */
diff --git a/ccan/ccan/container_of/LICENSE b/ccan/ccan/container_of/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ccan/container_of/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/ccan/container_of/_info b/ccan/ccan/container_of/_info
new file mode 100644
index 0000000..b116052
--- /dev/null
+++ b/ccan/ccan/container_of/_info
@@ -0,0 +1,65 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * container_of - routine for upcasting
+ *
+ * It is often convenient to create code where the caller registers a pointer
+ * to a generic structure and a callback. The callback might know that the
+ * pointer points to within a larger structure, and container_of gives a
+ * convenient and fairly type-safe way of returning to the enclosing structure.
+ *
+ * This idiom is an alternative to providing a void * pointer for every
+ * callback.
+ *
+ * Example:
+ * #include <stdio.h>
+ * #include <ccan/container_of/container_of.h>
+ *
+ * struct timer {
+ * void *members;
+ * };
+ *
+ * struct info {
+ * int my_stuff;
+ * struct timer timer;
+ * };
+ *
+ * static void my_timer_callback(struct timer *timer)
+ * {
+ * struct info *info = container_of(timer, struct info, timer);
+ * printf("my_stuff is %u\n", info->my_stuff);
+ * }
+ *
+ * static void register_timer(struct timer *timer)
+ * {
+ * (void)timer;
+ * (void)my_timer_callback;
+ * //...
+ * }
+ *
+ * int main(void)
+ * {
+ * struct info info = { .my_stuff = 1 };
+ *
+ * register_timer(&info.timer);
+ * // ...
+ * return 0;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/check_type\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/ccan/container_of/container_of.h b/ccan/ccan/container_of/container_of.h
new file mode 100644
index 0000000..47a34d8
--- /dev/null
+++ b/ccan/ccan/container_of/container_of.h
@@ -0,0 +1,145 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_CONTAINER_OF_H
+#define CCAN_CONTAINER_OF_H
+#include <stddef.h>
+
+#include "config.h"
+#include <ccan/check_type/check_type.h>
+
+/**
+ * container_of - get pointer to enclosing structure
+ * @member_ptr: pointer to the structure member
+ * @containing_type: the type this member is within
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does pointer
+ * subtraction to return the pointer to the enclosing type.
+ *
+ * Example:
+ * struct foo {
+ * int fielda, fieldb;
+ * // ...
+ * };
+ * struct info {
+ * int some_other_field;
+ * struct foo my_foo;
+ * };
+ *
+ * static struct info *foo_to_info(struct foo *foo)
+ * {
+ * return container_of(foo, struct info, my_foo);
+ * }
+ */
+#define container_of(member_ptr, containing_type, member) \
+ ((containing_type *) \
+ ((char *)(member_ptr) \
+ - container_off(containing_type, member)) \
+ + check_types_match(*(member_ptr), ((containing_type *)0)->member))
+
+
+/**
+ * container_of_or_null - get pointer to enclosing structure, or NULL
+ * @member_ptr: pointer to the structure member
+ * @containing_type: the type this member is within
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does pointer
+ * subtraction to return the pointer to the enclosing type, unless it
+ * is given NULL, in which case it also returns NULL.
+ *
+ * Example:
+ * struct foo {
+ * int fielda, fieldb;
+ * // ...
+ * };
+ * struct info {
+ * int some_other_field;
+ * struct foo my_foo;
+ * };
+ *
+ * static struct info *foo_to_info_allowing_null(struct foo *foo)
+ * {
+ * return container_of_or_null(foo, struct info, my_foo);
+ * }
+ */
+static inline char *container_of_or_null_(void *member_ptr, size_t offset)
+{
+ return member_ptr ? (char *)member_ptr - offset : NULL;
+}
+#define container_of_or_null(member_ptr, containing_type, member) \
+ ((containing_type *) \
+ container_of_or_null_(member_ptr, \
+ container_off(containing_type, member)) \
+ + check_types_match(*(member_ptr), ((containing_type *)0)->member))
+
+/**
+ * container_off - get offset to enclosing structure
+ * @containing_type: the type this member is within
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does
+ * typechecking and figures out the offset to the enclosing type.
+ *
+ * Example:
+ * struct foo {
+ * int fielda, fieldb;
+ * // ...
+ * };
+ * struct info {
+ * int some_other_field;
+ * struct foo my_foo;
+ * };
+ *
+ * static struct info *foo_to_info(struct foo *foo)
+ * {
+ * size_t off = container_off(struct info, my_foo);
+ * return (void *)((char *)foo - off);
+ * }
+ */
+#define container_off(containing_type, member) \
+ offsetof(containing_type, member)
+
+/**
+ * container_of_var - get pointer to enclosing structure using a variable
+ * @member_ptr: pointer to the structure member
+ * @container_var: a pointer of same type as this member's container
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does pointer
+ * subtraction to return the pointer to the enclosing type.
+ *
+ * Example:
+ * static struct info *foo_to_i(struct foo *foo)
+ * {
+ * struct info *i = container_of_var(foo, i, my_foo);
+ * return i;
+ * }
+ */
+#if HAVE_TYPEOF
+#define container_of_var(member_ptr, container_var, member) \
+ container_of(member_ptr, typeof(*container_var), member)
+#else
+#define container_of_var(member_ptr, container_var, member) \
+ ((void *)((char *)(member_ptr) - \
+ container_off_var(container_var, member)))
+#endif
+
+/**
+ * container_off_var - get offset of a field in enclosing structure
+ * @container_var: a pointer to a container structure
+ * @member: the name of a member within the structure.
+ *
+ * Given (any) pointer to a structure and a its member name, this
+ * macro does pointer subtraction to return offset of member in a
+ * structure memory layout.
+ *
+ */
+#if HAVE_TYPEOF
+#define container_off_var(var, member) \
+ container_off(typeof(*var), member)
+#else
+#define container_off_var(var, member) \
+ ((const char *)&(var)->member - (const char *)(var))
+#endif
+
+#endif /* CCAN_CONTAINER_OF_H */
diff --git a/ccan/ccan/endian/LICENSE b/ccan/ccan/endian/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ccan/endian/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/ccan/endian/_info b/ccan/ccan/endian/_info
new file mode 100644
index 0000000..efe5a8b
--- /dev/null
+++ b/ccan/ccan/endian/_info
@@ -0,0 +1,55 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * endian - endian conversion macros for simple types
+ *
+ * Portable protocols (such as on-disk formats, or network protocols)
+ * are often defined to be a particular endian: little-endian (least
+ * significant bytes first) or big-endian (most significant bytes
+ * first).
+ *
+ * Similarly, some CPUs lay out values in memory in little-endian
+ * order (most commonly, Intel's 8086 and derivatives), or big-endian
+ * order (almost everyone else).
+ *
+ * This module provides conversion routines, inspired by the linux kernel.
+ * It also provides leint32_t, beint32_t etc typedefs, which are annotated for
+ * the sparse checker.
+ *
+ * Example:
+ * #include <stdio.h>
+ * #include <err.h>
+ * #include <ccan/endian/endian.h>
+ *
+ * //
+ * int main(int argc, char *argv[])
+ * {
+ * uint32_t value;
+ *
+ * if (argc != 2)
+ * errx(1, "Usage: %s <value>", argv[0]);
+ *
+ * value = atoi(argv[1]);
+ * printf("native: %08x\n", value);
+ * printf("little-endian: %08x\n", cpu_to_le32(value));
+ * printf("big-endian: %08x\n", cpu_to_be32(value));
+ * printf("byte-reversed: %08x\n", bswap_32(value));
+ * exit(0);
+ * }
+ *
+ * License: License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0)
+ /* Nothing */
+ return 0;
+
+ return 1;
+}
diff --git a/ccan/ccan/endian/endian.h b/ccan/ccan/endian/endian.h
new file mode 100644
index 0000000..3753f49
--- /dev/null
+++ b/ccan/ccan/endian/endian.h
@@ -0,0 +1,363 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_ENDIAN_H
+#define CCAN_ENDIAN_H
+#include <stdint.h>
+#include "config.h"
+
+/**
+ * BSWAP_16 - reverse bytes in a constant uint16_t value.
+ * @val: constant value whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_16(0x1234)];
+ * };
+ */
+#define BSWAP_16(val) \
+ ((((uint16_t)(val) & 0x00ff) << 8) \
+ | (((uint16_t)(val) & 0xff00) >> 8))
+
+/**
+ * BSWAP_32 - reverse bytes in a constant uint32_t value.
+ * @val: constant value whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_32(0xff000000)];
+ * };
+ */
+#define BSWAP_32(val) \
+ ((((uint32_t)(val) & 0x000000ff) << 24) \
+ | (((uint32_t)(val) & 0x0000ff00) << 8) \
+ | (((uint32_t)(val) & 0x00ff0000) >> 8) \
+ | (((uint32_t)(val) & 0xff000000) >> 24))
+
+/**
+ * BSWAP_64 - reverse bytes in a constant uint64_t value.
+ * @val: constantvalue whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_64(0xff00000000000000ULL)];
+ * };
+ */
+#define BSWAP_64(val) \
+ ((((uint64_t)(val) & 0x00000000000000ffULL) << 56) \
+ | (((uint64_t)(val) & 0x000000000000ff00ULL) << 40) \
+ | (((uint64_t)(val) & 0x0000000000ff0000ULL) << 24) \
+ | (((uint64_t)(val) & 0x00000000ff000000ULL) << 8) \
+ | (((uint64_t)(val) & 0x000000ff00000000ULL) >> 8) \
+ | (((uint64_t)(val) & 0x0000ff0000000000ULL) >> 24) \
+ | (((uint64_t)(val) & 0x00ff000000000000ULL) >> 40) \
+ | (((uint64_t)(val) & 0xff00000000000000ULL) >> 56))
+
+#if HAVE_BYTESWAP_H
+#include <byteswap.h>
+#else
+/**
+ * bswap_16 - reverse bytes in a uint16_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 4 as two bytes reversed"
+ * printf("1024 is %u as two bytes reversed\n", bswap_16(1024));
+ */
+static inline uint16_t bswap_16(uint16_t val)
+{
+ return BSWAP_16(val);
+}
+
+/**
+ * bswap_32 - reverse bytes in a uint32_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 262144 as four bytes reversed"
+ * printf("1024 is %u as four bytes reversed\n", bswap_32(1024));
+ */
+static inline uint32_t bswap_32(uint32_t val)
+{
+ return BSWAP_32(val);
+}
+#endif /* !HAVE_BYTESWAP_H */
+
+#if !HAVE_BSWAP_64
+/**
+ * bswap_64 - reverse bytes in a uint64_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 1125899906842624 as eight bytes reversed"
+ * printf("1024 is %llu as eight bytes reversed\n",
+ * (unsigned long long)bswap_64(1024));
+ */
+static inline uint64_t bswap_64(uint64_t val)
+{
+ return BSWAP_64(val);
+}
+#endif
+
+/* Needed for Glibc like endiness check */
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+
+/* Sanity check the defines. We don't handle weird endianness. */
+#if !HAVE_LITTLE_ENDIAN && !HAVE_BIG_ENDIAN
+#error "Unknown endian"
+#elif HAVE_LITTLE_ENDIAN && HAVE_BIG_ENDIAN
+#error "Can't compile for both big and little endian."
+#elif HAVE_LITTLE_ENDIAN
+#ifndef __BYTE_ORDER
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#elif __BYTE_ORDER != __LITTLE_ENDIAN
+#error "__BYTE_ORDER already defined, but not equal to __LITTLE_ENDIAN"
+#endif
+#elif HAVE_BIG_ENDIAN
+#ifndef __BYTE_ORDER
+#define __BYTE_ORDER __BIG_ENDIAN
+#elif __BYTE_ORDER != __BIG_ENDIAN
+#error "__BYTE_ORDER already defined, but not equal to __BIG_ENDIAN"
+#endif
+#endif
+
+
+#ifdef __CHECKER__
+/* sparse needs forcing to remove bitwise attribute from ccan/short_types */
+#define ENDIAN_CAST __attribute__((force))
+#define ENDIAN_TYPE __attribute__((bitwise))
+#else
+#define ENDIAN_CAST
+#define ENDIAN_TYPE
+#endif
+
+typedef uint64_t ENDIAN_TYPE leint64_t;
+typedef uint64_t ENDIAN_TYPE beint64_t;
+typedef uint32_t ENDIAN_TYPE leint32_t;
+typedef uint32_t ENDIAN_TYPE beint32_t;
+typedef uint16_t ENDIAN_TYPE leint16_t;
+typedef uint16_t ENDIAN_TYPE beint16_t;
+
+#if HAVE_LITTLE_ENDIAN
+/**
+ * CPU_TO_LE64 - convert a constant uint64_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)(native))
+
+/**
+ * CPU_TO_LE32 - convert a constant uint32_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)(native))
+
+/**
+ * CPU_TO_LE16 - convert a constant uint16_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)(native))
+
+/**
+ * LE64_TO_CPU - convert a little-endian uint64_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val))
+
+/**
+ * LE32_TO_CPU - convert a little-endian uint32_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val))
+
+/**
+ * LE16_TO_CPU - convert a little-endian uint16_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val))
+
+#else /* ... HAVE_BIG_ENDIAN */
+#define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)BSWAP_64(native))
+#define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)BSWAP_32(native))
+#define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)BSWAP_16(native))
+#define LE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val)
+#define LE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val)
+#define LE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val)
+#endif /* HAVE_BIG_ENDIAN */
+
+#if HAVE_BIG_ENDIAN
+/**
+ * CPU_TO_BE64 - convert a constant uint64_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)(native))
+
+/**
+ * CPU_TO_BE32 - convert a constant uint32_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)(native))
+
+/**
+ * CPU_TO_BE16 - convert a constant uint16_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)(native))
+
+/**
+ * BE64_TO_CPU - convert a big-endian uint64_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val))
+
+/**
+ * BE32_TO_CPU - convert a big-endian uint32_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val))
+
+/**
+ * BE16_TO_CPU - convert a big-endian uint16_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val))
+
+#else /* ... HAVE_LITTLE_ENDIAN */
+#define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)BSWAP_64(native))
+#define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)BSWAP_32(native))
+#define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)BSWAP_16(native))
+#define BE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val)
+#define BE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val)
+#define BE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val)
+#endif /* HAVE_LITTE_ENDIAN */
+
+
+/**
+ * cpu_to_le64 - convert a uint64_t value to little-endian
+ * @native: value to convert
+ */
+static inline leint64_t cpu_to_le64(uint64_t native)
+{
+ return CPU_TO_LE64(native);
+}
+
+/**
+ * cpu_to_le32 - convert a uint32_t value to little-endian
+ * @native: value to convert
+ */
+static inline leint32_t cpu_to_le32(uint32_t native)
+{
+ return CPU_TO_LE32(native);
+}
+
+/**
+ * cpu_to_le16 - convert a uint16_t value to little-endian
+ * @native: value to convert
+ */
+static inline leint16_t cpu_to_le16(uint16_t native)
+{
+ return CPU_TO_LE16(native);
+}
+
+/**
+ * le64_to_cpu - convert a little-endian uint64_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint64_t le64_to_cpu(leint64_t le_val)
+{
+ return LE64_TO_CPU(le_val);
+}
+
+/**
+ * le32_to_cpu - convert a little-endian uint32_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint32_t le32_to_cpu(leint32_t le_val)
+{
+ return LE32_TO_CPU(le_val);
+}
+
+/**
+ * le16_to_cpu - convert a little-endian uint16_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint16_t le16_to_cpu(leint16_t le_val)
+{
+ return LE16_TO_CPU(le_val);
+}
+
+/**
+ * cpu_to_be64 - convert a uint64_t value to big endian.
+ * @native: value to convert
+ */
+static inline beint64_t cpu_to_be64(uint64_t native)
+{
+ return CPU_TO_BE64(native);
+}
+
+/**
+ * cpu_to_be32 - convert a uint32_t value to big endian.
+ * @native: value to convert
+ */
+static inline beint32_t cpu_to_be32(uint32_t native)
+{
+ return CPU_TO_BE32(native);
+}
+
+/**
+ * cpu_to_be16 - convert a uint16_t value to big endian.
+ * @native: value to convert
+ */
+static inline beint16_t cpu_to_be16(uint16_t native)
+{
+ return CPU_TO_BE16(native);
+}
+
+/**
+ * be64_to_cpu - convert a big-endian uint64_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint64_t be64_to_cpu(beint64_t be_val)
+{
+ return BE64_TO_CPU(be_val);
+}
+
+/**
+ * be32_to_cpu - convert a big-endian uint32_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint32_t be32_to_cpu(beint32_t be_val)
+{
+ return BE32_TO_CPU(be_val);
+}
+
+/**
+ * be16_to_cpu - convert a big-endian uint16_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint16_t be16_to_cpu(beint16_t be_val)
+{
+ return BE16_TO_CPU(be_val);
+}
+
+/* Whichever they include first, they get these definitions. */
+#ifdef CCAN_SHORT_TYPES_H
+/**
+ * be64/be32/be16 - 64/32/16 bit big-endian representation.
+ */
+typedef beint64_t be64;
+typedef beint32_t be32;
+typedef beint16_t be16;
+
+/**
+ * le64/le32/le16 - 64/32/16 bit little-endian representation.
+ */
+typedef leint64_t le64;
+typedef leint32_t le32;
+typedef leint16_t le16;
+#endif
+#endif /* CCAN_ENDIAN_H */
diff --git a/ccan/ccan/list/LICENSE b/ccan/ccan/list/LICENSE
new file mode 120000
index 0000000..2354d12
--- /dev/null
+++ b/ccan/ccan/list/LICENSE
@@ -0,0 +1 @@
+../../licenses/BSD-MIT \ No newline at end of file
diff --git a/ccan/ccan/list/_info b/ccan/ccan/list/_info
new file mode 100644
index 0000000..c4f3e2a
--- /dev/null
+++ b/ccan/ccan/list/_info
@@ -0,0 +1,72 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * list - double linked list routines
+ *
+ * The list header contains routines for manipulating double linked lists.
+ * It defines two types: struct list_head used for anchoring lists, and
+ * struct list_node which is usually embedded in the structure which is placed
+ * in the list.
+ *
+ * Example:
+ * #include <err.h>
+ * #include <stdio.h>
+ * #include <stdlib.h>
+ * #include <ccan/list/list.h>
+ *
+ * struct parent {
+ * const char *name;
+ * struct list_head children;
+ * unsigned int num_children;
+ * };
+ *
+ * struct child {
+ * const char *name;
+ * struct list_node list;
+ * };
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * struct parent p;
+ * struct child *c;
+ * int i;
+ *
+ * if (argc < 2)
+ * errx(1, "Usage: %s parent children...", argv[0]);
+ *
+ * p.name = argv[1];
+ * list_head_init(&p.children);
+ * p.num_children = 0;
+ * for (i = 2; i < argc; i++) {
+ * c = malloc(sizeof(*c));
+ * c->name = argv[i];
+ * list_add(&p.children, &c->list);
+ * p.num_children++;
+ * }
+ *
+ * printf("%s has %u children:", p.name, p.num_children);
+ * list_for_each(&p.children, c, list)
+ * printf("%s ", c->name);
+ * printf("\n");
+ * return 0;
+ * }
+ *
+ * License: BSD-MIT
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/str\n");
+ printf("ccan/container_of\n");
+ printf("ccan/check_type\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/ccan/list/list.c b/ccan/ccan/list/list.c
new file mode 100644
index 0000000..2717fa3
--- /dev/null
+++ b/ccan/ccan/list/list.c
@@ -0,0 +1,43 @@
+/* Licensed under BSD-MIT - see LICENSE file for details */
+#include <stdio.h>
+#include <stdlib.h>
+#include "list.h"
+
+static void *corrupt(const char *abortstr,
+ const struct list_node *head,
+ const struct list_node *node,
+ unsigned int count)
+{
+ if (abortstr) {
+ fprintf(stderr,
+ "%s: prev corrupt in node %p (%u) of %p\n",
+ abortstr, node, count, head);
+ abort();
+ }
+ return NULL;
+}
+
+struct list_node *list_check_node(const struct list_node *node,
+ const char *abortstr)
+{
+ const struct list_node *p, *n;
+ int count = 0;
+
+ for (p = node, n = node->next; n != node; p = n, n = n->next) {
+ count++;
+ if (n->prev != p)
+ return corrupt(abortstr, node, n, count);
+ }
+ /* Check prev on head node. */
+ if (node->prev != p)
+ return corrupt(abortstr, node, node, 0);
+
+ return (struct list_node *)node;
+}
+
+struct list_head *list_check(const struct list_head *h, const char *abortstr)
+{
+ if (!list_check_node(&h->n, abortstr))
+ return NULL;
+ return (struct list_head *)h;
+}
diff --git a/ccan/ccan/list/list.h b/ccan/ccan/list/list.h
new file mode 100644
index 0000000..a15321c
--- /dev/null
+++ b/ccan/ccan/list/list.h
@@ -0,0 +1,842 @@
+/* Licensed under BSD-MIT - see LICENSE file for details */
+#ifndef CCAN_LIST_H
+#define CCAN_LIST_H
+//#define CCAN_LIST_DEBUG 1
+#include <stdbool.h>
+#include <assert.h>
+#include <ccan/str/str.h>
+#include <ccan/container_of/container_of.h>
+#include <ccan/check_type/check_type.h>
+
+/**
+ * struct list_node - an entry in a doubly-linked list
+ * @next: next entry (self if empty)
+ * @prev: previous entry (self if empty)
+ *
+ * This is used as an entry in a linked list.
+ * Example:
+ * struct child {
+ * const char *name;
+ * // Linked list of all us children.
+ * struct list_node list;
+ * };
+ */
+struct list_node
+{
+ struct list_node *next, *prev;
+};
+
+/**
+ * struct list_head - the head of a doubly-linked list
+ * @h: the list_head (containing next and prev pointers)
+ *
+ * This is used as the head of a linked list.
+ * Example:
+ * struct parent {
+ * const char *name;
+ * struct list_head children;
+ * unsigned int num_children;
+ * };
+ */
+struct list_head
+{
+ struct list_node n;
+};
+
+/**
+ * list_check - check head of a list for consistency
+ * @h: the list_head
+ * @abortstr: the location to print on aborting, or NULL.
+ *
+ * Because list_nodes have redundant information, consistency checking between
+ * the back and forward links can be done. This is useful as a debugging check.
+ * If @abortstr is non-NULL, that will be printed in a diagnostic if the list
+ * is inconsistent, and the function will abort.
+ *
+ * Returns the list head if the list is consistent, NULL if not (it
+ * can never return NULL if @abortstr is set).
+ *
+ * See also: list_check_node()
+ *
+ * Example:
+ * static void dump_parent(struct parent *p)
+ * {
+ * struct child *c;
+ *
+ * printf("%s (%u children):\n", p->name, p->num_children);
+ * list_check(&p->children, "bad child list");
+ * list_for_each(&p->children, c, list)
+ * printf(" -> %s\n", c->name);
+ * }
+ */
+struct list_head *list_check(const struct list_head *h, const char *abortstr);
+
+/**
+ * list_check_node - check node of a list for consistency
+ * @n: the list_node
+ * @abortstr: the location to print on aborting, or NULL.
+ *
+ * Check consistency of the list node is in (it must be in one).
+ *
+ * See also: list_check()
+ *
+ * Example:
+ * static void dump_child(const struct child *c)
+ * {
+ * list_check_node(&c->list, "bad child list");
+ * printf("%s\n", c->name);
+ * }
+ */
+struct list_node *list_check_node(const struct list_node *n,
+ const char *abortstr);
+
+#define LIST_LOC __FILE__ ":" stringify(__LINE__)
+#ifdef CCAN_LIST_DEBUG
+#define list_debug(h, loc) list_check((h), loc)
+#define list_debug_node(n, loc) list_check_node((n), loc)
+#else
+#define list_debug(h, loc) ((void)loc, h)
+#define list_debug_node(n, loc) ((void)loc, n)
+#endif
+
+/**
+ * LIST_HEAD_INIT - initializer for an empty list_head
+ * @name: the name of the list.
+ *
+ * Explicit initializer for an empty list.
+ *
+ * See also:
+ * LIST_HEAD, list_head_init()
+ *
+ * Example:
+ * static struct list_head my_list = LIST_HEAD_INIT(my_list);
+ */
+#define LIST_HEAD_INIT(name) { { &(name).n, &(name).n } }
+
+/**
+ * LIST_HEAD - define and initialize an empty list_head
+ * @name: the name of the list.
+ *
+ * The LIST_HEAD macro defines a list_head and initializes it to an empty
+ * list. It can be prepended by "static" to define a static list_head.
+ *
+ * See also:
+ * LIST_HEAD_INIT, list_head_init()
+ *
+ * Example:
+ * static LIST_HEAD(my_global_list);
+ */
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+/**
+ * list_head_init - initialize a list_head
+ * @h: the list_head to set to the empty list
+ *
+ * Example:
+ * ...
+ * struct parent *parent = malloc(sizeof(*parent));
+ *
+ * list_head_init(&parent->children);
+ * parent->num_children = 0;
+ */
+static inline void list_head_init(struct list_head *h)
+{
+ h->n.next = h->n.prev = &h->n;
+}
+
+/**
+ * list_node_init - initialize a list_node
+ * @n: the list_node to link to itself.
+ *
+ * You don't need to use this normally! But it lets you list_del(@n)
+ * safely.
+ */
+static inline void list_node_init(struct list_node *n)
+{
+ n->next = n->prev = n;
+}
+
+/**
+ * list_add_after - add an entry after an existing node in a linked list
+ * @h: the list_head to add the node to (for debugging)
+ * @p: the existing list_node to add the node after
+ * @n: the new list_node to add to the list.
+ *
+ * The existing list_node must already be a member of the list.
+ * The new list_node does not need to be initialized; it will be overwritten.
+ *
+ * Example:
+ * struct child c1, c2, c3;
+ * LIST_HEAD(h);
+ *
+ * list_add_tail(&h, &c1.list);
+ * list_add_tail(&h, &c3.list);
+ * list_add_after(&h, &c1.list, &c2.list);
+ */
+#define list_add_after(h, p, n) list_add_after_(h, p, n, LIST_LOC)
+static inline void list_add_after_(struct list_head *h,
+ struct list_node *p,
+ struct list_node *n,
+ const char *abortstr)
+{
+ n->next = p->next;
+ n->prev = p;
+ p->next->prev = n;
+ p->next = n;
+ (void)list_debug(h, abortstr);
+}
+
+/**
+ * list_add - add an entry at the start of a linked list.
+ * @h: the list_head to add the node to
+ * @n: the list_node to add to the list.
+ *
+ * The list_node does not need to be initialized; it will be overwritten.
+ * Example:
+ * struct child *child = malloc(sizeof(*child));
+ *
+ * child->name = "marvin";
+ * list_add(&parent->children, &child->list);
+ * parent->num_children++;
+ */
+#define list_add(h, n) list_add_(h, n, LIST_LOC)
+static inline void list_add_(struct list_head *h,
+ struct list_node *n,
+ const char *abortstr)
+{
+ list_add_after_(h, &h->n, n, abortstr);
+}
+
+/**
+ * list_add_before - add an entry before an existing node in a linked list
+ * @h: the list_head to add the node to (for debugging)
+ * @p: the existing list_node to add the node before
+ * @n: the new list_node to add to the list.
+ *
+ * The existing list_node must already be a member of the list.
+ * The new list_node does not need to be initialized; it will be overwritten.
+ *
+ * Example:
+ * list_head_init(&h);
+ * list_add_tail(&h, &c1.list);
+ * list_add_tail(&h, &c3.list);
+ * list_add_before(&h, &c3.list, &c2.list);
+ */
+#define list_add_before(h, p, n) list_add_before_(h, p, n, LIST_LOC)
+static inline void list_add_before_(struct list_head *h,
+ struct list_node *p,
+ struct list_node *n,
+ const char *abortstr)
+{
+ n->next = p;
+ n->prev = p->prev;
+ p->prev->next = n;
+ p->prev = n;
+ (void)list_debug(h, abortstr);
+}
+
+/**
+ * list_add_tail - add an entry at the end of a linked list.
+ * @h: the list_head to add the node to
+ * @n: the list_node to add to the list.
+ *
+ * The list_node does not need to be initialized; it will be overwritten.
+ * Example:
+ * list_add_tail(&parent->children, &child->list);
+ * parent->num_children++;
+ */
+#define list_add_tail(h, n) list_add_tail_(h, n, LIST_LOC)
+static inline void list_add_tail_(struct list_head *h,
+ struct list_node *n,
+ const char *abortstr)
+{
+ list_add_before_(h, &h->n, n, abortstr);
+}
+
+/**
+ * list_empty - is a list empty?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true.
+ *
+ * Example:
+ * assert(list_empty(&parent->children) == (parent->num_children == 0));
+ */
+#define list_empty(h) list_empty_(h, LIST_LOC)
+static inline bool list_empty_(const struct list_head *h, const char* abortstr)
+{
+ (void)list_debug(h, abortstr);
+ return h->n.next == &h->n;
+}
+
+/**
+ * list_empty_nodebug - is a list empty (and don't perform debug checks)?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true.
+ * This differs from list_empty() in that if CCAN_LIST_DEBUG is set it
+ * will NOT perform debug checks. Only use this function if you REALLY
+ * know what you're doing.
+ *
+ * Example:
+ * assert(list_empty_nodebug(&parent->children) == (parent->num_children == 0));
+ */
+#ifndef CCAN_LIST_DEBUG
+#define list_empty_nodebug(h) list_empty(h)
+#else
+static inline bool list_empty_nodebug(const struct list_head *h)
+{
+ return h->n.next == &h->n;
+}
+#endif
+
+/**
+ * list_empty_nocheck - is a list empty?
+ * @h: the list_head
+ *
+ * If the list is empty, returns true. This doesn't perform any
+ * debug check for list consistency, so it can be called without
+ * locks, racing with the list being modified. This is ok for
+ * checks where an incorrect result is not an issue (optimized
+ * bail out path for example).
+ */
+static inline bool list_empty_nocheck(const struct list_head *h)
+{
+ return h->n.next == &h->n;
+}
+
+/**
+ * list_del - delete an entry from an (unknown) linked list.
+ * @n: the list_node to delete from the list.
+ *
+ * Note that this leaves @n in an undefined state; it can be added to
+ * another list, but not deleted again.
+ *
+ * See also:
+ * list_del_from(), list_del_init()
+ *
+ * Example:
+ * list_del(&child->list);
+ * parent->num_children--;
+ */
+#define list_del(n) list_del_(n, LIST_LOC)
+static inline void list_del_(struct list_node *n, const char* abortstr)
+{
+ (void)list_debug_node(n, abortstr);
+ n->next->prev = n->prev;
+ n->prev->next = n->next;
+#ifdef CCAN_LIST_DEBUG
+ /* Catch use-after-del. */
+ n->next = n->prev = NULL;
+#endif
+}
+
+/**
+ * list_del_init - delete a node, and reset it so it can be deleted again.
+ * @n: the list_node to be deleted.
+ *
+ * list_del(@n) or list_del_init() again after this will be safe,
+ * which can be useful in some cases.
+ *
+ * See also:
+ * list_del_from(), list_del()
+ *
+ * Example:
+ * list_del_init(&child->list);
+ * parent->num_children--;
+ */
+#define list_del_init(n) list_del_init_(n, LIST_LOC)
+static inline void list_del_init_(struct list_node *n, const char *abortstr)
+{
+ list_del_(n, abortstr);
+ list_node_init(n);
+}
+
+/**
+ * list_del_from - delete an entry from a known linked list.
+ * @h: the list_head the node is in.
+ * @n: the list_node to delete from the list.
+ *
+ * This explicitly indicates which list a node is expected to be in,
+ * which is better documentation and can catch more bugs.
+ *
+ * See also: list_del()
+ *
+ * Example:
+ * list_del_from(&parent->children, &child->list);
+ * parent->num_children--;
+ */
+static inline void list_del_from(struct list_head *h, struct list_node *n)
+{
+#ifdef CCAN_LIST_DEBUG
+ {
+ /* Thorough check: make sure it was in list! */
+ struct list_node *i;
+ for (i = h->n.next; i != n; i = i->next)
+ assert(i != &h->n);
+ }
+#endif /* CCAN_LIST_DEBUG */
+
+ /* Quick test that catches a surprising number of bugs. */
+ assert(!list_empty(h));
+ list_del(n);
+}
+
+/**
+ * list_swap - swap out an entry from an (unknown) linked list for a new one.
+ * @o: the list_node to replace from the list.
+ * @n: the list_node to insert in place of the old one.
+ *
+ * Note that this leaves @o in an undefined state; it can be added to
+ * another list, but not deleted/swapped again.
+ *
+ * See also:
+ * list_del()
+ *
+ * Example:
+ * struct child x1, x2;
+ * LIST_HEAD(xh);
+ *
+ * list_add(&xh, &x1.list);
+ * list_swap(&x1.list, &x2.list);
+ */
+#define list_swap(o, n) list_swap_(o, n, LIST_LOC)
+static inline void list_swap_(struct list_node *o,
+ struct list_node *n,
+ const char* abortstr)
+{
+ (void)list_debug_node(o, abortstr);
+ *n = *o;
+ n->next->prev = n;
+ n->prev->next = n;
+#ifdef CCAN_LIST_DEBUG
+ /* Catch use-after-del. */
+ o->next = o->prev = NULL;
+#endif
+}
+
+/**
+ * list_entry - convert a list_node back into the structure containing it.
+ * @n: the list_node
+ * @type: the type of the entry
+ * @member: the list_node member of the type
+ *
+ * Example:
+ * // First list entry is children.next; convert back to child.
+ * child = list_entry(parent->children.n.next, struct child, list);
+ *
+ * See Also:
+ * list_top(), list_for_each()
+ */
+#define list_entry(n, type, member) container_of(n, type, member)
+
+/**
+ * list_top - get the first entry in a list
+ * @h: the list_head
+ * @type: the type of the entry
+ * @member: the list_node member of the type
+ *
+ * If the list is empty, returns NULL.
+ *
+ * Example:
+ * struct child *first;
+ * first = list_top(&parent->children, struct child, list);
+ * if (!first)
+ * printf("Empty list!\n");
+ */
+#define list_top(h, type, member) \
+ ((type *)list_top_((h), list_off_(type, member)))
+
+static inline const void *list_top_(const struct list_head *h, size_t off)
+{
+ if (list_empty(h))
+ return NULL;
+ return (const char *)h->n.next - off;
+}
+
+/**
+ * list_pop - remove the first entry in a list
+ * @h: the list_head
+ * @type: the type of the entry
+ * @member: the list_node member of the type
+ *
+ * If the list is empty, returns NULL.
+ *
+ * Example:
+ * struct child *one;
+ * one = list_pop(&parent->children, struct child, list);
+ * if (!one)
+ * printf("Empty list!\n");
+ */
+#define list_pop(h, type, member) \
+ ((type *)list_pop_((h), list_off_(type, member)))
+
+static inline const void *list_pop_(const struct list_head *h, size_t off)
+{
+ struct list_node *n;
+
+ if (list_empty(h))
+ return NULL;
+ n = h->n.next;
+ list_del(n);
+ return (const char *)n - off;
+}
+
+/**
+ * list_tail - get the last entry in a list
+ * @h: the list_head
+ * @type: the type of the entry
+ * @member: the list_node member of the type
+ *
+ * If the list is empty, returns NULL.
+ *
+ * Example:
+ * struct child *last;
+ * last = list_tail(&parent->children, struct child, list);
+ * if (!last)
+ * printf("Empty list!\n");
+ */
+#define list_tail(h, type, member) \
+ ((type *)list_tail_((h), list_off_(type, member)))
+
+static inline const void *list_tail_(const struct list_head *h, size_t off)
+{
+ if (list_empty(h))
+ return NULL;
+ return (const char *)h->n.prev - off;
+}
+
+/**
+ * list_for_each - iterate through a list.
+ * @h: the list_head (warning: evaluated multiple times!)
+ * @i: the structure containing the list_node
+ * @member: the list_node member of the structure
+ *
+ * This is a convenient wrapper to iterate @i over the entire list. It's
+ * a for loop, so you can break and continue as normal.
+ *
+ * Example:
+ * list_for_each(&parent->children, child, list)
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each(h, i, member) \
+ list_for_each_off(h, i, list_off_var_(i, member))
+
+/**
+ * list_for_each_rev - iterate through a list backwards.
+ * @h: the list_head
+ * @i: the structure containing the list_node
+ * @member: the list_node member of the structure
+ *
+ * This is a convenient wrapper to iterate @i over the entire list. It's
+ * a for loop, so you can break and continue as normal.
+ *
+ * Example:
+ * list_for_each_rev(&parent->children, child, list)
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_rev(h, i, member) \
+ list_for_each_rev_off(h, i, list_off_var_(i, member))
+
+/**
+ * list_for_each_rev_safe - iterate through a list backwards,
+ * maybe during deletion
+ * @h: the list_head
+ * @i: the structure containing the list_node
+ * @nxt: the structure containing the list_node
+ * @member: the list_node member of the structure
+ *
+ * This is a convenient wrapper to iterate @i over the entire list backwards.
+ * It's a for loop, so you can break and continue as normal. The extra
+ * variable * @nxt is used to hold the next element, so you can delete @i
+ * from the list.
+ *
+ * Example:
+ * struct child *next;
+ * list_for_each_rev_safe(&parent->children, child, next, list) {
+ * printf("Name: %s\n", child->name);
+ * }
+ */
+#define list_for_each_rev_safe(h, i, nxt, member) \
+ list_for_each_rev_safe_off(h, i, nxt, list_off_var_(i, member))
+
+/**
+ * list_for_each_safe - iterate through a list, maybe during deletion
+ * @h: the list_head
+ * @i: the structure containing the list_node
+ * @nxt: the structure containing the list_node
+ * @member: the list_node member of the structure
+ *
+ * This is a convenient wrapper to iterate @i over the entire list. It's
+ * a for loop, so you can break and continue as normal. The extra variable
+ * @nxt is used to hold the next element, so you can delete @i from the list.
+ *
+ * Example:
+ * list_for_each_safe(&parent->children, child, next, list) {
+ * list_del(&child->list);
+ * parent->num_children--;
+ * }
+ */
+#define list_for_each_safe(h, i, nxt, member) \
+ list_for_each_safe_off(h, i, nxt, list_off_var_(i, member))
+
+/**
+ * list_next - get the next entry in a list
+ * @h: the list_head
+ * @i: a pointer to an entry in the list.
+ * @member: the list_node member of the structure
+ *
+ * If @i was the last entry in the list, returns NULL.
+ *
+ * Example:
+ * struct child *second;
+ * second = list_next(&parent->children, first, list);
+ * if (!second)
+ * printf("No second child!\n");
+ */
+#define list_next(h, i, member) \
+ ((list_typeof(i))list_entry_or_null(list_debug(h, \
+ __FILE__ ":" stringify(__LINE__)), \
+ (i)->member.next, \
+ list_off_var_((i), member)))
+
+/**
+ * list_prev - get the previous entry in a list
+ * @h: the list_head
+ * @i: a pointer to an entry in the list.
+ * @member: the list_node member of the structure
+ *
+ * If @i was the first entry in the list, returns NULL.
+ *
+ * Example:
+ * first = list_prev(&parent->children, second, list);
+ * if (!first)
+ * printf("Can't go back to first child?!\n");
+ */
+#define list_prev(h, i, member) \
+ ((list_typeof(i))list_entry_or_null(list_debug(h, \
+ __FILE__ ":" stringify(__LINE__)), \
+ (i)->member.prev, \
+ list_off_var_((i), member)))
+
+/**
+ * list_append_list - empty one list onto the end of another.
+ * @to: the list to append into
+ * @from: the list to empty.
+ *
+ * This takes the entire contents of @from and moves it to the end of
+ * @to. After this @from will be empty.
+ *
+ * Example:
+ * struct list_head adopter;
+ *
+ * list_append_list(&adopter, &parent->children);
+ * assert(list_empty(&parent->children));
+ * parent->num_children = 0;
+ */
+#define list_append_list(t, f) list_append_list_(t, f, \
+ __FILE__ ":" stringify(__LINE__))
+static inline void list_append_list_(struct list_head *to,
+ struct list_head *from,
+ const char *abortstr)
+{
+ struct list_node *from_tail = list_debug(from, abortstr)->n.prev;
+ struct list_node *to_tail = list_debug(to, abortstr)->n.prev;
+
+ /* Sew in head and entire list. */
+ to->n.prev = from_tail;
+ from_tail->next = &to->n;
+ to_tail->next = &from->n;
+ from->n.prev = to_tail;
+
+ /* Now remove head. */
+ list_del(&from->n);
+ list_head_init(from);
+}
+
+/**
+ * list_prepend_list - empty one list into the start of another.
+ * @to: the list to prepend into
+ * @from: the list to empty.
+ *
+ * This takes the entire contents of @from and moves it to the start
+ * of @to. After this @from will be empty.
+ *
+ * Example:
+ * list_prepend_list(&adopter, &parent->children);
+ * assert(list_empty(&parent->children));
+ * parent->num_children = 0;
+ */
+#define list_prepend_list(t, f) list_prepend_list_(t, f, LIST_LOC)
+static inline void list_prepend_list_(struct list_head *to,
+ struct list_head *from,
+ const char *abortstr)
+{
+ struct list_node *from_tail = list_debug(from, abortstr)->n.prev;
+ struct list_node *to_head = list_debug(to, abortstr)->n.next;
+
+ /* Sew in head and entire list. */
+ to->n.next = &from->n;
+ from->n.prev = &to->n;
+ to_head->prev = from_tail;
+ from_tail->next = to_head;
+
+ /* Now remove head. */
+ list_del(&from->n);
+ list_head_init(from);
+}
+
+/* internal macros, do not use directly */
+#define list_for_each_off_dir_(h, i, off, dir) \
+ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \
+ (off)); \
+ list_node_from_off_((void *)i, (off)) != &(h)->n; \
+ i = list_node_to_off_(list_node_from_off_((void *)i, (off))->dir, \
+ (off)))
+
+#define list_for_each_safe_off_dir_(h, i, nxt, off, dir) \
+ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \
+ (off)), \
+ nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \
+ (off)); \
+ list_node_from_off_(i, (off)) != &(h)->n; \
+ i = nxt, \
+ nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \
+ (off)))
+
+/**
+ * list_for_each_off - iterate through a list of memory regions.
+ * @h: the list_head
+ * @i: the pointer to a memory region which contains list node data.
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * This is a low-level wrapper to iterate @i over the entire list, used to
+ * implement all oher, more high-level, for-each constructs. It's a for loop,
+ * so you can break and continue as normal.
+ *
+ * WARNING! Being the low-level macro that it is, this wrapper doesn't know
+ * nor care about the type of @i. The only assumption made is that @i points
+ * to a chunk of memory that at some @offset, relative to @i, contains a
+ * properly filled `struct list_node' which in turn contains pointers to
+ * memory chunks and it's turtles all the way down. With all that in mind
+ * remember that given the wrong pointer/offset couple this macro will
+ * happily churn all you memory until SEGFAULT stops it, in other words
+ * caveat emptor.
+ *
+ * It is worth mentioning that one of legitimate use-cases for that wrapper
+ * is operation on opaque types with known offset for `struct list_node'
+ * member(preferably 0), because it allows you not to disclose the type of
+ * @i.
+ *
+ * Example:
+ * list_for_each_off(&parent->children, child,
+ * offsetof(struct child, list))
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_off(h, i, off) \
+ list_for_each_off_dir_((h),(i),(off),next)
+
+/**
+ * list_for_each_rev_off - iterate through a list of memory regions backwards
+ * @h: the list_head
+ * @i: the pointer to a memory region which contains list node data.
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * See list_for_each_off for details
+ */
+#define list_for_each_rev_off(h, i, off) \
+ list_for_each_off_dir_((h),(i),(off),prev)
+
+/**
+ * list_for_each_safe_off - iterate through a list of memory regions, maybe
+ * during deletion
+ * @h: the list_head
+ * @i: the pointer to a memory region which contains list node data.
+ * @nxt: the structure containing the list_node
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * For details see `list_for_each_off' and `list_for_each_safe'
+ * descriptions.
+ *
+ * Example:
+ * list_for_each_safe_off(&parent->children, child,
+ * next, offsetof(struct child, list))
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_safe_off(h, i, nxt, off) \
+ list_for_each_safe_off_dir_((h),(i),(nxt),(off),next)
+
+/**
+ * list_for_each_rev_safe_off - iterate backwards through a list of
+ * memory regions, maybe during deletion
+ * @h: the list_head
+ * @i: the pointer to a memory region which contains list node data.
+ * @nxt: the structure containing the list_node
+ * @off: offset(relative to @i) at which list node data resides.
+ *
+ * For details see `list_for_each_rev_off' and `list_for_each_rev_safe'
+ * descriptions.
+ *
+ * Example:
+ * list_for_each_rev_safe_off(&parent->children, child,
+ * next, offsetof(struct child, list))
+ * printf("Name: %s\n", child->name);
+ */
+#define list_for_each_rev_safe_off(h, i, nxt, off) \
+ list_for_each_safe_off_dir_((h),(i),(nxt),(off),prev)
+
+/* Other -off variants. */
+#define list_entry_off(n, type, off) \
+ ((type *)list_node_from_off_((n), (off)))
+
+#define list_head_off(h, type, off) \
+ ((type *)list_head_off((h), (off)))
+
+#define list_tail_off(h, type, off) \
+ ((type *)list_tail_((h), (off)))
+
+#define list_add_off(h, n, off) \
+ list_add((h), list_node_from_off_((n), (off)))
+
+#define list_del_off(n, off) \
+ list_del(list_node_from_off_((n), (off)))
+
+#define list_del_from_off(h, n, off) \
+ list_del_from(h, list_node_from_off_((n), (off)))
+
+/* Offset helper functions so we only single-evaluate. */
+static inline void *list_node_to_off_(struct list_node *node, size_t off)
+{
+ return (void *)((char *)node - off);
+}
+static inline struct list_node *list_node_from_off_(void *ptr, size_t off)
+{
+ return (struct list_node *)((char *)ptr + off);
+}
+
+/* Get the offset of the member, but make sure it's a list_node. */
+#define list_off_(type, member) \
+ (container_off(type, member) + \
+ check_type(((type *)0)->member, struct list_node))
+
+#define list_off_var_(var, member) \
+ (container_off_var(var, member) + \
+ check_type(var->member, struct list_node))
+
+#if HAVE_TYPEOF
+#define list_typeof(var) typeof(var)
+#else
+#define list_typeof(var) void *
+#endif
+
+/* Returns member, or NULL if at end of list. */
+static inline void *list_entry_or_null(const struct list_head *h,
+ const struct list_node *n,
+ size_t off)
+{
+ if (n == &h->n)
+ return NULL;
+ return (char *)n - off;
+}
+#endif /* CCAN_LIST_H */
diff --git a/ccan/ccan/minmax/LICENSE b/ccan/ccan/minmax/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ccan/minmax/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/ccan/minmax/_info b/ccan/ccan/minmax/_info
new file mode 100644
index 0000000..2f6437e
--- /dev/null
+++ b/ccan/ccan/minmax/_info
@@ -0,0 +1,48 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * minmax - typesafe minimum and maximum functions
+ *
+ * The classic implementation of minimum / maximum macros in C can be
+ * very dangerous. If the two arguments have different sizes, or
+ * different signedness, type promotion rules can lead to very
+ * surprising results.
+ *
+ * This module implements typesafe versions, which will generate a
+ * compile time error, if the arguments have different types.
+ *
+ * Example:
+ * #include <ccan/minmax/minmax.h>
+ * #include <stdio.h>
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * printf("Signed max: %d\n", max(1, -1));
+ * printf("Unsigned max: %u\n", max(1U, -1U));
+ * return 0;
+ * }
+ *
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ * License: CC0 (Public domain)
+ */
+int main(int argc, char *argv[])
+{
+ /* Expect exactly one argument */
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/build_assert\n");
+ return 0;
+ }
+
+ if (strcmp(argv[1], "ccanlint") == 0) {
+ /* We need several gcc extensions */
+ printf("tests_compile_without_features FAIL\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/ccan/minmax/minmax.h b/ccan/ccan/minmax/minmax.h
new file mode 100644
index 0000000..d111d1b
--- /dev/null
+++ b/ccan/ccan/minmax/minmax.h
@@ -0,0 +1,65 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_MINMAX_H
+#define CCAN_MINMAX_H
+
+#include "config.h"
+
+#include <ccan/build_assert/build_assert.h>
+
+#if !HAVE_STATEMENT_EXPR || !HAVE_TYPEOF
+/*
+ * Without these, there's no way to avoid unsafe double evaluation of
+ * the arguments
+ */
+#error Sorry, minmax module requires statement expressions and typeof
+#endif
+
+#if HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#define MINMAX_ASSERT_COMPATIBLE(a, b) \
+ BUILD_ASSERT(__builtin_types_compatible_p(a, b))
+#else
+#define MINMAX_ASSERT_COMPATIBLE(a, b) \
+ do { } while (0)
+#endif
+
+#define min(a, b) \
+ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ MINMAX_ASSERT_COMPATIBLE(typeof(_a), typeof(_b)); \
+ _a < _b ? _a : _b; \
+ })
+
+#define max(a, b) \
+ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ MINMAX_ASSERT_COMPATIBLE(typeof(_a), typeof(_b)); \
+ _a > _b ? _a : _b; \
+ })
+
+#define clamp(v, f, c) (max(min((v), (c)), (f)))
+
+
+#define min_t(t, a, b) \
+ ({ \
+ t _ta = (a); \
+ t _tb = (b); \
+ min(_ta, _tb); \
+ })
+#define max_t(t, a, b) \
+ ({ \
+ t _ta = (a); \
+ t _tb = (b); \
+ max(_ta, _tb); \
+ })
+
+#define clamp_t(t, v, f, c) \
+ ({ \
+ t _tv = (v); \
+ t _tf = (f); \
+ t _tc = (c); \
+ clamp(_tv, _tf, _tc); \
+ })
+
+#endif /* CCAN_MINMAX_H */
diff --git a/ccan/ccan/short_types/LICENSE b/ccan/ccan/short_types/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ccan/short_types/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/ccan/short_types/short_types.h b/ccan/ccan/short_types/short_types.h
new file mode 100644
index 0000000..175377e
--- /dev/null
+++ b/ccan/ccan/short_types/short_types.h
@@ -0,0 +1,35 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_SHORT_TYPES_H
+#define CCAN_SHORT_TYPES_H
+#include <stdint.h>
+
+/**
+ * u64/s64/u32/s32/u16/s16/u8/s8 - short names for explicitly-sized types.
+ */
+typedef uint64_t u64;
+typedef int64_t s64;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint8_t u8;
+typedef int8_t s8;
+
+/* Whichever they include first, they get these definitions. */
+#ifdef CCAN_ENDIAN_H
+/**
+ * be64/be32/be16 - 64/32/16 bit big-endian representation.
+ */
+typedef beint64_t be64;
+typedef beint32_t be32;
+typedef beint16_t be16;
+
+/**
+ * le64/le32/le16 - 64/32/16 bit little-endian representation.
+ */
+typedef leint64_t le64;
+typedef leint32_t le32;
+typedef leint16_t le16;
+#endif
+
+#endif /* CCAN_SHORT_TYPES_H */
diff --git a/ccan/ccan/str/LICENSE b/ccan/ccan/str/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ccan/str/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0 \ No newline at end of file
diff --git a/ccan/ccan/str/_info b/ccan/ccan/str/_info
new file mode 100644
index 0000000..b579525
--- /dev/null
+++ b/ccan/ccan/str/_info
@@ -0,0 +1,52 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * str - string helper routines
+ *
+ * This is a grab bag of functions for string operations, designed to enhance
+ * the standard string.h.
+ *
+ * Note that if you define CCAN_STR_DEBUG, you will get extra compile
+ * checks on common misuses of the following functions (they will now
+ * be out-of-line, so there is a runtime penalty!).
+ *
+ * strstr, strchr, strrchr:
+ * Return const char * if first argument is const (gcc only).
+ *
+ * isalnum, isalpha, isascii, isblank, iscntrl, isdigit, isgraph,
+ * islower, isprint, ispunct, isspace, isupper, isxdigit:
+ * Static and runtime check that input is EOF or an *unsigned*
+ * char, as per C standard (really!).
+ *
+ * Example:
+ * #include <stdio.h>
+ * #include <ccan/str/str.h>
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * if (argc > 1 && streq(argv[1], "--verbose"))
+ * printf("verbose set\n");
+ * if (argc > 1 && strstarts(argv[1], "--"))
+ * printf("Some option set\n");
+ * if (argc > 1 && strends(argv[1], "cow-powers"))
+ * printf("Magic option set\n");
+ * return 0;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/build_assert\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/ccan/str/debug.c b/ccan/ccan/str/debug.c
new file mode 100644
index 0000000..8c51944
--- /dev/null
+++ b/ccan/ccan/str/debug.c
@@ -0,0 +1,108 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#include "config.h"
+#include <ccan/str/str_debug.h>
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+
+#ifdef CCAN_STR_DEBUG
+/* Because we mug the real ones with macros, we need our own wrappers. */
+int str_isalnum(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isalnum(i);
+}
+
+int str_isalpha(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isalpha(i);
+}
+
+int str_isascii(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isascii(i);
+}
+
+#if HAVE_ISBLANK
+int str_isblank(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isblank(i);
+}
+#endif
+
+int str_iscntrl(int i)
+{
+ assert(i >= -1 && i < 256);
+ return iscntrl(i);
+}
+
+int str_isdigit(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isdigit(i);
+}
+
+int str_isgraph(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isgraph(i);
+}
+
+int str_islower(int i)
+{
+ assert(i >= -1 && i < 256);
+ return islower(i);
+}
+
+int str_isprint(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isprint(i);
+}
+
+int str_ispunct(int i)
+{
+ assert(i >= -1 && i < 256);
+ return ispunct(i);
+}
+
+int str_isspace(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isspace(i);
+}
+
+int str_isupper(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isupper(i);
+}
+
+int str_isxdigit(int i)
+{
+ assert(i >= -1 && i < 256);
+ return isxdigit(i);
+}
+
+#undef strstr
+#undef strchr
+#undef strrchr
+
+char *str_strstr(const char *haystack, const char *needle)
+{
+ return strstr(haystack, needle);
+}
+
+char *str_strchr(const char *haystack, int c)
+{
+ return strchr(haystack, c);
+}
+
+char *str_strrchr(const char *haystack, int c)
+{
+ return strrchr(haystack, c);
+}
+#endif
diff --git a/ccan/ccan/str/str.c b/ccan/ccan/str/str.c
new file mode 100644
index 0000000..a9245c1
--- /dev/null
+++ b/ccan/ccan/str/str.c
@@ -0,0 +1,13 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#include <ccan/str/str.h>
+
+size_t strcount(const char *haystack, const char *needle)
+{
+ size_t i = 0, nlen = strlen(needle);
+
+ while ((haystack = strstr(haystack, needle)) != NULL) {
+ i++;
+ haystack += nlen;
+ }
+ return i;
+}
diff --git a/ccan/ccan/str/str.h b/ccan/ccan/str/str.h
new file mode 100644
index 0000000..d919b84
--- /dev/null
+++ b/ccan/ccan/str/str.h
@@ -0,0 +1,228 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_STR_H
+#define CCAN_STR_H
+#include "config.h"
+#include <string.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <ctype.h>
+
+/**
+ * streq - Are two strings equal?
+ * @a: first string
+ * @b: first string
+ *
+ * This macro is arguably more readable than "!strcmp(a, b)".
+ *
+ * Example:
+ * if (streq(somestring, ""))
+ * printf("String is empty!\n");
+ */
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+/**
+ * strstarts - Does this string start with this prefix?
+ * @str: string to test
+ * @prefix: prefix to look for at start of str
+ *
+ * Example:
+ * if (strstarts(somestring, "foo"))
+ * printf("String %s begins with 'foo'!\n", somestring);
+ */
+#define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0)
+
+/**
+ * strends - Does this string end with this postfix?
+ * @str: string to test
+ * @postfix: postfix to look for at end of str
+ *
+ * Example:
+ * if (strends(somestring, "foo"))
+ * printf("String %s end with 'foo'!\n", somestring);
+ */
+static inline bool strends(const char *str, const char *postfix)
+{
+ if (strlen(str) < strlen(postfix))
+ return false;
+
+ return streq(str + strlen(str) - strlen(postfix), postfix);
+}
+
+/**
+ * stringify - Turn expression into a string literal
+ * @expr: any C expression
+ *
+ * Example:
+ * #define PRINT_COND_IF_FALSE(cond) \
+ * ((cond) || printf("%s is false!", stringify(cond)))
+ */
+#define stringify(expr) stringify_1(expr)
+/* Double-indirection required to stringify expansions */
+#define stringify_1(expr) #expr
+
+/**
+ * strcount - Count number of (non-overlapping) occurrences of a substring.
+ * @haystack: a C string
+ * @needle: a substring
+ *
+ * Example:
+ * assert(strcount("aaa aaa", "a") == 6);
+ * assert(strcount("aaa aaa", "ab") == 0);
+ * assert(strcount("aaa aaa", "aa") == 2);
+ */
+size_t strcount(const char *haystack, const char *needle);
+
+/**
+ * STR_MAX_CHARS - Maximum possible size of numeric string for this type.
+ * @type_or_expr: a pointer or integer type or expression.
+ *
+ * This provides enough space for a nul-terminated string which represents the
+ * largest possible value for the type or expression.
+ *
+ * Note: The implementation adds extra space so hex values or negative
+ * values will fit (eg. sprintf(... "%p"). )
+ *
+ * Example:
+ * char str[STR_MAX_CHARS(int)];
+ *
+ * sprintf(str, "%i", 7);
+ */
+#define STR_MAX_CHARS(type_or_expr) \
+ ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \
+ + STR_MAX_CHARS_TCHECK_(type_or_expr))
+
+#if HAVE_TYPEOF
+/* Only a simple type can have 0 assigned, so test that. */
+#define STR_MAX_CHARS_TCHECK_(type_or_expr) \
+ (sizeof(({ typeof(type_or_expr) x = 0; x; }))*0)
+#else
+#define STR_MAX_CHARS_TCHECK_(type_or_expr) 0
+#endif
+
+/**
+ * cisalnum - isalnum() which takes a char (and doesn't accept EOF)
+ * @c: a character
+ *
+ * Surprisingly, the standard ctype.h isalnum() takes an int, which
+ * must have the value of EOF (-1) or an unsigned char. This variant
+ * takes a real char, and doesn't accept EOF.
+ */
+static inline bool cisalnum(char c)
+{
+ return isalnum((unsigned char)c);
+}
+static inline bool cisalpha(char c)
+{
+ return isalpha((unsigned char)c);
+}
+static inline bool cisascii(char c)
+{
+ return isascii((unsigned char)c);
+}
+#if HAVE_ISBLANK
+static inline bool cisblank(char c)
+{
+ return isblank((unsigned char)c);
+}
+#endif
+static inline bool ciscntrl(char c)
+{
+ return iscntrl((unsigned char)c);
+}
+static inline bool cisdigit(char c)
+{
+ return isdigit((unsigned char)c);
+}
+static inline bool cisgraph(char c)
+{
+ return isgraph((unsigned char)c);
+}
+static inline bool cislower(char c)
+{
+ return islower((unsigned char)c);
+}
+static inline bool cisprint(char c)
+{
+ return isprint((unsigned char)c);
+}
+static inline bool cispunct(char c)
+{
+ return ispunct((unsigned char)c);
+}
+static inline bool cisspace(char c)
+{
+ return isspace((unsigned char)c);
+}
+static inline bool cisupper(char c)
+{
+ return isupper((unsigned char)c);
+}
+static inline bool cisxdigit(char c)
+{
+ return isxdigit((unsigned char)c);
+}
+
+#include <ccan/str/str_debug.h>
+
+/* These checks force things out of line, hence they are under DEBUG. */
+#ifdef CCAN_STR_DEBUG
+#include <ccan/build_assert/build_assert.h>
+
+/* These are commonly misused: they take -1 or an *unsigned* char value. */
+#undef isalnum
+#undef isalpha
+#undef isascii
+#undef isblank
+#undef iscntrl
+#undef isdigit
+#undef isgraph
+#undef islower
+#undef isprint
+#undef ispunct
+#undef isspace
+#undef isupper
+#undef isxdigit
+
+/* You can use a char if char is unsigned. */
+#if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF
+#define str_check_arg_(i) \
+ ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(i), \
+ char) \
+ || (char)255 > 0))
+#else
+#define str_check_arg_(i) (i)
+#endif
+
+#define isalnum(i) str_isalnum(str_check_arg_(i))
+#define isalpha(i) str_isalpha(str_check_arg_(i))
+#define isascii(i) str_isascii(str_check_arg_(i))
+#if HAVE_ISBLANK
+#define isblank(i) str_isblank(str_check_arg_(i))
+#endif
+#define iscntrl(i) str_iscntrl(str_check_arg_(i))
+#define isdigit(i) str_isdigit(str_check_arg_(i))
+#define isgraph(i) str_isgraph(str_check_arg_(i))
+#define islower(i) str_islower(str_check_arg_(i))
+#define isprint(i) str_isprint(str_check_arg_(i))
+#define ispunct(i) str_ispunct(str_check_arg_(i))
+#define isspace(i) str_isspace(str_check_arg_(i))
+#define isupper(i) str_isupper(str_check_arg_(i))
+#define isxdigit(i) str_isxdigit(str_check_arg_(i))
+
+#if HAVE_TYPEOF
+/* With GNU magic, we can make const-respecting standard string functions. */
+#undef strstr
+#undef strchr
+#undef strrchr
+
+/* + 0 is needed to decay array into pointer. */
+#define strstr(haystack, needle) \
+ ((typeof((haystack) + 0))str_strstr((haystack), (needle)))
+#define strchr(haystack, c) \
+ ((typeof((haystack) + 0))str_strchr((haystack), (c)))
+#define strrchr(haystack, c) \
+ ((typeof((haystack) + 0))str_strrchr((haystack), (c)))
+#endif
+#endif /* CCAN_STR_DEBUG */
+
+#endif /* CCAN_STR_H */
diff --git a/ccan/ccan/str/str_debug.h b/ccan/ccan/str/str_debug.h
new file mode 100644
index 0000000..92c10c4
--- /dev/null
+++ b/ccan/ccan/str/str_debug.h
@@ -0,0 +1,30 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_STR_DEBUG_H
+#define CCAN_STR_DEBUG_H
+
+/* #define CCAN_STR_DEBUG 1 */
+
+#ifdef CCAN_STR_DEBUG
+/* Because we mug the real ones with macros, we need our own wrappers. */
+int str_isalnum(int i);
+int str_isalpha(int i);
+int str_isascii(int i);
+#if HAVE_ISBLANK
+int str_isblank(int i);
+#endif
+int str_iscntrl(int i);
+int str_isdigit(int i);
+int str_isgraph(int i);
+int str_islower(int i);
+int str_isprint(int i);
+int str_ispunct(int i);
+int str_isspace(int i);
+int str_isupper(int i);
+int str_isxdigit(int i);
+
+char *str_strstr(const char *haystack, const char *needle);
+char *str_strchr(const char *s, int c);
+char *str_strrchr(const char *s, int c);
+#endif /* CCAN_STR_DEBUG */
+
+#endif /* CCAN_STR_DEBUG_H */
diff --git a/ccan/licenses/BSD-MIT b/ccan/licenses/BSD-MIT
new file mode 100644
index 0000000..89de354
--- /dev/null
+++ b/ccan/licenses/BSD-MIT
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/ccan/licenses/CC0 b/ccan/licenses/CC0
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/ccan/licenses/CC0
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/ccan/meson.build b/ccan/meson.build
new file mode 100644
index 0000000..a77240a
--- /dev/null
+++ b/ccan/meson.build
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2021 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+#
+
+sources = [
+ 'ccan/list/list.c',
+ 'ccan/str/debug.c',
+ 'ccan/str/str.c',
+]
+
+if get_option('buildtype') == 'debug'
+ add_project_arguments('-DCCAN_LIST_DEBUG=1', language : ['c', 'cpp'])
+ add_project_arguments('-DCCAN_STR_DEBUG=1', language : ['c', 'cpp'])
+endif
+
+libccan = static_library(
+ 'ccan',
+ sources,
+ install: false,
+ include_directories: [incdir, internal_incdir],
+ dependencies: config_dep,
+)
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..d40a8ea
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,6 @@
+---
+ignore:
+ - 'subprojects'
+ - 'ccan'
+ - 'test'
+ - 'examples'
diff --git a/doc/api.rst.in b/doc/api.rst.in
new file mode 100644
index 0000000..3478766
--- /dev/null
+++ b/doc/api.rst.in
@@ -0,0 +1,18 @@
+===
+API
+===
+
+.. module:: libnvme
+
+This part of the documentation lists the full API reference of all public classes and
+functions.
+
+.. include:: rst/types.rst
+.. include:: rst/ioctl.rst
+.. include:: rst/fabrics.rst
+.. include:: rst/linux.rst
+.. include:: rst/mi.rst
+.. include:: rst/tree.rst
+.. include:: rst/filters.rst
+.. include:: rst/util.rst
+.. include:: rst/log.rst
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..b9d25e5
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,32 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'libnvme'
+copyright = '2020, Keith Busch'
+author = 'Keith Busch <kbusch@kernel.org>'
+master_doc = 'index'
+
+release = '1.8'
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ['html', 'man', 'Thumbs.db', '.DS_Store']
diff --git a/doc/conf.py.in b/doc/conf.py.in
new file mode 100644
index 0000000..3415bbf
--- /dev/null
+++ b/doc/conf.py.in
@@ -0,0 +1,32 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'libnvme'
+copyright = '2020, Keith Busch'
+author = 'Keith Busch <kbusch@kernel.org>'
+master_doc = 'index'
+
+release = '@VERSION@'
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ['html', 'man', 'Thumbs.db', '.DS_Store']
diff --git a/doc/config-schema.json b/doc/config-schema.json
new file mode 100644
index 0000000..6d9e36a
--- /dev/null
+++ b/doc/config-schema.json
@@ -0,0 +1,189 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://github.com/linux-nvme/libnvme/doc/config-schema.json",
+ "title": "config.json",
+ "description": "libnvme JSON configuration",
+ "type": "object",
+ "properties": {
+ "hosts": {
+ "description": "Array of NVMe Host properties",
+ "type": "array",
+ "items": { "$ref": "#/$defs/host" }
+ }
+ },
+ "$defs": {
+ "host": {
+ "description": "NVMe Host properties",
+ "type": "object",
+ "properties": {
+ "hostnqn": {
+ "description": "NVMe host NQN",
+ "type": "string",
+ "maxLength": 223
+ },
+ "hostid": {
+ "description": "NVMe host ID",
+ "type": "string"
+ },
+ "dhchap_key": {
+ "description": "Host DH-HMAC-CHAP key",
+ "type": "string"
+ },
+ "hostsymname": {
+ "description": "NVMe host symbolic name",
+ "type": "string"
+ },
+ "persistent_discovery_ctrl": {
+ "description": "Enable/disable Persistent Discovery Controller",
+ "type": "boolean"
+ },
+ "required": [ "hostnqn" ],
+ "subsystems": {
+ "description": "Array of NVMe subsystem properties",
+ "type": "array",
+ "items": { "$ref": "#/$defs/subsystem" }
+ }
+ }
+ },
+ "subsystem": {
+ "description": "NVMe subsystem properties",
+ "type": "object",
+ "properties": {
+ "nqn": {
+ "description": "Subsystem NQN",
+ "type": "string",
+ "maxLength": 223
+ },
+ "ports": {
+ "description": "Array of NVMe subsystem ports",
+ "type": "array",
+ "items": { "$ref": "#/$defs/port" }
+ },
+ "application": {
+ "description": "Program managing this subsystem",
+ "type": "string"
+ },
+ "required": [ "nqn" ]
+ }
+ },
+ "port": {
+ "description": "NVMe subsystem port",
+ "type": "object",
+ "properties": {
+ "transport": {
+ "description": "Transport type",
+ "type": "string"
+ },
+ "traddr": {
+ "description": "Transport address",
+ "type": "string"
+ },
+ "host_traddr": {
+ "description": "Host transport address",
+ "type": "string"
+ },
+ "host_iface": {
+ "description": "Host interface name",
+ "type": "string"
+ },
+ "trsvcid": {
+ "description": "Transport service identifier",
+ "type": "string"
+ },
+ "dhchap_key": {
+ "description": "Host DH-HMAC-CHAP key",
+ "type": "string"
+ },
+ "dhchap_ctrl_key": {
+ "description": "Controller DH-HMAC-CHAP key",
+ "type": "string"
+ },
+ "nr_io_queues": {
+ "description": "Number of I/O queues",
+ "type": "integer"
+ },
+ "nr_write_queues": {
+ "description": "Number of write queues",
+ "type": "integer"
+ },
+ "nr_poll_queues": {
+ "description": "Number of poll queues",
+ "type": "integer"
+ },
+ "queue_size": {
+ "description": "Queue size",
+ "type": "integer"
+ },
+ "keep_alive_tmo": {
+ "description": "Keep-Alive timeout (in seconds)",
+ "type": "integer"
+ },
+ "reconnect_delay": {
+ "description": "Reconnect delay (in seconds)",
+ "type": "integer"
+ },
+ "ctrl_loss_tmo": {
+ "description": "Controller loss timeout (in seconds)",
+ "type": "integer"
+ },
+ "fast_io_fail_tmo": {
+ "description": "Fast I/O Fail timeout (in seconds)",
+ "type": "integer",
+ "default": 600
+ },
+ "tos": {
+ "description": "Type of service",
+ "type": "integer",
+ "default": -1
+ },
+ "duplicate_connect": {
+ "description": "Allow duplicate connections",
+ "type": "boolean",
+ "default": false
+ },
+ "disable_sqflow": {
+ "description": "Explicitly disable SQ flow control",
+ "type": "boolean",
+ "default": false
+ },
+ "hdr_digest": {
+ "description": "Enable header digest",
+ "type": "boolean",
+ "default": false
+ },
+ "data_digest": {
+ "description": "Enable data digest",
+ "type": "boolean",
+ "default": false
+ },
+ "keyring": {
+ "description": "Keyring for TLS key lookup",
+ "type": "integer"
+ },
+ "tls_key": {
+ "description": "TLS key for the connection",
+ "type": "integer"
+ },
+ "tls": {
+ "description": "Enable TLS encryption",
+ "type": "boolean",
+ "default": false
+ },
+ "concat": {
+ "description": "Enable secure concatenation",
+ "type": "boolean",
+ "default": false
+ },
+ "persistent": {
+ "description": "Create persistent discovery connection",
+ "type": "boolean"
+ },
+ "discovery": {
+ "description": "Connect to a discovery controller",
+ "type": "boolean"
+ }
+ },
+ "required": [ "transport" ]
+ }
+ }
+}
diff --git a/doc/config-schema.json.in b/doc/config-schema.json.in
new file mode 100644
index 0000000..6d9e36a
--- /dev/null
+++ b/doc/config-schema.json.in
@@ -0,0 +1,189 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://github.com/linux-nvme/libnvme/doc/config-schema.json",
+ "title": "config.json",
+ "description": "libnvme JSON configuration",
+ "type": "object",
+ "properties": {
+ "hosts": {
+ "description": "Array of NVMe Host properties",
+ "type": "array",
+ "items": { "$ref": "#/$defs/host" }
+ }
+ },
+ "$defs": {
+ "host": {
+ "description": "NVMe Host properties",
+ "type": "object",
+ "properties": {
+ "hostnqn": {
+ "description": "NVMe host NQN",
+ "type": "string",
+ "maxLength": 223
+ },
+ "hostid": {
+ "description": "NVMe host ID",
+ "type": "string"
+ },
+ "dhchap_key": {
+ "description": "Host DH-HMAC-CHAP key",
+ "type": "string"
+ },
+ "hostsymname": {
+ "description": "NVMe host symbolic name",
+ "type": "string"
+ },
+ "persistent_discovery_ctrl": {
+ "description": "Enable/disable Persistent Discovery Controller",
+ "type": "boolean"
+ },
+ "required": [ "hostnqn" ],
+ "subsystems": {
+ "description": "Array of NVMe subsystem properties",
+ "type": "array",
+ "items": { "$ref": "#/$defs/subsystem" }
+ }
+ }
+ },
+ "subsystem": {
+ "description": "NVMe subsystem properties",
+ "type": "object",
+ "properties": {
+ "nqn": {
+ "description": "Subsystem NQN",
+ "type": "string",
+ "maxLength": 223
+ },
+ "ports": {
+ "description": "Array of NVMe subsystem ports",
+ "type": "array",
+ "items": { "$ref": "#/$defs/port" }
+ },
+ "application": {
+ "description": "Program managing this subsystem",
+ "type": "string"
+ },
+ "required": [ "nqn" ]
+ }
+ },
+ "port": {
+ "description": "NVMe subsystem port",
+ "type": "object",
+ "properties": {
+ "transport": {
+ "description": "Transport type",
+ "type": "string"
+ },
+ "traddr": {
+ "description": "Transport address",
+ "type": "string"
+ },
+ "host_traddr": {
+ "description": "Host transport address",
+ "type": "string"
+ },
+ "host_iface": {
+ "description": "Host interface name",
+ "type": "string"
+ },
+ "trsvcid": {
+ "description": "Transport service identifier",
+ "type": "string"
+ },
+ "dhchap_key": {
+ "description": "Host DH-HMAC-CHAP key",
+ "type": "string"
+ },
+ "dhchap_ctrl_key": {
+ "description": "Controller DH-HMAC-CHAP key",
+ "type": "string"
+ },
+ "nr_io_queues": {
+ "description": "Number of I/O queues",
+ "type": "integer"
+ },
+ "nr_write_queues": {
+ "description": "Number of write queues",
+ "type": "integer"
+ },
+ "nr_poll_queues": {
+ "description": "Number of poll queues",
+ "type": "integer"
+ },
+ "queue_size": {
+ "description": "Queue size",
+ "type": "integer"
+ },
+ "keep_alive_tmo": {
+ "description": "Keep-Alive timeout (in seconds)",
+ "type": "integer"
+ },
+ "reconnect_delay": {
+ "description": "Reconnect delay (in seconds)",
+ "type": "integer"
+ },
+ "ctrl_loss_tmo": {
+ "description": "Controller loss timeout (in seconds)",
+ "type": "integer"
+ },
+ "fast_io_fail_tmo": {
+ "description": "Fast I/O Fail timeout (in seconds)",
+ "type": "integer",
+ "default": 600
+ },
+ "tos": {
+ "description": "Type of service",
+ "type": "integer",
+ "default": -1
+ },
+ "duplicate_connect": {
+ "description": "Allow duplicate connections",
+ "type": "boolean",
+ "default": false
+ },
+ "disable_sqflow": {
+ "description": "Explicitly disable SQ flow control",
+ "type": "boolean",
+ "default": false
+ },
+ "hdr_digest": {
+ "description": "Enable header digest",
+ "type": "boolean",
+ "default": false
+ },
+ "data_digest": {
+ "description": "Enable data digest",
+ "type": "boolean",
+ "default": false
+ },
+ "keyring": {
+ "description": "Keyring for TLS key lookup",
+ "type": "integer"
+ },
+ "tls_key": {
+ "description": "TLS key for the connection",
+ "type": "integer"
+ },
+ "tls": {
+ "description": "Enable TLS encryption",
+ "type": "boolean",
+ "default": false
+ },
+ "concat": {
+ "description": "Enable secure concatenation",
+ "type": "boolean",
+ "default": false
+ },
+ "persistent": {
+ "description": "Create persistent discovery connection",
+ "type": "boolean"
+ },
+ "discovery": {
+ "description": "Connect to a discovery controller",
+ "type": "boolean"
+ }
+ },
+ "required": [ "transport" ]
+ }
+ }
+}
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..54c2415
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,23 @@
+Welcome to libnvme's documentation!
+===================================
+
+This is the libnvme development C library. libnvme provides type definitions for
+NVMe specification structures, enumerations, and bit fields,
+helper functions to construct, dispatch, and decode commands and payloads,
+and utilities to connect, scan, and manage nvme devices on a Linux system.
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ installation.rst
+ quickstart.rst
+ mi.rst
+ api.rst
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/doc/index.rst.in b/doc/index.rst.in
new file mode 100644
index 0000000..54c2415
--- /dev/null
+++ b/doc/index.rst.in
@@ -0,0 +1,23 @@
+Welcome to libnvme's documentation!
+===================================
+
+This is the libnvme development C library. libnvme provides type definitions for
+NVMe specification structures, enumerations, and bit fields,
+helper functions to construct, dispatch, and decode commands and payloads,
+and utilities to connect, scan, and manage nvme devices on a Linux system.
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ installation.rst
+ quickstart.rst
+ mi.rst
+ api.rst
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/doc/installation.rst.in b/doc/installation.rst.in
new file mode 100644
index 0000000..ea0415d
--- /dev/null
+++ b/doc/installation.rst.in
@@ -0,0 +1,55 @@
+Installation
+============
+
+Debian / Ubuntu:
+----------------
+
+.. code-block:: sh
+
+ $ apt-get install libnvme
+
+Fedora / Red Hat:
+-----------------
+
+.. code-block:: sh
+
+ $ dnf install libnvme
+
+Python binding
+--------------
+
+Python Version
+^^^^^^^^^^^^^^
+
+The latest Python 3 version is always recommended, since it has all
+the latest bells and whistles. libnvme supports Python 3.6 and above.
+
+Dependencies
+^^^^^^^^^^^^
+
+libnvme only uses packages from the standard library, so no additional
+dependencies will be installed when installing libnvme.
+
+Install libnvme python
+^^^^^^^^^^^^^^^^^^^^^^
+
+Debian / Ubuntu:
+
+.. code-block:: sh
+
+ $ apt-get install python3-libnvme
+
+Fedora / Red Hat:
+
+.. code-block:: sh
+
+ $ dnf install python3-libnvme
+
+libnvme is available on `PyPI`_, and can be installed using pip. The
+version on PyPI is always the latest stable release.
+
+.. _PyPi: https://pypi.org/project/libnvme/
+
+.. code-block:: sh
+
+ $ pip install libnvme
diff --git a/doc/man/nbft_control.2 b/doc/man/nbft_control.2
new file mode 100644
index 0000000..6eee036
--- /dev/null
+++ b/doc/man/nbft_control.2
@@ -0,0 +1,169 @@
+.TH "libnvme" 9 "struct nbft_control" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_control \- NBFT Table - Control Descriptor (Figure 8)
+.SH SYNOPSIS
+struct nbft_control {
+.br
+.BI " __u8 structure_id;"
+.br
+.BI " __u8 major_revision;"
+.br
+.BI " __u8 minor_revision;"
+.br
+.BI " __u8 reserved1;"
+.br
+.BI " __le16 csl;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __u8 reserved2;"
+.br
+.BI " struct nbft_heap_obj hdesc;"
+.br
+.BI " __u8 hsv;"
+.br
+.BI " __u8 reserved3;"
+.br
+.BI " __le32 hfio;"
+.br
+.BI " __le16 hfil;"
+.br
+.BI " __u8 hfiv;"
+.br
+.BI " __u8 num_hfi;"
+.br
+.BI " __le32 ssnso;"
+.br
+.BI " __le16 ssnsl;"
+.br
+.BI " __u8 ssnsv;"
+.br
+.BI " __u8 num_ssns;"
+.br
+.BI " __le32 seco;"
+.br
+.BI " __le16 secl;"
+.br
+.BI " __u8 secv;"
+.br
+.BI " __u8 num_sec;"
+.br
+.BI " __le32 disco;"
+.br
+.BI " __le16 discl;"
+.br
+.BI " __u8 discv;"
+.br
+.BI " __u8 num_disc;"
+.br
+.BI " __u8 reserved4[16];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "structure_id" 12
+Structure ID: This field specifies the element (refer to
+\fIenum nbft_desc_type\fP). This field shall be set to 1h (i.e.,
+Control, #NBFT_DESC_CONTROL).
+.IP "major_revision" 12
+Major Revision: The major revision of the structure corresponding
+to the Signature field. Larger major revision numbers should
+not be assumed backward compatible to lower major revision
+numbers with the same signature.
+.IP "minor_revision" 12
+Minor Revision: The minor revision of the structure corresponding
+to the signature field. If the major revision numbers are
+the same, any minor revision number differences shall be backwards
+compatible with the same signature.
+.IP "reserved1" 12
+Reserved.
+.IP "csl" 12
+Control Structure Length (CSL): This field indicates the length
+in bytes of the Control Descriptor.
+.IP "flags" 12
+Flags, see \fIenum nbft_control_flags\fP.
+.IP "reserved2" 12
+Reserved.
+.IP "hdesc" 12
+Host Descriptor (HDESC): This field indicates the location
+and length of the Host Descriptor (see \fIstruct nbft_host\fP).
+.IP "hsv" 12
+Host Descriptor Version (HSV): This field indicates the version
+of the Host Descriptor.
+.IP "reserved3" 12
+Reserved.
+.IP "hfio" 12
+HFI Descriptor List Offset (HFIO): If this field is set to
+a non-zero value, then this field indicates the offset in bytes
+of the HFI Descriptor List, if any, from byte offset 0h of the
+NBFT Table Header. If the \fInum_hfi\fP field is cleared to 0h,
+then this field is reserved.
+.IP "hfil" 12
+HFI Descriptor Length (HFIL): This field indicates the length
+in bytes of each HFI Descriptor, if any. If the \fInum_hfi\fP field
+is cleared to 0h, then this field is reserved.
+.IP "hfiv" 12
+HFI Descriptor Version (HFIV): This field indicates the version
+of each HFI Descriptor.
+.IP "num_hfi" 12
+Number of Host Fabric Interface Descriptors (NumHFI): This field
+indicates the number of HFI Descriptors (see \fIstruct nbft_hfi\fP)
+in the HFI Descriptor List, if any. If no interfaces have been
+configured, then this field shall be cleared to 0h.
+.IP "ssnso" 12
+SSNS Descriptor List Offset (SSNSO):: This field indicates
+the offset in bytes of the SSNS Descriptor List, if any, from
+byte offset 0h of the NBFT Table Header. If the \fInum_ssns\fP field
+is cleared to 0h, then this field is reserved.
+.IP "ssnsl" 12
+SSNS Descriptor Length (SSNSL): This field indicates the length
+in bytes of each SSNS Descriptor, if any. If the \fInum_ssns\fP
+field is cleared to 0h, then this field is reserved.
+.IP "ssnsv" 12
+SSNS Descriptor Version (SSNSV): This field indicates the version
+of the SSNS Descriptor.
+.IP "num_ssns" 12
+Number of Subsystem and Namespace Descriptors (NumSSNS): This
+field indicates the number of Subsystem Namespace (SSNS)
+Descriptors (see \fIstruct nbft_ssns\fP) in the SSNS Descriptor List,
+if any.
+.IP "seco" 12
+Security Profile Descriptor List Offset (SECO): This field
+indicates the offset in bytes of the Security Profile Descriptor
+List, if any, from byte offset 0h of the NBFT Table Header.
+If the \fInum_sec\fP field is cleared to 0h, then this field
+is reserved.
+.IP "secl" 12
+Security Profile Descriptor Length (SECL): This field indicates
+the length in bytes of each Security Profile Descriptor, if any.
+If the \fInum_sec\fP field is cleared to 0h, then this field
+is reserved.
+.IP "secv" 12
+Security Profile Descriptor Version (SECV): This field indicates
+the version of the Security Profile Descriptor.
+.IP "num_sec" 12
+Number of Security Profile Descriptors (NumSec): This field
+indicates the number of Security Profile Descriptors
+(see \fIstruct nbft_security\fP), if any, in the Security Profile
+Descriptor List.
+.IP "disco" 12
+Discovery Descriptor Offset (DISCO): This field indicates
+the offset in bytes of the Discovery Descriptor List, if any,
+from byte offset 0h of the NBFT Table Header. If the \fInum_disc\fP
+field is cleared to 0h, then this field is reserved.
+.IP "discl" 12
+Discovery Descriptor Length (DISCL): This field indicates
+the length in bytes of each Discovery Descriptor, if any.
+If the \fInum_disc\fP field is cleared to 0h, then this field
+is reserved.
+.IP "discv" 12
+Discovery Descriptor Version (DISCV): This field indicates
+the version of the Discovery Descriptor.
+.IP "num_disc" 12
+Number of Discovery Descriptors (NumDisc): This field indicates
+the number of Discovery Descriptors (see \fIstruct nbft_discovery\fP),
+if any, in the Discovery Descriptor List, if any.
+.IP "reserved4" 12
+Reserved.
diff --git a/doc/man/nbft_control_flags.2 b/doc/man/nbft_control_flags.2
new file mode 100644
index 0000000..e2343d2
--- /dev/null
+++ b/doc/man/nbft_control_flags.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nbft_control_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_control_flags \- Control Descriptor Flags
+.SH SYNOPSIS
+enum nbft_control_flags {
+.br
+.BI " NBFT_CONTROL_VALID"
+
+};
+.SH Constants
+.IP "NBFT_CONTROL_VALID" 12
+Block Valid: indicates that the structure is valid.
diff --git a/doc/man/nbft_desc_type.2 b/doc/man/nbft_desc_type.2
new file mode 100644
index 0000000..890ab33
--- /dev/null
+++ b/doc/man/nbft_desc_type.2
@@ -0,0 +1,74 @@
+.TH "libnvme" 9 "enum nbft_desc_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_desc_type \- NBFT Elements - Descriptor Types (Figure 5)
+.SH SYNOPSIS
+enum nbft_desc_type {
+.br
+.BI " NBFT_DESC_HEADER"
+,
+.br
+.br
+.BI " NBFT_DESC_CONTROL"
+,
+.br
+.br
+.BI " NBFT_DESC_HOST"
+,
+.br
+.br
+.BI " NBFT_DESC_HFI"
+,
+.br
+.br
+.BI " NBFT_DESC_SSNS"
+,
+.br
+.br
+.BI " NBFT_DESC_SECURITY"
+,
+.br
+.br
+.BI " NBFT_DESC_DISCOVERY"
+,
+.br
+.br
+.BI " NBFT_DESC_HFI_TRINFO"
+,
+.br
+.br
+.BI " NBFT_DESC_RESERVED_8"
+,
+.br
+.br
+.BI " NBFT_DESC_SSNS_EXT_INFO"
+
+};
+.SH Constants
+.IP "NBFT_DESC_HEADER" 12
+Header: an ACPI structure header with some additional
+NBFT specific info.
+.IP "NBFT_DESC_CONTROL" 12
+Control Descriptor: indicates the location of host,
+HFI, SSNS, security, and discovery descriptors.
+.IP "NBFT_DESC_HOST" 12
+Host Descriptor: host information.
+.IP "NBFT_DESC_HFI" 12
+HFI Descriptor: an indexable table of HFI Descriptors,
+one for each fabric interface on the host.
+.IP "NBFT_DESC_SSNS" 12
+Subsystem Namespace Descriptor: an indexable table
+of SSNS Descriptors.
+.IP "NBFT_DESC_SECURITY" 12
+Security Descriptor: an indexable table of Security
+descriptors.
+.IP "NBFT_DESC_DISCOVERY" 12
+Discovery Descriptor: an indexable table of Discovery
+Descriptors.
+.IP "NBFT_DESC_HFI_TRINFO" 12
+HFI Transport Descriptor: indicated by an HFI Descriptor,
+corresponds to a specific transport for a single HFI.
+.IP "NBFT_DESC_RESERVED_8" 12
+Reserved.
+.IP "NBFT_DESC_SSNS_EXT_INFO" 12
+SSNS Extended Info Descriptor: indicated by an SSNS
+Descriptor if required.
diff --git a/doc/man/nbft_discovery.2 b/doc/man/nbft_discovery.2
new file mode 100644
index 0000000..c3de0e9
--- /dev/null
+++ b/doc/man/nbft_discovery.2
@@ -0,0 +1,71 @@
+.TH "libnvme" 9 "struct nbft_discovery" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_discovery \- Discovery Descriptor (Figure 24)
+.SH SYNOPSIS
+struct nbft_discovery {
+.br
+.BI " __u8 structure_id;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __u8 index;"
+.br
+.BI " __u8 hfi_index;"
+.br
+.BI " __u8 sec_index;"
+.br
+.BI " __u8 reserved1;"
+.br
+.BI " struct nbft_heap_obj discovery_ctrl_addr_obj;"
+.br
+.BI " struct nbft_heap_obj discovery_ctrl_nqn_obj;"
+.br
+.BI " __u8 reserved2[14];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "structure_id" 12
+Structure ID: This field shall be set to 6h
+(i.e., Discovery Descriptor; #NBFT_DESC_DISCOVERY).
+.IP "flags" 12
+Discovery Descriptor Flags, see \fIenum nbft_discovery_flags\fP.
+.IP "index" 12
+Discovery Descriptor Index: This field indicates
+the number of this Discovery Descriptor in
+the Discovery Descriptor List.
+.IP "hfi_index" 12
+HFI Descriptor Index: This field indicates the value
+of the HFI Descriptor Index field of the HFI Descriptor
+associated with this Discovery Descriptor. If multiple
+HFIs share a common Discovery controller, there shall
+be multiple Discovery Descriptor entries with one per HFI.
+.IP "sec_index" 12
+Security Profile Descriptor Index: This field indicates
+the value of the Security Profile Descriptor Index
+field of the Security Descriptor associated with
+this Discovery Descriptor.
+.IP "reserved1" 12
+Reserved.
+.IP "discovery_ctrl_addr_obj" 12
+Discovery Controller Address Heap Object Reference:
+This field indicates the location and size of a heap
+object containing a URI which indicates an NVMe Discovery
+controller associated with this Discovery Descriptor.
+If this field is cleared to 0h, then no URI is specified.
+.IP "discovery_ctrl_nqn_obj" 12
+Discovery Controller NQN Heap Object Reference:
+If set to a non-zero value, this field indicates
+the location and size of a heap object containing
+an NVMe Discovery controller NQN. If the NVMe Discovery
+controller referenced by this record requires secure
+authentication with a well known Subsystem NQN, this
+field indicates the unique NQN for that NVMe Discovery
+controller. This record is involved formatted as an NQN
+string. If this field is cleared to 0h, then this
+field is reserved and the OS shall use the well
+known discovery NQN for this record.
+.IP "reserved2" 12
+Reserved.
diff --git a/doc/man/nbft_discovery_flags.2 b/doc/man/nbft_discovery_flags.2
new file mode 100644
index 0000000..232490b
--- /dev/null
+++ b/doc/man/nbft_discovery_flags.2
@@ -0,0 +1,14 @@
+.TH "libnvme" 9 "enum nbft_discovery_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_discovery_flags \- Discovery Descriptor Flags
+.SH SYNOPSIS
+enum nbft_discovery_flags {
+.br
+.BI " NBFT_DISCOVERY_VALID"
+
+};
+.SH Constants
+.IP "NBFT_DISCOVERY_VALID" 12
+Descriptor Valid: if set to 1h, then this descriptor
+is valid. If cleared to 0h, then this descriptor
+is reserved.
diff --git a/doc/man/nbft_header.2 b/doc/man/nbft_header.2
new file mode 100644
index 0000000..ff59e3d
--- /dev/null
+++ b/doc/man/nbft_header.2
@@ -0,0 +1,93 @@
+.TH "libnvme" 9 "struct nbft_header" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_header \- NBFT Table - Header (Figure 8)
+.SH SYNOPSIS
+struct nbft_header {
+.br
+.BI " char signature[4];"
+.br
+.BI " __le32 length;"
+.br
+.BI " __u8 major_revision;"
+.br
+.BI " __u8 checksum;"
+.br
+.BI " char oem_id[6];"
+.br
+.BI " char oem_table_id[8];"
+.br
+.BI " __le32 oem_revision;"
+.br
+.BI " __le32 creator_id;"
+.br
+.BI " __le32 creator_revision;"
+.br
+.BI " __le32 heap_offset;"
+.br
+.BI " __le32 heap_length;"
+.br
+.BI " struct nbft_heap_obj driver_dev_path_sig;"
+.br
+.BI " __u8 minor_revision;"
+.br
+.BI " __u8 reserved[13];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "signature" 12
+Signature: An ASCII string representation of the table
+identifier. This field shall be set to the value 4E424654h
+(i.e. "NBFT", see #NBFT_HEADER_SIG).
+.IP "length" 12
+Length: The length of the table, in bytes, including the
+header, starting from offset 0h. This field is used to record
+the size of the entire table.
+.IP "major_revision" 12
+Major Revision: The major revision of the structure
+corresponding to the Signature field. Larger major revision
+numbers should not be assumed backward compatible to lower
+major revision numbers with the same signature.
+.IP "checksum" 12
+Checksum: The entire table, including the Checksum field,
+shall sum to 0h to be considered valid.
+.IP "oem_id" 12
+OEMID shall be populated by the NBFT driver writer by
+an OEM-supplied string that identifies the OEM. All
+trailing bytes shall be NULL.
+.IP "oem_table_id" 12
+OEM Table ID: This field shall be populated by the NBFT
+driver writer with an OEM-supplied string that the OEM
+uses to identify the particular data table. This field is
+particularly useful when defining a definition block to
+distinguish definition block functions. The OEM assigns
+each dissimilar table a new OEM Table ID.
+.IP "oem_revision" 12
+OEM Revision: An OEM-supplied revision number. Larger
+numbers are assumed to be newer revisions.
+.IP "creator_id" 12
+Creator ID: Vendor ID of utility that created the table.
+For instance, this may be the ID for the ASL Compiler.
+.IP "creator_revision" 12
+Creator Revision: Revision of utility that created the
+table. For instance, this may be the ID for the ASL Compiler.
+.IP "heap_offset" 12
+Heap Offset (HO): This field indicates the offset in bytes
+of the heap, if any, from byte offset 0h of the NBFT
+Table Header.
+.IP "heap_length" 12
+Heap Length (HL): The length of the heap, if any.
+.IP "driver_dev_path_sig" 12
+Driver Signature Heap Object Reference: This field indicates
+the offset in bytes of a heap object containing the Driver
+Signature, if any, from byte offset 0h of the NBFT Table
+Header.
+.IP "minor_revision" 12
+Minor Revision: The minor revision of the structure
+corresponding to the Signature field. If the major revision
+numbers are the same, any minor revision number differences
+shall be backwards compatible with the same signature.
+.IP "reserved" 12
+Reserved.
diff --git a/doc/man/nbft_heap_obj.2 b/doc/man/nbft_heap_obj.2
new file mode 100644
index 0000000..a7c7fab
--- /dev/null
+++ b/doc/man/nbft_heap_obj.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "struct nbft_heap_obj" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_heap_obj \- NBFT Header Driver Signature
+.SH SYNOPSIS
+struct nbft_heap_obj {
+.br
+.BI " __le32 offset;"
+.br
+.BI " __le16 length;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "offset" 12
+Offset in bytes of the heap object, if any, from byte offset 0h
+of the NBFT Table Header.
+.IP "length" 12
+Length in bytes of the heap object, if any.
diff --git a/doc/man/nbft_hfi.2 b/doc/man/nbft_hfi.2
new file mode 100644
index 0000000..1caf368
--- /dev/null
+++ b/doc/man/nbft_hfi.2
@@ -0,0 +1,44 @@
+.TH "libnvme" 9 "struct nbft_hfi" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_hfi \- Host Fabric Interface (HFI) Descriptor (Figure 11)
+.SH SYNOPSIS
+struct nbft_hfi {
+.br
+.BI " __u8 structure_id;"
+.br
+.BI " __u8 index;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __u8 trtype;"
+.br
+.BI " __u8 reserved1[12];"
+.br
+.BI " struct nbft_heap_obj trinfo_obj;"
+.br
+.BI " __u8 reserved2[10];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "structure_id" 12
+Structure ID: This field shall be set to 3h (i.e., Host Fabric
+Interface Descriptor; #NBFT_DESC_HFI).
+.IP "index" 12
+HFI Descriptor Index: This field indicates the number of this
+HFI Descriptor in the Host Fabric Interface Descriptor List.
+.IP "flags" 12
+HFI Descriptor Flags, see \fIenum nbft_hfi_flags\fP.
+.IP "trtype" 12
+HFI Transport Type, see \fIenum nbft_trtype\fP.
+.IP "reserved1" 12
+Reserved.
+.IP "trinfo_obj" 12
+HFI Transport Info Descriptor Heap Object Reference: If this
+field is set to a non-zero value, then this field indicates
+the location and size of a heap object containing
+a HFI Transport Info.
+.IP "reserved2" 12
+Reserved.
diff --git a/doc/man/nbft_hfi_flags.2 b/doc/man/nbft_hfi_flags.2
new file mode 100644
index 0000000..558c767
--- /dev/null
+++ b/doc/man/nbft_hfi_flags.2
@@ -0,0 +1,13 @@
+.TH "libnvme" 9 "enum nbft_hfi_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_hfi_flags \- HFI Descriptor Flags
+.SH SYNOPSIS
+enum nbft_hfi_flags {
+.br
+.BI " NBFT_HFI_VALID"
+
+};
+.SH Constants
+.IP "NBFT_HFI_VALID" 12
+Descriptor Valid: If set to 1h, then this descriptor is valid.
+If cleared to 0h, then this descriptor is reserved.
diff --git a/doc/man/nbft_hfi_info_tcp.2 b/doc/man/nbft_hfi_info_tcp.2
new file mode 100644
index 0000000..53436df
--- /dev/null
+++ b/doc/man/nbft_hfi_info_tcp.2
@@ -0,0 +1,137 @@
+.TH "libnvme" 9 "struct nbft_hfi_info_tcp" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_hfi_info_tcp \- HFI Transport Info Descriptor - NVMe/TCP (Figure 13)
+.SH SYNOPSIS
+struct nbft_hfi_info_tcp {
+.br
+.BI " __u8 structure_id;"
+.br
+.BI " __u8 version;"
+.br
+.BI " __u8 trtype;"
+.br
+.BI " __u8 trinfo_version;"
+.br
+.BI " __le16 hfi_index;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __le32 pci_sbdf;"
+.br
+.BI " __u8 mac_addr[6];"
+.br
+.BI " __le16 vlan;"
+.br
+.BI " __u8 ip_origin;"
+.br
+.BI " __u8 ip_address[16];"
+.br
+.BI " __u8 subnet_mask_prefix;"
+.br
+.BI " __u8 ip_gateway[16];"
+.br
+.BI " __u8 reserved1;"
+.br
+.BI " __le16 route_metric;"
+.br
+.BI " __u8 primary_dns[16];"
+.br
+.BI " __u8 secondary_dns[16];"
+.br
+.BI " __u8 dhcp_server[16];"
+.br
+.BI " struct nbft_heap_obj host_name_obj;"
+.br
+.BI " __u8 reserved2[18];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "structure_id" 12
+Structure ID: This field shall be set to 7h (i.e.,
+HFI Transport Info; #NBFT_DESC_HFI_TRINFO).
+.IP "version" 12
+Version: This field shall be set to 1h.
+.IP "trtype" 12
+HFI Transport Type, see \fIenum nbft_trtype\fP: This field
+shall be set to 03h (i.e., NVMe/TCP; #NBFT_TRTYPE_TCP).
+.IP "trinfo_version" 12
+Transport Info Version: Implementations compliant to this
+specification shall set this field to 1h.
+.IP "hfi_index" 12
+HFI Descriptor Index: The value of the HFI Descriptor Index
+field of the HFI Descriptor (see \fIstruct nbft_hfi\fP.index)
+whose HFI Transport Info Descriptor Heap Object Reference
+field indicates this HFI Transport Info Descriptor.
+.IP "flags" 12
+HFI Transport Flags, see \fIenum nbft_hfi_info_tcp_flags\fP.
+.IP "pci_sbdf" 12
+PCI Express Routing ID for the HFI Transport Function:
+This field indicates the PCI Express Routing ID as specified
+in the PCI Express Base Specification.
+.IP "mac_addr" 12
+MAC Address: The MAC address of this HFI, in EUI-48TM format,
+as defined in the IEEE Guidelines for Use of Extended Unique
+Identifiers. This field shall be set to a non-zero value.
+.IP "vlan" 12
+VLAN: If this field is set to a non-zero value, then this
+field contains the VLAN identifier if the VLAN associated
+with this HFI, as defined in IEEE 802.1q-2018. If no VLAN
+is associated with this HFI, then this field shall be cleared
+to 0h.
+.IP "ip_origin" 12
+IP Origin: If this field is set to a non-zero value, then
+this field indicates the source of Ethernet L3 configuration
+information used by the driver for this interface. Valid
+values are defined in the Win 32 API: NL_PREFIX_ORIGIN
+enumeration specification. This field should be cleared
+to 0h if the IP Origin field is unused by driver.
+.IP "ip_address" 12
+IP Address: This field indicates the IPv4 or IPv6 address
+of this HFI. This field shall be set to a non-zero value.
+.IP "subnet_mask_prefix" 12
+Subnet Mask Prefix: This field indicates the IPv4 or IPv6
+subnet mask in CIDR routing prefix notation.
+.IP "ip_gateway" 12
+IP Gateway: If this field is set to a non-zero value, this
+field indicates the IPv4 or IPv6 address of the IP gateway
+for this HFI. If this field is cleared to 0h, then
+no IP gateway is specified.
+.IP "reserved1" 12
+Reserved.
+.IP "route_metric" 12
+Route Metric: If this field is set to a non-zero value,
+this field indicates the cost value for the route indicated
+by this HF. This field contains the value utilized by the
+pre-OS driver when chosing among all available routes. Lower
+values relate to higher priority. Refer to IETF RFC 4249.
+If the pre-OS driver supports routing and did not configure
+a specific route metric for this interface, then the pre-OS
+driver should set this value to 500. If the pre-OS driver
+does not support routing, then this field should be cleared
+to 0h.
+.IP "primary_dns" 12
+Primary DNS: If this field is set to a non-zero value,
+this field indicates the IPv4 or IPv6 address of the
+Primary DNS server for this HFI, if any, from byte offset
+0h of the NBFT Table Header. If this field is cleared to 0h,
+then no Primary DNS is specified.
+.IP "secondary_dns" 12
+Secondary DNS: If this field is set to a non-zero value,
+this field indicates the IPv4 or IPv6 address of
+the Secondary DNS server for this HFI, if any, from byte
+offset 0h of the NBFT Table Header. If this field is
+cleared to 0h, then no Secondary DNS is specified.
+.IP "dhcp_server" 12
+DHCP Server: If the DHCP Override bit is set to 1h, then
+this field indicates the IPv4 or IPv6 address of the DHCP
+server used to assign this HFI address. If that bit is
+cleared to 0h, then this field is reserved.
+.IP "host_name_obj" 12
+Host Name Heap Object Reference: If this field is set
+to a non-zero value, then this field indicates the location
+and size of a heap object containing a Host Name string.
+.IP "reserved2" 12
+Reserved.
diff --git a/doc/man/nbft_hfi_info_tcp_flags.2 b/doc/man/nbft_hfi_info_tcp_flags.2
new file mode 100644
index 0000000..c8efaef
--- /dev/null
+++ b/doc/man/nbft_hfi_info_tcp_flags.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "enum nbft_hfi_info_tcp_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_hfi_info_tcp_flags \- HFI Transport Flags
+.SH SYNOPSIS
+enum nbft_hfi_info_tcp_flags {
+.br
+.BI " NBFT_HFI_INFO_TCP_VALID"
+,
+.br
+.br
+.BI " NBFT_HFI_INFO_TCP_GLOBAL_ROUTE"
+,
+.br
+.br
+.BI " NBFT_HFI_INFO_TCP_DHCP_OVERRIDE"
+
+};
+.SH Constants
+.IP "NBFT_HFI_INFO_TCP_VALID" 12
+Descriptor Valid: if set to 1h, then this
+descriptor is valid. If cleared to 0h, then
+this descriptor is reserved.
+.IP "NBFT_HFI_INFO_TCP_GLOBAL_ROUTE" 12
+Global Route vs. Link Local Override Flag:
+if set to 1h, then the BIOS utilized this
+interface described by HFI to be the default
+route with highest priority. If cleared to 0h,
+then routes are local to their own scope.
+.IP "NBFT_HFI_INFO_TCP_DHCP_OVERRIDE" 12
+DHCP Override: if set to 1, then HFI information
+was populated by consuming the DHCP on this
+interface. If cleared to 0h, then the HFI
+information was set administratively by
+a configuration interface to the driver and
+pre-OS envrionment.
diff --git a/doc/man/nbft_host.2 b/doc/man/nbft_host.2
new file mode 100644
index 0000000..27c77bd
--- /dev/null
+++ b/doc/man/nbft_host.2
@@ -0,0 +1,36 @@
+.TH "libnvme" 9 "struct nbft_host" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_host \- Host Descriptor (Figure 9)
+.SH SYNOPSIS
+struct nbft_host {
+.br
+.BI " __u8 structure_id;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __u8 host_id[16];"
+.br
+.BI " struct nbft_heap_obj host_nqn_obj;"
+.br
+.BI " __u8 reserved[8];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "structure_id" 12
+Structure ID: This field shall be set to 2h (i.e.,
+Host Descriptor; #NBFT_DESC_HOST).
+.IP "flags" 12
+Host Flags, see \fIenum nbft_host_flags\fP.
+.IP "host_id" 12
+Host ID: This field shall be set to the Host Identifier. This
+field shall not be empty if the NBFT and NVMe Boot are supported
+by the Platform.
+.IP "host_nqn_obj" 12
+Host NQN Heap Object Reference: this field indicates a heap
+object containing a Host NQN. This object shall not be empty
+if the NBFT and NVMe Boot are supported by the Platform.
+.IP "reserved" 12
+Reserved.
diff --git a/doc/man/nbft_host_flags.2 b/doc/man/nbft_host_flags.2
new file mode 100644
index 0000000..1443059
--- /dev/null
+++ b/doc/man/nbft_host_flags.2
@@ -0,0 +1,75 @@
+.TH "libnvme" 9 "enum nbft_host_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_host_flags \- Host Flags
+.SH SYNOPSIS
+enum nbft_host_flags {
+.br
+.BI " NBFT_HOST_VALID"
+,
+.br
+.br
+.BI " NBFT_HOST_HOSTID_CONFIGURED"
+,
+.br
+.br
+.BI " NBFT_HOST_HOSTNQN_CONFIGURED"
+,
+.br
+.br
+.BI " NBFT_HOST_PRIMARY_ADMIN_MASK"
+,
+.br
+.br
+.BI " NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED"
+,
+.br
+.br
+.BI " NBFT_HOST_PRIMARY_ADMIN_UNSELECTED"
+,
+.br
+.br
+.BI " NBFT_HOST_PRIMARY_ADMIN_SELECTED"
+
+};
+.SH Constants
+.IP "NBFT_HOST_VALID" 12
+Descriptor Valid: If set to 1h, then this
+descriptor is valid. If cleared to 0h, then
+this descriptor is reserved.
+.IP "NBFT_HOST_HOSTID_CONFIGURED" 12
+HostID Configured: If set to 1h, then the
+Host ID field contains an administratively-configured
+value. If cleared to 0h, then the Host ID
+field contains a driver default value.
+.IP "NBFT_HOST_HOSTNQN_CONFIGURED" 12
+Host NQN Configured: If set to 1h, then the
+Host NQN indicated by the Host NQN Heap Object
+Reference field (\fIstruct nbft_host\fP.host_nqn)
+contains an administratively-configured value.
+If cleared to 0h, then the Host NQN indicated
+by the Host NQN Offset field contains a driver
+default value.
+.IP "NBFT_HOST_PRIMARY_ADMIN_MASK" 12
+Mask to get Primary Administrative Host Descriptor:
+indicates whether the Host Descriptor in this
+NBFT was selected as the primary NBFT for
+administrative purposes of platform identity
+as a hint to the OS. If multiple NBFT tables
+are present, only one NBFT should be administratively
+selected. There is no enforcement mechanism
+for this to be coordinated between multiple NBFT
+tables, but this field should be set to Selected
+(#NBFT_HOST_PRIMARY_ADMIN_SELECTED) if
+more than one NBFT is present.
+.IP "NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED" 12
+Not Indicated by Driver: The driver that created
+this NBFT provided no administrative priority
+hint for this NBFT.
+.IP "NBFT_HOST_PRIMARY_ADMIN_UNSELECTED" 12
+Unselected: The driver that created this NBFT
+explicitly indicated that this NBFT should
+not be prioritized over any other NBFT.
+.IP "NBFT_HOST_PRIMARY_ADMIN_SELECTED" 12
+Selected: The driver that created this NBFT
+explicitly indicated that this NBFT should
+be prioritized over any other NBFT.
diff --git a/doc/man/nbft_info.2 b/doc/man/nbft_info.2
new file mode 100644
index 0000000..565b9cf
--- /dev/null
+++ b/doc/man/nbft_info.2
@@ -0,0 +1,43 @@
+.TH "libnvme" 9 "struct nbft_info" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_info \- The parsed NBFT table data.
+.SH SYNOPSIS
+struct nbft_info {
+.br
+.BI " char *filename;"
+.br
+.BI " __u8 *raw_nbft;"
+.br
+.BI " ssize_t raw_nbft_size;"
+.br
+.BI " struct nbft_info_host host;"
+.br
+.BI " struct nbft_info_hfi **hfi_list;"
+.br
+.BI " struct nbft_info_security **security_list;"
+.br
+.BI " struct nbft_info_discovery **discovery_list;"
+.br
+.BI " struct nbft_info_subsystem_ns **subsystem_ns_list;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "filename" 12
+Path to the NBFT table.
+.IP "raw_nbft" 12
+The original NBFT table contents.
+.IP "raw_nbft_size" 12
+Size of \fIraw_nbft\fP.
+.IP "host" 12
+The Host Descriptor (should match other NBFTs).
+.IP "hfi_list" 12
+The HFI Descriptor List (null-terminated array).
+.IP "security_list" 12
+The Security Profile Descriptor List (null-terminated array).
+.IP "discovery_list" 12
+The Discovery Descriptor List (null-terminated array).
+.IP "subsystem_ns_list" 12
+The SSNS Descriptor List (null-terminated array).
diff --git a/doc/man/nbft_info_discovery.2 b/doc/man/nbft_info_discovery.2
new file mode 100644
index 0000000..d4555e7
--- /dev/null
+++ b/doc/man/nbft_info_discovery.2
@@ -0,0 +1,34 @@
+.TH "libnvme" 9 "struct nbft_info_discovery" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_info_discovery \- Discovery Descriptor
+.SH SYNOPSIS
+struct nbft_info_discovery {
+.br
+.BI " int index;"
+.br
+.BI " struct nbft_info_security *security;"
+.br
+.BI " struct nbft_info_hfi *hfi;"
+.br
+.BI " char *uri;"
+.br
+.BI " char *nqn;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "index" 12
+The number of this Discovery Descriptor in the Discovery
+Descriptor List.
+.IP "security" 12
+The Security Profile Descriptor, see \fIstruct nbft_info_security\fP.
+.IP "hfi" 12
+The HFI Descriptor associated with this Discovery Descriptor.
+See \fIstruct nbft_info_hfi\fP.
+.IP "uri" 12
+A URI which indicates an NVMe Discovery controller associated
+with this Discovery Descriptor.
+.IP "nqn" 12
+An NVMe Discovery controller NQN.
diff --git a/doc/man/nbft_info_hfi.2 b/doc/man/nbft_info_hfi.2
new file mode 100644
index 0000000..7566e2b
--- /dev/null
+++ b/doc/man/nbft_info_hfi.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "struct nbft_info_hfi" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_info_hfi \- Host Fabric Interface (HFI) Descriptor
+.SH SYNOPSIS
+struct nbft_info_hfi {
+.br
+.BI " int index;"
+.br
+.BI " char transport[8];"
+.br
+.BI " struct nbft_info_hfi_info_tcp tcp_info;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "index" 12
+HFI Descriptor Index: indicates the number of this HFI Descriptor
+in the Host Fabric Interface Descriptor List.
+.IP "transport" 12
+Transport Type string (e.g. 'tcp').
+.IP "tcp_info" 12
+The HFI Transport Info Descriptor, see \fIstruct nbft_info_hfi_info_tcp\fP.
diff --git a/doc/man/nbft_info_hfi_info_tcp.2 b/doc/man/nbft_info_hfi_info_tcp.2
new file mode 100644
index 0000000..52e3077
--- /dev/null
+++ b/doc/man/nbft_info_hfi_info_tcp.2
@@ -0,0 +1,83 @@
+.TH "libnvme" 9 "struct nbft_info_hfi_info_tcp" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_info_hfi_info_tcp \- HFI Transport Info Descriptor - NVMe/TCP
+.SH SYNOPSIS
+struct nbft_info_hfi_info_tcp {
+.br
+.BI " __u32 pci_sbdf;"
+.br
+.BI " __u8 mac_addr[6];"
+.br
+.BI " __u16 vlan;"
+.br
+.BI " __u8 ip_origin;"
+.br
+.BI " char ipaddr[40];"
+.br
+.BI " __u8 subnet_mask_prefix;"
+.br
+.BI " char gateway_ipaddr[40];"
+.br
+.BI " __u16 route_metric;"
+.br
+.BI " char primary_dns_ipaddr[40];"
+.br
+.BI " char secondary_dns_ipaddr[40];"
+.br
+.BI " char dhcp_server_ipaddr[40];"
+.br
+.BI " char *host_name;"
+.br
+.BI " bool this_hfi_is_default_route;"
+.br
+.BI " bool dhcp_override;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "pci_sbdf" 12
+PCI Express Routing ID for the HFI Transport Function.
+.IP "mac_addr" 12
+MAC Address: The MAC address of this HFI,
+in EUI-48TM format.
+.IP "vlan" 12
+The VLAN identifier if the VLAN is associated with
+this HFI, as defined in IEEE 802.1q-2018 or zeroes
+if no VLAN is associated with this HFI.
+.IP "ip_origin" 12
+The source of Ethernet L3 configuration information
+used by the driver or 0 if not used.
+.IP "ipaddr" 12
+The IPv4 or IPv6 address of this HFI.
+.IP "subnet_mask_prefix" 12
+The IPv4 or IPv6 subnet mask in CIDR routing prefix
+notation.
+.IP "gateway_ipaddr" 12
+The IPv4 or IPv6 address of the IP gateway for this
+HFI or zeroes if no IP gateway is specified.
+.IP "route_metric" 12
+The cost value for the route indicated by this HFI.
+.IP "primary_dns_ipaddr" 12
+The IPv4 or IPv6 address of the Primary DNS server
+for this HFI.
+.IP "secondary_dns_ipaddr" 12
+The IPv4 or IPv6 address of the Secondary DNS server
+for this HFI.
+.IP "dhcp_server_ipaddr" 12
+The IPv4 or IPv6 address of the DHCP server used
+to assign this HFI address.
+.IP "host_name" 12
+The Host Name string.
+.IP "this_hfi_is_default_route" 12
+If True, then the BIOS utilized this interface
+described by HFI to be the default route with highest
+priority. If False, then routes are local to their
+own scope.
+.IP "dhcp_override" 12
+If True, then HFI information was populated
+by consuming the DHCP on this interface. If False,
+then the HFI information was set administratively
+by a configuration interface to the driver and
+pre-OS envrionment.
diff --git a/doc/man/nbft_info_host.2 b/doc/man/nbft_info_host.2
new file mode 100644
index 0000000..4afc90b
--- /dev/null
+++ b/doc/man/nbft_info_host.2
@@ -0,0 +1,36 @@
+.TH "libnvme" 9 "struct nbft_info_host" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_info_host \- Host Descriptor
+.SH SYNOPSIS
+struct nbft_info_host {
+.br
+.BI " unsigned char *id;"
+.br
+.BI " char *nqn;"
+.br
+.BI " bool host_id_configured;"
+.br
+.BI " bool host_nqn_configured;"
+.br
+.BI " enum nbft_info_primary_admin_host_flag primary;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "id" 12
+Host ID (raw UUID, length = 16 bytes).
+.IP "nqn" 12
+Host NQN.
+.IP "host_id_configured" 12
+HostID Configured Flag: value of True indicates that \fIid\fP
+contains administratively-configured value, or driver
+default value if False.
+.IP "host_nqn_configured" 12
+Host NQN Configured Flag: value of True indicates that
+\fInqn\fP contains administratively-configured value,
+or driver default value if False.
+.IP "primary" 12
+Primary Administrative Host Descriptor, see
+\fIenum nbft_info_primary_admin_host_flag\fP.
diff --git a/doc/man/nbft_info_nid_type.2 b/doc/man/nbft_info_nid_type.2
new file mode 100644
index 0000000..74599ad
--- /dev/null
+++ b/doc/man/nbft_info_nid_type.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nbft_info_nid_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_info_nid_type \- Namespace Identifier Type (NIDT)
+.SH SYNOPSIS
+enum nbft_info_nid_type {
+.br
+.BI " NBFT_INFO_NID_TYPE_NONE"
+,
+.br
+.br
+.BI " NBFT_INFO_NID_TYPE_EUI64"
+,
+.br
+.br
+.BI " NBFT_INFO_NID_TYPE_NGUID"
+,
+.br
+.br
+.BI " NBFT_INFO_NID_TYPE_NS_UUID"
+
+};
+.SH Constants
+.IP "NBFT_INFO_NID_TYPE_NONE" 12
+No identifier available.
+.IP "NBFT_INFO_NID_TYPE_EUI64" 12
+The EUI-64 identifier.
+.IP "NBFT_INFO_NID_TYPE_NGUID" 12
+The NSGUID identifier.
+.IP "NBFT_INFO_NID_TYPE_NS_UUID" 12
+The UUID identifier.
diff --git a/doc/man/nbft_info_primary_admin_host_flag.2 b/doc/man/nbft_info_primary_admin_host_flag.2
new file mode 100644
index 0000000..a1c6db3
--- /dev/null
+++ b/doc/man/nbft_info_primary_admin_host_flag.2
@@ -0,0 +1,39 @@
+.TH "libnvme" 9 "enum nbft_info_primary_admin_host_flag" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_info_primary_admin_host_flag \- Primary Administrative Host Descriptor Flags
+.SH SYNOPSIS
+enum nbft_info_primary_admin_host_flag {
+.br
+.BI " NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED"
+,
+.br
+.br
+.BI " NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED"
+,
+.br
+.br
+.BI " NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED"
+,
+.br
+.br
+.BI " NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED"
+
+};
+.SH Constants
+.IP "NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED" 12
+Not Indicated by Driver: The driver
+that created this NBFT provided no
+administrative priority hint for
+this NBFT.
+.IP "NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED" 12
+Unselected: The driver that created
+this NBFT explicitly indicated that
+this NBFT should not be prioritized
+over any other NBFT.
+.IP "NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED" 12
+Selected: The driver that created
+this NBFT explicitly indicated that
+this NBFT should be prioritized over
+any other NBFT.
+.IP "NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED" 12
+Reserved.
diff --git a/doc/man/nbft_info_security.2 b/doc/man/nbft_info_security.2
new file mode 100644
index 0000000..915aeb7
--- /dev/null
+++ b/doc/man/nbft_info_security.2
@@ -0,0 +1,16 @@
+.TH "libnvme" 9 "struct nbft_info_security" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_info_security \- Security Profile Descriptor
+.SH SYNOPSIS
+struct nbft_info_security {
+.br
+.BI " int index;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "index" 12
+The number of this Security Profile Descriptor in the Security
+Profile Descriptor List.
diff --git a/doc/man/nbft_info_subsystem_ns.2 b/doc/man/nbft_info_subsystem_ns.2
new file mode 100644
index 0000000..ba11740
--- /dev/null
+++ b/doc/man/nbft_info_subsystem_ns.2
@@ -0,0 +1,94 @@
+.TH "libnvme" 9 "struct nbft_info_subsystem_ns" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_info_subsystem_ns \- Subsystem Namespace (SSNS) info
+.SH SYNOPSIS
+struct nbft_info_subsystem_ns {
+.br
+.BI " int index;"
+.br
+.BI " struct nbft_info_discovery *discovery;"
+.br
+.BI " struct nbft_info_security *security;"
+.br
+.BI " int num_hfis;"
+.br
+.BI " struct nbft_info_hfi **hfis;"
+.br
+.BI " char transport[8];"
+.br
+.BI " char traddr[40];"
+.br
+.BI " char *trsvcid;"
+.br
+.BI " __u16 subsys_port_id;"
+.br
+.BI " __u32 nsid;"
+.br
+.BI " enum nbft_info_nid_type nid_type;"
+.br
+.BI " __u8 *nid;"
+.br
+.BI " char *subsys_nqn;"
+.br
+.BI " bool pdu_header_digest_required;"
+.br
+.BI " bool data_digest_required;"
+.br
+.BI " int controller_id;"
+.br
+.BI " int asqsz;"
+.br
+.BI " char *dhcp_root_path_string;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "index" 12
+SSNS Descriptor Index in the descriptor list.
+.IP "discovery" 12
+Primary Discovery Controller associated with
+this SSNS Descriptor.
+.IP "security" 12
+Security Profile Descriptor associated with
+this namespace.
+.IP "num_hfis" 12
+Number of HFIs.
+.IP "hfis" 12
+List of HFIs associated with this namespace.
+Includes the primary HFI at the first position
+and all secondary HFIs. This array is null-terminated.
+.IP "transport" 12
+Transport Type string (e.g. 'tcp').
+.IP "traddr" 12
+Subsystem Transport Address.
+.IP "trsvcid" 12
+Subsystem Transport Service Identifier.
+.IP "subsys_port_id" 12
+The Subsystem Port ID.
+.IP "nsid" 12
+The Namespace ID of this descriptor or when \fInid\fP
+should be used instead.
+.IP "nid_type" 12
+Namespace Identifier Type, see \fIenum nbft_info_nid_type\fP.
+.IP "nid" 12
+The Namespace Identifier value.
+.IP "subsys_nqn" 12
+Subsystem and Namespace NQN.
+.IP "pdu_header_digest_required" 12
+PDU Header Digest (HDGST) Flag: the use of NVM Header
+Digest Enabled is required.
+.IP "data_digest_required" 12
+Data Digest (DDGST) Flag: the use of NVM Data Digest
+Enabled is required.
+.IP "controller_id" 12
+Controller ID (SSNS Extended Information Descriptor):
+The controller ID associated with the Admin Queue
+or 0 if not supported.
+.IP "asqsz" 12
+Admin Submission Queue Size (SSNS Extended Information
+Descriptor) or 0 if not supported.
+.IP "dhcp_root_path_string" 12
+DHCP Root Path Override string (SSNS Extended
+Information Descriptor).
diff --git a/doc/man/nbft_security.2 b/doc/man/nbft_security.2
new file mode 100644
index 0000000..c8060b0
--- /dev/null
+++ b/doc/man/nbft_security.2
@@ -0,0 +1,98 @@
+.TH "libnvme" 9 "struct nbft_security" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_security \- Security Profile Descriptor (Figure 21)
+.SH SYNOPSIS
+struct nbft_security {
+.br
+.BI " __u8 structure_id;"
+.br
+.BI " __u8 index;"
+.br
+.BI " __le16 flags;"
+.br
+.BI " __u8 secret_type;"
+.br
+.BI " __u8 reserved1;"
+.br
+.BI " struct nbft_heap_obj sec_chan_alg_obj;"
+.br
+.BI " struct nbft_heap_obj auth_proto_obj;"
+.br
+.BI " struct nbft_heap_obj cipher_suite_obj;"
+.br
+.BI " struct nbft_heap_obj dh_grp_obj;"
+.br
+.BI " struct nbft_heap_obj sec_hash_func_obj;"
+.br
+.BI " struct nbft_heap_obj sec_keypath_obj;"
+.br
+.BI " __u8 reserved2[22];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "structure_id" 12
+Structure ID: This field shall be set to 5h
+(i.e., Security; #NBFT_DESC_SECURITY).
+.IP "index" 12
+Security Profile Descriptor Index: This field indicates
+the number of this Security Profile Descriptor in the
+Security Profile Descriptor List.
+.IP "flags" 12
+Security Profile Descriptor Flags, see \fIenum nbft_security_flags\fP.
+.IP "secret_type" 12
+Secret Type, see \fIenum nbft_security_secret_type\fP.
+.IP "reserved1" 12
+Reserved.
+.IP "sec_chan_alg_obj" 12
+Secure Channel Algorithm Heap Object Reference: If the
+Security Policy List field is set to 1h, then this field
+indicates the location and size of a heap object containing
+a list of secure channel algorithms. The list is an array
+of bytes and the values are defined in the Security Type
+(SECTYPE) field in the Transport Specific Address Subtype
+Definition in the NVMe TCP Transport Specification.
+If the Security Policy List field is cleared to 0h, then
+this field is reserved.
+.IP "auth_proto_obj" 12
+Authentication Protocols Heap Object Reference: If the
+Authentication Policy List field is set to 1h, then this
+field indicates the location and size of a heap object
+containing a list of authentication protocol identifiers.
+If the Authentication Policy List field is cleared to 0h,
+then this field is reserved.
+.IP "cipher_suite_obj" 12
+Cipher Suite Offset Heap Object Reference: If the Cipher
+Suites Restricted by Policy bit is set to 1h, then this
+field indicates the location and size of a heap object
+containing a list of cipher suite identifiers. The list,
+if any, is an array of bytes and the values are defined
+in the IANA TLS Parameters Registry. If the Cipher Suites
+Restricted by Policy bit is cleared to 0h, then this field
+is reserved.
+.IP "dh_grp_obj" 12
+DH Groups Heap Object Reference: If the Authentication DH Groups
+Restricted by Policy List bit is set to 1h, then this field
+indicates the location and size of a heap object containing
+a list of DH-HMAC-CHAP Diffie-Hellman (DH) group identifiers.
+If the Authentication DH Groups Restricted by Policy List
+bit is cleared to 0h, then this field is reserved.
+.IP "sec_hash_func_obj" 12
+Secure Hash Functions Offset Heap Object Reference: If the
+Secure Hash Functions Policy List bit is set to 1h, then
+this field indicates the offset in bytes of a heap object
+containing a list of DH-HMAC-CHAP hash function identifiers.
+The list is an array of bytes and the values are defined
+in the NVM Express Base Specification. If the Secure Hash
+Functions Policy List bit is cleared to 0h, then this
+field is reserved.
+.IP "sec_keypath_obj" 12
+Secret Keypath Offset Heap Object Reference: if this field
+is set to a non-zero value, then this field indicates
+the location and size of a heap object containing a URI.
+The type of the URI is specified in the Secret Type field.
+If this field is cleared to 0h, then this field is reserved.
+.IP "reserved2" 12
+Reserved.
diff --git a/doc/man/nbft_security_flags.2 b/doc/man/nbft_security_flags.2
new file mode 100644
index 0000000..8047a8b
--- /dev/null
+++ b/doc/man/nbft_security_flags.2
@@ -0,0 +1,179 @@
+.TH "libnvme" 9 "enum nbft_security_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_security_flags \- Security Profile Descriptor Flags (Figure 22)
+.SH SYNOPSIS
+enum nbft_security_flags {
+.br
+.BI " NBFT_SECURITY_VALID"
+,
+.br
+.br
+.BI " NBFT_SECURITY_IN_BAND_AUTH_MASK"
+,
+.br
+.br
+.BI " NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_IN_BAND_AUTH_REQUIRED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_AUTH_POLICY_LIST_MASK"
+,
+.br
+.br
+.BI " NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER"
+,
+.br
+.br
+.BI " NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_CHAN_NEG_MASK"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_POLICY_LIST_MASK"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_POLICY_LIST_DRIVER"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_POLICY_LIST_ADMIN"
+,
+.br
+.br
+.BI " NBFT_SECURITY_CIPHER_RESTRICTED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED"
+,
+.br
+.br
+.BI " NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST"
+
+};
+.SH Constants
+.IP "NBFT_SECURITY_VALID" 12
+Descriptor Valid: If set to 1h, then
+this descriptor is valid. If cleared
+to 0h, then this descriptor is not valid.
+.IP "NBFT_SECURITY_IN_BAND_AUTH_MASK" 12
+Mask to get the In-Band Authentication
+Required field.
+.IP "NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED" 12
+In-band authentication is not supported
+by the NVM subsystem.
+.IP "NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED" 12
+In-band authentication is supported by
+the NVM subsystem and is not required.
+.IP "NBFT_SECURITY_IN_BAND_AUTH_REQUIRED" 12
+In-band authentication is supported by
+the NVM subsystem and is required.
+.IP "NBFT_SECURITY_AUTH_POLICY_LIST_MASK" 12
+Mask to get the Authentication Policy List
+flag: This field indicates whether
+authentication protocols were indicated
+by policy from driver defaults or
+administrative configuration.
+.IP "NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED" 12
+Authentication Protocols Heap Object Reference
+field Offset and Length are reserved.
+.IP "NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER" 12
+Authentication Protocols Offset field and
+the Authentication Protocols Length field
+indicate a list of authentication protocols
+used by the driver.
+.IP "NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN" 12
+Authentication Protocols Offset field and
+the Authentication Protocols Length field
+indicate a list of authentication protocols
+that were administratively set and used
+by the driver.
+.IP "NBFT_SECURITY_SEC_CHAN_NEG_MASK" 12
+Mask to get the Secure Channel Negotiation
+Required flag: This field indicates whether
+secure channel negotiation (e.g. TLS)
+is required.
+.IP "NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED" 12
+Secure channel negotiation is not supported
+by the NVM subsystem.
+.IP "NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED" 12
+Secure channel negotiation is supported
+by the NVM subsystem and is not required.
+.IP "NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED" 12
+Secure channel negotiation is supported
+by the NVM subsystem and is required.
+.IP "NBFT_SECURITY_SEC_POLICY_LIST_MASK" 12
+Mask to get the Security Policy List flag:
+This field indicates whether secure channel
+protocols were indicated by policy from driver
+defaults or administrative configuration.
+.IP "NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED" 12
+The Offset field and Length field in the
+Secure Channel Algorithm Heap Object Reference
+field are reserved.
+.IP "NBFT_SECURITY_SEC_POLICY_LIST_DRIVER" 12
+The Heap Object specified by the Secure Channel
+Algorithm Heap Object Reference field indicates
+a list of authentication protocols used
+by the driver.
+.IP "NBFT_SECURITY_SEC_POLICY_LIST_ADMIN" 12
+The Heap Object specified by the Secure Channel
+Algorithm Heap Object Reference field indicates
+a list of authentication protocols that were
+administratively set and used by the driver.
+.IP "NBFT_SECURITY_CIPHER_RESTRICTED" 12
+Cipher Suites Restricted by Policy: If set to 1h,
+then the Cipher Suite Offset field and the
+Ciper Suite Length field indicate a list
+of supported cipher suites by the driver.
+If cleared to 0h, then the Cipher Suite Offset
+field and the Cipher Suite Length field
+are reserved.
+.IP "NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED" 12
+Authentication DH Groups Restricted
+by Policy List: If set to 1h, then connections
+shall use one of the authentication DH groups
+in the Authentication DH Groups List is required.
+If cleared to 0h, then no Authentication DH Groups
+List is indicated and use of an authentication
+DH Group is not required.
+.IP "NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST" 12
+Secure Hash Functions Policy List: If set to 1h,
+then connections shall use one of the secure
+hash functions in the Secure Hash Functions
+Policy List is required. If cleared to 0h,
+then no Secure Hash Functions Policy
+List is indicated and use of a secure
+hash function is not required.
diff --git a/doc/man/nbft_security_secret_type.2 b/doc/man/nbft_security_secret_type.2
new file mode 100644
index 0000000..e7ecaaa
--- /dev/null
+++ b/doc/man/nbft_security_secret_type.2
@@ -0,0 +1,16 @@
+.TH "libnvme" 9 "enum nbft_security_secret_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_security_secret_type \- Security Profile Descriptor Secret Type
+.SH SYNOPSIS
+enum nbft_security_secret_type {
+.br
+.BI " NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI"
+
+};
+.SH Constants
+.IP "NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI" 12
+Redfish Host Interface URI:
+If set to 1h, then the Secret Keypath
+Object Reference is a URI pointing
+to a Redfish Key Collection Object
+that contains the PSK.
diff --git a/doc/man/nbft_ssns.2 b/doc/man/nbft_ssns.2
new file mode 100644
index 0000000..f2982c7
--- /dev/null
+++ b/doc/man/nbft_ssns.2
@@ -0,0 +1,159 @@
+.TH "libnvme" 9 "struct nbft_ssns" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_ssns \- Subsystem Namespace (SSNS) Descriptor (Figure 15)
+.SH SYNOPSIS
+struct nbft_ssns {
+.br
+.BI " __u8 structure_id;"
+.br
+.BI " __le16 index;"
+.br
+.BI " __le16 flags;"
+.br
+.BI " __u8 trtype;"
+.br
+.BI " __le16 trflags;"
+.br
+.BI " __u8 primary_discovery_ctrl_index;"
+.br
+.BI " __u8 reserved1;"
+.br
+.BI " struct nbft_heap_obj subsys_traddr_obj;"
+.br
+.BI " struct nbft_heap_obj subsys_trsvcid_obj;"
+.br
+.BI " __le16 subsys_port_id;"
+.br
+.BI " __le32 nsid;"
+.br
+.BI " __u8 nidt;"
+.br
+.BI " __u8 nid[16];"
+.br
+.BI " __u8 security_desc_index;"
+.br
+.BI " __u8 primary_hfi_desc_index;"
+.br
+.BI " __u8 reserved2;"
+.br
+.BI " struct nbft_heap_obj secondary_hfi_assoc_obj;"
+.br
+.BI " struct nbft_heap_obj subsys_ns_nqn_obj;"
+.br
+.BI " struct nbft_heap_obj ssns_extended_info_desc_obj;"
+.br
+.BI " __u8 reserved3[62];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "structure_id" 12
+Structure ID: This field shall be set to 4h
+(i.e., SSNS; #NBFT_DESC_SSNS).
+.IP "index" 12
+SSNS Descriptor Index: This field indicates the number
+of this Subsystem Namespace Descriptor in the
+Subsystem Namespace Descriptor List.
+.IP "flags" 12
+SSNS Flags, see \fIenum nbft_ssns_flags\fP.
+.IP "trtype" 12
+Transport Type, see \fIenum nbft_trtype\fP.
+.IP "trflags" 12
+Transport Specific Flags, see \fIenum nbft_ssns_trflags\fP.
+.IP "primary_discovery_ctrl_index" 12
+Primary Discovery Controller Index: The Discovery
+Descriptor Index field of the Discovery Descriptor
+(see \fIstruct nbft_discovery\fP) that is associated with
+this SSNS Descriptor. If a Discovery controller was
+used to establish this record this value shall
+be set to a non-zero value. If this namespace was
+associated with multiple Discovery controllers,
+those Discovery controllers shall have records
+in the Discovery Descriptor to facilitate multi-path
+rediscovery as required. If no Discovery controller
+was utilized to inform this namespace record,
+this field shall be cleared to 0h.
+.IP "reserved1" 12
+Reserved.
+.IP "subsys_traddr_obj" 12
+Subsystem Transport Address Heap Object Reference:
+This field indicates the location and size of a heap
+object containing the Subsystem Transport Address.
+For IP based transports types, shall be an IP Address.
+.IP "subsys_trsvcid_obj" 12
+Subsystem Transport Service Identifier Heap Object Reference:
+This field indicates the location and size of a heap
+object containing an array of bytes indicating
+the Subsystem Transport Service Identifier.
+See \fIenum nbft_trtype\fP.
+.IP "subsys_port_id" 12
+Subsystem Port ID: Port in the NVM subsystem
+associated with this transport address used by
+the pre-OS driver.
+.IP "nsid" 12
+Namespace ID: This field indicates the namespace
+identifier (NSID) of the namespace indicated by
+this descriptor. This field shall be cleared to 0h
+if not specified by the user. If this value is cleared
+to 0h, then consumers of the NBFT shall rely
+on the NID.
+.IP "nidt" 12
+Namespace Identifier Type (NIDT): This field
+contains the value of the Namespace Identifier Type (NIDT)
+field in the Namespace Identification Descriptor
+for the namespace indicated by this descriptor.
+If a namespace supports multiple NIDT entries
+for uniqueness, the order of preference is NIDT field
+value of 3h (i.e., UUID) before 2h (i.e., NSGUID),
+and 2h before 1h (i.e., EUI-64).
+.IP "nid" 12
+Namespace Identifier (NID): This field contains
+the value of the Namespace Identifier (NID) field
+in the Namespace Identification Descriptor for
+the namespace indicated by this descriptor.
+.IP "security_desc_index" 12
+Security Profile Descriptor Index: If the Use Security
+Flag bit in the SSNS Flags field is set to 1h, then
+this field indicates the value of the Security Profile
+Descriptor Index field of the Security Profile
+Descriptor (see \fIstruct nbft_security\fP) associated
+with this namespace. If the Use Security Flag bit
+is cleared to 0h, then no Security Profile Descriptor
+is associated with this namespace and this field
+is reserved.
+.IP "primary_hfi_desc_index" 12
+Primary HFI Descriptor Index: This field indicates
+the value of the HFI Descriptor Index field of the
+HFI Descriptor (see \fIstruct nbft_hfi\fP) for the
+interface associated with this namespace. If multiple
+HFIs are associated with this record, subsequent
+interfaces should be populated in the Secondary
+HFI Associations field.
+.IP "reserved2" 12
+Reserved.
+.IP "secondary_hfi_assoc_obj" 12
+Secondary HFI Associations Heap Object Reference:
+If this field is set to a non-zero value, then
+this field indicates an array of bytes, in which
+each byte contains the value of the HFI Descriptor
+Index field of an HFI Descriptor in the HFI Descriptor
+List. If this field is cleared to 0h, then no
+secondary HFI associations are specified.
+.IP "subsys_ns_nqn_obj" 12
+Subsystem and Namespace NQN Heap Object Reference:
+This field indicates the location and size of
+a heap object containing the Subsystem and Namespace NQN.
+.IP "ssns_extended_info_desc_obj" 12
+SSNS Extended Information Descriptor Heap Object
+Reference: If the SSNS Extended Info In-use Flag
+bit is set to 1h, then this field indicates the
+offset in bytes of a heap object containing an
+SSNS Extended Information Descriptor
+(see \fIstruct nbft_ssns_ext_info\fP) heap object
+from byte offset 0h of the NBFT Table Header.
+If the SSNS Extended Info In-use Flag bit is cleared
+to 0h, then this field is reserved.
+.IP "reserved3" 12
+Reserved.
diff --git a/doc/man/nbft_ssns_ext_info.2 b/doc/man/nbft_ssns_ext_info.2
new file mode 100644
index 0000000..465f4d7
--- /dev/null
+++ b/doc/man/nbft_ssns_ext_info.2
@@ -0,0 +1,55 @@
+.TH "libnvme" 9 "struct nbft_ssns_ext_info" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nbft_ssns_ext_info \- Subsystem and Namespace Extended Information Descriptor (Figure 19)
+.SH SYNOPSIS
+struct nbft_ssns_ext_info {
+.br
+.BI " __u8 structure_id;"
+.br
+.BI " __u8 version;"
+.br
+.BI " __le16 ssns_index;"
+.br
+.BI " __le32 flags;"
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " __le16 asqsz;"
+.br
+.BI " struct nbft_heap_obj dhcp_root_path_str_obj;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "structure_id" 12
+Structure ID: This field shall be set to 9h
+(i.e., SSNS Extended Info; #NBFT_DESC_SSNS_EXT_INFO).
+.IP "version" 12
+Version: This field shall be set to 1h.
+.IP "ssns_index" 12
+SSNS Descriptor Index: This field indicates the value
+of the SSNS Descriptor Index field of the Subsystem
+and Namespace Descriptor (see \fIstruct nbft_ssns\fP) whose
+SSNS Extended Information Descriptor Heap Object
+Reference field indicates this descriptor.
+.IP "flags" 12
+Flags, see \fIenum nbft_ssns_ext_info_flags\fP.
+.IP "cntlid" 12
+Controller ID: The controller identifier of the first
+controller associated with the Admin Queue by the driver.
+If a controller identifier is not administratively
+specified or direct configuration is not supported
+by the driver, then this field shall be cleared to 0h.
+.IP "asqsz" 12
+Admin Submission Queue Size (ASQSZ): The Admin Submission
+Queue Size utilized for the respective SSNS by the driver.
+.IP "dhcp_root_path_str_obj" 12
+DHCP Root Path String Heap Object Reference: If the
+SSNS DHCP Root Path Override (#NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE)
+flag bit is set to 1h, then this field indicates
+the offset in bytes of a heap object containing
+an DHCP Root Path String used by the driver. If the
+SNSS DHCP Root Path Override flag bit is cleared to 0h,
+then this field is reserved.
diff --git a/doc/man/nbft_ssns_ext_info_flags.2 b/doc/man/nbft_ssns_ext_info_flags.2
new file mode 100644
index 0000000..00932f4
--- /dev/null
+++ b/doc/man/nbft_ssns_ext_info_flags.2
@@ -0,0 +1,25 @@
+.TH "libnvme" 9 "enum nbft_ssns_ext_info_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_ssns_ext_info_flags \- Subsystem and Namespace Extended Information Descriptor Flags
+.SH SYNOPSIS
+enum nbft_ssns_ext_info_flags {
+.br
+.BI " NBFT_SSNS_EXT_INFO_VALID"
+,
+.br
+.br
+.BI " NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ"
+
+};
+.SH Constants
+.IP "NBFT_SSNS_EXT_INFO_VALID" 12
+Descriptor Valid: If set to 1h, then this descriptor
+is valid. If cleared to 0h, then this descriptor
+is reserved.
+.IP "NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ" 12
+Administrative ASQSZ: If set to 1h, then the value
+of the ASQSZ field was provided by administrative
+configuration for this SSNS record. If cleared
+to 0h, then the value of the ASQSZ field was
+either obtained by discovery or assumed
+by the driver.
diff --git a/doc/man/nbft_ssns_flags.2 b/doc/man/nbft_ssns_flags.2
new file mode 100644
index 0000000..b421e46
--- /dev/null
+++ b/doc/man/nbft_ssns_flags.2
@@ -0,0 +1,121 @@
+.TH "libnvme" 9 "enum nbft_ssns_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_ssns_flags \- Subsystem and Namespace Specific Flags Field (Figure 16)
+.SH SYNOPSIS
+enum nbft_ssns_flags {
+.br
+.BI " NBFT_SSNS_VALID"
+,
+.br
+.br
+.BI " NBFT_SSNS_NON_BOOTABLE_ENTRY"
+,
+.br
+.br
+.BI " NBFT_SSNS_USE_SECURITY_FIELD"
+,
+.br
+.br
+.BI " NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE"
+,
+.br
+.br
+.BI " NBFT_SSNS_EXTENDED_INFO_IN_USE"
+,
+.br
+.br
+.BI " NBFT_SSNS_SEPARATE_DISCOVERY_CTRL"
+,
+.br
+.br
+.BI " NBFT_SSNS_DISCOVERED_NAMESPACE"
+,
+.br
+.br
+.BI " NBFT_SSNS_UNAVAIL_NAMESPACE_MASK"
+,
+.br
+.br
+.BI " NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND"
+,
+.br
+.br
+.BI " NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL"
+,
+.br
+.br
+.BI " NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL"
+
+};
+.SH Constants
+.IP "NBFT_SSNS_VALID" 12
+Descriptor Valid: If set to 1h, then this descriptor
+is valid. If cleared to 0h, then this descriptor
+is not valid. A host that supports NVMe-oF Boot,
+but does not currently have a remote Subsystem
+and Namespace assigned may clear this bit to 0h.
+.IP "NBFT_SSNS_NON_BOOTABLE_ENTRY" 12
+Non-bootable Entry Flag: If set to 1h, this flag
+indicates that this SSNS Descriptor contains
+a namespace of administrative purpose to the boot
+process, but the pre-OS may not have established
+connectivity to or evaluated the contents of this
+Descriptor. Such namespaces may contain supplemental
+data deemed relevant by the Administrator as part
+of the pre-OS to OS hand off. This may include
+properties such as a UEFI device path that may
+not have been created for this namespace. This means
+an OS runtime may still require the contents
+of such a namespace to complete later stages
+of boot. If cleared to 0h, then this namespace did
+not have any special administrative intent.
+.IP "NBFT_SSNS_USE_SECURITY_FIELD" 12
+Use Security Flag: If set to 1h, then there is
+a Security Profile Descriptor associated with this
+SSNS record and the Security Profile Descriptor Index
+field is valid. If cleared to 0h, then there is
+no Security Profile Descriptor associated with this
+SSNS record and the Security Profile Descriptor Index
+field is not valid.
+.IP "NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE" 12
+DHCP Root-Path Override Flag: If set to 1h, then
+this SSNS descriptor was populated by consuming
+the DHCP Root-Path on this interface. If cleared
+to 0h, then the DHCP Root-Path was not used
+in populating the SSNS descriptor.
+.IP "NBFT_SSNS_EXTENDED_INFO_IN_USE" 12
+SSNS Extended Info In-use Flag: If set to 1h,
+then the SSNS Extended Information Offset field
+and the SSNS Extended Information Length field
+are valid. This flag, if set to 1h, indicates
+that a Subsystem and Namespace Extended Information
+Descriptor corresponding to this descriptor is present.
+.IP "NBFT_SSNS_SEPARATE_DISCOVERY_CTRL" 12
+Separate Discovery Controller Flag: If set to 1h,
+then the Discovery controller associated with
+this volume is on a different transport address
+than the specified in the Subsystem Transport
+Address Heap Object Reference. If cleared to 0h,
+then the Discovery controller is the same as the
+Subsystem Transport Address Heap Object Reference.
+.IP "NBFT_SSNS_DISCOVERED_NAMESPACE" 12
+Discovered Namespace Flag: If set to 1h, then
+this namespace was acquired through discovery.
+If cleared to 0h, then this namespace was
+explicitly configured in the system.
+.IP "NBFT_SSNS_UNAVAIL_NAMESPACE_MASK" 12
+Mask to get Unavailable Namespace Flag: This
+field indicates the availability of the namespace
+at a specific point in time. Such use is only
+a hint and its use does not guarantee the availability
+of that referenced namespace at any future point in time.
+.IP "NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND" 12
+Not Indicated by Driver: No information is provided.
+.IP "NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL" 12
+Available: A referenced namespace described by this
+flag was previously accessible by the pre-OS driver.
+.IP "NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL" 12
+Unavailable: This namespace was administratively
+configured but unattempted, unavailable or
+inaccessible when establishing connectivity
+by the pre-OS driver.
diff --git a/doc/man/nbft_ssns_trflags.2 b/doc/man/nbft_ssns_trflags.2
new file mode 100644
index 0000000..2c177b8
--- /dev/null
+++ b/doc/man/nbft_ssns_trflags.2
@@ -0,0 +1,44 @@
+.TH "libnvme" 9 "enum nbft_ssns_trflags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_ssns_trflags \- SSNS Transport Specific Flags Field (Figure 17)
+.SH SYNOPSIS
+enum nbft_ssns_trflags {
+.br
+.BI " NBFT_SSNS_TRFLAG_VALID"
+,
+.br
+.br
+.BI " NBFT_SSNS_PDU_HEADER_DIGEST"
+,
+.br
+.br
+.BI " NBFT_SSNS_DATA_DIGEST"
+
+};
+.SH Constants
+.IP "NBFT_SSNS_TRFLAG_VALID" 12
+Transport Specific Flags in Use: If set to 1h, then
+this descriptor is valid. If cleared to 0h, then
+this descriptor is not valid.
+.IP "NBFT_SSNS_PDU_HEADER_DIGEST" 12
+PDU Header Digest (HDGST) Flag: If set to 1h, then
+the host or administrator required the connection
+described by this Subsystem and Namespace Descriptor
+to use the NVM Header Digest Enabled. A consumer
+of this information should attempt to use NVM Header
+Digest when recreating this connection if enabled.
+If cleared to 0h, then the host or administrator
+did not require the connection described by this
+Subsystem and Namespace Descriptor to use the
+NVM Header Digest Enabled.
+.IP "NBFT_SSNS_DATA_DIGEST" 12
+Data Digest (DDGST) Flag: If set to 1h, then
+the host or administrator required the connection
+described by this Subsystem and Namespace Descriptor
+to use the NVM Data Digest Enabled. If cleared
+to 0h, then the host or administrator did not
+require the connection described by this Subsystem
+and Namespace Descriptor to use the NVM Data Digest
+Enabled. A consumer of this field should attempt
+to use NVM Data Digest when recreating this
+connection if enabled.
diff --git a/doc/man/nbft_trtype.2 b/doc/man/nbft_trtype.2
new file mode 100644
index 0000000..8d3b7ea
--- /dev/null
+++ b/doc/man/nbft_trtype.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nbft_trtype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nbft_trtype \- NBFT Interface Transport Types (Figure 7)
+.SH SYNOPSIS
+enum nbft_trtype {
+.br
+.BI " NBFT_TRTYPE_TCP"
+
+};
+.SH Constants
+.IP "NBFT_TRTYPE_TCP" 12
+NVMe/TCP (802.3 + TCP/IP). String Designator "tcp".
diff --git a/doc/man/nvme_admin_opcode.2 b/doc/man/nvme_admin_opcode.2
new file mode 100644
index 0000000..5ddc3be
--- /dev/null
+++ b/doc/man/nvme_admin_opcode.2
@@ -0,0 +1,216 @@
+.TH "libnvme" 9 "enum nvme_admin_opcode" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_admin_opcode \- Known NVMe admin opcodes
+.SH SYNOPSIS
+enum nvme_admin_opcode {
+.br
+.BI " nvme_admin_delete_sq"
+,
+.br
+.br
+.BI " nvme_admin_create_sq"
+,
+.br
+.br
+.BI " nvme_admin_get_log_page"
+,
+.br
+.br
+.BI " nvme_admin_delete_cq"
+,
+.br
+.br
+.BI " nvme_admin_create_cq"
+,
+.br
+.br
+.BI " nvme_admin_identify"
+,
+.br
+.br
+.BI " nvme_admin_abort_cmd"
+,
+.br
+.br
+.BI " nvme_admin_set_features"
+,
+.br
+.br
+.BI " nvme_admin_get_features"
+,
+.br
+.br
+.BI " nvme_admin_async_event"
+,
+.br
+.br
+.BI " nvme_admin_ns_mgmt"
+,
+.br
+.br
+.BI " nvme_admin_fw_commit"
+,
+.br
+.br
+.BI " nvme_admin_fw_activate"
+,
+.br
+.br
+.BI " nvme_admin_fw_download"
+,
+.br
+.br
+.BI " nvme_admin_dev_self_test"
+,
+.br
+.br
+.BI " nvme_admin_ns_attach"
+,
+.br
+.br
+.BI " nvme_admin_keep_alive"
+,
+.br
+.br
+.BI " nvme_admin_directive_send"
+,
+.br
+.br
+.BI " nvme_admin_directive_recv"
+,
+.br
+.br
+.BI " nvme_admin_virtual_mgmt"
+,
+.br
+.br
+.BI " nvme_admin_nvme_mi_send"
+,
+.br
+.br
+.BI " nvme_admin_nvme_mi_recv"
+,
+.br
+.br
+.BI " nvme_admin_capacity_mgmt"
+,
+.br
+.br
+.BI " nvme_admin_discovery_info_mgmt"
+,
+.br
+.br
+.BI " nvme_admin_fabric_zoning_recv"
+,
+.br
+.br
+.BI " nvme_admin_lockdown"
+,
+.br
+.br
+.BI " nvme_admin_fabric_zoning_lookup"
+,
+.br
+.br
+.BI " nvme_admin_fabric_zoning_send"
+,
+.br
+.br
+.BI " nvme_admin_dbbuf"
+,
+.br
+.br
+.BI " nvme_admin_fabrics"
+,
+.br
+.br
+.BI " nvme_admin_format_nvm"
+,
+.br
+.br
+.BI " nvme_admin_security_send"
+,
+.br
+.br
+.BI " nvme_admin_security_recv"
+,
+.br
+.br
+.BI " nvme_admin_sanitize_nvm"
+,
+.br
+.br
+.BI " nvme_admin_get_lba_status"
+
+};
+.SH Constants
+.IP "nvme_admin_delete_sq" 12
+Delete I/O Submission Queue
+.IP "nvme_admin_create_sq" 12
+Create I/O Submission Queue
+.IP "nvme_admin_get_log_page" 12
+Get Log Page
+.IP "nvme_admin_delete_cq" 12
+Delete I/O Completion Queue
+.IP "nvme_admin_create_cq" 12
+Create I/O Completion Queue
+.IP "nvme_admin_identify" 12
+Identify
+.IP "nvme_admin_abort_cmd" 12
+Abort
+.IP "nvme_admin_set_features" 12
+Set Features
+.IP "nvme_admin_get_features" 12
+Get Features
+.IP "nvme_admin_async_event" 12
+Asynchronous Event Request
+.IP "nvme_admin_ns_mgmt" 12
+Namespace Management
+.IP "nvme_admin_fw_commit" 12
+Firmware Commit
+.IP "nvme_admin_fw_activate" 12
+Firmware Commit
+.IP "nvme_admin_fw_download" 12
+Firmware Image Download
+.IP "nvme_admin_dev_self_test" 12
+Device Self-test
+.IP "nvme_admin_ns_attach" 12
+Namespace Attachment
+.IP "nvme_admin_keep_alive" 12
+Keep Alive
+.IP "nvme_admin_directive_send" 12
+Directive Send
+.IP "nvme_admin_directive_recv" 12
+Directive Receive
+.IP "nvme_admin_virtual_mgmt" 12
+Virtualization Management
+.IP "nvme_admin_nvme_mi_send" 12
+NVMe-MI Send
+.IP "nvme_admin_nvme_mi_recv" 12
+NVMe-MI Receive
+.IP "nvme_admin_capacity_mgmt" 12
+Capacity Management
+.IP "nvme_admin_discovery_info_mgmt" 12
+Discovery Information Management (DIM)
+.IP "nvme_admin_fabric_zoning_recv" 12
+Fabric Zoning Receive
+.IP "nvme_admin_lockdown" 12
+Lockdown
+.IP "nvme_admin_fabric_zoning_lookup" 12
+Fabric Zoning Lookup
+.IP "nvme_admin_fabric_zoning_send" 12
+Fabric Zoning Send
+.IP "nvme_admin_dbbuf" 12
+Doorbell Buffer Config
+.IP "nvme_admin_fabrics" 12
+Fabrics Commands
+.IP "nvme_admin_format_nvm" 12
+Format NVM
+.IP "nvme_admin_security_send" 12
+Security Send
+.IP "nvme_admin_security_recv" 12
+Security Receive
+.IP "nvme_admin_sanitize_nvm" 12
+Sanitize
+.IP "nvme_admin_get_lba_status" 12
+Get LBA Status
diff --git a/doc/man/nvme_admin_passthru.2 b/doc/man/nvme_admin_passthru.2
new file mode 100644
index 0000000..817fd74
--- /dev/null
+++ b/doc/man/nvme_admin_passthru.2
@@ -0,0 +1,71 @@
+.TH "nvme_admin_passthru" 9 "nvme_admin_passthru" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_admin_passthru \- Submit an nvme passthrough command
+.SH SYNOPSIS
+.B "int" nvme_admin_passthru
+.BI "(int fd " ","
+.BI "__u8 opcode " ","
+.BI "__u8 flags " ","
+.BI "__u16 rsvd " ","
+.BI "__u32 nsid " ","
+.BI "__u32 cdw2 " ","
+.BI "__u32 cdw3 " ","
+.BI "__u32 cdw10 " ","
+.BI "__u32 cdw11 " ","
+.BI "__u32 cdw12 " ","
+.BI "__u32 cdw13 " ","
+.BI "__u32 cdw14 " ","
+.BI "__u32 cdw15 " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 metadata_len " ","
+.BI "void *metadata " ","
+.BI "__u32 timeout_ms " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "opcode" 12
+The nvme io command to send
+.IP "flags" 12
+NVMe command flags (not used)
+.IP "rsvd" 12
+Reserved for future use
+.IP "nsid" 12
+Namespace identifier
+.IP "cdw2" 12
+Command dword 2
+.IP "cdw3" 12
+Command dword 3
+.IP "cdw10" 12
+Command dword 10
+.IP "cdw11" 12
+Command dword 11
+.IP "cdw12" 12
+Command dword 12
+.IP "cdw13" 12
+Command dword 13
+.IP "cdw14" 12
+Command dword 14
+.IP "cdw15" 12
+Command dword 15
+.IP "data_len" 12
+Length of the data transferred in this command in bytes
+.IP "data" 12
+Pointer to user address of the data buffer
+.IP "metadata_len" 12
+Length of metadata transferred in this command
+.IP "metadata" 12
+Pointer to user address of the metadata buffer
+.IP "timeout_ms" 12
+How long the kernel waits for the command to complete
+.IP "result" 12
+Optional field to return the result from the CQE dword 0
+.SH "DESCRIPTION"
+Parameterized form of \fBnvme_submit_admin_passthru\fP. This sets up and
+submits a \fIstruct nvme_passthru_cmd\fP.
+
+Known values for \fIopcode\fP are defined in \fIenum nvme_admin_opcode\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_admin_passthru64.2 b/doc/man/nvme_admin_passthru64.2
new file mode 100644
index 0000000..f003d78
--- /dev/null
+++ b/doc/man/nvme_admin_passthru64.2
@@ -0,0 +1,71 @@
+.TH "nvme_admin_passthru64" 9 "nvme_admin_passthru64" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_admin_passthru64 \- Submit a 64-bit nvme passthrough command
+.SH SYNOPSIS
+.B "int" nvme_admin_passthru64
+.BI "(int fd " ","
+.BI "__u8 opcode " ","
+.BI "__u8 flags " ","
+.BI "__u16 rsvd " ","
+.BI "__u32 nsid " ","
+.BI "__u32 cdw2 " ","
+.BI "__u32 cdw3 " ","
+.BI "__u32 cdw10 " ","
+.BI "__u32 cdw11 " ","
+.BI "__u32 cdw12 " ","
+.BI "__u32 cdw13 " ","
+.BI "__u32 cdw14 " ","
+.BI "__u32 cdw15 " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 metadata_len " ","
+.BI "void *metadata " ","
+.BI "__u32 timeout_ms " ","
+.BI "__u64 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "opcode" 12
+The nvme io command to send
+.IP "flags" 12
+NVMe command flags (not used)
+.IP "rsvd" 12
+Reserved for future use
+.IP "nsid" 12
+Namespace identifier
+.IP "cdw2" 12
+Command dword 2
+.IP "cdw3" 12
+Command dword 3
+.IP "cdw10" 12
+Command dword 10
+.IP "cdw11" 12
+Command dword 11
+.IP "cdw12" 12
+Command dword 12
+.IP "cdw13" 12
+Command dword 13
+.IP "cdw14" 12
+Command dword 14
+.IP "cdw15" 12
+Command dword 15
+.IP "data_len" 12
+Length of the data transferred in this command in bytes
+.IP "data" 12
+Pointer to user address of the data buffer
+.IP "metadata_len" 12
+Length of metadata transferred in this command
+.IP "metadata" 12
+Pointer to user address of the metadata buffer
+.IP "timeout_ms" 12
+How long the kernel waits for the command to complete
+.IP "result" 12
+Optional field to return the result from the CQE dword 0
+.SH "DESCRIPTION"
+Parameterized form of \fBnvme_submit_admin_passthru64\fP. This sets up and
+submits a \fIstruct nvme_passthru_cmd64\fP.
+
+Known values for \fIopcode\fP are defined in \fIenum nvme_admin_opcode\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ae_info_css_nvm.2 b/doc/man/nvme_ae_info_css_nvm.2
new file mode 100644
index 0000000..76f2ecf
--- /dev/null
+++ b/doc/man/nvme_ae_info_css_nvm.2
@@ -0,0 +1,25 @@
+.TH "libnvme" 9 "enum nvme_ae_info_css_nvm" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ae_info_css_nvm \- Asynchronous Event Information - I/O Command Specific Status
+.SH SYNOPSIS
+enum nvme_ae_info_css_nvm {
+.br
+.BI " NVME_AER_CSS_NVM_RESERVATION"
+,
+.br
+.br
+.BI " NVME_AER_CSS_NVM_SANITIZE_COMPLETED"
+,
+.br
+.br
+.BI " NVME_AER_CSS_NVM_UNEXPECTED_SANITIZE_DEALLOC"
+
+};
+.SH Constants
+.IP "NVME_AER_CSS_NVM_RESERVATION" 12
+Reservation Log Page Available
+.IP "NVME_AER_CSS_NVM_SANITIZE_COMPLETED" 12
+Sanitize Operation Completed
+.IP "NVME_AER_CSS_NVM_UNEXPECTED_SANITIZE_DEALLOC" 12
+Sanitize Operation Completed
+With Unexpected Deallocation
diff --git a/doc/man/nvme_ae_info_error.2 b/doc/man/nvme_ae_info_error.2
new file mode 100644
index 0000000..57c40fa
--- /dev/null
+++ b/doc/man/nvme_ae_info_error.2
@@ -0,0 +1,42 @@
+.TH "libnvme" 9 "enum nvme_ae_info_error" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ae_info_error \- Asynchronous Event Information - Error Status
+.SH SYNOPSIS
+enum nvme_ae_info_error {
+.br
+.BI " NVME_AER_ERROR_INVALID_DB_REG"
+,
+.br
+.br
+.BI " NVME_AER_ERROR_INVALID_DB_VAL"
+,
+.br
+.br
+.BI " NVME_AER_ERROR_DIAG_FAILURE"
+,
+.br
+.br
+.BI " NVME_AER_ERROR_PERSISTENT_INTERNAL_ERROR"
+,
+.br
+.br
+.BI " NVME_AER_ERROR_TRANSIENT_INTERNAL_ERROR"
+,
+.br
+.br
+.BI " NVME_AER_ERROR_FW_IMAGE_LOAD_ERROR"
+
+};
+.SH Constants
+.IP "NVME_AER_ERROR_INVALID_DB_REG" 12
+Write to Invalid Doorbell Register
+.IP "NVME_AER_ERROR_INVALID_DB_VAL" 12
+Invalid Doorbell Write Value
+.IP "NVME_AER_ERROR_DIAG_FAILURE" 12
+Diagnostic Failure
+.IP "NVME_AER_ERROR_PERSISTENT_INTERNAL_ERROR" 12
+Persistent Internal Error
+.IP "NVME_AER_ERROR_TRANSIENT_INTERNAL_ERROR" 12
+Transient Internal Error
+.IP "NVME_AER_ERROR_FW_IMAGE_LOAD_ERROR" 12
+Firmware Image Load Error
diff --git a/doc/man/nvme_ae_info_notice.2 b/doc/man/nvme_ae_info_notice.2
new file mode 100644
index 0000000..e52cd26
--- /dev/null
+++ b/doc/man/nvme_ae_info_notice.2
@@ -0,0 +1,54 @@
+.TH "libnvme" 9 "enum nvme_ae_info_notice" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ae_info_notice \- Asynchronous Event Information - Notice
+.SH SYNOPSIS
+enum nvme_ae_info_notice {
+.br
+.BI " NVME_AER_NOTICE_NS_CHANGED"
+,
+.br
+.br
+.BI " NVME_AER_NOTICE_FW_ACT_STARTING"
+,
+.br
+.br
+.BI " NVME_AER_NOTICE_TELEMETRY"
+,
+.br
+.br
+.BI " NVME_AER_NOTICE_ANA"
+,
+.br
+.br
+.BI " NVME_AER_NOTICE_PL_EVENT"
+,
+.br
+.br
+.BI " NVME_AER_NOTICE_LBA_STATUS_ALERT"
+,
+.br
+.br
+.BI " NVME_AER_NOTICE_EG_EVENT"
+,
+.br
+.br
+.BI " NVME_AER_NOTICE_DISC_CHANGED"
+
+};
+.SH Constants
+.IP "NVME_AER_NOTICE_NS_CHANGED" 12
+Namespace Attribute Changed
+.IP "NVME_AER_NOTICE_FW_ACT_STARTING" 12
+Firmware Activation Starting
+.IP "NVME_AER_NOTICE_TELEMETRY" 12
+Telemetry Log Changed
+.IP "NVME_AER_NOTICE_ANA" 12
+Asymmetric Namespace Access Change
+.IP "NVME_AER_NOTICE_PL_EVENT" 12
+Predictable Latency Event Aggregate Log Change
+.IP "NVME_AER_NOTICE_LBA_STATUS_ALERT" 12
+LBA Status Information Alert
+.IP "NVME_AER_NOTICE_EG_EVENT" 12
+Endurance Group Event Aggregate Log Page Change
+.IP "NVME_AER_NOTICE_DISC_CHANGED" 12
+Discovery Log Page Change
diff --git a/doc/man/nvme_ae_info_smart.2 b/doc/man/nvme_ae_info_smart.2
new file mode 100644
index 0000000..c7c9b1f
--- /dev/null
+++ b/doc/man/nvme_ae_info_smart.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_ae_info_smart" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ae_info_smart \- Asynchronous Event Information - SMART / Health Status
+.SH SYNOPSIS
+enum nvme_ae_info_smart {
+.br
+.BI " NVME_AER_SMART_SUBSYSTEM_RELIABILITY"
+,
+.br
+.br
+.BI " NVME_AER_SMART_TEMPERATURE_THRESHOLD"
+,
+.br
+.br
+.BI " NVME_AER_SMART_SPARE_THRESHOLD"
+
+};
+.SH Constants
+.IP "NVME_AER_SMART_SUBSYSTEM_RELIABILITY" 12
+NVM subsystem Reliability
+.IP "NVME_AER_SMART_TEMPERATURE_THRESHOLD" 12
+Temperature Threshold
+.IP "NVME_AER_SMART_SPARE_THRESHOLD" 12
+Spare Below Threshold
diff --git a/doc/man/nvme_ae_type.2 b/doc/man/nvme_ae_type.2
new file mode 100644
index 0000000..23c5b23
--- /dev/null
+++ b/doc/man/nvme_ae_type.2
@@ -0,0 +1,36 @@
+.TH "libnvme" 9 "enum nvme_ae_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ae_type \- Asynchronous Event Type
+.SH SYNOPSIS
+enum nvme_ae_type {
+.br
+.BI " NVME_AER_ERROR"
+,
+.br
+.br
+.BI " NVME_AER_SMART"
+,
+.br
+.br
+.BI " NVME_AER_NOTICE"
+,
+.br
+.br
+.BI " NVME_AER_CSS"
+,
+.br
+.br
+.BI " NVME_AER_VS"
+
+};
+.SH Constants
+.IP "NVME_AER_ERROR" 12
+Error event
+.IP "NVME_AER_SMART" 12
+SMART / Health Status event
+.IP "NVME_AER_NOTICE" 12
+Notice event
+.IP "NVME_AER_CSS" 12
+NVM Command Set Specific events
+.IP "NVME_AER_VS" 12
+Vendor Specific event
diff --git a/doc/man/nvme_aggregate_endurance_group_event.2 b/doc/man/nvme_aggregate_endurance_group_event.2
new file mode 100644
index 0000000..7400320
--- /dev/null
+++ b/doc/man/nvme_aggregate_endurance_group_event.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_aggregate_endurance_group_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_aggregate_endurance_group_event \- Endurance Group Event Aggregate
+.SH SYNOPSIS
+struct nvme_aggregate_endurance_group_event {
+.br
+.BI " __le64 num_entries;"
+.br
+.BI " __le16 entries[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "num_entries" 12
+Number or entries
+.IP "entries" 12
+List of entries
diff --git a/doc/man/nvme_aggregate_predictable_lat_event.2 b/doc/man/nvme_aggregate_predictable_lat_event.2
new file mode 100644
index 0000000..95174aa
--- /dev/null
+++ b/doc/man/nvme_aggregate_predictable_lat_event.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_aggregate_predictable_lat_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_aggregate_predictable_lat_event \- Predictable Latency Event Aggregate Log Page
+.SH SYNOPSIS
+struct nvme_aggregate_predictable_lat_event {
+.br
+.BI " __le64 num_entries;"
+.br
+.BI " __le16 entries[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "num_entries" 12
+Number of entries
+.IP "entries" 12
+Entry list
diff --git a/doc/man/nvme_ana_group_desc.2 b/doc/man/nvme_ana_group_desc.2
new file mode 100644
index 0000000..d479fee
--- /dev/null
+++ b/doc/man/nvme_ana_group_desc.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_ana_group_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_ana_group_desc \- ANA Group Descriptor
+.SH SYNOPSIS
+struct nvme_ana_group_desc {
+.br
+.BI " __le32 grpid;"
+.br
+.BI " __le32 nnsids;"
+.br
+.BI " __le64 chgcnt;"
+.br
+.BI " __u8 state;"
+.br
+.BI " __u8 rsvd17[15];"
+.br
+.BI " __le32 nsids[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "grpid" 12
+ANA group id
+.IP "nnsids" 12
+Number of namespaces in \fInsids\fP
+.IP "chgcnt" 12
+Change counter
+.IP "state" 12
+ANA state
+.IP "rsvd17" 12
+Reserved
+.IP "nsids" 12
+List of namespaces
diff --git a/doc/man/nvme_ana_log.2 b/doc/man/nvme_ana_log.2
new file mode 100644
index 0000000..4ceb9ac
--- /dev/null
+++ b/doc/man/nvme_ana_log.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_ana_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_ana_log \- Asymmetric Namespace Access Log
+.SH SYNOPSIS
+struct nvme_ana_log {
+.br
+.BI " __le64 chgcnt;"
+.br
+.BI " __le16 ngrps;"
+.br
+.BI " __u8 rsvd10[6];"
+.br
+.BI " struct nvme_ana_group_desc descs[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "chgcnt" 12
+Change Count
+.IP "ngrps" 12
+Number of ANA Group Descriptors
+.IP "rsvd10" 12
+Reserved
+.IP "descs" 12
+ANA Group Descriptor
diff --git a/doc/man/nvme_ana_state.2 b/doc/man/nvme_ana_state.2
new file mode 100644
index 0000000..c3c8909
--- /dev/null
+++ b/doc/man/nvme_ana_state.2
@@ -0,0 +1,36 @@
+.TH "libnvme" 9 "enum nvme_ana_state" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ana_state \- ANA Group Descriptor - Asymmetric Namespace Access State
+.SH SYNOPSIS
+enum nvme_ana_state {
+.br
+.BI " NVME_ANA_STATE_OPTIMIZED"
+,
+.br
+.br
+.BI " NVME_ANA_STATE_NONOPTIMIZED"
+,
+.br
+.br
+.BI " NVME_ANA_STATE_INACCESSIBLE"
+,
+.br
+.br
+.BI " NVME_ANA_STATE_PERSISTENT_LOSS"
+,
+.br
+.br
+.BI " NVME_ANA_STATE_CHANGE"
+
+};
+.SH Constants
+.IP "NVME_ANA_STATE_OPTIMIZED" 12
+ANA Optimized state
+.IP "NVME_ANA_STATE_NONOPTIMIZED" 12
+ANA Non-Optimized state
+.IP "NVME_ANA_STATE_INACCESSIBLE" 12
+ANA Inaccessible state
+.IP "NVME_ANA_STATE_PERSISTENT_LOSS" 12
+ANA Persistent Loss state
+.IP "NVME_ANA_STATE_CHANGE" 12
+ANA Change state
diff --git a/doc/man/nvme_apst_entry.2 b/doc/man/nvme_apst_entry.2
new file mode 100644
index 0000000..73485cd
--- /dev/null
+++ b/doc/man/nvme_apst_entry.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_apst_entry" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_apst_entry \- Autonomous Power State Transition
+.SH SYNOPSIS
+enum nvme_apst_entry {
+.br
+.BI " NVME_APST_ENTRY_ITPS_SHIFT"
+,
+.br
+.br
+.BI " NVME_APST_ENTRY_ITPT_SHIFT"
+,
+.br
+.br
+.BI " NVME_APST_ENTRY_ITPS_MASK"
+,
+.br
+.br
+.BI " NVME_APST_ENTRY_ITPT_MASK"
+
+};
+.SH Constants
+.IP "NVME_APST_ENTRY_ITPS_SHIFT" 12
+Idle Transition Power State Shift
+.IP "NVME_APST_ENTRY_ITPT_SHIFT" 12
+Idle Time Prior to Transition Shift
+.IP "NVME_APST_ENTRY_ITPS_MASK" 12
+Idle Transition Power State Mask
+.IP "NVME_APST_ENTRY_ITPT_MASK" 12
+Idle Time Prior to Transition Mask
diff --git a/doc/man/nvme_boot_partition.2 b/doc/man/nvme_boot_partition.2
new file mode 100644
index 0000000..6187f5e
--- /dev/null
+++ b/doc/man/nvme_boot_partition.2
@@ -0,0 +1,32 @@
+.TH "libnvme" 9 "struct nvme_boot_partition" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_boot_partition \- Boot Partition Log
+.SH SYNOPSIS
+struct nvme_boot_partition {
+.br
+.BI " __u8 lid;"
+.br
+.BI " __u8 rsvd1[3];"
+.br
+.BI " __le32 bpinfo;"
+.br
+.BI " __u8 rsvd8[8];"
+.br
+.BI " __u8 boot_partition_data[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "lid" 12
+Boot Partition Identifier
+.IP "rsvd1" 12
+Reserved
+.IP "bpinfo" 12
+Boot Partition Information
+.IP "rsvd8" 12
+Reserved
+.IP "boot_partition_data" 12
+Contains the contents of the
+specified Boot Partition
diff --git a/doc/man/nvme_capacity_config_desc.2 b/doc/man/nvme_capacity_config_desc.2
new file mode 100644
index 0000000..0637575
--- /dev/null
+++ b/doc/man/nvme_capacity_config_desc.2
@@ -0,0 +1,33 @@
+.TH "libnvme" 9 "struct nvme_capacity_config_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_capacity_config_desc \- Capacity Configuration structure definitions
+.SH SYNOPSIS
+struct nvme_capacity_config_desc {
+.br
+.BI " __le16 cap_config_id;"
+.br
+.BI " __le16 domainid;"
+.br
+.BI " __le16 egcn;"
+.br
+.BI " __u8 rsvd6[26];"
+.br
+.BI " struct nvme_end_grp_config_desc egcd[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "cap_config_id" 12
+Capacity Configuration Identifier
+.IP "domainid" 12
+Domain Identifier
+.IP "egcn" 12
+Number Endurance Group Configuration
+Descriptors
+.IP "rsvd6" 12
+Reserved
+.IP "egcd" 12
+Endurance Group Config descriptors.
+See \fIstruct\fP nvme_end_grp_config_desc
diff --git a/doc/man/nvme_capacity_mgmt.2 b/doc/man/nvme_capacity_mgmt.2
new file mode 100644
index 0000000..b9300d4
--- /dev/null
+++ b/doc/man/nvme_capacity_mgmt.2
@@ -0,0 +1,12 @@
+.TH "nvme_capacity_mgmt" 9 "nvme_capacity_mgmt" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_capacity_mgmt \- Capacity management command
+.SH SYNOPSIS
+.B "int" nvme_capacity_mgmt
+.BI "(struct nvme_capacity_mgmt_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_capacity_mgmt_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_change_ns_event.2 b/doc/man/nvme_change_ns_event.2
new file mode 100644
index 0000000..b0ca401
--- /dev/null
+++ b/doc/man/nvme_change_ns_event.2
@@ -0,0 +1,63 @@
+.TH "libnvme" 9 "struct nvme_change_ns_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_change_ns_event \- Change Namespace Event Data
+.SH SYNOPSIS
+struct nvme_change_ns_event {
+.br
+.BI " __le32 nsmgt_cdw10;"
+.br
+.BI " __u8 rsvd4[4];"
+.br
+.BI " __le64 nsze;"
+.br
+.BI " __u8 rsvd16[8];"
+.br
+.BI " __le64 nscap;"
+.br
+.BI " __u8 flbas;"
+.br
+.BI " __u8 dps;"
+.br
+.BI " __u8 nmic;"
+.br
+.BI " __u8 rsvd35;"
+.br
+.BI " __le32 ana_grp_id;"
+.br
+.BI " __le16 nvmset_id;"
+.br
+.BI " __le16 rsvd42;"
+.br
+.BI " __le32 nsid;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nsmgt_cdw10" 12
+Namespace Management CDW10
+.IP "rsvd4" 12
+Reserved
+.IP "nsze" 12
+Namespace Size
+.IP "rsvd16" 12
+Reserved
+.IP "nscap" 12
+Namespace Capacity
+.IP "flbas" 12
+Formatted LBA Size
+.IP "dps" 12
+End-to-end Data Protection Type Settings
+.IP "nmic" 12
+Namespace Multi-path I/O and Namespace Sharing Capabilities
+.IP "rsvd35" 12
+Reserved
+.IP "ana_grp_id" 12
+ANA Group Identifier
+.IP "nvmset_id" 12
+NVM Set Identifier
+.IP "rsvd42" 12
+Reserved
+.IP "nsid" 12
+Namespace ID
diff --git a/doc/man/nvme_channel_config_desc.2 b/doc/man/nvme_channel_config_desc.2
new file mode 100644
index 0000000..e52dd76
--- /dev/null
+++ b/doc/man/nvme_channel_config_desc.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "struct nvme_channel_config_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_channel_config_desc \- Channel Configuration Descriptor
+.SH SYNOPSIS
+struct nvme_channel_config_desc {
+.br
+.BI " __le16 chanid;"
+.br
+.BI " __le16 chmus;"
+.br
+.BI " struct nvme_media_unit_config_desc mu_config_desc[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "chanid" 12
+Channel Identifier
+.IP "chmus" 12
+Number Channel Media Units
+.IP "mu_config_desc" 12
+Channel Unit config descriptors.
+See \fIstruct\fP nvme_media_unit_config_desc
diff --git a/doc/man/nvme_cmb_size.2 b/doc/man/nvme_cmb_size.2
new file mode 100644
index 0000000..7de9c04
--- /dev/null
+++ b/doc/man/nvme_cmb_size.2
@@ -0,0 +1,11 @@
+.TH "nvme_cmb_size" 9 "nvme_cmb_size" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_cmb_size \- Calculate size of the controller memory buffer
+.SH SYNOPSIS
+.B "__u64" nvme_cmb_size
+.BI "(__u32 cmbsz " ");"
+.SH ARGUMENTS
+.IP "cmbsz" 12
+Value from controller register NVME_REG_CMBSZ
+.SH "RETURN"
+size of controller memory buffer in bytes
diff --git a/doc/man/nvme_cmd_effects.2 b/doc/man/nvme_cmd_effects.2
new file mode 100644
index 0000000..3e253fa
--- /dev/null
+++ b/doc/man/nvme_cmd_effects.2
@@ -0,0 +1,48 @@
+.TH "libnvme" 9 "enum nvme_cmd_effects" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_cmd_effects \- Commands Supported and Effects
+.SH SYNOPSIS
+enum nvme_cmd_effects {
+.br
+.BI " NVME_CMD_EFFECTS_CSUPP"
+,
+.br
+.br
+.BI " NVME_CMD_EFFECTS_LBCC"
+,
+.br
+.br
+.BI " NVME_CMD_EFFECTS_NCC"
+,
+.br
+.br
+.BI " NVME_CMD_EFFECTS_NIC"
+,
+.br
+.br
+.BI " NVME_CMD_EFFECTS_CCC"
+,
+.br
+.br
+.BI " NVME_CMD_EFFECTS_CSE_MASK"
+,
+.br
+.br
+.BI " NVME_CMD_EFFECTS_UUID_SEL"
+
+};
+.SH Constants
+.IP "NVME_CMD_EFFECTS_CSUPP" 12
+Command Supported
+.IP "NVME_CMD_EFFECTS_LBCC" 12
+Logical Block Content Change
+.IP "NVME_CMD_EFFECTS_NCC" 12
+Namespace Capability Change
+.IP "NVME_CMD_EFFECTS_NIC" 12
+Namespace Inventory Change
+.IP "NVME_CMD_EFFECTS_CCC" 12
+Controller Capability Change
+.IP "NVME_CMD_EFFECTS_CSE_MASK" 12
+Command Submission and Execution
+.IP "NVME_CMD_EFFECTS_UUID_SEL" 12
+UUID Selection Supported
diff --git a/doc/man/nvme_cmd_effects_log.2 b/doc/man/nvme_cmd_effects_log.2
new file mode 100644
index 0000000..4f3a706
--- /dev/null
+++ b/doc/man/nvme_cmd_effects_log.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_cmd_effects_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_cmd_effects_log \- Commands Supported and Effects Log
+.SH SYNOPSIS
+struct nvme_cmd_effects_log {
+.br
+.BI " __le32 acs[256];"
+.br
+.BI " __le32 iocs[256];"
+.br
+.BI " __u8 rsvd[2048];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "acs" 12
+Admin Command Supported
+.IP "iocs" 12
+I/O Command Supported
+.IP "rsvd" 12
+Reserved
diff --git a/doc/man/nvme_cmd_format_mset.2 b/doc/man/nvme_cmd_format_mset.2
new file mode 100644
index 0000000..541bdc8
--- /dev/null
+++ b/doc/man/nvme_cmd_format_mset.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "enum nvme_cmd_format_mset" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_cmd_format_mset \- Format NVM - Metadata Settings
+.SH SYNOPSIS
+enum nvme_cmd_format_mset {
+.br
+.BI " NVME_FORMAT_MSET_SEPARATE"
+,
+.br
+.br
+.BI " NVME_FORMAT_MSET_EXTENDED"
+
+};
+.SH Constants
+.IP "NVME_FORMAT_MSET_SEPARATE" 12
+indicates that the metadata is transferred
+as part of a separate buffer.
+.IP "NVME_FORMAT_MSET_EXTENDED" 12
+indicates that the metadata is transferred
+as part of an extended data LBA.
diff --git a/doc/man/nvme_cmd_format_pi.2 b/doc/man/nvme_cmd_format_pi.2
new file mode 100644
index 0000000..e5dec06
--- /dev/null
+++ b/doc/man/nvme_cmd_format_pi.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_cmd_format_pi" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_cmd_format_pi \- Format NVM - Protection Information
+.SH SYNOPSIS
+enum nvme_cmd_format_pi {
+.br
+.BI " NVME_FORMAT_PI_DISABLE"
+,
+.br
+.br
+.BI " NVME_FORMAT_PI_TYPE1"
+,
+.br
+.br
+.BI " NVME_FORMAT_PI_TYPE2"
+,
+.br
+.br
+.BI " NVME_FORMAT_PI_TYPE3"
+
+};
+.SH Constants
+.IP "NVME_FORMAT_PI_DISABLE" 12
+Protection information is not enabled.
+.IP "NVME_FORMAT_PI_TYPE1" 12
+Protection information is enabled, Type 1.
+.IP "NVME_FORMAT_PI_TYPE2" 12
+Protection information is enabled, Type 2.
+.IP "NVME_FORMAT_PI_TYPE3" 12
+Protection information is enabled, Type 3.
diff --git a/doc/man/nvme_cmd_format_pil.2 b/doc/man/nvme_cmd_format_pil.2
new file mode 100644
index 0000000..3fa74f7
--- /dev/null
+++ b/doc/man/nvme_cmd_format_pil.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "enum nvme_cmd_format_pil" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_cmd_format_pil \- Format NVM - Protection Information Location
+.SH SYNOPSIS
+enum nvme_cmd_format_pil {
+.br
+.BI " NVME_FORMAT_PIL_LAST"
+,
+.br
+.br
+.BI " NVME_FORMAT_PIL_FIRST"
+
+};
+.SH Constants
+.IP "NVME_FORMAT_PIL_LAST" 12
+Protection information is transferred as the last
+bytes of metadata.
+.IP "NVME_FORMAT_PIL_FIRST" 12
+Protection information is transferred as the first
+bytes of metadata.
diff --git a/doc/man/nvme_cmd_format_ses.2 b/doc/man/nvme_cmd_format_ses.2
new file mode 100644
index 0000000..344f0fb
--- /dev/null
+++ b/doc/man/nvme_cmd_format_ses.2
@@ -0,0 +1,33 @@
+.TH "libnvme" 9 "enum nvme_cmd_format_ses" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_cmd_format_ses \- Format NVM - Secure Erase Settings
+.SH SYNOPSIS
+enum nvme_cmd_format_ses {
+.br
+.BI " NVME_FORMAT_SES_NONE"
+,
+.br
+.br
+.BI " NVME_FORMAT_SES_USER_DATA_ERASE"
+,
+.br
+.br
+.BI " NVME_FORMAT_SES_CRYPTO_ERASE"
+
+};
+.SH Constants
+.IP "NVME_FORMAT_SES_NONE" 12
+No secure erase operation requested.
+.IP "NVME_FORMAT_SES_USER_DATA_ERASE" 12
+User Data Erase: All user data shall be erased,
+contents of the user data after the erase is
+indeterminate (e.g. the user data may be zero
+filled, one filled, etc.). If a User Data Erase
+is requested and all affected user data is
+encrypted, then the controller is allowed
+to use a cryptographic erase to perform
+the requested User Data Erase.
+.IP "NVME_FORMAT_SES_CRYPTO_ERASE" 12
+Cryptographic Erase: All user data shall
+be erased cryptographically. This is
+accomplished by deleting the encryption key.
diff --git a/doc/man/nvme_cmd_get_log_lid.2 b/doc/man/nvme_cmd_get_log_lid.2
new file mode 100644
index 0000000..f1e9ff0
--- /dev/null
+++ b/doc/man/nvme_cmd_get_log_lid.2
@@ -0,0 +1,186 @@
+.TH "libnvme" 9 "enum nvme_cmd_get_log_lid" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_cmd_get_log_lid \- Get Log Page -Log Page Identifiers
+.SH SYNOPSIS
+enum nvme_cmd_get_log_lid {
+.br
+.BI " NVME_LOG_LID_SUPPORTED_LOG_PAGES"
+,
+.br
+.br
+.BI " NVME_LOG_LID_ERROR"
+,
+.br
+.br
+.BI " NVME_LOG_LID_SMART"
+,
+.br
+.br
+.BI " NVME_LOG_LID_FW_SLOT"
+,
+.br
+.br
+.BI " NVME_LOG_LID_CHANGED_NS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_CMD_EFFECTS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_DEVICE_SELF_TEST"
+,
+.br
+.br
+.BI " NVME_LOG_LID_TELEMETRY_HOST"
+,
+.br
+.br
+.BI " NVME_LOG_LID_TELEMETRY_CTRL"
+,
+.br
+.br
+.BI " NVME_LOG_LID_ENDURANCE_GROUP"
+,
+.br
+.br
+.BI " NVME_LOG_LID_PREDICTABLE_LAT_NVMSET"
+,
+.br
+.br
+.BI " NVME_LOG_LID_PREDICTABLE_LAT_AGG"
+,
+.br
+.br
+.BI " NVME_LOG_LID_ANA"
+,
+.br
+.br
+.BI " NVME_LOG_LID_PERSISTENT_EVENT"
+,
+.br
+.br
+.BI " NVME_LOG_LID_LBA_STATUS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_ENDURANCE_GRP_EVT"
+,
+.br
+.br
+.BI " NVME_LOG_LID_MEDIA_UNIT_STATUS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST"
+,
+.br
+.br
+.BI " NVME_LOG_LID_FID_SUPPORTED_EFFECTS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_BOOT_PARTITION"
+,
+.br
+.br
+.BI " NVME_LOG_LID_PHY_RX_EOM"
+,
+.br
+.br
+.BI " NVME_LOG_LID_FDP_CONFIGS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_FDP_RUH_USAGE"
+,
+.br
+.br
+.BI " NVME_LOG_LID_FDP_STATS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_FDP_EVENTS"
+,
+.br
+.br
+.BI " NVME_LOG_LID_DISCOVER"
+,
+.br
+.br
+.BI " NVME_LOG_LID_RESERVATION"
+,
+.br
+.br
+.BI " NVME_LOG_LID_SANITIZE"
+,
+.br
+.br
+.BI " NVME_LOG_LID_ZNS_CHANGED_ZONES"
+
+};
+.SH Constants
+.IP "NVME_LOG_LID_SUPPORTED_LOG_PAGES" 12
+Supported Log Pages
+.IP "NVME_LOG_LID_ERROR" 12
+Error Information
+.IP "NVME_LOG_LID_SMART" 12
+SMART / Health Information
+.IP "NVME_LOG_LID_FW_SLOT" 12
+Firmware Slot Information
+.IP "NVME_LOG_LID_CHANGED_NS" 12
+Changed Namespace List
+.IP "NVME_LOG_LID_CMD_EFFECTS" 12
+Commands Supported and Effects
+.IP "NVME_LOG_LID_DEVICE_SELF_TEST" 12
+Device Self-test
+.IP "NVME_LOG_LID_TELEMETRY_HOST" 12
+Telemetry Host-Initiated
+.IP "NVME_LOG_LID_TELEMETRY_CTRL" 12
+Telemetry Controller-Initiated
+.IP "NVME_LOG_LID_ENDURANCE_GROUP" 12
+Endurance Group Information
+.IP "NVME_LOG_LID_PREDICTABLE_LAT_NVMSET" 12
+Predictable Latency Per NVM Set
+.IP "NVME_LOG_LID_PREDICTABLE_LAT_AGG" 12
+Predictable Latency Event Aggregate
+.IP "NVME_LOG_LID_ANA" 12
+Asymmetric Namespace Access
+.IP "NVME_LOG_LID_PERSISTENT_EVENT" 12
+Persistent Event Log
+.IP "NVME_LOG_LID_LBA_STATUS" 12
+LBA Status Information
+.IP "NVME_LOG_LID_ENDURANCE_GRP_EVT" 12
+Endurance Group Event Aggregate
+.IP "NVME_LOG_LID_MEDIA_UNIT_STATUS" 12
+Media Unit Status
+.IP "NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST" 12
+Supported Capacity Configuration Lis
+.IP "NVME_LOG_LID_FID_SUPPORTED_EFFECTS" 12
+Feature Identifiers Supported and Effects
+.IP "NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS" 12
+NVMe-MI Commands Supported and Effects
+.IP "NVME_LOG_LID_BOOT_PARTITION" 12
+Boot Partition
+.IP "NVME_LOG_LID_PHY_RX_EOM" 12
+Physical Interface Receiver Eye Opening Measurement
+.IP "NVME_LOG_LID_FDP_CONFIGS" 12
+FDP Configurations
+.IP "NVME_LOG_LID_FDP_RUH_USAGE" 12
+Reclaim Unit Handle Usage
+.IP "NVME_LOG_LID_FDP_STATS" 12
+FDP Statistics
+.IP "NVME_LOG_LID_FDP_EVENTS" 12
+FDP Events
+.IP "NVME_LOG_LID_DISCOVER" 12
+Discovery
+.IP "NVME_LOG_LID_RESERVATION" 12
+Reservation Notification
+.IP "NVME_LOG_LID_SANITIZE" 12
+Sanitize Status
+.IP "NVME_LOG_LID_ZNS_CHANGED_ZONES" 12
+Changed Zone List
diff --git a/doc/man/nvme_cmd_get_log_telemetry_host_lsp.2 b/doc/man/nvme_cmd_get_log_telemetry_host_lsp.2
new file mode 100644
index 0000000..7b82a0c
--- /dev/null
+++ b/doc/man/nvme_cmd_get_log_telemetry_host_lsp.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_cmd_get_log_telemetry_host_lsp" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_cmd_get_log_telemetry_host_lsp \- Telemetry Host-Initiated log specific field
+.SH SYNOPSIS
+enum nvme_cmd_get_log_telemetry_host_lsp {
+.br
+.BI " NVME_LOG_TELEM_HOST_LSP_RETAIN"
+,
+.br
+.br
+.BI " NVME_LOG_TELEM_HOST_LSP_CREATE"
+
+};
+.SH Constants
+.IP "NVME_LOG_TELEM_HOST_LSP_RETAIN" 12
+Get Telemetry Data Blocks
+.IP "NVME_LOG_TELEM_HOST_LSP_CREATE" 12
+Create Telemetry Data Blocks
diff --git a/doc/man/nvme_compare.2 b/doc/man/nvme_compare.2
new file mode 100644
index 0000000..d3554f0
--- /dev/null
+++ b/doc/man/nvme_compare.2
@@ -0,0 +1,12 @@
+.TH "nvme_compare" 9 "nvme_compare" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_compare \- Submit an nvme user compare command
+.SH SYNOPSIS
+.B "int" nvme_compare
+.BI "(struct nvme_io_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_connect_err.2 b/doc/man/nvme_connect_err.2
new file mode 100644
index 0000000..50e04b2
--- /dev/null
+++ b/doc/man/nvme_connect_err.2
@@ -0,0 +1,126 @@
+.TH "libnvme" 9 "enum nvme_connect_err" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_connect_err \- nvme connect error codes
+.SH SYNOPSIS
+enum nvme_connect_err {
+.br
+.BI " ENVME_CONNECT_RESOLVE"
+,
+.br
+.br
+.BI " ENVME_CONNECT_ADDRFAM"
+,
+.br
+.br
+.BI " ENVME_CONNECT_TRADDR"
+,
+.br
+.br
+.BI " ENVME_CONNECT_TARG"
+,
+.br
+.br
+.BI " ENVME_CONNECT_AARG"
+,
+.br
+.br
+.BI " ENVME_CONNECT_OPEN"
+,
+.br
+.br
+.BI " ENVME_CONNECT_WRITE"
+,
+.br
+.br
+.BI " ENVME_CONNECT_READ"
+,
+.br
+.br
+.BI " ENVME_CONNECT_PARSE"
+,
+.br
+.br
+.BI " ENVME_CONNECT_INVAL_TR"
+,
+.br
+.br
+.BI " ENVME_CONNECT_LOOKUP_SUBSYS_NAME"
+,
+.br
+.br
+.BI " ENVME_CONNECT_LOOKUP_SUBSYS"
+,
+.br
+.br
+.BI " ENVME_CONNECT_ALREADY"
+,
+.br
+.br
+.BI " ENVME_CONNECT_INVAL"
+,
+.br
+.br
+.BI " ENVME_CONNECT_ADDRINUSE"
+,
+.br
+.br
+.BI " ENVME_CONNECT_NODEV"
+,
+.br
+.br
+.BI " ENVME_CONNECT_OPNOTSUPP"
+,
+.br
+.br
+.BI " ENVME_CONNECT_CONNREFUSED"
+,
+.br
+.br
+.BI " ENVME_CONNECT_ADDRNOTAVAIL"
+,
+.br
+.br
+.BI " ENVME_CONNECT_IGNORED"
+
+};
+.SH Constants
+.IP "ENVME_CONNECT_RESOLVE" 12
+failed to resolve host
+.IP "ENVME_CONNECT_ADDRFAM" 12
+unrecognized address family
+.IP "ENVME_CONNECT_TRADDR" 12
+failed to get traddr
+.IP "ENVME_CONNECT_TARG" 12
+need a transport (-t) argument
+.IP "ENVME_CONNECT_AARG" 12
+need a address (-a) argument
+.IP "ENVME_CONNECT_OPEN" 12
+failed to open nvme-fabrics device
+.IP "ENVME_CONNECT_WRITE" 12
+failed to write to nvme-fabrics device
+.IP "ENVME_CONNECT_READ" 12
+failed to read from nvme-fabrics device
+.IP "ENVME_CONNECT_PARSE" 12
+failed to parse ctrl info
+.IP "ENVME_CONNECT_INVAL_TR" 12
+invalid transport type
+.IP "ENVME_CONNECT_LOOKUP_SUBSYS_NAME" 12
+failed to lookup subsystem name
+.IP "ENVME_CONNECT_LOOKUP_SUBSYS" 12
+failed to lookup subsystem
+.IP "ENVME_CONNECT_ALREADY" 12
+the connect attempt failed, already connected
+.IP "ENVME_CONNECT_INVAL" 12
+invalid arguments/configuration
+.IP "ENVME_CONNECT_ADDRINUSE" 12
+hostnqn already in use
+.IP "ENVME_CONNECT_NODEV" 12
+invalid interface
+.IP "ENVME_CONNECT_OPNOTSUPP" 12
+not supported
+.IP "ENVME_CONNECT_CONNREFUSED" 12
+connection refused
+.IP "ENVME_CONNECT_ADDRNOTAVAIL" 12
+cannot assign requested address
+.IP "ENVME_CONNECT_IGNORED" 12
+connect attempt is ignored due to configuration
diff --git a/doc/man/nvme_constants.2 b/doc/man/nvme_constants.2
new file mode 100644
index 0000000..167d6ae
--- /dev/null
+++ b/doc/man/nvme_constants.2
@@ -0,0 +1,199 @@
+.TH "libnvme" 9 "enum nvme_constants" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_constants \- A place to stash various constant nvme values
+.SH SYNOPSIS
+enum nvme_constants {
+.br
+.BI " NVME_NSID_ALL"
+,
+.br
+.br
+.BI " NVME_NSID_NONE"
+,
+.br
+.br
+.BI " NVME_UUID_NONE"
+,
+.br
+.br
+.BI " NVME_CNTLID_NONE"
+,
+.br
+.br
+.BI " NVME_CNSSPECID_NONE"
+,
+.br
+.br
+.BI " NVME_LOG_LSP_NONE"
+,
+.br
+.br
+.BI " NVME_LOG_LSI_NONE"
+,
+.br
+.br
+.BI " NVME_LOG_LPO_NONE"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_DATA_SIZE"
+,
+.br
+.br
+.BI " NVME_LOG_SUPPORTED_LOG_PAGES_MAX"
+,
+.br
+.br
+.BI " NVME_ID_NVMSET_LIST_MAX"
+,
+.br
+.br
+.BI " NVME_ID_UUID_LIST_MAX"
+,
+.br
+.br
+.BI " NVME_ID_CTRL_LIST_MAX"
+,
+.br
+.br
+.BI " NVME_ID_NS_LIST_MAX"
+,
+.br
+.br
+.BI " NVME_ID_SECONDARY_CTRL_MAX"
+,
+.br
+.br
+.BI " NVME_ID_DOMAIN_LIST_MAX"
+,
+.br
+.br
+.BI " NVME_ID_ENDURANCE_GROUP_LIST_MAX"
+,
+.br
+.br
+.BI " NVME_ID_ND_DESCRIPTOR_MAX"
+,
+.br
+.br
+.BI " NVME_FEAT_LBA_RANGE_MAX"
+,
+.br
+.br
+.BI " NVME_LOG_ST_MAX_RESULTS"
+,
+.br
+.br
+.BI " NVME_LOG_TELEM_BLOCK_SIZE"
+,
+.br
+.br
+.BI " NVME_LOG_FID_SUPPORTED_EFFECTS_MAX"
+,
+.br
+.br
+.BI " NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX"
+,
+.br
+.br
+.BI " NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED"
+,
+.br
+.br
+.BI " NVME_DSM_MAX_RANGES"
+,
+.br
+.br
+.BI " NVME_NQN_LENGTH"
+,
+.br
+.br
+.BI " NVMF_TRADDR_SIZE"
+,
+.br
+.br
+.BI " NVMF_TSAS_SIZE"
+,
+.br
+.br
+.BI " NVME_ZNS_CHANGED_ZONES_MAX"
+
+};
+.SH Constants
+.IP "NVME_NSID_ALL" 12
+A broadcast value that is used to specify all
+namespaces
+.IP "NVME_NSID_NONE" 12
+The invalid namespace id, for when the nsid
+parameter is not used in a command
+.IP "NVME_UUID_NONE" 12
+Use to omit a uuid command parameter
+.IP "NVME_CNTLID_NONE" 12
+Use to omit a cntlid command parameter
+.IP "NVME_CNSSPECID_NONE" 12
+Use to omit a cns_specific_id command parameter
+.IP "NVME_LOG_LSP_NONE" 12
+Use to omit a log lsp command parameter
+.IP "NVME_LOG_LSI_NONE" 12
+Use to omit a log lsi command parameter
+.IP "NVME_LOG_LPO_NONE" 12
+Use to omit a log lpo command parameter
+.IP "NVME_IDENTIFY_DATA_SIZE" 12
+The transfer size for nvme identify commands
+.IP "NVME_LOG_SUPPORTED_LOG_PAGES_MAX" 12
+The largest possible index in the supported
+log pages log.
+.IP "NVME_ID_NVMSET_LIST_MAX" 12
+The largest possible nvmset index in identify
+nvmeset
+.IP "NVME_ID_UUID_LIST_MAX" 12
+The largest possible uuid index in identify
+uuid list
+.IP "NVME_ID_CTRL_LIST_MAX" 12
+The largest possible controller index in
+identify controller list
+.IP "NVME_ID_NS_LIST_MAX" 12
+The largest possible namespace index in
+identify namespace list
+.IP "NVME_ID_SECONDARY_CTRL_MAX" 12
+The largest possible secondary controller index
+in identify secondary controller
+.IP "NVME_ID_DOMAIN_LIST_MAX" 12
+The largest possible domain index in the
+in domain list
+.IP "NVME_ID_ENDURANCE_GROUP_LIST_MAX" 12
+The largest possible endurance group
+index in the endurance group list
+.IP "NVME_ID_ND_DESCRIPTOR_MAX" 12
+The largest possible namespace granularity
+index in the namespace granularity descriptor
+list
+.IP "NVME_FEAT_LBA_RANGE_MAX" 12
+The largest possible LBA range index in feature
+lba range type
+.IP "NVME_LOG_ST_MAX_RESULTS" 12
+The largest possible self test result index in the
+device self test log
+.IP "NVME_LOG_TELEM_BLOCK_SIZE" 12
+Specification defined size of Telemetry Data Blocks
+.IP "NVME_LOG_FID_SUPPORTED_EFFECTS_MAX" 12
+The largest possible FID index in the
+feature identifiers effects log.
+.IP "NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX" 12
+The largest possible MI Command index
+in the MI Command effects log.
+.IP "NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED" 12
+The reserved space in the MI Command
+effects log.
+.IP "NVME_DSM_MAX_RANGES" 12
+The largest possible range index in a data-set
+management command
+.IP "NVME_NQN_LENGTH" 12
+Max length for NVMe Qualified Name
+.IP "NVMF_TRADDR_SIZE" 12
+Max Transport Address size
+.IP "NVMF_TSAS_SIZE" 12
+Max Transport Specific Address Subtype size
+.IP "NVME_ZNS_CHANGED_ZONES_MAX" 12
+Max number of zones in the changed zones log
+page
diff --git a/doc/man/nvme_copy.2 b/doc/man/nvme_copy.2
new file mode 100644
index 0000000..30e693c
--- /dev/null
+++ b/doc/man/nvme_copy.2
@@ -0,0 +1,12 @@
+.TH "nvme_copy" 9 "nvme_copy" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_copy \- Copy command
+.SH SYNOPSIS
+.B "int" nvme_copy
+.BI "(struct nvme_copy_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_copy_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_copy_range.2 b/doc/man/nvme_copy_range.2
new file mode 100644
index 0000000..0733e70
--- /dev/null
+++ b/doc/man/nvme_copy_range.2
@@ -0,0 +1,40 @@
+.TH "libnvme" 9 "struct nvme_copy_range" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_copy_range \- Copy - Source Range Entries Descriptor Format
+.SH SYNOPSIS
+struct nvme_copy_range {
+.br
+.BI " __u8 rsvd0[8];"
+.br
+.BI " __le64 slba;"
+.br
+.BI " __le16 nlb;"
+.br
+.BI " __u8 rsvd18[6];"
+.br
+.BI " __le32 eilbrt;"
+.br
+.BI " __le16 elbat;"
+.br
+.BI " __le16 elbatm;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "rsvd0" 12
+Reserved
+.IP "slba" 12
+Starting LBA
+.IP "nlb" 12
+Number of Logical Blocks
+.IP "rsvd18" 12
+Reserved
+.IP "eilbrt" 12
+Expected Initial Logical Block Reference Tag /
+Expected Logical Block Storage Tag
+.IP "elbat" 12
+Expected Logical Block Application Tag
+.IP "elbatm" 12
+Expected Logical Block Application Tag Mask
diff --git a/doc/man/nvme_copy_range_f1.2 b/doc/man/nvme_copy_range_f1.2
new file mode 100644
index 0000000..63920ba
--- /dev/null
+++ b/doc/man/nvme_copy_range_f1.2
@@ -0,0 +1,40 @@
+.TH "libnvme" 9 "struct nvme_copy_range_f1" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_copy_range_f1 \- Copy - Source Range Entries Descriptor Format 1h
+.SH SYNOPSIS
+struct nvme_copy_range_f1 {
+.br
+.BI " __u8 rsvd0[8];"
+.br
+.BI " __le64 slba;"
+.br
+.BI " __le16 nlb;"
+.br
+.BI " __u8 rsvd18[8];"
+.br
+.BI " __u8 elbt[10];"
+.br
+.BI " __le16 elbat;"
+.br
+.BI " __le16 elbatm;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "rsvd0" 12
+Reserved
+.IP "slba" 12
+Starting LBA
+.IP "nlb" 12
+Number of Logical Blocks
+.IP "rsvd18" 12
+Reserved
+.IP "elbt" 12
+Expected Initial Logical Block Reference Tag /
+Expected Logical Block Storage Tag
+.IP "elbat" 12
+Expected Logical Block Application Tag
+.IP "elbatm" 12
+Expected Logical Block Application Tag Mask
diff --git a/doc/man/nvme_copy_range_f2.2 b/doc/man/nvme_copy_range_f2.2
new file mode 100644
index 0000000..a8530b5
--- /dev/null
+++ b/doc/man/nvme_copy_range_f2.2
@@ -0,0 +1,48 @@
+.TH "libnvme" 9 "struct nvme_copy_range_f2" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_copy_range_f2 \- Copy - Source Range Entries Descriptor Format 2h
+.SH SYNOPSIS
+struct nvme_copy_range_f2 {
+.br
+.BI " __le32 snsid;"
+.br
+.BI " __u8 rsvd4[4];"
+.br
+.BI " __le64 slba;"
+.br
+.BI " __le16 nlb;"
+.br
+.BI " __u8 rsvd18[4];"
+.br
+.BI " __le16 sopt;"
+.br
+.BI " __le32 eilbrt;"
+.br
+.BI " __le16 elbat;"
+.br
+.BI " __le16 elbatm;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "snsid" 12
+Source Namespace Identifier
+.IP "rsvd4" 12
+Reserved
+.IP "slba" 12
+Starting LBA
+.IP "nlb" 12
+Number of Logical Blocks
+.IP "rsvd18" 12
+Reserved
+.IP "sopt" 12
+Source Options
+.IP "eilbrt" 12
+Expected Initial Logical Block Reference Tag /
+Expected Logical Block Storage Tag
+.IP "elbat" 12
+Expected Logical Block Application Tag
+.IP "elbatm" 12
+Expected Logical Block Application Tag Mask
diff --git a/doc/man/nvme_copy_range_f3.2 b/doc/man/nvme_copy_range_f3.2
new file mode 100644
index 0000000..cb20072
--- /dev/null
+++ b/doc/man/nvme_copy_range_f3.2
@@ -0,0 +1,52 @@
+.TH "libnvme" 9 "struct nvme_copy_range_f3" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_copy_range_f3 \- Copy - Source Range Entries Descriptor Format 3h
+.SH SYNOPSIS
+struct nvme_copy_range_f3 {
+.br
+.BI " __le32 snsid;"
+.br
+.BI " __u8 rsvd4[4];"
+.br
+.BI " __le64 slba;"
+.br
+.BI " __le16 nlb;"
+.br
+.BI " __u8 rsvd18[4];"
+.br
+.BI " __le16 sopt;"
+.br
+.BI " __u8 rsvd24[2];"
+.br
+.BI " __u8 elbt[10];"
+.br
+.BI " __le16 elbat;"
+.br
+.BI " __le16 elbatm;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "snsid" 12
+Source Namespace Identifier
+.IP "rsvd4" 12
+Reserved
+.IP "slba" 12
+Starting LBA
+.IP "nlb" 12
+Number of Logical Blocks
+.IP "rsvd18" 12
+Reserved
+.IP "sopt" 12
+Source Options
+.IP "rsvd24" 12
+Reserved
+.IP "elbt" 12
+Expected Initial Logical Block Reference Tag /
+Expected Logical Block Storage Tag
+.IP "elbat" 12
+Expected Logical Block Application Tag
+.IP "elbatm" 12
+Expected Logical Block Application Tag Mask
diff --git a/doc/man/nvme_copy_range_sopt.2 b/doc/man/nvme_copy_range_sopt.2
new file mode 100644
index 0000000..5ec5c09
--- /dev/null
+++ b/doc/man/nvme_copy_range_sopt.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nvme_copy_range_sopt" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_copy_range_sopt \- NVMe Copy Range Source Options
+.SH SYNOPSIS
+enum nvme_copy_range_sopt {
+.br
+.BI " NVME_COPY_SOPT_FCO"
+
+};
+.SH Constants
+.IP "NVME_COPY_SOPT_FCO" 12
+NVMe Copy Source Option Fast Copy Only
diff --git a/doc/man/nvme_create_ctrl.2 b/doc/man/nvme_create_ctrl.2
new file mode 100644
index 0000000..43cec53
--- /dev/null
+++ b/doc/man/nvme_create_ctrl.2
@@ -0,0 +1,31 @@
+.TH "nvme_create_ctrl" 9 "nvme_create_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_create_ctrl \- Allocate an unconnected NVMe controller
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvme_create_ctrl
+.BI "(nvme_root_t r " ","
+.BI "const char *subsysnqn " ","
+.BI "const char *transport " ","
+.BI "const char *traddr " ","
+.BI "const char *host_traddr " ","
+.BI "const char *host_iface " ","
+.BI "const char *trsvcid " ");"
+.SH ARGUMENTS
+.IP "r" 12
+NVMe root element
+.IP "subsysnqn" 12
+Subsystem NQN
+.IP "transport" 12
+Transport type
+.IP "traddr" 12
+Transport address
+.IP "host_traddr" 12
+Host transport address
+.IP "host_iface" 12
+Host interface name
+.IP "trsvcid" 12
+Transport service ID
+.SH "DESCRIPTION"
+Creates an unconnected controller to be used for \fBnvme_add_ctrl\fP.
+.SH "RETURN"
+Controller instance
diff --git a/doc/man/nvme_create_root.2 b/doc/man/nvme_create_root.2
new file mode 100644
index 0000000..ae60098
--- /dev/null
+++ b/doc/man/nvme_create_root.2
@@ -0,0 +1,14 @@
+.TH "nvme_create_root" 9 "nvme_create_root" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_create_root \- Initialize root object
+.SH SYNOPSIS
+.B "nvme_root_t" nvme_create_root
+.BI "(FILE *fp " ","
+.BI "int log_level " ");"
+.SH ARGUMENTS
+.IP "fp" 12
+File descriptor for logging messages
+.IP "log_level" 12
+Logging level to use
+.SH "RETURN"
+Initialized \fInvme_root_t\fP object
diff --git a/doc/man/nvme_csi.2 b/doc/man/nvme_csi.2
new file mode 100644
index 0000000..b826bef
--- /dev/null
+++ b/doc/man/nvme_csi.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_csi" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_csi \- Defined command set indicators
+.SH SYNOPSIS
+enum nvme_csi {
+.br
+.BI " NVME_CSI_NVM"
+,
+.br
+.br
+.BI " NVME_CSI_KV"
+,
+.br
+.br
+.BI " NVME_CSI_ZNS"
+
+};
+.SH Constants
+.IP "NVME_CSI_NVM" 12
+NVM Command Set Indicator
+.IP "NVME_CSI_KV" 12
+Key Value Command Set
+.IP "NVME_CSI_ZNS" 12
+Zoned Namespace Command Set
diff --git a/doc/man/nvme_ctrl_config_match.2 b/doc/man/nvme_ctrl_config_match.2
new file mode 100644
index 0000000..44a8a11
--- /dev/null
+++ b/doc/man/nvme_ctrl_config_match.2
@@ -0,0 +1,33 @@
+.TH "nvme_ctrl_config_match" 9 "nvme_ctrl_config_match" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_config_match \- Check if ctrl @c matches config params
+.SH SYNOPSIS
+.B "bool" nvme_ctrl_config_match
+.BI "(struct nvme_ctrl *c " ","
+.BI "const char *transport " ","
+.BI "const char *traddr " ","
+.BI "const char *trsvcid " ","
+.BI "const char *subsysnqn " ","
+.BI "const char *host_traddr " ","
+.BI "const char *host_iface " ");"
+.SH ARGUMENTS
+.IP "c" 12
+An existing controller instance
+.IP "transport" 12
+Transport name
+.IP "traddr" 12
+Transport address
+.IP "trsvcid" 12
+Transport service identifier
+.IP "subsysnqn" 12
+Subsystem NQN
+.IP "host_traddr" 12
+Host transport address
+.IP "host_iface" 12
+Host interface name
+.SH "DESCRIPTION"
+Check that controller \fIc\fP matches parameters: \fItransport\fP, \fItraddr\fP,
+\fItrsvcid\fP, \fIsubsysnqn\fP, \fIhost_traddr\fP, and \fIhost_iface\fP. Parameters set
+to NULL will be ignored.
+.SH "RETURN"
+true if there's a match, false otherwise.
diff --git a/doc/man/nvme_ctrl_find.2 b/doc/man/nvme_ctrl_find.2
new file mode 100644
index 0000000..cb2cf61
--- /dev/null
+++ b/doc/man/nvme_ctrl_find.2
@@ -0,0 +1,37 @@
+.TH "nvme_ctrl_find" 9 "nvme_ctrl_find" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_find \- Locate an existing controller
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvme_ctrl_find
+.BI "(nvme_subsystem_t s " ","
+.BI "const char *transport " ","
+.BI "const char *traddr " ","
+.BI "const char *trsvcid " ","
+.BI "const char *subsysnqn " ","
+.BI "const char *host_traddr " ","
+.BI "const char *host_iface " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "transport" 12
+Transport name
+.IP "traddr" 12
+Transport address
+.IP "trsvcid" 12
+Transport service identifier
+.IP "subsysnqn" 12
+Subsystem NQN
+.IP "host_traddr" 12
+Host transport address
+.IP "host_iface" 12
+Host interface name
+.SH "DESCRIPTION"
+Lookup a controller in \fIs\fP based on \fItransport\fP, \fItraddr\fP, \fItrsvcid\fP,
+\fIsubsysnqn\fP, \fIhost_traddr\fP, and \fIhost_iface\fP. \fItransport\fP must be specified,
+other fields may be required depending on the transport. Parameters set
+to NULL will be ignored.
+
+Unlike \fBnvme_lookup_ctrl\fP, this function does not create a new object if
+an existing controller cannot be found.
+.SH "RETURN"
+Controller instance on success, NULL otherwise.
diff --git a/doc/man/nvme_ctrl_first_ns.2 b/doc/man/nvme_ctrl_first_ns.2
new file mode 100644
index 0000000..f9e174a
--- /dev/null
+++ b/doc/man/nvme_ctrl_first_ns.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_first_ns" 9 "nvme_ctrl_first_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_first_ns \- Start namespace iterator
+.SH SYNOPSIS
+.B "nvme_ns_t" nvme_ctrl_first_ns
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+First \fInvme_ns_t\fP object of an \fIc\fP iterator
diff --git a/doc/man/nvme_ctrl_first_path.2 b/doc/man/nvme_ctrl_first_path.2
new file mode 100644
index 0000000..2c682cd
--- /dev/null
+++ b/doc/man/nvme_ctrl_first_path.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_first_path" 9 "nvme_ctrl_first_path" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_first_path \- Start path iterator
+.SH SYNOPSIS
+.B "nvme_path_t" nvme_ctrl_first_path
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+First \fInvme_path_t\fP object of an \fIc\fP iterator
diff --git a/doc/man/nvme_ctrl_for_each_ns.2 b/doc/man/nvme_ctrl_for_each_ns.2
new file mode 100644
index 0000000..71fdd5f
--- /dev/null
+++ b/doc/man/nvme_ctrl_for_each_ns.2
@@ -0,0 +1,12 @@
+.TH "nvme_ctrl_for_each_ns" 9 "nvme_ctrl_for_each_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_for_each_ns \- Traverse namespaces
+.SH SYNOPSIS
+.B "nvme_ctrl_for_each_ns
+.BI "(c " ","
+.BI "n " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "n" 12
+\fInvme_ns_t\fP object
diff --git a/doc/man/nvme_ctrl_for_each_ns_safe.2 b/doc/man/nvme_ctrl_for_each_ns_safe.2
new file mode 100644
index 0000000..78c9c03
--- /dev/null
+++ b/doc/man/nvme_ctrl_for_each_ns_safe.2
@@ -0,0 +1,15 @@
+.TH "nvme_ctrl_for_each_ns_safe" 9 "nvme_ctrl_for_each_ns_safe" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_for_each_ns_safe \- Traverse namespaces
+.SH SYNOPSIS
+.B "nvme_ctrl_for_each_ns_safe
+.BI "(c " ","
+.BI "n " ","
+.BI "_n " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "n" 12
+\fInvme_ns_t\fP object
+.IP "_n" 12
+A \fInvme_ns_t_node\fP to use as temporary storage
diff --git a/doc/man/nvme_ctrl_for_each_path.2 b/doc/man/nvme_ctrl_for_each_path.2
new file mode 100644
index 0000000..aa192c2
--- /dev/null
+++ b/doc/man/nvme_ctrl_for_each_path.2
@@ -0,0 +1,12 @@
+.TH "nvme_ctrl_for_each_path" 9 "nvme_ctrl_for_each_path" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_for_each_path \- Traverse paths
+.SH SYNOPSIS
+.B "nvme_ctrl_for_each_path
+.BI "(c " ","
+.BI "p " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "p" 12
+\fInvme_path_t\fP object
diff --git a/doc/man/nvme_ctrl_for_each_path_safe.2 b/doc/man/nvme_ctrl_for_each_path_safe.2
new file mode 100644
index 0000000..c8dfc29
--- /dev/null
+++ b/doc/man/nvme_ctrl_for_each_path_safe.2
@@ -0,0 +1,15 @@
+.TH "nvme_ctrl_for_each_path_safe" 9 "nvme_ctrl_for_each_path_safe" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_for_each_path_safe \- Traverse paths
+.SH SYNOPSIS
+.B "nvme_ctrl_for_each_path_safe
+.BI "(c " ","
+.BI "p " ","
+.BI "_p " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "p" 12
+\fInvme_path_t\fP object
+.IP "_p" 12
+A \fInvme_path_t_node\fP to use as temporary storage
diff --git a/doc/man/nvme_ctrl_get_address.2 b/doc/man/nvme_ctrl_get_address.2
new file mode 100644
index 0000000..6d15499
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_address.2
@@ -0,0 +1,12 @@
+.TH "nvme_ctrl_get_address" 9 "nvme_ctrl_get_address" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_address \- Address string of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_address
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+NVMe-over-Fabrics address string of \fIc\fP or empty string
+of no address is present.
diff --git a/doc/man/nvme_ctrl_get_config.2 b/doc/man/nvme_ctrl_get_config.2
new file mode 100644
index 0000000..3cbc9e4
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_config.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_config" 9 "nvme_ctrl_get_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_config \- Fabrics configuration of a controller
+.SH SYNOPSIS
+.B "struct nvme_fabrics_config *" nvme_ctrl_get_config
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Fabrics configuration of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_dhchap_host_key.2 b/doc/man/nvme_ctrl_get_dhchap_host_key.2
new file mode 100644
index 0000000..65e7e40
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_dhchap_host_key.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_dhchap_host_key" 9 "nvme_ctrl_get_dhchap_host_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_dhchap_host_key \- Return host key
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_dhchap_host_key
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller to be checked
+.SH "RETURN"
+DH-HMAC-CHAP host key or NULL if not set
diff --git a/doc/man/nvme_ctrl_get_dhchap_key.2 b/doc/man/nvme_ctrl_get_dhchap_key.2
new file mode 100644
index 0000000..8fb206a
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_dhchap_key.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_dhchap_key" 9 "nvme_ctrl_get_dhchap_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_dhchap_key \- Return controller key
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_dhchap_key
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller for which the key should be set
+.SH "RETURN"
+DH-HMAC-CHAP controller key or NULL if not set
diff --git a/doc/man/nvme_ctrl_get_fd.2 b/doc/man/nvme_ctrl_get_fd.2
new file mode 100644
index 0000000..e8da7f4
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_fd.2
@@ -0,0 +1,18 @@
+.TH "nvme_ctrl_get_fd" 9 "nvme_ctrl_get_fd" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_fd \- Get associated file descriptor
+.SH SYNOPSIS
+.B "int" nvme_ctrl_get_fd
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "DESCRIPTION"
+libnvme will \fBopen\fP the file (if not already opened) and keep
+an internal copy of the file descriptor. Following calls to
+this API retrieve the internal cached copy of the file
+descriptor. The file will remain opened and the fd will
+remain cached until the controller object is deleted or
+\fBnvme_ctrl_release_fd\fP is called.
+.SH "RETURN"
+File descriptor associated with \fIc\fP or -1
diff --git a/doc/man/nvme_ctrl_get_firmware.2 b/doc/man/nvme_ctrl_get_firmware.2
new file mode 100644
index 0000000..f30586f
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_firmware.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_firmware" 9 "nvme_ctrl_get_firmware" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_firmware \- Firmware string of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_firmware
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Firmware string of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_host_iface.2 b/doc/man/nvme_ctrl_get_host_iface.2
new file mode 100644
index 0000000..79737f5
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_host_iface.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_host_iface" 9 "nvme_ctrl_get_host_iface" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_host_iface \- Host interface name of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_host_iface
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Host interface name of \fIc\fP (if present)
diff --git a/doc/man/nvme_ctrl_get_host_traddr.2 b/doc/man/nvme_ctrl_get_host_traddr.2
new file mode 100644
index 0000000..0e6d097
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_host_traddr.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_host_traddr" 9 "nvme_ctrl_get_host_traddr" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_host_traddr \- Host transport address of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_host_traddr
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Host transport address of \fIc\fP (if present)
diff --git a/doc/man/nvme_ctrl_get_model.2 b/doc/man/nvme_ctrl_get_model.2
new file mode 100644
index 0000000..70a1d3f
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_model.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_model" 9 "nvme_ctrl_get_model" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_model \- Model of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_model
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Model string of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_name.2 b/doc/man/nvme_ctrl_get_name.2
new file mode 100644
index 0000000..44f1da3
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_name.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_name" 9 "nvme_ctrl_get_name" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_name \- sysfs name of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_name
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+sysfs name of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_numa_node.2 b/doc/man/nvme_ctrl_get_numa_node.2
new file mode 100644
index 0000000..33411aa
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_numa_node.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_numa_node" 9 "nvme_ctrl_get_numa_node" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_numa_node \- NUMA node of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_numa_node
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+String indicating the NUMA node
diff --git a/doc/man/nvme_ctrl_get_phy_slot.2 b/doc/man/nvme_ctrl_get_phy_slot.2
new file mode 100644
index 0000000..c4f777f
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_phy_slot.2
@@ -0,0 +1,12 @@
+.TH "nvme_ctrl_get_phy_slot" 9 "nvme_ctrl_get_phy_slot" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_phy_slot \- PCI physical slot number of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_phy_slot
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+PCI physical slot number of \fIc\fP or empty string if slot
+number is not present.
diff --git a/doc/man/nvme_ctrl_get_queue_count.2 b/doc/man/nvme_ctrl_get_queue_count.2
new file mode 100644
index 0000000..4bf4b1a
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_queue_count.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_queue_count" 9 "nvme_ctrl_get_queue_count" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_queue_count \- Queue count of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_queue_count
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Queue count of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_serial.2 b/doc/man/nvme_ctrl_get_serial.2
new file mode 100644
index 0000000..f7cf63a
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_serial.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_serial" 9 "nvme_ctrl_get_serial" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_serial \- Serial number of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_serial
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Serial number string of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_sqsize.2 b/doc/man/nvme_ctrl_get_sqsize.2
new file mode 100644
index 0000000..319b978
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_sqsize.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_sqsize" 9 "nvme_ctrl_get_sqsize" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_sqsize \- SQ size of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_sqsize
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+SQ size (as string) of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_src_addr.2 b/doc/man/nvme_ctrl_get_src_addr.2
new file mode 100644
index 0000000..effafa4
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_src_addr.2
@@ -0,0 +1,17 @@
+.TH "nvme_ctrl_get_src_addr" 9 "nvme_ctrl_get_src_addr" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_src_addr \- Extract src_addr from the c->address string
+.SH SYNOPSIS
+.B "char *" nvme_ctrl_get_src_addr
+.BI "(nvme_ctrl_t c " ","
+.BI "char *src_addr " ","
+.BI "size_t src_addr_len " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "src_addr" 12
+Where to copy the src_addr. Size must be at least INET6_ADDRSTRLEN.
+.IP "src_addr_len" 12
+Length of the buffer \fIsrc_addr\fP.
+.SH "RETURN"
+Pointer to \fIsrc_addr\fP on success. NULL on failure to extract the src_addr.
diff --git a/doc/man/nvme_ctrl_get_state.2 b/doc/man/nvme_ctrl_get_state.2
new file mode 100644
index 0000000..2beae14
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_state.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_state" 9 "nvme_ctrl_get_state" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_state \- Running state of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_state
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+String indicating the running state of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_subsysnqn.2 b/doc/man/nvme_ctrl_get_subsysnqn.2
new file mode 100644
index 0000000..ebc3ff8
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_subsysnqn.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_subsysnqn" 9 "nvme_ctrl_get_subsysnqn" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_subsysnqn \- Subsystem NQN of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_subsysnqn
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Subsystem NQN of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_subsystem.2 b/doc/man/nvme_ctrl_get_subsystem.2
new file mode 100644
index 0000000..06308cd
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_subsystem.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_subsystem" 9 "nvme_ctrl_get_subsystem" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_subsystem \- Parent subsystem of a controller
+.SH SYNOPSIS
+.B "nvme_subsystem_t" nvme_ctrl_get_subsystem
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Parent nvme_subsystem_t object
diff --git a/doc/man/nvme_ctrl_get_sysfs_dir.2 b/doc/man/nvme_ctrl_get_sysfs_dir.2
new file mode 100644
index 0000000..902a3fb
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_sysfs_dir.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_sysfs_dir" 9 "nvme_ctrl_get_sysfs_dir" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_sysfs_dir \- sysfs directory of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_sysfs_dir
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+sysfs directory name of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_traddr.2 b/doc/man/nvme_ctrl_get_traddr.2
new file mode 100644
index 0000000..bfaa4b7
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_traddr.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_traddr" 9 "nvme_ctrl_get_traddr" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_traddr \- Transport address of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_traddr
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Transport address of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_transport.2 b/doc/man/nvme_ctrl_get_transport.2
new file mode 100644
index 0000000..d267a73
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_transport.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_transport" 9 "nvme_ctrl_get_transport" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_transport \- Transport type of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_transport
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Transport type of \fIc\fP
diff --git a/doc/man/nvme_ctrl_get_trsvcid.2 b/doc/man/nvme_ctrl_get_trsvcid.2
new file mode 100644
index 0000000..42672b6
--- /dev/null
+++ b/doc/man/nvme_ctrl_get_trsvcid.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_get_trsvcid" 9 "nvme_ctrl_get_trsvcid" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_get_trsvcid \- Transport service identifier of a controller
+.SH SYNOPSIS
+.B "const char *" nvme_ctrl_get_trsvcid
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Transport service identifier of \fIc\fP (if present)
diff --git a/doc/man/nvme_ctrl_identify.2 b/doc/man/nvme_ctrl_identify.2
new file mode 100644
index 0000000..c4ff85b
--- /dev/null
+++ b/doc/man/nvme_ctrl_identify.2
@@ -0,0 +1,17 @@
+.TH "nvme_ctrl_identify" 9 "nvme_ctrl_identify" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_identify \- Issues an 'identify controller' command
+.SH SYNOPSIS
+.B "int" nvme_ctrl_identify
+.BI "(nvme_ctrl_t c " ","
+.BI "struct nvme_id_ctrl *id " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "id" 12
+Identify controller data structure
+.SH "DESCRIPTION"
+Issues an 'identify controller' command to \fIc\fP and copies the
+data into \fIid\fP.
+.SH "RETURN"
+0 on success or -1 on failure.
diff --git a/doc/man/nvme_ctrl_is_discovered.2 b/doc/man/nvme_ctrl_is_discovered.2
new file mode 100644
index 0000000..28361a7
--- /dev/null
+++ b/doc/man/nvme_ctrl_is_discovered.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_is_discovered" 9 "nvme_ctrl_is_discovered" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_is_discovered \- Returns the value of the 'discovered' flag
+.SH SYNOPSIS
+.B "bool" nvme_ctrl_is_discovered
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Value of the 'discovered' flag of \fIc\fP
diff --git a/doc/man/nvme_ctrl_is_discovery_ctrl.2 b/doc/man/nvme_ctrl_is_discovery_ctrl.2
new file mode 100644
index 0000000..cb62dca
--- /dev/null
+++ b/doc/man/nvme_ctrl_is_discovery_ctrl.2
@@ -0,0 +1,14 @@
+.TH "nvme_ctrl_is_discovery_ctrl" 9 "nvme_ctrl_is_discovery_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_is_discovery_ctrl \- Check the 'discovery_ctrl' flag
+.SH SYNOPSIS
+.B "bool" nvme_ctrl_is_discovery_ctrl
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller to be checked
+.SH "DESCRIPTION"
+Returns the value of the 'discovery_ctrl' flag which specifies whether
+\fIc\fP connects to a discovery subsystem.
+.SH "RETURN"
+Value of the 'discover_ctrl' flag
diff --git a/doc/man/nvme_ctrl_is_persistent.2 b/doc/man/nvme_ctrl_is_persistent.2
new file mode 100644
index 0000000..05a9c35
--- /dev/null
+++ b/doc/man/nvme_ctrl_is_persistent.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_is_persistent" 9 "nvme_ctrl_is_persistent" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_is_persistent \- Returns the value of the 'persistent' flag
+.SH SYNOPSIS
+.B "bool" nvme_ctrl_is_persistent
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "RETURN"
+Value of the 'persistent' flag of \fIc\fP
diff --git a/doc/man/nvme_ctrl_is_unique_discovery_ctrl.2 b/doc/man/nvme_ctrl_is_unique_discovery_ctrl.2
new file mode 100644
index 0000000..3540959
--- /dev/null
+++ b/doc/man/nvme_ctrl_is_unique_discovery_ctrl.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrl_is_unique_discovery_ctrl" 9 "nvme_ctrl_is_unique_discovery_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_is_unique_discovery_ctrl \- Check the 'unique_discovery_ctrl' flag
+.SH SYNOPSIS
+.B "bool" nvme_ctrl_is_unique_discovery_ctrl
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller to be checked
+.SH "RETURN"
+Value of the 'unique_discovery_ctrl' flag
diff --git a/doc/man/nvme_ctrl_list.2 b/doc/man/nvme_ctrl_list.2
new file mode 100644
index 0000000..40c0531
--- /dev/null
+++ b/doc/man/nvme_ctrl_list.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_ctrl_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_ctrl_list \- Controller List
+.SH SYNOPSIS
+struct nvme_ctrl_list {
+.br
+.BI " __le16 num;"
+.br
+.BI " __le16 identifier[NVME_ID_CTRL_LIST_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "num" 12
+Number of Identifiers
+.IP "identifier" 12
+NVM subsystem unique controller identifier
diff --git a/doc/man/nvme_ctrl_metadata_type.2 b/doc/man/nvme_ctrl_metadata_type.2
new file mode 100644
index 0000000..e81fb4c
--- /dev/null
+++ b/doc/man/nvme_ctrl_metadata_type.2
@@ -0,0 +1,108 @@
+.TH "libnvme" 9 "enum nvme_ctrl_metadata_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ctrl_metadata_type \- Controller Metadata Element Types
+.SH SYNOPSIS
+enum nvme_ctrl_metadata_type {
+.br
+.BI " NVME_CTRL_METADATA_OS_CTRL_NAME"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_OS_DRIVER_NAME"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_OS_DRIVER_VER"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_SYS_PROC_MODEL"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_CHIPSET_DRV_NAME"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_CHIPSET_DRV_VERSION"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_OS_NAME_AND_BUILD"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_SYS_PROD_NAME"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_FIRMWARE_VERSION"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_OS_DRIVER_FILENAME"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_DISPLAY_DRV_NAME"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_DISPLAY_DRV_VERSION"
+,
+.br
+.br
+.BI " NVME_CTRL_METADATA_HOST_DET_FAIL_REC"
+
+};
+.SH Constants
+.IP "NVME_CTRL_METADATA_OS_CTRL_NAME" 12
+Name of the controller in
+the operating system.
+.IP "NVME_CTRL_METADATA_OS_DRIVER_NAME" 12
+Name of the driver in the
+operating system.
+.IP "NVME_CTRL_METADATA_OS_DRIVER_VER" 12
+Version of the driver in
+the operating system.
+.IP "NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME" 12
+Name of the controller in
+the pre-boot environment.
+.IP "NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME" 12
+Name of the driver in the
+pre-boot environment.
+.IP "NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER" 12
+Version of the driver in the
+pre-boot environment.
+.IP "NVME_CTRL_METADATA_SYS_PROC_MODEL" 12
+Model of the processor.
+.IP "NVME_CTRL_METADATA_CHIPSET_DRV_NAME" 12
+Chipset driver name.
+.IP "NVME_CTRL_METADATA_CHIPSET_DRV_VERSION" 12
+Chipset driver version.
+.IP "NVME_CTRL_METADATA_OS_NAME_AND_BUILD" 12
+Operating system name and build.
+.IP "NVME_CTRL_METADATA_SYS_PROD_NAME" 12
+System product name.
+.IP "NVME_CTRL_METADATA_FIRMWARE_VERSION" 12
+Host firmware (e.g UEFI) version.
+.IP "NVME_CTRL_METADATA_OS_DRIVER_FILENAME" 12
+Operating system driver filename.
+.IP "NVME_CTRL_METADATA_DISPLAY_DRV_NAME" 12
+Display driver name.
+.IP "NVME_CTRL_METADATA_DISPLAY_DRV_VERSION" 12
+Display driver version.
+.IP "NVME_CTRL_METADATA_HOST_DET_FAIL_REC" 12
+Failure record.
diff --git a/doc/man/nvme_ctrl_next_ns.2 b/doc/man/nvme_ctrl_next_ns.2
new file mode 100644
index 0000000..635350e
--- /dev/null
+++ b/doc/man/nvme_ctrl_next_ns.2
@@ -0,0 +1,14 @@
+.TH "nvme_ctrl_next_ns" 9 "nvme_ctrl_next_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_next_ns \- Next namespace iterator
+.SH SYNOPSIS
+.B "nvme_ns_t" nvme_ctrl_next_ns
+.BI "(nvme_ctrl_t c " ","
+.BI "nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "n" 12
+Previous nvme_ns_t iterator
+.SH "RETURN"
+Next nvme_ns_t object of an \fIc\fP iterator
diff --git a/doc/man/nvme_ctrl_next_path.2 b/doc/man/nvme_ctrl_next_path.2
new file mode 100644
index 0000000..90743ac
--- /dev/null
+++ b/doc/man/nvme_ctrl_next_path.2
@@ -0,0 +1,14 @@
+.TH "nvme_ctrl_next_path" 9 "nvme_ctrl_next_path" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_next_path \- Next path iterator
+.SH SYNOPSIS
+.B "nvme_path_t" nvme_ctrl_next_path
+.BI "(nvme_ctrl_t c " ","
+.BI "nvme_path_t p " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "p" 12
+Previous \fInvme_path_t\fP object of an \fIc\fP iterator
+.SH "RETURN"
+Next \fInvme_path_t\fP object of an \fIc\fP iterator
diff --git a/doc/man/nvme_ctrl_release_fd.2 b/doc/man/nvme_ctrl_release_fd.2
new file mode 100644
index 0000000..1b44359
--- /dev/null
+++ b/doc/man/nvme_ctrl_release_fd.2
@@ -0,0 +1,9 @@
+.TH "nvme_ctrl_release_fd" 9 "nvme_ctrl_release_fd" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_release_fd \- Close fd and clear fd from controller object
+.SH SYNOPSIS
+.B "void" nvme_ctrl_release_fd
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
diff --git a/doc/man/nvme_ctrl_reset.2 b/doc/man/nvme_ctrl_reset.2
new file mode 100644
index 0000000..134b312
--- /dev/null
+++ b/doc/man/nvme_ctrl_reset.2
@@ -0,0 +1,13 @@
+.TH "nvme_ctrl_reset" 9 "nvme_ctrl_reset" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_reset \- Initiate a controller reset
+.SH SYNOPSIS
+.B "int" nvme_ctrl_reset
+.BI "(int fd " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.SH "DESCRIPTION"
+This should only be sent to controller handles, not to namespaces.
+.SH "RETURN"
+0 if a reset was initiated or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ctrl_set_dhchap_host_key.2 b/doc/man/nvme_ctrl_set_dhchap_host_key.2
new file mode 100644
index 0000000..abc46ba
--- /dev/null
+++ b/doc/man/nvme_ctrl_set_dhchap_host_key.2
@@ -0,0 +1,12 @@
+.TH "nvme_ctrl_set_dhchap_host_key" 9 "nvme_ctrl_set_dhchap_host_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_set_dhchap_host_key \- Set host key
+.SH SYNOPSIS
+.B "void" nvme_ctrl_set_dhchap_host_key
+.BI "(nvme_ctrl_t c " ","
+.BI "const char *key " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Host for which the key should be set
+.IP "key" 12
+DH-HMAC-CHAP Key to set or NULL to clear existing key
diff --git a/doc/man/nvme_ctrl_set_dhchap_key.2 b/doc/man/nvme_ctrl_set_dhchap_key.2
new file mode 100644
index 0000000..85a58c8
--- /dev/null
+++ b/doc/man/nvme_ctrl_set_dhchap_key.2
@@ -0,0 +1,12 @@
+.TH "nvme_ctrl_set_dhchap_key" 9 "nvme_ctrl_set_dhchap_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_set_dhchap_key \- Set controller key
+.SH SYNOPSIS
+.B "void" nvme_ctrl_set_dhchap_key
+.BI "(nvme_ctrl_t c " ","
+.BI "const char *key " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller for which the key should be set
+.IP "key" 12
+DH-HMAC-CHAP Key to set or NULL to clear existing key
diff --git a/doc/man/nvme_ctrl_set_discovered.2 b/doc/man/nvme_ctrl_set_discovered.2
new file mode 100644
index 0000000..10f1355
--- /dev/null
+++ b/doc/man/nvme_ctrl_set_discovered.2
@@ -0,0 +1,14 @@
+.TH "nvme_ctrl_set_discovered" 9 "nvme_ctrl_set_discovered" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_set_discovered \- Set the 'discovered' flag
+.SH SYNOPSIS
+.B "void" nvme_ctrl_set_discovered
+.BI "(nvme_ctrl_t c " ","
+.BI "bool discovered " ");"
+.SH ARGUMENTS
+.IP "c" 12
+nvme_ctrl_t object
+.IP "discovered" 12
+Value of the 'discovered' flag
+.SH "DESCRIPTION"
+Set the 'discovered' flag of \fIc\fP to \fIdiscovered\fP
diff --git a/doc/man/nvme_ctrl_set_discovery_ctrl.2 b/doc/man/nvme_ctrl_set_discovery_ctrl.2
new file mode 100644
index 0000000..509e76f
--- /dev/null
+++ b/doc/man/nvme_ctrl_set_discovery_ctrl.2
@@ -0,0 +1,15 @@
+.TH "nvme_ctrl_set_discovery_ctrl" 9 "nvme_ctrl_set_discovery_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_set_discovery_ctrl \- Set the 'discovery_ctrl' flag
+.SH SYNOPSIS
+.B "void" nvme_ctrl_set_discovery_ctrl
+.BI "(nvme_ctrl_t c " ","
+.BI "bool discovery " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller to be modified
+.IP "discovery" 12
+value of the discovery_ctrl flag
+.SH "DESCRIPTION"
+Sets the 'discovery_ctrl' flag in \fIc\fP to specify whether
+\fIc\fP connects to a discovery subsystem.
diff --git a/doc/man/nvme_ctrl_set_persistent.2 b/doc/man/nvme_ctrl_set_persistent.2
new file mode 100644
index 0000000..456c7cc
--- /dev/null
+++ b/doc/man/nvme_ctrl_set_persistent.2
@@ -0,0 +1,14 @@
+.TH "nvme_ctrl_set_persistent" 9 "nvme_ctrl_set_persistent" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_set_persistent \- Set the 'persistent' flag
+.SH SYNOPSIS
+.B "void" nvme_ctrl_set_persistent
+.BI "(nvme_ctrl_t c " ","
+.BI "bool persistent " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "persistent" 12
+value of the 'persistent' flag
+.SH "DESCRIPTION"
+Set the 'persistent' flag of \fIc\fP to \fIpersistent\fP
diff --git a/doc/man/nvme_ctrl_set_unique_discovery_ctrl.2 b/doc/man/nvme_ctrl_set_unique_discovery_ctrl.2
new file mode 100644
index 0000000..e0cca59
--- /dev/null
+++ b/doc/man/nvme_ctrl_set_unique_discovery_ctrl.2
@@ -0,0 +1,15 @@
+.TH "nvme_ctrl_set_unique_discovery_ctrl" 9 "nvme_ctrl_set_unique_discovery_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrl_set_unique_discovery_ctrl \- Set the 'unique_discovery_ctrl' flag
+.SH SYNOPSIS
+.B "void" nvme_ctrl_set_unique_discovery_ctrl
+.BI "(nvme_ctrl_t c " ","
+.BI "bool unique " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller to be modified
+.IP "unique" 12
+value of the unique_disc_ctrl flag
+.SH "DESCRIPTION"
+Sets the 'unique_discovery_ctrl' flag in \fIc\fP to specify wheter
+\fIc\fP is a unique discovery controller
diff --git a/doc/man/nvme_ctrls_filter.2 b/doc/man/nvme_ctrls_filter.2
new file mode 100644
index 0000000..1227c5c
--- /dev/null
+++ b/doc/man/nvme_ctrls_filter.2
@@ -0,0 +1,11 @@
+.TH "nvme_ctrls_filter" 9 "nvme_ctrls_filter" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ctrls_filter \- Filter for controllers
+.SH SYNOPSIS
+.B "int" nvme_ctrls_filter
+.BI "(const struct dirent *d " ");"
+.SH ARGUMENTS
+.IP "d" 12
+dirent to check
+.SH "RETURN"
+1 if \fId\fP matches, 0 otherwise
diff --git a/doc/man/nvme_data_tfr.2 b/doc/man/nvme_data_tfr.2
new file mode 100644
index 0000000..107ed58
--- /dev/null
+++ b/doc/man/nvme_data_tfr.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_data_tfr" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_data_tfr \- Data transfer direction of the command
+.SH SYNOPSIS
+enum nvme_data_tfr {
+.br
+.BI " NVME_DATA_TFR_NO_DATA_TFR"
+,
+.br
+.br
+.BI " NVME_DATA_TFR_HOST_TO_CTRL"
+,
+.br
+.br
+.BI " NVME_DATA_TFR_CTRL_TO_HOST"
+,
+.br
+.br
+.BI " NVME_DATA_TFR_BIDIRECTIONAL"
+
+};
+.SH Constants
+.IP "NVME_DATA_TFR_NO_DATA_TFR" 12
+No data transfer
+.IP "NVME_DATA_TFR_HOST_TO_CTRL" 12
+Host to controller
+.IP "NVME_DATA_TFR_CTRL_TO_HOST" 12
+Controller to host
+.IP "NVME_DATA_TFR_BIDIRECTIONAL" 12
+Bidirectional
diff --git a/doc/man/nvme_default_host.2 b/doc/man/nvme_default_host.2
new file mode 100644
index 0000000..6b78c17
--- /dev/null
+++ b/doc/man/nvme_default_host.2
@@ -0,0 +1,14 @@
+.TH "nvme_default_host" 9 "nvme_default_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_default_host \- Initializes the default host
+.SH SYNOPSIS
+.B "nvme_host_t" nvme_default_host
+.BI "(nvme_root_t r " ");"
+.SH ARGUMENTS
+.IP "r" 12
+\fInvme_root_t\fP object
+.SH "DESCRIPTION"
+Initializes the default host object based on the values in
+/etc/nvme/hostnqn and /etc/nvme/hostid and attaches it to \fIr\fP.
+.SH "RETURN"
+\fInvme_host_t\fP object
diff --git a/doc/man/nvme_describe_key_serial.2 b/doc/man/nvme_describe_key_serial.2
new file mode 100644
index 0000000..6cd0e08
--- /dev/null
+++ b/doc/man/nvme_describe_key_serial.2
@@ -0,0 +1,15 @@
+.TH "nvme_describe_key_serial" 9 "nvme_describe_key_serial" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_describe_key_serial \- Return key description
+.SH SYNOPSIS
+.B "char *" nvme_describe_key_serial
+.BI "(long key_id " ");"
+.SH ARGUMENTS
+.IP "key_id" 12
+Key serial number
+.SH "DESCRIPTION"
+Fetches the description of the key or keyring identified
+by the serial number \fIkey_id\fP.
+.SH "RETURN"
+The description of \fIkey_id\fP or NULL on failure.
+The returned string needs to be freed by the caller.
diff --git a/doc/man/nvme_dev_self_test.2 b/doc/man/nvme_dev_self_test.2
new file mode 100644
index 0000000..76627ec
--- /dev/null
+++ b/doc/man/nvme_dev_self_test.2
@@ -0,0 +1,23 @@
+.TH "nvme_dev_self_test" 9 "nvme_dev_self_test" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_dev_self_test \- Start or abort a self test
+.SH SYNOPSIS
+.B "int" nvme_dev_self_test
+.BI "(struct nvme_dev_self_test_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_dev_self_test\fP argument structure
+.SH "DESCRIPTION"
+The Device Self-test command starts a device self-test operation or abort a
+device self-test operation. A device self-test operation is a diagnostic
+testing sequence that tests the integrity and functionality of the
+controller and may include testing of the media associated with namespaces.
+The controller may return a response to this command immediately while
+running the self-test in the background.
+
+Set the 'nsid' field to 0 to not include namespaces in the test. Set to
+0xffffffff to test all namespaces. All other values tests a specific
+namespace, if present.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_dtype.2 b/doc/man/nvme_directive_dtype.2
new file mode 100644
index 0000000..b4d1abd
--- /dev/null
+++ b/doc/man/nvme_directive_dtype.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_directive_dtype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_directive_dtype \- Directive Types
+.SH SYNOPSIS
+enum nvme_directive_dtype {
+.br
+.BI " NVME_DIRECTIVE_DTYPE_IDENTIFY"
+,
+.br
+.br
+.BI " NVME_DIRECTIVE_DTYPE_STREAMS"
+
+};
+.SH Constants
+.IP "NVME_DIRECTIVE_DTYPE_IDENTIFY" 12
+Identify directive type
+.IP "NVME_DIRECTIVE_DTYPE_STREAMS" 12
+Streams directive type
diff --git a/doc/man/nvme_directive_receive_doper.2 b/doc/man/nvme_directive_receive_doper.2
new file mode 100644
index 0000000..2c87145
--- /dev/null
+++ b/doc/man/nvme_directive_receive_doper.2
@@ -0,0 +1,26 @@
+.TH "libnvme" 9 "enum nvme_directive_receive_doper" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_directive_receive_doper \- Directive Receive Directive Operation
+.SH SYNOPSIS
+enum nvme_directive_receive_doper {
+.br
+.BI " NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM"
+,
+.br
+.br
+.BI " NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM"
+,
+.br
+.br
+.BI " NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS"
+,
+.br
+.br
+.BI " NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE"
+
+};
+.SH Constants
+.IP "NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM" 12
+.IP "NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM" 12
+.IP "NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS" 12
+.IP "NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE" 12
diff --git a/doc/man/nvme_directive_recv.2 b/doc/man/nvme_directive_recv.2
new file mode 100644
index 0000000..fdbbe1a
--- /dev/null
+++ b/doc/man/nvme_directive_recv.2
@@ -0,0 +1,12 @@
+.TH "nvme_directive_recv" 9 "nvme_directive_recv" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_recv \- Receive directive specific data
+.SH SYNOPSIS
+.B "int" nvme_directive_recv
+.BI "(struct nvme_directive_recv_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_directive_recv_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_recv_identify_parameters.2 b/doc/man/nvme_directive_recv_identify_parameters.2
new file mode 100644
index 0000000..e8cad1a
--- /dev/null
+++ b/doc/man/nvme_directive_recv_identify_parameters.2
@@ -0,0 +1,18 @@
+.TH "nvme_directive_recv_identify_parameters" 9 "nvme_directive_recv_identify_parameters" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_recv_identify_parameters \- Directive receive identifier parameters
+.SH SYNOPSIS
+.B "int" nvme_directive_recv_identify_parameters
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_id_directives *id " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "id" 12
+Identify parameters buffer
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_recv_stream_allocate.2 b/doc/man/nvme_directive_recv_stream_allocate.2
new file mode 100644
index 0000000..9a33428
--- /dev/null
+++ b/doc/man/nvme_directive_recv_stream_allocate.2
@@ -0,0 +1,21 @@
+.TH "nvme_directive_recv_stream_allocate" 9 "nvme_directive_recv_stream_allocate" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_recv_stream_allocate \- Directive receive stream allocate
+.SH SYNOPSIS
+.B "int" nvme_directive_recv_stream_allocate
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u16 nsr " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "nsr" 12
+Namespace Streams Requested
+.IP "result" 12
+If successful, the CQE dword0 value
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_recv_stream_parameters.2 b/doc/man/nvme_directive_recv_stream_parameters.2
new file mode 100644
index 0000000..4a05dba
--- /dev/null
+++ b/doc/man/nvme_directive_recv_stream_parameters.2
@@ -0,0 +1,18 @@
+.TH "nvme_directive_recv_stream_parameters" 9 "nvme_directive_recv_stream_parameters" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_recv_stream_parameters \- Directive receive stream parameters
+.SH SYNOPSIS
+.B "int" nvme_directive_recv_stream_parameters
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_streams_directive_params *parms " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "parms" 12
+Streams directive parameters buffer
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_recv_stream_status.2 b/doc/man/nvme_directive_recv_stream_status.2
new file mode 100644
index 0000000..ae290d3
--- /dev/null
+++ b/doc/man/nvme_directive_recv_stream_status.2
@@ -0,0 +1,21 @@
+.TH "nvme_directive_recv_stream_status" 9 "nvme_directive_recv_stream_status" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_recv_stream_status \- Directive receive stream status
+.SH SYNOPSIS
+.B "int" nvme_directive_recv_stream_status
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "unsigned int nr_entries " ","
+.BI "struct nvme_streams_directive_status *id " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "nr_entries" 12
+Number of streams to receive
+.IP "id" 12
+Stream status buffer
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_send.2 b/doc/man/nvme_directive_send.2
new file mode 100644
index 0000000..a4e441b
--- /dev/null
+++ b/doc/man/nvme_directive_send.2
@@ -0,0 +1,18 @@
+.TH "nvme_directive_send" 9 "nvme_directive_send" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_send \- Send directive command
+.SH SYNOPSIS
+.B "int" nvme_directive_send
+.BI "(struct nvme_directive_send_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_directive_send_args\fP argument structure
+.SH "DESCRIPTION"
+Directives is a mechanism to enable host and NVM subsystem or controller
+information exchange. The Directive Send command transfers data related to a
+specific Directive Type from the host to the controller.
+
+See the NVMe specification for more information.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_send_doper.2 b/doc/man/nvme_directive_send_doper.2
new file mode 100644
index 0000000..82d47ad
--- /dev/null
+++ b/doc/man/nvme_directive_send_doper.2
@@ -0,0 +1,21 @@
+.TH "libnvme" 9 "enum nvme_directive_send_doper" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_directive_send_doper \- Directive Send Directive Operation
+.SH SYNOPSIS
+enum nvme_directive_send_doper {
+.br
+.BI " NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR"
+,
+.br
+.br
+.BI " NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER"
+,
+.br
+.br
+.BI " NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE"
+
+};
+.SH Constants
+.IP "NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR" 12
+.IP "NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER" 12
+.IP "NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE" 12
diff --git a/doc/man/nvme_directive_send_id_endir.2 b/doc/man/nvme_directive_send_id_endir.2
new file mode 100644
index 0000000..9b0efa6
--- /dev/null
+++ b/doc/man/nvme_directive_send_id_endir.2
@@ -0,0 +1,24 @@
+.TH "nvme_directive_send_id_endir" 9 "nvme_directive_send_id_endir" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_send_id_endir \- Directive Send Enable Directive
+.SH SYNOPSIS
+.B "int" nvme_directive_send_id_endir
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "bool endir " ","
+.BI "enum nvme_directive_dtype dtype " ","
+.BI "struct nvme_id_directives *id " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace Identifier
+.IP "endir" 12
+Enable Directive
+.IP "dtype" 12
+Directive Type
+.IP "id" 12
+Pointer to structure nvme_id_directives
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_send_identify_endir.2 b/doc/man/nvme_directive_send_identify_endir.2
new file mode 100644
index 0000000..12c301b
--- /dev/null
+++ b/doc/man/nvme_directive_send_identify_endir.2
@@ -0,0 +1,16 @@
+.TH "libnvme" 9 "enum nvme_directive_send_identify_endir" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_directive_send_identify_endir \- Enable Directive
+.SH SYNOPSIS
+enum nvme_directive_send_identify_endir {
+.br
+.BI " NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_DISABLE"
+,
+.br
+.br
+.BI " NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_ENABLE"
+
+};
+.SH Constants
+.IP "NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_DISABLE" 12
+.IP "NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_ENABLE" 12
diff --git a/doc/man/nvme_directive_send_stream_release_identifier.2 b/doc/man/nvme_directive_send_stream_release_identifier.2
new file mode 100644
index 0000000..d541f5a
--- /dev/null
+++ b/doc/man/nvme_directive_send_stream_release_identifier.2
@@ -0,0 +1,18 @@
+.TH "nvme_directive_send_stream_release_identifier" 9 "nvme_directive_send_stream_release_identifier" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_send_stream_release_identifier \- Directive Send Stream release
+.SH SYNOPSIS
+.B "int" nvme_directive_send_stream_release_identifier
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u16 stream_id " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "stream_id" 12
+Stream identifier
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_send_stream_release_resource.2 b/doc/man/nvme_directive_send_stream_release_resource.2
new file mode 100644
index 0000000..edac8b5
--- /dev/null
+++ b/doc/man/nvme_directive_send_stream_release_resource.2
@@ -0,0 +1,15 @@
+.TH "nvme_directive_send_stream_release_resource" 9 "nvme_directive_send_stream_release_resource" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_directive_send_stream_release_resource \- Directive Send Stream release resources
+.SH SYNOPSIS
+.B "int" nvme_directive_send_stream_release_resource
+.BI "(int fd " ","
+.BI "__u32 nsid " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_directive_types.2 b/doc/man/nvme_directive_types.2
new file mode 100644
index 0000000..91a64ea
--- /dev/null
+++ b/doc/man/nvme_directive_types.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_directive_types" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_directive_types \- Directives Supported or Enabled
+.SH SYNOPSIS
+enum nvme_directive_types {
+.br
+.BI " NVME_ID_DIR_ID_BIT"
+,
+.br
+.br
+.BI " NVME_ID_DIR_SD_BIT"
+,
+.br
+.br
+.BI " NVME_ID_DIR_DP_BIT"
+
+};
+.SH Constants
+.IP "NVME_ID_DIR_ID_BIT" 12
+Identify directive is supported
+.IP "NVME_ID_DIR_SD_BIT" 12
+Streams directive is supported
+.IP "NVME_ID_DIR_DP_BIT" 12
+Direct Placement directive is supported
diff --git a/doc/man/nvme_disconnect_ctrl.2 b/doc/man/nvme_disconnect_ctrl.2
new file mode 100644
index 0000000..8fa1021
--- /dev/null
+++ b/doc/man/nvme_disconnect_ctrl.2
@@ -0,0 +1,13 @@
+.TH "nvme_disconnect_ctrl" 9 "nvme_disconnect_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_disconnect_ctrl \- Disconnect a controller
+.SH SYNOPSIS
+.B "int" nvme_disconnect_ctrl
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.SH "DESCRIPTION"
+Issues a 'disconnect' fabrics command to \fIc\fP
+.SH "RETURN"
+0 on success, -1 on failure.
diff --git a/doc/man/nvme_dsm.2 b/doc/man/nvme_dsm.2
new file mode 100644
index 0000000..8ec5991
--- /dev/null
+++ b/doc/man/nvme_dsm.2
@@ -0,0 +1,18 @@
+.TH "nvme_dsm" 9 "nvme_dsm" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_dsm \- Send an nvme data set management command
+.SH SYNOPSIS
+.B "int" nvme_dsm
+.BI "(struct nvme_dsm_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_dsm_args\fP argument structure
+.SH "DESCRIPTION"
+The Dataset Management command is used by the host to indicate attributes
+for ranges of logical blocks. This includes attributes like frequency that
+data is read or written, access size, and other information that may be used
+to optimize performance and reliability, and may be used to
+deallocate/unmap/trim those logical blocks.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_dsm_attributes.2 b/doc/man/nvme_dsm_attributes.2
new file mode 100644
index 0000000..93ab648
--- /dev/null
+++ b/doc/man/nvme_dsm_attributes.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_dsm_attributes" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_dsm_attributes \- Dataset Management attributes
+.SH SYNOPSIS
+enum nvme_dsm_attributes {
+.br
+.BI " NVME_DSMGMT_IDR"
+,
+.br
+.br
+.BI " NVME_DSMGMT_IDW"
+,
+.br
+.br
+.BI " NVME_DSMGMT_AD"
+
+};
+.SH Constants
+.IP "NVME_DSMGMT_IDR" 12
+Attribute -Integral Dataset for Read
+.IP "NVME_DSMGMT_IDW" 12
+Attribute - Integral Dataset for Write
+.IP "NVME_DSMGMT_AD" 12
+Attribute - Deallocate
diff --git a/doc/man/nvme_dsm_range.2 b/doc/man/nvme_dsm_range.2
new file mode 100644
index 0000000..ee9acc4
--- /dev/null
+++ b/doc/man/nvme_dsm_range.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_dsm_range" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_dsm_range \- Dataset Management - Range Definition
+.SH SYNOPSIS
+struct nvme_dsm_range {
+.br
+.BI " __le32 cattr;"
+.br
+.BI " __le32 nlb;"
+.br
+.BI " __le64 slba;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "cattr" 12
+Context Attributes
+.IP "nlb" 12
+Length in logical blocks
+.IP "slba" 12
+Starting LBA
diff --git a/doc/man/nvme_dst_stc.2 b/doc/man/nvme_dst_stc.2
new file mode 100644
index 0000000..320e068
--- /dev/null
+++ b/doc/man/nvme_dst_stc.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_dst_stc" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_dst_stc \- Action taken by the Device Self-test command
+.SH SYNOPSIS
+enum nvme_dst_stc {
+.br
+.BI " NVME_DST_STC_SHORT"
+,
+.br
+.br
+.BI " NVME_DST_STC_LONG"
+,
+.br
+.br
+.BI " NVME_DST_STC_VS"
+,
+.br
+.br
+.BI " NVME_DST_STC_ABORT"
+
+};
+.SH Constants
+.IP "NVME_DST_STC_SHORT" 12
+Start a short device self-test operation
+.IP "NVME_DST_STC_LONG" 12
+Start an extended device self-test operation
+.IP "NVME_DST_STC_VS" 12
+Start a vendor specific device self-test operation
+.IP "NVME_DST_STC_ABORT" 12
+Abort device self-test operation
diff --git a/doc/man/nvme_dump_config.2 b/doc/man/nvme_dump_config.2
new file mode 100644
index 0000000..3eef2de
--- /dev/null
+++ b/doc/man/nvme_dump_config.2
@@ -0,0 +1,14 @@
+.TH "nvme_dump_config" 9 "nvme_dump_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_dump_config \- Print the JSON configuration
+.SH SYNOPSIS
+.B "int" nvme_dump_config
+.BI "(nvme_root_t r " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t object
+.SH "DESCRIPTION"
+Prints the current contents of the JSON configuration
+file to stdout.
+.SH "RETURN"
+0 on success, -1 on failure.
diff --git a/doc/man/nvme_dump_tree.2 b/doc/man/nvme_dump_tree.2
new file mode 100644
index 0000000..c157925
--- /dev/null
+++ b/doc/man/nvme_dump_tree.2
@@ -0,0 +1,14 @@
+.TH "nvme_dump_tree" 9 "nvme_dump_tree" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_dump_tree \- Dump internal object tree
+.SH SYNOPSIS
+.B "int" nvme_dump_tree
+.BI "(nvme_root_t r " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t object
+.SH "DESCRIPTION"
+Prints the internal object tree in JSON format
+to stdout.
+.SH "RETURN"
+0 on success, -1 on failure.
diff --git a/doc/man/nvme_eg_critical_warning_flags.2 b/doc/man/nvme_eg_critical_warning_flags.2
new file mode 100644
index 0000000..c6e79e3
--- /dev/null
+++ b/doc/man/nvme_eg_critical_warning_flags.2
@@ -0,0 +1,26 @@
+.TH "libnvme" 9 "enum nvme_eg_critical_warning_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_eg_critical_warning_flags \- Endurance Group Information Log - Critical Warning
+.SH SYNOPSIS
+enum nvme_eg_critical_warning_flags {
+.br
+.BI " NVME_EG_CRITICAL_WARNING_SPARE"
+,
+.br
+.br
+.BI " NVME_EG_CRITICAL_WARNING_DEGRADED"
+,
+.br
+.br
+.BI " NVME_EG_CRITICAL_WARNING_READ_ONLY"
+
+};
+.SH Constants
+.IP "NVME_EG_CRITICAL_WARNING_SPARE" 12
+Available spare capacity of the Endurance Group
+has fallen below the threshold
+.IP "NVME_EG_CRITICAL_WARNING_DEGRADED" 12
+Endurance Group reliability has been degraded
+.IP "NVME_EG_CRITICAL_WARNING_READ_ONLY" 12
+Endurance Group have been placed in read only
+mode
diff --git a/doc/man/nvme_eg_event_aggregate_log.2 b/doc/man/nvme_eg_event_aggregate_log.2
new file mode 100644
index 0000000..dba6c49
--- /dev/null
+++ b/doc/man/nvme_eg_event_aggregate_log.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_eg_event_aggregate_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_eg_event_aggregate_log \- Endurance Group Event Aggregate
+.SH SYNOPSIS
+struct nvme_eg_event_aggregate_log {
+.br
+.BI " __le64 nr_entries;"
+.br
+.BI " __le16 egids[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nr_entries" 12
+Number of Entries
+.IP "egids" 12
+Endurance Group Identifier
diff --git a/doc/man/nvme_end_grp_chan_desc.2 b/doc/man/nvme_end_grp_chan_desc.2
new file mode 100644
index 0000000..9a862f2
--- /dev/null
+++ b/doc/man/nvme_end_grp_chan_desc.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "struct nvme_end_grp_chan_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_end_grp_chan_desc \- Endurance Group Channel Configuration Descriptor
+.SH SYNOPSIS
+struct nvme_end_grp_chan_desc {
+.br
+.BI " __le16 egchans;"
+.br
+.BI " struct nvme_channel_config_desc chan_config_desc[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "egchans" 12
+Number of Channels
+.IP "chan_config_desc" 12
+Channel config descriptors.
+See \fIstruct\fP nvme_channel_config_desc
diff --git a/doc/man/nvme_end_grp_config_desc.2 b/doc/man/nvme_end_grp_config_desc.2
new file mode 100644
index 0000000..bcbd669
--- /dev/null
+++ b/doc/man/nvme_end_grp_config_desc.2
@@ -0,0 +1,47 @@
+.TH "libnvme" 9 "struct nvme_end_grp_config_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_end_grp_config_desc \- Endurance Group Configuration Descriptor
+.SH SYNOPSIS
+struct nvme_end_grp_config_desc {
+.br
+.BI " __le16 endgid;"
+.br
+.BI " __le16 cap_adj_factor;"
+.br
+.BI " __u8 rsvd4[12];"
+.br
+.BI " __u8 tegcap[16];"
+.br
+.BI " __u8 segcap[16];"
+.br
+.BI " __u8 end_est[16];"
+.br
+.BI " __u8 rsvd64[16];"
+.br
+.BI " __le16 egsets;"
+.br
+.BI " __le16 nvmsetid[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "endgid" 12
+Endurance Group Identifier
+.IP "cap_adj_factor" 12
+Capacity Adjustment Factor
+.IP "rsvd4" 12
+Reserved
+.IP "tegcap" 12
+Total Endurance Group Capacity
+.IP "segcap" 12
+Spare Endurance Group Capacity
+.IP "end_est" 12
+Endurance Estimate
+.IP "rsvd64" 12
+Reserved
+.IP "egsets" 12
+Number of NVM Sets
+.IP "nvmsetid" 12
+NVM Set Identifier
diff --git a/doc/man/nvme_endurance_group_log.2 b/doc/man/nvme_endurance_group_log.2
new file mode 100644
index 0000000..74b759e
--- /dev/null
+++ b/doc/man/nvme_endurance_group_log.2
@@ -0,0 +1,87 @@
+.TH "libnvme" 9 "struct nvme_endurance_group_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_endurance_group_log \- Endurance Group Information Log
+.SH SYNOPSIS
+struct nvme_endurance_group_log {
+.br
+.BI " __u8 critical_warning;"
+.br
+.BI " __u8 endurance_group_features;"
+.br
+.BI " __u8 rsvd2;"
+.br
+.BI " __u8 avl_spare;"
+.br
+.BI " __u8 avl_spare_threshold;"
+.br
+.BI " __u8 percent_used;"
+.br
+.BI " __le16 domain_identifier;"
+.br
+.BI " __u8 rsvd8[24];"
+.br
+.BI " __u8 endurance_estimate[16];"
+.br
+.BI " __u8 data_units_read[16];"
+.br
+.BI " __u8 data_units_written[16];"
+.br
+.BI " __u8 media_units_written[16];"
+.br
+.BI " __u8 host_read_cmds[16];"
+.br
+.BI " __u8 host_write_cmds[16];"
+.br
+.BI " __u8 media_data_integrity_err[16];"
+.br
+.BI " __u8 num_err_info_log_entries[16];"
+.br
+.BI " __u8 total_end_grp_cap[16];"
+.br
+.BI " __u8 unalloc_end_grp_cap[16];"
+.br
+.BI " __u8 rsvd192[320];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "critical_warning" 12
+Critical Warning
+.IP "endurance_group_features" 12
+Endurance Group Features
+.IP "rsvd2" 12
+Reserved
+.IP "avl_spare" 12
+Available Spare
+.IP "avl_spare_threshold" 12
+Available Spare Threshold
+.IP "percent_used" 12
+Percentage Used
+.IP "domain_identifier" 12
+Domain Identifier
+.IP "rsvd8" 12
+Reserved
+.IP "endurance_estimate" 12
+Endurance Estimate
+.IP "data_units_read" 12
+Data Units Read
+.IP "data_units_written" 12
+Data Units Written
+.IP "media_units_written" 12
+Media Units Written
+.IP "host_read_cmds" 12
+Host Read Commands
+.IP "host_write_cmds" 12
+Host Write Commands
+.IP "media_data_integrity_err" 12
+Media and Data Integrity Errors
+.IP "num_err_info_log_entries" 12
+Number of Error Information Log Entries
+.IP "total_end_grp_cap" 12
+Total Endurance Group Capacity
+.IP "unalloc_end_grp_cap" 12
+Unallocated Endurance Group Capacity
+.IP "rsvd192" 12
+Reserved
diff --git a/doc/man/nvme_eom_lane_desc.2 b/doc/man/nvme_eom_lane_desc.2
new file mode 100644
index 0000000..eaa51c9
--- /dev/null
+++ b/doc/man/nvme_eom_lane_desc.2
@@ -0,0 +1,63 @@
+.TH "libnvme" 9 "struct nvme_eom_lane_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_eom_lane_desc \- EOM Lane Descriptor
+.SH SYNOPSIS
+struct nvme_eom_lane_desc {
+.br
+.BI " __u8 rsvd0;"
+.br
+.BI " __u8 mstatus;"
+.br
+.BI " __u8 lane;"
+.br
+.BI " __u8 eye;"
+.br
+.BI " __le16 top;"
+.br
+.BI " __le16 bottom;"
+.br
+.BI " __le16 left;"
+.br
+.BI " __le16 right;"
+.br
+.BI " __le16 nrows;"
+.br
+.BI " __le16 ncols;"
+.br
+.BI " __le16 edlen;"
+.br
+.BI " __u8 rsvd18[14];"
+.br
+.BI " __u8 eye_desc[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "rsvd0" 12
+Reserved
+.IP "mstatus" 12
+Measurement Status
+.IP "lane" 12
+Lane number
+.IP "eye" 12
+Eye number
+.IP "top" 12
+Absolute number of rows from center to top edge of eye
+.IP "bottom" 12
+Absolute number of rows from center to bottom edge of eye
+.IP "left" 12
+Absolute number of rows from center to left edge of eye
+.IP "right" 12
+Absolute number of rows from center to right edge of eye
+.IP "nrows" 12
+Number of Rows
+.IP "ncols" 12
+Number of Columns
+.IP "edlen" 12
+Eye Data Length
+.IP "rsvd18" 12
+Reserved
+.IP "eye_desc" 12
+Printable Eye, Eye Data, and any Padding
diff --git a/doc/man/nvme_eom_optional_data.2 b/doc/man/nvme_eom_optional_data.2
new file mode 100644
index 0000000..3ba3484
--- /dev/null
+++ b/doc/man/nvme_eom_optional_data.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_eom_optional_data" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_eom_optional_data \- EOM Optional Data Present Fields
+.SH SYNOPSIS
+enum nvme_eom_optional_data {
+.br
+.BI " NVME_EOM_EYE_DATA_PRESENT"
+,
+.br
+.br
+.BI " NVME_EOM_PRINTABLE_EYE_PRESENT"
+
+};
+.SH Constants
+.IP "NVME_EOM_EYE_DATA_PRESENT" 12
+Eye Data Present
+.IP "NVME_EOM_PRINTABLE_EYE_PRESENT" 12
+Printable Eye Present
diff --git a/doc/man/nvme_errno_to_string.2 b/doc/man/nvme_errno_to_string.2
new file mode 100644
index 0000000..0c39e0f
--- /dev/null
+++ b/doc/man/nvme_errno_to_string.2
@@ -0,0 +1,11 @@
+.TH "nvme_errno_to_string" 9 "nvme_errno_to_string" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_errno_to_string \- Returns string describing nvme connect failures
+.SH SYNOPSIS
+.B "const char *" nvme_errno_to_string
+.BI "(int err " ");"
+.SH ARGUMENTS
+.IP "err" 12
+Returned error code from \fBnvme_add_ctrl\fP
+.SH "RETURN"
+String representation of the nvme connect error codes
diff --git a/doc/man/nvme_error_log_page.2 b/doc/man/nvme_error_log_page.2
new file mode 100644
index 0000000..e930329
--- /dev/null
+++ b/doc/man/nvme_error_log_page.2
@@ -0,0 +1,121 @@
+.TH "libnvme" 9 "struct nvme_error_log_page" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_error_log_page \- Error Information Log Entry (Log Identifier 01h)
+.SH SYNOPSIS
+struct nvme_error_log_page {
+.br
+.BI " __le64 error_count;"
+.br
+.BI " __le16 sqid;"
+.br
+.BI " __le16 cmdid;"
+.br
+.BI " __le16 status_field;"
+.br
+.BI " __le16 parm_error_location;"
+.br
+.BI " __le64 lba;"
+.br
+.BI " __le32 nsid;"
+.br
+.BI " __u8 vs;"
+.br
+.BI " __u8 trtype;"
+.br
+.BI " __u8 csi;"
+.br
+.BI " __u8 opcode;"
+.br
+.BI " __le64 cs;"
+.br
+.BI " __le16 trtype_spec_info;"
+.br
+.BI " __u8 rsvd[21];"
+.br
+.BI " __u8 log_page_version;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "error_count" 12
+Error Count: a 64-bit incrementing error count,
+indicating a unique identifier for this error. The error
+count starts at 1h, is incremented for each unique error
+log entry, and is retained across power off conditions.
+A value of 0h indicates an invalid entry; this value
+is used when there are lost entries or when there are
+fewer errors than the maximum number of entries the
+controller supports. If the value of this field is
+FFFFFFFFh, then the field shall be set to 1h when
+incremented (i.e., rolls over to 1h). Prior to NVMe
+1.4, processing of incrementing beyond FFFFFFFFh is
+unspecified.
+.IP "sqid" 12
+Submission Queue ID: indicates the Submission Queue
+Identifier of the command that the error information is
+associated with. If the error is not specific to
+a particular command, then this field shall be set to
+FFFFh.
+.IP "cmdid" 12
+Command ID: indicates the Command Identifier of the
+command that the error is associated with. If the error
+is not specific to a particular command, then this field
+shall be set to FFFFh.
+.IP "status_field" 12
+Bits 15-1: Status Field: indicates the Status Field for
+the command that completed. If the error is not specific
+to a particular command, then this field reports the most
+applicable status value.
+Bit 0: Phase Tag: may indicate the Phase Tag posted for
+the command.
+.IP "parm_error_location" 12
+Parameter Error Location: indicates the byte and bit of
+the command parameter that the error is associated with,
+if applicable. If the parameter spans multiple bytes or
+bits, then the location indicates the first byte and bit
+of the parameter.
+Bits 10-8: Bit in command that contained the error.
+Valid values are 0 to 7.
+Bits 7-0: Byte in command that contained the error.
+Valid values are 0 to 63.
+.IP "lba" 12
+LBA: This field indicates the first LBA that experienced
+the error condition, if applicable.
+.IP "nsid" 12
+Namespace: This field indicates the NSID of the namespace
+that the error is associated with, if applicable.
+.IP "vs" 12
+Vendor Specific Information Available: If there is
+additional vendor specific error information available,
+this field provides the log page identifier associated
+with that page. A value of 0h indicates that no additional
+information is available. Valid values are in the range
+of 80h to FFh.
+.IP "trtype" 12
+Transport Type (TRTYPE): indicates the Transport Type of
+the transport associated with the error. The values in
+this field are the same as the TRTYPE values in the
+Discovery Log Page Entry. If the error is not transport
+related, this field shall be cleared to 0h. If the error
+is transport related, this field shall be set to the type
+of the transport - see \fIenum nvme_trtype\fP.
+.IP "csi" 12
+Command Set Indicator: This field contains command set
+indicator for the command that the error is associated
+with.
+.IP "opcode" 12
+Opcode: This field contains opcode for the command that
+the error is associated with.
+.IP "cs" 12
+Command Specific Information: This field contains command
+specific information. If used, the command definition
+specifies the information returned.
+.IP "trtype_spec_info" 12
+Transport Type Specific Information
+.IP "rsvd" 12
+Reserved: [62:42]
+.IP "log_page_version" 12
+This field shall be set to 1h. If set, \fIcsi\fP and \fIopcode\fP
+will have valid values.
diff --git a/doc/man/nvme_fabrics_config.2 b/doc/man/nvme_fabrics_config.2
new file mode 100644
index 0000000..1a110d4
--- /dev/null
+++ b/doc/man/nvme_fabrics_config.2
@@ -0,0 +1,87 @@
+.TH "libnvme" 9 "struct nvme_fabrics_config" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fabrics_config \- Defines all linux nvme fabrics initiator options
+.SH SYNOPSIS
+struct nvme_fabrics_config {
+.br
+.BI " char *host_traddr;"
+.br
+.BI " char *host_iface;"
+.br
+.BI " int queue_size;"
+.br
+.BI " int nr_io_queues;"
+.br
+.BI " int reconnect_delay;"
+.br
+.BI " int ctrl_loss_tmo;"
+.br
+.BI " int fast_io_fail_tmo;"
+.br
+.BI " int keep_alive_tmo;"
+.br
+.BI " int nr_write_queues;"
+.br
+.BI " int nr_poll_queues;"
+.br
+.BI " int tos;"
+.br
+.BI " int keyring;"
+.br
+.BI " int tls_key;"
+.br
+.BI " bool duplicate_connect;"
+.br
+.BI " bool disable_sqflow;"
+.br
+.BI " bool hdr_digest;"
+.br
+.BI " bool data_digest;"
+.br
+.BI " bool tls;"
+.br
+.BI " bool concat;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "host_traddr" 12
+Host transport address
+.IP "host_iface" 12
+Host interface name
+.IP "queue_size" 12
+Number of IO queue entries
+.IP "nr_io_queues" 12
+Number of controller IO queues to establish
+.IP "reconnect_delay" 12
+Time between two consecutive reconnect attempts.
+.IP "ctrl_loss_tmo" 12
+Override the default controller reconnect attempt timeout in seconds
+.IP "fast_io_fail_tmo" 12
+Set the fast I/O fail timeout in seconds.
+.IP "keep_alive_tmo" 12
+Override the default keep-alive-timeout to this value in seconds
+.IP "nr_write_queues" 12
+Number of queues to use for exclusively for writing
+.IP "nr_poll_queues" 12
+Number of queues to reserve for polling completions
+.IP "tos" 12
+Type of service
+.IP "keyring" 12
+Keyring to store and lookup keys
+.IP "tls_key" 12
+TLS PSK for the connection
+.IP "duplicate_connect" 12
+Allow multiple connections to the same target
+.IP "disable_sqflow" 12
+Disable controller sq flow control
+.IP "hdr_digest" 12
+Generate/verify header digest (TCP)
+.IP "data_digest" 12
+Generate/verify data digest (TCP)
+.IP "tls" 12
+Start TLS on the connection (TCP)
+.IP "concat" 12
+Enable secure concatenation (TCP)
diff --git a/doc/man/nvme_fctype.2 b/doc/man/nvme_fctype.2
new file mode 100644
index 0000000..00dbb70
--- /dev/null
+++ b/doc/man/nvme_fctype.2
@@ -0,0 +1,42 @@
+.TH "libnvme" 9 "enum nvme_fctype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fctype \- Fabrics Command Types
+.SH SYNOPSIS
+enum nvme_fctype {
+.br
+.BI " nvme_fabrics_type_property_set"
+,
+.br
+.br
+.BI " nvme_fabrics_type_connect"
+,
+.br
+.br
+.BI " nvme_fabrics_type_property_get"
+,
+.br
+.br
+.BI " nvme_fabrics_type_auth_send"
+,
+.br
+.br
+.BI " nvme_fabrics_type_auth_receive"
+,
+.br
+.br
+.BI " nvme_fabrics_type_disconnect"
+
+};
+.SH Constants
+.IP "nvme_fabrics_type_property_set" 12
+Property set
+.IP "nvme_fabrics_type_connect" 12
+Connect
+.IP "nvme_fabrics_type_property_get" 12
+Property Get
+.IP "nvme_fabrics_type_auth_send" 12
+Authentication Send
+.IP "nvme_fabrics_type_auth_receive" 12
+Authentication Receive
+.IP "nvme_fabrics_type_disconnect" 12
+Disconnect
diff --git a/doc/man/nvme_fdp_config_desc.2 b/doc/man/nvme_fdp_config_desc.2
new file mode 100644
index 0000000..e80c814
--- /dev/null
+++ b/doc/man/nvme_fdp_config_desc.2
@@ -0,0 +1,55 @@
+.TH "libnvme" 9 "struct nvme_fdp_config_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_config_desc \- FDP Configuration Descriptor
+.SH SYNOPSIS
+struct nvme_fdp_config_desc {
+.br
+.BI " __le16 size;"
+.br
+.BI " __u8 fdpa;"
+.br
+.BI " __u8 vss;"
+.br
+.BI " __le32 nrg;"
+.br
+.BI " __le16 nruh;"
+.br
+.BI " __le16 maxpids;"
+.br
+.BI " __le32 nnss;"
+.br
+.BI " __le64 runs;"
+.br
+.BI " __le32 erutl;"
+.br
+.BI " __u8 rsvd28[36];"
+.br
+.BI " struct nvme_fdp_ruh_desc ruhs[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "size" 12
+Descriptor size
+.IP "fdpa" 12
+FDP Attributes (\fIenum nvme_fdp_config_fdpa\fP)
+.IP "vss" 12
+Vendor Specific Size
+.IP "nrg" 12
+Number of Reclaim Groups
+.IP "nruh" 12
+Number of Reclaim Unit Handles
+.IP "maxpids" 12
+Max Placement Identifiers
+.IP "nnss" 12
+Number of Namespaces Supported
+.IP "runs" 12
+Reclaim Unit Nominal Size
+.IP "erutl" 12
+Estimated Reclaim Unit Time Limit
+.IP "rsvd28" 12
+Reserved
+.IP "ruhs" 12
+Reclaim Unit Handle descriptors (\fIstruct nvme_fdp_ruh_desc\fP)
diff --git a/doc/man/nvme_fdp_config_fdpa.2 b/doc/man/nvme_fdp_config_fdpa.2
new file mode 100644
index 0000000..f0662bf
--- /dev/null
+++ b/doc/man/nvme_fdp_config_fdpa.2
@@ -0,0 +1,42 @@
+.TH "libnvme" 9 "enum nvme_fdp_config_fdpa" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fdp_config_fdpa \- FDP Attributes
+.SH SYNOPSIS
+enum nvme_fdp_config_fdpa {
+.br
+.BI " NVME_FDP_CONFIG_FDPA_RGIF_SHIFT"
+,
+.br
+.br
+.BI " NVME_FDP_CONFIG_FDPA_RGIF_MASK"
+,
+.br
+.br
+.BI " NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT"
+,
+.br
+.br
+.BI " NVME_FDP_CONFIG_FDPA_FDPVWC_MASK"
+,
+.br
+.br
+.BI " NVME_FDP_CONFIG_FDPA_VALID_SHIFT"
+,
+.br
+.br
+.BI " NVME_FDP_CONFIG_FDPA_VALID_MASK"
+
+};
+.SH Constants
+.IP "NVME_FDP_CONFIG_FDPA_RGIF_SHIFT" 12
+Reclaim Group Identifier Format Shift
+.IP "NVME_FDP_CONFIG_FDPA_RGIF_MASK" 12
+Reclaim Group Identifier Format Mask
+.IP "NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT" 12
+FDP Volatile Write Cache Shift
+.IP "NVME_FDP_CONFIG_FDPA_FDPVWC_MASK" 12
+FDP Volatile Write Cache Mask
+.IP "NVME_FDP_CONFIG_FDPA_VALID_SHIFT" 12
+FDP Configuration Valid Shift
+.IP "NVME_FDP_CONFIG_FDPA_VALID_MASK" 12
+FDP Configuration Valid Mask
diff --git a/doc/man/nvme_fdp_config_log.2 b/doc/man/nvme_fdp_config_log.2
new file mode 100644
index 0000000..c7fb7eb
--- /dev/null
+++ b/doc/man/nvme_fdp_config_log.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_fdp_config_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_config_log \- FDP Configurations Log Page
+.SH SYNOPSIS
+struct nvme_fdp_config_log {
+.br
+.BI " __le16 n;"
+.br
+.BI " __u8 version;"
+.br
+.BI " __u8 rsvd3;"
+.br
+.BI " __le32 size;"
+.br
+.BI " __u8 rsvd8[8];"
+.br
+.BI " struct nvme_fdp_config_desc configs[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "n" 12
+Number of FDP Configurations
+.IP "version" 12
+Log page version
+.IP "rsvd3" 12
+Reserved
+.IP "size" 12
+Log page size in bytes
+.IP "rsvd8" 12
+Reserved
+.IP "configs" 12
+FDP Configuration descriptors (\fIstruct nvme_fdp_config_desc\fP)
diff --git a/doc/man/nvme_fdp_event.2 b/doc/man/nvme_fdp_event.2
new file mode 100644
index 0000000..d6dfa05
--- /dev/null
+++ b/doc/man/nvme_fdp_event.2
@@ -0,0 +1,51 @@
+.TH "libnvme" 9 "struct nvme_fdp_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_event \- FDP Event
+.SH SYNOPSIS
+struct nvme_fdp_event {
+.br
+.BI " __u8 type;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __le16 pid;"
+.br
+.BI " struct nvme_timestamp ts;"
+.br
+.BI " __le32 nsid;"
+.br
+.BI " __u8 type_specific[16];"
+.br
+.BI " __le16 rgid;"
+.br
+.BI " __u8 ruhid;"
+.br
+.BI " __u8 rsvd35[5];"
+.br
+.BI " __u8 vs[24];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "type" 12
+Event Type (\fIenum nvme_fdp_event_type\fP)
+.IP "flags" 12
+Event Flags (\fIenum nvme_fdp_event_flags\fP)
+.IP "pid" 12
+Placement Identifier
+.IP "ts" 12
+Timestamp
+.IP "nsid" 12
+Namespace Identifier
+.IP "type_specific" 12
+Event Type Specific Information
+.IP "rgid" 12
+Reclaim Group Identifier
+.IP "ruhid" 12
+Reclaim Unit Handle Identifier
+.IP "rsvd35" 12
+Reserved
+.IP "vs" 12
+Vendor Specific
diff --git a/doc/man/nvme_fdp_event_flags.2 b/doc/man/nvme_fdp_event_flags.2
new file mode 100644
index 0000000..2da0442
--- /dev/null
+++ b/doc/man/nvme_fdp_event_flags.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_fdp_event_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fdp_event_flags \- FDP Event Flags
+.SH SYNOPSIS
+enum nvme_fdp_event_flags {
+.br
+.BI " NVME_FDP_EVENT_F_PIV"
+,
+.br
+.br
+.BI " NVME_FDP_EVENT_F_NSIDV"
+,
+.br
+.br
+.BI " NVME_FDP_EVENT_F_LV"
+
+};
+.SH Constants
+.IP "NVME_FDP_EVENT_F_PIV" 12
+Placement Identifier Valid
+.IP "NVME_FDP_EVENT_F_NSIDV" 12
+Namespace Identifier Valid
+.IP "NVME_FDP_EVENT_F_LV" 12
+Location Valid
diff --git a/doc/man/nvme_fdp_event_realloc.2 b/doc/man/nvme_fdp_event_realloc.2
new file mode 100644
index 0000000..15856d3
--- /dev/null
+++ b/doc/man/nvme_fdp_event_realloc.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_fdp_event_realloc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_event_realloc \- Media Reallocated Event Type Specific Information
+.SH SYNOPSIS
+struct nvme_fdp_event_realloc {
+.br
+.BI " __u8 flags;"
+.br
+.BI " __u8 rsvd1;"
+.br
+.BI " __le16 nlbam;"
+.br
+.BI " __le64 lba;"
+.br
+.BI " __u8 rsvd12[4];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "flags" 12
+Event Type Specific flags (\fIenum nvme_fdp_event_realloc_flags\fP)
+.IP "rsvd1" 12
+Reserved
+.IP "nlbam" 12
+Number of LBAs Moved
+.IP "lba" 12
+Logical Block Address
+.IP "rsvd12" 12
+Reserved
diff --git a/doc/man/nvme_fdp_event_realloc_flags.2 b/doc/man/nvme_fdp_event_realloc_flags.2
new file mode 100644
index 0000000..a47245a
--- /dev/null
+++ b/doc/man/nvme_fdp_event_realloc_flags.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nvme_fdp_event_realloc_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fdp_event_realloc_flags \- Media Reallocated Event Type Specific Flags
+.SH SYNOPSIS
+enum nvme_fdp_event_realloc_flags {
+.br
+.BI " NVME_FDP_EVENT_REALLOC_F_LBAV"
+
+};
+.SH Constants
+.IP "NVME_FDP_EVENT_REALLOC_F_LBAV" 12
+LBA Valid
diff --git a/doc/man/nvme_fdp_event_type.2 b/doc/man/nvme_fdp_event_type.2
new file mode 100644
index 0000000..b726436
--- /dev/null
+++ b/doc/man/nvme_fdp_event_type.2
@@ -0,0 +1,42 @@
+.TH "libnvme" 9 "enum nvme_fdp_event_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fdp_event_type \- FDP Event Types
+.SH SYNOPSIS
+enum nvme_fdp_event_type {
+.br
+.BI " NVME_FDP_EVENT_RUNFW"
+,
+.br
+.br
+.BI " NVME_FDP_EVENT_RUTLE"
+,
+.br
+.br
+.BI " NVME_FDP_EVENT_RESET"
+,
+.br
+.br
+.BI " NVME_FDP_EVENT_PID"
+,
+.br
+.br
+.BI " NVME_FDP_EVENT_REALLOC"
+,
+.br
+.br
+.BI " NVME_FDP_EVENT_MODIFY"
+
+};
+.SH Constants
+.IP "NVME_FDP_EVENT_RUNFW" 12
+Reclaim Unit Not Fully Written
+.IP "NVME_FDP_EVENT_RUTLE" 12
+Reclaim Unit Time Limit Exceeded
+.IP "NVME_FDP_EVENT_RESET" 12
+Controller Level Reset Modified Reclaim Unit Handles
+.IP "NVME_FDP_EVENT_PID" 12
+Invalid Placement Identifier
+.IP "NVME_FDP_EVENT_REALLOC" 12
+Media Reallocated
+.IP "NVME_FDP_EVENT_MODIFY" 12
+Implicitly Modified Reclaim Unit Handle
diff --git a/doc/man/nvme_fdp_events_log.2 b/doc/man/nvme_fdp_events_log.2
new file mode 100644
index 0000000..a02b169
--- /dev/null
+++ b/doc/man/nvme_fdp_events_log.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_fdp_events_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_events_log \- FDP Events Log Page
+.SH SYNOPSIS
+struct nvme_fdp_events_log {
+.br
+.BI " __le32 n;"
+.br
+.BI " __u8 rsvd4[60];"
+.br
+.BI " struct nvme_fdp_event events[63];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "n" 12
+Number of FDP Events
+.IP "rsvd4" 12
+Reserved
+.IP "events" 12
+FDP Events (\fIstruct nvme_fdp_event\fP)
diff --git a/doc/man/nvme_fdp_reclaim_unit_handle_status.2 b/doc/man/nvme_fdp_reclaim_unit_handle_status.2
new file mode 100644
index 0000000..aceccfc
--- /dev/null
+++ b/doc/man/nvme_fdp_reclaim_unit_handle_status.2
@@ -0,0 +1,21 @@
+.TH "nvme_fdp_reclaim_unit_handle_status" 9 "nvme_fdp_reclaim_unit_handle_status" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_fdp_reclaim_unit_handle_status \- Get reclaim unit handle status
+.SH SYNOPSIS
+.B "int" nvme_fdp_reclaim_unit_handle_status
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace identifier
+.IP "data_len" 12
+Length of response buffer
+.IP "data" 12
+Response buffer
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_fdp_reclaim_unit_handle_update.2 b/doc/man/nvme_fdp_reclaim_unit_handle_update.2
new file mode 100644
index 0000000..c5e5e0d
--- /dev/null
+++ b/doc/man/nvme_fdp_reclaim_unit_handle_update.2
@@ -0,0 +1,21 @@
+.TH "nvme_fdp_reclaim_unit_handle_update" 9 "nvme_fdp_reclaim_unit_handle_update" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_fdp_reclaim_unit_handle_update \- Update a list of reclaim unit handles
+.SH SYNOPSIS
+.B "int" nvme_fdp_reclaim_unit_handle_update
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "unsigned int npids " ","
+.BI "__u16 *pids " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace identifier
+.IP "npids" 12
+Number of placement identifiers
+.IP "pids" 12
+List of placement identifiers
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_fdp_ruh_desc.2 b/doc/man/nvme_fdp_ruh_desc.2
new file mode 100644
index 0000000..638d1ad
--- /dev/null
+++ b/doc/man/nvme_fdp_ruh_desc.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_fdp_ruh_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_ruh_desc \- Reclaim Unit Handle Descriptor
+.SH SYNOPSIS
+struct nvme_fdp_ruh_desc {
+.br
+.BI " __u8 ruht;"
+.br
+.BI " __u8 rsvd1[3];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "ruht" 12
+Reclaim Unit Handle Type
+.IP "rsvd1" 12
+Reserved
diff --git a/doc/man/nvme_fdp_ruh_status.2 b/doc/man/nvme_fdp_ruh_status.2
new file mode 100644
index 0000000..fde919c
--- /dev/null
+++ b/doc/man/nvme_fdp_ruh_status.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_fdp_ruh_status" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_ruh_status \- Reclaim Unit Handle Status
+.SH SYNOPSIS
+struct nvme_fdp_ruh_status {
+.br
+.BI " __u8 rsvd0[14];"
+.br
+.BI " __le16 nruhsd;"
+.br
+.BI " struct nvme_fdp_ruh_status_desc ruhss[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "rsvd0" 12
+Reserved
+.IP "nruhsd" 12
+Number of Reclaim Unit Handle Status Descriptors
+.IP "ruhss" 12
+Reclaim Unit Handle Status descriptors
diff --git a/doc/man/nvme_fdp_ruh_status_desc.2 b/doc/man/nvme_fdp_ruh_status_desc.2
new file mode 100644
index 0000000..e5f8595
--- /dev/null
+++ b/doc/man/nvme_fdp_ruh_status_desc.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_fdp_ruh_status_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_ruh_status_desc \- Reclaim Unit Handle Status Descriptor
+.SH SYNOPSIS
+struct nvme_fdp_ruh_status_desc {
+.br
+.BI " __le16 pid;"
+.br
+.BI " __le16 ruhid;"
+.br
+.BI " __le32 earutr;"
+.br
+.BI " __le64 ruamw;"
+.br
+.BI " __u8 rsvd16[16];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "pid" 12
+Placement Identifier
+.IP "ruhid" 12
+Reclaim Unit Handle Identifier
+.IP "earutr" 12
+Estimated Active Reclaim Unit Time Remaining
+.IP "ruamw" 12
+Reclaim Unit Available Media Writes
+.IP "rsvd16" 12
+Reserved
diff --git a/doc/man/nvme_fdp_ruh_type.2 b/doc/man/nvme_fdp_ruh_type.2
new file mode 100644
index 0000000..6ba003a
--- /dev/null
+++ b/doc/man/nvme_fdp_ruh_type.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_fdp_ruh_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fdp_ruh_type \- Reclaim Unit Handle Type
+.SH SYNOPSIS
+enum nvme_fdp_ruh_type {
+.br
+.BI " NVME_FDP_RUHT_INITIALLY_ISOLATED"
+,
+.br
+.br
+.BI " NVME_FDP_RUHT_PERSISTENTLY_ISOLATED"
+
+};
+.SH Constants
+.IP "NVME_FDP_RUHT_INITIALLY_ISOLATED" 12
+Initially Isolated
+.IP "NVME_FDP_RUHT_PERSISTENTLY_ISOLATED" 12
+Persistently Isolated
diff --git a/doc/man/nvme_fdp_ruha.2 b/doc/man/nvme_fdp_ruha.2
new file mode 100644
index 0000000..dd1afa1
--- /dev/null
+++ b/doc/man/nvme_fdp_ruha.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_fdp_ruha" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fdp_ruha \- Reclaim Unit Handle Attributes
+.SH SYNOPSIS
+enum nvme_fdp_ruha {
+.br
+.BI " NVME_FDP_RUHA_HOST_SHIFT"
+,
+.br
+.br
+.BI " NVME_FDP_RUHA_HOST_MASK"
+,
+.br
+.br
+.BI " NVME_FDP_RUHA_CTRL_SHIFT"
+,
+.br
+.br
+.BI " NVME_FDP_RUHA_CTRL_MASK"
+
+};
+.SH Constants
+.IP "NVME_FDP_RUHA_HOST_SHIFT" 12
+Host Specified Reclaim Unit Handle Shift
+.IP "NVME_FDP_RUHA_HOST_MASK" 12
+Host Specified Reclaim Unit Handle Mask
+.IP "NVME_FDP_RUHA_CTRL_SHIFT" 12
+Controller Specified Reclaim Unit Handle Shift
+.IP "NVME_FDP_RUHA_CTRL_MASK" 12
+Controller Specified Reclaim Unit Handle Mask
diff --git a/doc/man/nvme_fdp_ruhu_desc.2 b/doc/man/nvme_fdp_ruhu_desc.2
new file mode 100644
index 0000000..0ed80c8
--- /dev/null
+++ b/doc/man/nvme_fdp_ruhu_desc.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_fdp_ruhu_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_ruhu_desc \- Reclaim Unit Handle Usage Descriptor
+.SH SYNOPSIS
+struct nvme_fdp_ruhu_desc {
+.br
+.BI " __u8 ruha;"
+.br
+.BI " __u8 rsvd1[7];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "ruha" 12
+Reclaim Unit Handle Attributes (\fIenum nvme_fdp_ruha\fP)
+.IP "rsvd1" 12
+Reserved
diff --git a/doc/man/nvme_fdp_ruhu_log.2 b/doc/man/nvme_fdp_ruhu_log.2
new file mode 100644
index 0000000..c11b4ff
--- /dev/null
+++ b/doc/man/nvme_fdp_ruhu_log.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_fdp_ruhu_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_ruhu_log \- Reclaim Unit Handle Usage Log Page
+.SH SYNOPSIS
+struct nvme_fdp_ruhu_log {
+.br
+.BI " __le16 nruh;"
+.br
+.BI " __u8 rsvd2[6];"
+.br
+.BI " struct nvme_fdp_ruhu_desc ruhus[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nruh" 12
+Number of Reclaim Unit Handles
+.IP "rsvd2" 12
+Reserved
+.IP "ruhus" 12
+Reclaim Unit Handle Usage descriptors
diff --git a/doc/man/nvme_fdp_stats_log.2 b/doc/man/nvme_fdp_stats_log.2
new file mode 100644
index 0000000..0a724bd
--- /dev/null
+++ b/doc/man/nvme_fdp_stats_log.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_fdp_stats_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_stats_log \- FDP Statistics Log Page
+.SH SYNOPSIS
+struct nvme_fdp_stats_log {
+.br
+.BI " __u8 hbmw[16];"
+.br
+.BI " __u8 mbmw[16];"
+.br
+.BI " __u8 mbe[16];"
+.br
+.BI " __u8 rsvd48[16];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "hbmw" 12
+Host Bytes with Metadata Written
+.IP "mbmw" 12
+Media Bytes with Metadata Written
+.IP "mbe" 12
+Media Bytes Erased
+.IP "rsvd48" 12
+Reserved
diff --git a/doc/man/nvme_fdp_supported_event_attributes.2 b/doc/man/nvme_fdp_supported_event_attributes.2
new file mode 100644
index 0000000..4ce8032
--- /dev/null
+++ b/doc/man/nvme_fdp_supported_event_attributes.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_fdp_supported_event_attributes" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fdp_supported_event_attributes \- Supported FDP Event Attributes
+.SH SYNOPSIS
+enum nvme_fdp_supported_event_attributes {
+.br
+.BI " NVME_FDP_SUPP_EVENT_ENABLED_SHIFT"
+,
+.br
+.br
+.BI " NVME_FDP_SUPP_EVENT_ENABLED_MASK"
+
+};
+.SH Constants
+.IP "NVME_FDP_SUPP_EVENT_ENABLED_SHIFT" 12
+FDP Event Enable Shift
+.IP "NVME_FDP_SUPP_EVENT_ENABLED_MASK" 12
+FDP Event Enable Mask
diff --git a/doc/man/nvme_fdp_supported_event_desc.2 b/doc/man/nvme_fdp_supported_event_desc.2
new file mode 100644
index 0000000..e58a403
--- /dev/null
+++ b/doc/man/nvme_fdp_supported_event_desc.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_fdp_supported_event_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fdp_supported_event_desc \- Supported FDP Event Descriptor
+.SH SYNOPSIS
+struct nvme_fdp_supported_event_desc {
+.br
+.BI " __u8 evt;"
+.br
+.BI " __u8 evta;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "evt" 12
+FDP Event Type
+.IP "evta" 12
+FDP Event Type Attributes (\fIenum nvme_fdp_supported_event_attributes\fP)
diff --git a/doc/man/nvme_feat.2 b/doc/man/nvme_feat.2
new file mode 100644
index 0000000..bc81227
--- /dev/null
+++ b/doc/man/nvme_feat.2
@@ -0,0 +1,526 @@
+.TH "libnvme" 9 "enum nvme_feat" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_feat \- Features Access Shifts/Masks values
+.SH SYNOPSIS
+enum nvme_feat {
+.br
+.BI " NVME_FEAT_ARBITRATION_BURST_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_ARBITRATION_BURST_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_ARBITRATION_LPW_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_ARBITRATION_LPW_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_ARBITRATION_MPW_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_ARBITRATION_MPW_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_ARBITRATION_HPW_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_ARBITRATION_HPW_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_PWRMGMT_PS_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_PWRMGMT_PS_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_PWRMGMT_WH_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_PWRMGMT_WH_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_LBAR_NR_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_LBAR_NR_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_TT_TMPTH_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_TT_TMPTH_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_TT_TMPSEL_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_TT_TMPSEL_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_TT_THSEL_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_TT_THSEL_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_ERROR_RECOVERY_TLER_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_ERROR_RECOVERY_DULBE_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_VWC_WCE_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_VWC_WCE_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_NRQS_NSQR_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_NRQS_NSQR_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_NRQS_NCQR_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_NRQS_NCQR_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_IRQC_THR_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_IRQC_THR_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_IRQC_TIME_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_IRQC_TIME_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_ICFG_IV_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_ICFG_IV_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_ICFG_CD_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_ICFG_CD_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_WA_DN_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_WA_DN_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_SMART_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_SMART_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_NAN_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_NAN_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_FW_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_FW_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_TELEM_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_TELEM_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_ANA_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_ANA_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_PLA_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_PLA_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_LBAS_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_LBAS_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_EGA_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_AE_EGA_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_APST_APSTE_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_APST_APSTE_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_HMEM_EHM_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_HMEM_EHM_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_HCTM_TMT2_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_HCTM_TMT2_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_HCTM_TMT1_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_HCTM_TMT1_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_NOPS_NOPPME_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_NOPS_NOPPME_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_RRL_RRL_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_RRL_RRL_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_PLM_PLME_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_PLM_PLME_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_PLMW_WS_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_PLMW_WS_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_LBAS_LSIRI_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_LBAS_LSIRI_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_LBAS_LSIPI_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_LBAS_LSIPI_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_SC_NODRM_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_SC_NODRM_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_EG_ENDGID_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_EG_ENDGID_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_EG_EGCW_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_EG_EGCW_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_SPM_PBSLC_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_SPM_PBSLC_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_HOSTID_EXHID_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_HOSTID_EXHID_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_RM_REGPRE_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_RM_REGPRE_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_RM_RESREL_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_RM_RESREL_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_RM_RESPRE_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_RM_RESPRE_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_RP_PTPL_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_RP_PTPL_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_WP_WPS_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_WP_WPS_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_IOCSP_IOCSCI_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_IOCSP_IOCSCI_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_FDP_ENABLED_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_FDP_ENABLED_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_FDP_INDEX_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_FDP_INDEX_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT"
+,
+.br
+.br
+.BI " NVME_FEAT_FDP_EVENTS_ENABLE_MASK"
+
+};
+.SH Constants
+.IP "NVME_FEAT_ARBITRATION_BURST_SHIFT" 12
+.IP "NVME_FEAT_ARBITRATION_BURST_MASK" 12
+.IP "NVME_FEAT_ARBITRATION_LPW_SHIFT" 12
+.IP "NVME_FEAT_ARBITRATION_LPW_MASK" 12
+.IP "NVME_FEAT_ARBITRATION_MPW_SHIFT" 12
+.IP "NVME_FEAT_ARBITRATION_MPW_MASK" 12
+.IP "NVME_FEAT_ARBITRATION_HPW_SHIFT" 12
+.IP "NVME_FEAT_ARBITRATION_HPW_MASK" 12
+.IP "NVME_FEAT_PWRMGMT_PS_SHIFT" 12
+.IP "NVME_FEAT_PWRMGMT_PS_MASK" 12
+.IP "NVME_FEAT_PWRMGMT_WH_SHIFT" 12
+.IP "NVME_FEAT_PWRMGMT_WH_MASK" 12
+.IP "NVME_FEAT_LBAR_NR_SHIFT" 12
+.IP "NVME_FEAT_LBAR_NR_MASK" 12
+.IP "NVME_FEAT_TT_TMPTH_SHIFT" 12
+.IP "NVME_FEAT_TT_TMPTH_MASK" 12
+.IP "NVME_FEAT_TT_TMPSEL_SHIFT" 12
+.IP "NVME_FEAT_TT_TMPSEL_MASK" 12
+.IP "NVME_FEAT_TT_THSEL_SHIFT" 12
+.IP "NVME_FEAT_TT_THSEL_MASK" 12
+.IP "NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT" 12
+.IP "NVME_FEAT_ERROR_RECOVERY_TLER_MASK" 12
+.IP "NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT" 12
+.IP "NVME_FEAT_ERROR_RECOVERY_DULBE_MASK" 12
+.IP "NVME_FEAT_VWC_WCE_SHIFT" 12
+.IP "NVME_FEAT_VWC_WCE_MASK" 12
+.IP "NVME_FEAT_NRQS_NSQR_SHIFT" 12
+.IP "NVME_FEAT_NRQS_NSQR_MASK" 12
+.IP "NVME_FEAT_NRQS_NCQR_SHIFT" 12
+.IP "NVME_FEAT_NRQS_NCQR_MASK" 12
+.IP "NVME_FEAT_IRQC_THR_SHIFT" 12
+.IP "NVME_FEAT_IRQC_THR_MASK" 12
+.IP "NVME_FEAT_IRQC_TIME_SHIFT" 12
+.IP "NVME_FEAT_IRQC_TIME_MASK" 12
+.IP "NVME_FEAT_ICFG_IV_SHIFT" 12
+.IP "NVME_FEAT_ICFG_IV_MASK" 12
+.IP "NVME_FEAT_ICFG_CD_SHIFT" 12
+.IP "NVME_FEAT_ICFG_CD_MASK" 12
+.IP "NVME_FEAT_WA_DN_SHIFT" 12
+.IP "NVME_FEAT_WA_DN_MASK" 12
+.IP "NVME_FEAT_AE_SMART_SHIFT" 12
+.IP "NVME_FEAT_AE_SMART_MASK" 12
+.IP "NVME_FEAT_AE_NAN_SHIFT" 12
+.IP "NVME_FEAT_AE_NAN_MASK" 12
+.IP "NVME_FEAT_AE_FW_SHIFT" 12
+.IP "NVME_FEAT_AE_FW_MASK" 12
+.IP "NVME_FEAT_AE_TELEM_SHIFT" 12
+.IP "NVME_FEAT_AE_TELEM_MASK" 12
+.IP "NVME_FEAT_AE_ANA_SHIFT" 12
+.IP "NVME_FEAT_AE_ANA_MASK" 12
+.IP "NVME_FEAT_AE_PLA_SHIFT" 12
+.IP "NVME_FEAT_AE_PLA_MASK" 12
+.IP "NVME_FEAT_AE_LBAS_SHIFT" 12
+.IP "NVME_FEAT_AE_LBAS_MASK" 12
+.IP "NVME_FEAT_AE_EGA_SHIFT" 12
+.IP "NVME_FEAT_AE_EGA_MASK" 12
+.IP "NVME_FEAT_APST_APSTE_SHIFT" 12
+.IP "NVME_FEAT_APST_APSTE_MASK" 12
+.IP "NVME_FEAT_HMEM_EHM_SHIFT" 12
+.IP "NVME_FEAT_HMEM_EHM_MASK" 12
+.IP "NVME_FEAT_HCTM_TMT2_SHIFT" 12
+.IP "NVME_FEAT_HCTM_TMT2_MASK" 12
+.IP "NVME_FEAT_HCTM_TMT1_SHIFT" 12
+.IP "NVME_FEAT_HCTM_TMT1_MASK" 12
+.IP "NVME_FEAT_NOPS_NOPPME_SHIFT" 12
+.IP "NVME_FEAT_NOPS_NOPPME_MASK" 12
+.IP "NVME_FEAT_RRL_RRL_SHIFT" 12
+.IP "NVME_FEAT_RRL_RRL_MASK" 12
+.IP "NVME_FEAT_PLM_PLME_SHIFT" 12
+.IP "NVME_FEAT_PLM_PLME_MASK" 12
+.IP "NVME_FEAT_PLMW_WS_SHIFT" 12
+.IP "NVME_FEAT_PLMW_WS_MASK" 12
+.IP "NVME_FEAT_LBAS_LSIRI_SHIFT" 12
+.IP "NVME_FEAT_LBAS_LSIRI_MASK" 12
+.IP "NVME_FEAT_LBAS_LSIPI_SHIFT" 12
+.IP "NVME_FEAT_LBAS_LSIPI_MASK" 12
+.IP "NVME_FEAT_SC_NODRM_SHIFT" 12
+.IP "NVME_FEAT_SC_NODRM_MASK" 12
+.IP "NVME_FEAT_EG_ENDGID_SHIFT" 12
+.IP "NVME_FEAT_EG_ENDGID_MASK" 12
+.IP "NVME_FEAT_EG_EGCW_SHIFT" 12
+.IP "NVME_FEAT_EG_EGCW_MASK" 12
+.IP "NVME_FEAT_SPM_PBSLC_SHIFT" 12
+.IP "NVME_FEAT_SPM_PBSLC_MASK" 12
+.IP "NVME_FEAT_HOSTID_EXHID_SHIFT" 12
+.IP "NVME_FEAT_HOSTID_EXHID_MASK" 12
+.IP "NVME_FEAT_RM_REGPRE_SHIFT" 12
+.IP "NVME_FEAT_RM_REGPRE_MASK" 12
+.IP "NVME_FEAT_RM_RESREL_SHIFT" 12
+.IP "NVME_FEAT_RM_RESREL_MASK" 12
+.IP "NVME_FEAT_RM_RESPRE_SHIFT" 12
+.IP "NVME_FEAT_RM_RESPRE_MASK" 12
+.IP "NVME_FEAT_RP_PTPL_SHIFT" 12
+.IP "NVME_FEAT_RP_PTPL_MASK" 12
+.IP "NVME_FEAT_WP_WPS_SHIFT" 12
+.IP "NVME_FEAT_WP_WPS_MASK" 12
+.IP "NVME_FEAT_IOCSP_IOCSCI_SHIFT" 12
+.IP "NVME_FEAT_IOCSP_IOCSCI_MASK" 12
+.IP "NVME_FEAT_FDP_ENABLED_SHIFT" 12
+.IP "NVME_FEAT_FDP_ENABLED_MASK" 12
+.IP "NVME_FEAT_FDP_INDEX_SHIFT" 12
+.IP "NVME_FEAT_FDP_INDEX_MASK" 12
+.IP "NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT" 12
+.IP "NVME_FEAT_FDP_EVENTS_ENABLE_MASK" 12
diff --git a/doc/man/nvme_feat_auto_pst.2 b/doc/man/nvme_feat_auto_pst.2
new file mode 100644
index 0000000..8d1ce60
--- /dev/null
+++ b/doc/man/nvme_feat_auto_pst.2
@@ -0,0 +1,15 @@
+.TH "libnvme" 9 "struct nvme_feat_auto_pst" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_feat_auto_pst \- Autonomous Power State Transition
+.SH SYNOPSIS
+struct nvme_feat_auto_pst {
+.br
+.BI " __le64 apst_entry[32];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "apst_entry" 12
+See \fIenum nvme_apst_entry\fP
diff --git a/doc/man/nvme_feat_fdp_events_cdw11.2 b/doc/man/nvme_feat_fdp_events_cdw11.2
new file mode 100644
index 0000000..db9da67
--- /dev/null
+++ b/doc/man/nvme_feat_fdp_events_cdw11.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_feat_fdp_events_cdw11" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_feat_fdp_events_cdw11 \- FDP Events Feature Command Dword 11
+.SH SYNOPSIS
+struct nvme_feat_fdp_events_cdw11 {
+.br
+.BI " __le16 phndl;"
+.br
+.BI " __u8 noet;"
+.br
+.BI " __u8 rsvd24;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "phndl" 12
+Placement Handle
+.IP "noet" 12
+Number of FDP Event Types
+.IP "rsvd24" 12
+Reserved
diff --git a/doc/man/nvme_feat_host_behavior.2 b/doc/man/nvme_feat_host_behavior.2
new file mode 100644
index 0000000..5c7dd1d
--- /dev/null
+++ b/doc/man/nvme_feat_host_behavior.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_feat_host_behavior" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_feat_host_behavior \- Host Behavior Support - Data Structure
+.SH SYNOPSIS
+struct nvme_feat_host_behavior {
+.br
+.BI " __u8 acre;"
+.br
+.BI " __u8 etdas;"
+.br
+.BI " __u8 lbafee;"
+.br
+.BI " __u8 rsvd3;"
+.br
+.BI " __u16 cdfe;"
+.br
+.BI " __u8 rsvd6[506];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "acre" 12
+Advanced Command Retry Enable
+.IP "etdas" 12
+Extended Telemetry Data Area 4 Supported
+.IP "lbafee" 12
+LBA Format Extension Enable
+.IP "rsvd3" 12
+Reserved
+.IP "cdfe" 12
+Copy Descriptor Formats Enable
+.IP "rsvd6" 12
+Reserved
diff --git a/doc/man/nvme_feat_nswpcfg_state.2 b/doc/man/nvme_feat_nswpcfg_state.2
new file mode 100644
index 0000000..9a7f350
--- /dev/null
+++ b/doc/man/nvme_feat_nswpcfg_state.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_feat_nswpcfg_state" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_feat_nswpcfg_state \- Write Protection - Write Protection State
+.SH SYNOPSIS
+enum nvme_feat_nswpcfg_state {
+.br
+.BI " NVME_FEAT_NS_NO_WRITE_PROTECT"
+,
+.br
+.br
+.BI " NVME_FEAT_NS_WRITE_PROTECT"
+,
+.br
+.br
+.BI " NVME_FEAT_NS_WRITE_PROTECT_PWR_CYCLE"
+,
+.br
+.br
+.BI " NVME_FEAT_NS_WRITE_PROTECT_PERMANENT"
+
+};
+.SH Constants
+.IP "NVME_FEAT_NS_NO_WRITE_PROTECT" 12
+No Write Protect
+.IP "NVME_FEAT_NS_WRITE_PROTECT" 12
+Write Protect
+.IP "NVME_FEAT_NS_WRITE_PROTECT_PWR_CYCLE" 12
+Write Protect Until Power Cycle
+.IP "NVME_FEAT_NS_WRITE_PROTECT_PERMANENT" 12
+Permanent Write Protect
diff --git a/doc/man/nvme_feat_plm_window_select.2 b/doc/man/nvme_feat_plm_window_select.2
new file mode 100644
index 0000000..0d9ee19
--- /dev/null
+++ b/doc/man/nvme_feat_plm_window_select.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_feat_plm_window_select" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_feat_plm_window_select \- Predictable Latency Per NVM Set Log
+.SH SYNOPSIS
+enum nvme_feat_plm_window_select {
+.br
+.BI " NVME_FEATURE_PLM_DTWIN"
+,
+.br
+.br
+.BI " NVME_FEATURE_PLM_NDWIN"
+
+};
+.SH Constants
+.IP "NVME_FEATURE_PLM_DTWIN" 12
+Deterministic Window select
+.IP "NVME_FEATURE_PLM_NDWIN" 12
+Non-Deterministic Window select
diff --git a/doc/man/nvme_feat_resv_notify_flags.2 b/doc/man/nvme_feat_resv_notify_flags.2
new file mode 100644
index 0000000..d25acbe
--- /dev/null
+++ b/doc/man/nvme_feat_resv_notify_flags.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_feat_resv_notify_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_feat_resv_notify_flags \- Reservation Notification Configuration
+.SH SYNOPSIS
+enum nvme_feat_resv_notify_flags {
+.br
+.BI " NVME_FEAT_RESV_NOTIFY_REGPRE"
+,
+.br
+.br
+.BI " NVME_FEAT_RESV_NOTIFY_RESREL"
+,
+.br
+.br
+.BI " NVME_FEAT_RESV_NOTIFY_RESPRE"
+
+};
+.SH Constants
+.IP "NVME_FEAT_RESV_NOTIFY_REGPRE" 12
+Mask Registration Preempted Notification
+.IP "NVME_FEAT_RESV_NOTIFY_RESREL" 12
+Mask Reservation Released Notification
+.IP "NVME_FEAT_RESV_NOTIFY_RESPRE" 12
+Mask Reservation Preempted Notification
diff --git a/doc/man/nvme_feat_tmpthresh_thsel.2 b/doc/man/nvme_feat_tmpthresh_thsel.2
new file mode 100644
index 0000000..0eccb43
--- /dev/null
+++ b/doc/man/nvme_feat_tmpthresh_thsel.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_feat_tmpthresh_thsel" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_feat_tmpthresh_thsel \- Temperature Threshold - Threshold Type Select
+.SH SYNOPSIS
+enum nvme_feat_tmpthresh_thsel {
+.br
+.BI " NVME_FEATURE_TEMPTHRESH_THSEL_OVER"
+,
+.br
+.br
+.BI " NVME_FEATURE_TEMPTHRESH_THSEL_UNDER"
+
+};
+.SH Constants
+.IP "NVME_FEATURE_TEMPTHRESH_THSEL_OVER" 12
+Over temperature threshold select
+.IP "NVME_FEATURE_TEMPTHRESH_THSEL_UNDER" 12
+Under temperature threshold select
diff --git a/doc/man/nvme_features_async_event_config_flags.2 b/doc/man/nvme_features_async_event_config_flags.2
new file mode 100644
index 0000000..83693a4
--- /dev/null
+++ b/doc/man/nvme_features_async_event_config_flags.2
@@ -0,0 +1,76 @@
+.TH "libnvme" 9 "enum nvme_features_async_event_config_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_features_async_event_config_flags \- Asynchronous Event Configuration configuration flags
+.SH SYNOPSIS
+enum nvme_features_async_event_config_flags {
+.br
+.BI " NVME_FEATURE_AENCFG_SMART_CRIT_SPARE"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_SMART_CRIT_TEMPERATURE"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_SMART_CRIT_DEGRADED"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_SMART_CRIT_VOLATILE_BACKUP"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY_PMR"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_NOTICE_NAMESPACE_ATTRIBUTES"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_NOTICE_FIRMWARE_ACTIVATION"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_NOTICE_TELEMETRY_LOG"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_NOTICE_ANA_CHANGE"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_NOTICE_PL_EVENT"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_NOTICE_LBA_STATUS"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_NOTICE_EG_EVENT"
+,
+.br
+.br
+.BI " NVME_FEATURE_AENCFG_NOTICE_DISCOVERY_CHANGE"
+
+};
+.SH Constants
+.IP "NVME_FEATURE_AENCFG_SMART_CRIT_SPARE" 12
+.IP "NVME_FEATURE_AENCFG_SMART_CRIT_TEMPERATURE" 12
+.IP "NVME_FEATURE_AENCFG_SMART_CRIT_DEGRADED" 12
+.IP "NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY" 12
+.IP "NVME_FEATURE_AENCFG_SMART_CRIT_VOLATILE_BACKUP" 12
+.IP "NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY_PMR" 12
+.IP "NVME_FEATURE_AENCFG_NOTICE_NAMESPACE_ATTRIBUTES" 12
+.IP "NVME_FEATURE_AENCFG_NOTICE_FIRMWARE_ACTIVATION" 12
+.IP "NVME_FEATURE_AENCFG_NOTICE_TELEMETRY_LOG" 12
+.IP "NVME_FEATURE_AENCFG_NOTICE_ANA_CHANGE" 12
+.IP "NVME_FEATURE_AENCFG_NOTICE_PL_EVENT" 12
+.IP "NVME_FEATURE_AENCFG_NOTICE_LBA_STATUS" 12
+.IP "NVME_FEATURE_AENCFG_NOTICE_EG_EVENT" 12
+.IP "NVME_FEATURE_AENCFG_NOTICE_DISCOVERY_CHANGE" 12
diff --git a/doc/man/nvme_features_id.2 b/doc/man/nvme_features_id.2
new file mode 100644
index 0000000..ad24bfb
--- /dev/null
+++ b/doc/man/nvme_features_id.2
@@ -0,0 +1,222 @@
+.TH "libnvme" 9 "enum nvme_features_id" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_features_id \- Features - Feature Identifiers
+.SH SYNOPSIS
+enum nvme_features_id {
+.br
+.BI " NVME_FEAT_FID_ARBITRATION"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_POWER_MGMT"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_LBA_RANGE"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_TEMP_THRESH"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_ERR_RECOVERY"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_VOLATILE_WC"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_NUM_QUEUES"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_IRQ_COALESCE"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_IRQ_CONFIG"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_WRITE_ATOMIC"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_ASYNC_EVENT"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_AUTO_PST"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_HOST_MEM_BUF"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_TIMESTAMP"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_KATO"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_HCTM"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_NOPSC"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_RRL"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_PLM_CONFIG"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_PLM_WINDOW"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_LBA_STS_INTERVAL"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_HOST_BEHAVIOR"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_SANITIZE"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_ENDURANCE_EVT_CFG"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_IOCS_PROFILE"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_SPINUP_CONTROL"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_FDP"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_FDP_EVENTS"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_ENH_CTRL_METADATA"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_CTRL_METADATA"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_NS_METADATA"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_SW_PROGRESS"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_HOST_ID"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_RESV_MASK"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_RESV_PERSIST"
+,
+.br
+.br
+.BI " NVME_FEAT_FID_WRITE_PROTECT"
+
+};
+.SH Constants
+.IP "NVME_FEAT_FID_ARBITRATION" 12
+Arbitration
+.IP "NVME_FEAT_FID_POWER_MGMT" 12
+Power Management
+.IP "NVME_FEAT_FID_LBA_RANGE" 12
+LBA Range Type
+.IP "NVME_FEAT_FID_TEMP_THRESH" 12
+Temperature Threshold
+.IP "NVME_FEAT_FID_ERR_RECOVERY" 12
+Error Recovery
+.IP "NVME_FEAT_FID_VOLATILE_WC" 12
+Volatile Write Cache
+.IP "NVME_FEAT_FID_NUM_QUEUES" 12
+Number of Queues
+.IP "NVME_FEAT_FID_IRQ_COALESCE" 12
+Interrupt Coalescing
+.IP "NVME_FEAT_FID_IRQ_CONFIG" 12
+Interrupt Vector Configuration
+.IP "NVME_FEAT_FID_WRITE_ATOMIC" 12
+Write Atomicity Normal
+.IP "NVME_FEAT_FID_ASYNC_EVENT" 12
+Asynchronous Event Configuration
+.IP "NVME_FEAT_FID_AUTO_PST" 12
+Autonomous Power State Transition
+.IP "NVME_FEAT_FID_HOST_MEM_BUF" 12
+Host Memory Buffer
+.IP "NVME_FEAT_FID_TIMESTAMP" 12
+Timestamp
+.IP "NVME_FEAT_FID_KATO" 12
+Keep Alive Timer
+.IP "NVME_FEAT_FID_HCTM" 12
+Host Controlled Thermal Management
+.IP "NVME_FEAT_FID_NOPSC" 12
+Non-Operational Power State Config
+.IP "NVME_FEAT_FID_RRL" 12
+Read Recovery Level Config
+.IP "NVME_FEAT_FID_PLM_CONFIG" 12
+Predictable Latency Mode Config
+.IP "NVME_FEAT_FID_PLM_WINDOW" 12
+Predictable Latency Mode Window
+.IP "NVME_FEAT_FID_LBA_STS_INTERVAL" 12
+LBA Status Information Report Interval
+.IP "NVME_FEAT_FID_HOST_BEHAVIOR" 12
+Host Behavior Support
+.IP "NVME_FEAT_FID_SANITIZE" 12
+Endurance Group Event Configuration
+.IP "NVME_FEAT_FID_ENDURANCE_EVT_CFG" 12
+Endurance Group Event Configuration
+.IP "NVME_FEAT_FID_IOCS_PROFILE" 12
+I/O Command Set Profile
+.IP "NVME_FEAT_FID_SPINUP_CONTROL" 12
+Spinup Control
+.IP "NVME_FEAT_FID_FDP" 12
+Flexible Data Placement
+.IP "NVME_FEAT_FID_FDP_EVENTS" 12
+FDP Events
+.IP "NVME_FEAT_FID_ENH_CTRL_METADATA" 12
+Enhanced Controller Metadata
+.IP "NVME_FEAT_FID_CTRL_METADATA" 12
+Controller Metadata
+.IP "NVME_FEAT_FID_NS_METADATA" 12
+Namespace Metadata
+.IP "NVME_FEAT_FID_SW_PROGRESS" 12
+Software Progress Marker
+.IP "NVME_FEAT_FID_HOST_ID" 12
+Host Identifier
+.IP "NVME_FEAT_FID_RESV_MASK" 12
+Reservation Notification Mask
+.IP "NVME_FEAT_FID_RESV_PERSIST" 12
+Reservation Persistence
+.IP "NVME_FEAT_FID_WRITE_PROTECT" 12
+Namespace Write Protection Config
diff --git a/doc/man/nvme_fid_supported_effects.2 b/doc/man/nvme_fid_supported_effects.2
new file mode 100644
index 0000000..512c36e
--- /dev/null
+++ b/doc/man/nvme_fid_supported_effects.2
@@ -0,0 +1,90 @@
+.TH "libnvme" 9 "enum nvme_fid_supported_effects" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fid_supported_effects \- FID Supported and Effects Data Structure definitions
+.SH SYNOPSIS
+enum nvme_fid_supported_effects {
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_FSUPP"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_UDCC"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_NCC"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_NIC"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_CCC"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_UUID_SEL"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_SCOPE_SHIFT"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_SCOPE_MASK"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_SCOPE_NS"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_SCOPE_CTRL"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_SCOPE_NVM_SET"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_SCOPE_ENDGRP"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_SCOPE_DOMAIN"
+,
+.br
+.br
+.BI " NVME_FID_SUPPORTED_EFFECTS_SCOPE_NSS"
+
+};
+.SH Constants
+.IP "NVME_FID_SUPPORTED_EFFECTS_FSUPP" 12
+FID Supported
+.IP "NVME_FID_SUPPORTED_EFFECTS_UDCC" 12
+User Data Content Change
+.IP "NVME_FID_SUPPORTED_EFFECTS_NCC" 12
+Namespace Capability Change
+.IP "NVME_FID_SUPPORTED_EFFECTS_NIC" 12
+Namespace Inventory Change
+.IP "NVME_FID_SUPPORTED_EFFECTS_CCC" 12
+Controller Capability Change
+.IP "NVME_FID_SUPPORTED_EFFECTS_UUID_SEL" 12
+UUID Selection Supported
+.IP "NVME_FID_SUPPORTED_EFFECTS_SCOPE_SHIFT" 12
+FID Scope Shift
+.IP "NVME_FID_SUPPORTED_EFFECTS_SCOPE_MASK" 12
+FID Scope Mask
+.IP "NVME_FID_SUPPORTED_EFFECTS_SCOPE_NS" 12
+Namespace Scope
+.IP "NVME_FID_SUPPORTED_EFFECTS_SCOPE_CTRL" 12
+Controller Scope
+.IP "NVME_FID_SUPPORTED_EFFECTS_SCOPE_NVM_SET" 12
+NVM Set Scope
+.IP "NVME_FID_SUPPORTED_EFFECTS_SCOPE_ENDGRP" 12
+Endurance Group Scope
+.IP "NVME_FID_SUPPORTED_EFFECTS_SCOPE_DOMAIN" 12
+Domain Scope
+.IP "NVME_FID_SUPPORTED_EFFECTS_SCOPE_NSS" 12
+NVM Subsystem Scope
diff --git a/doc/man/nvme_fid_supported_effects_log.2 b/doc/man/nvme_fid_supported_effects_log.2
new file mode 100644
index 0000000..8358f6c
--- /dev/null
+++ b/doc/man/nvme_fid_supported_effects_log.2
@@ -0,0 +1,15 @@
+.TH "libnvme" 9 "struct nvme_fid_supported_effects_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fid_supported_effects_log \- Feature Identifiers Supported and Effects
+.SH SYNOPSIS
+struct nvme_fid_supported_effects_log {
+.br
+.BI " __le32 fid_support[NVME_LOG_FID_SUPPORTED_EFFECTS_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "fid_support" 12
+Feature Identifier Supported
diff --git a/doc/man/nvme_firmware_slot.2 b/doc/man/nvme_firmware_slot.2
new file mode 100644
index 0000000..2fb1f7d
--- /dev/null
+++ b/doc/man/nvme_firmware_slot.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_firmware_slot" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_firmware_slot \- Firmware Slot Information Log
+.SH SYNOPSIS
+struct nvme_firmware_slot {
+.br
+.BI " __u8 afi;"
+.br
+.BI " __u8 rsvd1[7];"
+.br
+.BI " char frs[7][8];"
+.br
+.BI " __u8 rsvd2[448];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "afi" 12
+Active Firmware Info
+.IP "rsvd1" 12
+Reserved
+.IP "frs" 12
+Firmware Revision for Slot
+.IP "rsvd2" 12
+Reserved
diff --git a/doc/man/nvme_first_host.2 b/doc/man/nvme_first_host.2
new file mode 100644
index 0000000..ee2a5dd
--- /dev/null
+++ b/doc/man/nvme_first_host.2
@@ -0,0 +1,11 @@
+.TH "nvme_first_host" 9 "nvme_first_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_first_host \- Start host iterator
+.SH SYNOPSIS
+.B "nvme_host_t" nvme_first_host
+.BI "(nvme_root_t r " ");"
+.SH ARGUMENTS
+.IP "r" 12
+\fInvme_root_t\fP object
+.SH "RETURN"
+First \fInvme_host_t\fP object in an iterator
diff --git a/doc/man/nvme_first_subsystem.2 b/doc/man/nvme_first_subsystem.2
new file mode 100644
index 0000000..58559bc
--- /dev/null
+++ b/doc/man/nvme_first_subsystem.2
@@ -0,0 +1,11 @@
+.TH "nvme_first_subsystem" 9 "nvme_first_subsystem" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_first_subsystem \- Start subsystem iterator
+.SH SYNOPSIS
+.B "nvme_subsystem_t" nvme_first_subsystem
+.BI "(nvme_host_t h " ");"
+.SH ARGUMENTS
+.IP "h" 12
+\fInvme_host_t\fP object
+.SH "RETURN"
+first \fInvme_subsystem_t\fP object in an iterator
diff --git a/doc/man/nvme_flush.2 b/doc/man/nvme_flush.2
new file mode 100644
index 0000000..4c79401
--- /dev/null
+++ b/doc/man/nvme_flush.2
@@ -0,0 +1,18 @@
+.TH "nvme_flush" 9 "nvme_flush" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_flush \- Send an nvme flush command
+.SH SYNOPSIS
+.B "int" nvme_flush
+.BI "(int fd " ","
+.BI "__u32 nsid " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace identifier
+.SH "DESCRIPTION"
+The Flush command requests that the contents of volatile write cache be made
+non-volatile.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_for_each_host.2 b/doc/man/nvme_for_each_host.2
new file mode 100644
index 0000000..4e6d50f
--- /dev/null
+++ b/doc/man/nvme_for_each_host.2
@@ -0,0 +1,12 @@
+.TH "nvme_for_each_host" 9 "nvme_for_each_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_for_each_host \- Traverse host list
+.SH SYNOPSIS
+.B "nvme_for_each_host
+.BI "(r " ","
+.BI "h " ");"
+.SH ARGUMENTS
+.IP "r" 12
+\fInvme_root_t\fP object
+.IP "h" 12
+\fInvme_host_t\fP object
diff --git a/doc/man/nvme_for_each_host_safe.2 b/doc/man/nvme_for_each_host_safe.2
new file mode 100644
index 0000000..45f8250
--- /dev/null
+++ b/doc/man/nvme_for_each_host_safe.2
@@ -0,0 +1,15 @@
+.TH "nvme_for_each_host_safe" 9 "nvme_for_each_host_safe" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_for_each_host_safe \- Traverse host list
+.SH SYNOPSIS
+.B "nvme_for_each_host_safe
+.BI "(r " ","
+.BI "h " ","
+.BI "_h " ");"
+.SH ARGUMENTS
+.IP "r" 12
+\fInvme_root_t\fP object
+.IP "h" 12
+\fInvme_host_t\fP object
+.IP "_h" 12
+Temporary \fInvme_host_t\fP object
diff --git a/doc/man/nvme_for_each_subsystem.2 b/doc/man/nvme_for_each_subsystem.2
new file mode 100644
index 0000000..9b14dd8
--- /dev/null
+++ b/doc/man/nvme_for_each_subsystem.2
@@ -0,0 +1,12 @@
+.TH "nvme_for_each_subsystem" 9 "nvme_for_each_subsystem" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_for_each_subsystem \- Traverse subsystems
+.SH SYNOPSIS
+.B "nvme_for_each_subsystem
+.BI "(h " ","
+.BI "s " ");"
+.SH ARGUMENTS
+.IP "h" 12
+\fInvme_host_t\fP object
+.IP "s" 12
+\fInvme_subsystem_t\fP object
diff --git a/doc/man/nvme_for_each_subsystem_safe.2 b/doc/man/nvme_for_each_subsystem_safe.2
new file mode 100644
index 0000000..d2101f0
--- /dev/null
+++ b/doc/man/nvme_for_each_subsystem_safe.2
@@ -0,0 +1,15 @@
+.TH "nvme_for_each_subsystem_safe" 9 "nvme_for_each_subsystem_safe" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_for_each_subsystem_safe \- Traverse subsystems
+.SH SYNOPSIS
+.B "nvme_for_each_subsystem_safe
+.BI "(h " ","
+.BI "s " ","
+.BI "_s " ");"
+.SH ARGUMENTS
+.IP "h" 12
+\fInvme_host_t\fP object
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "_s" 12
+Temporary \fInvme_subsystem_t\fP object
diff --git a/doc/man/nvme_format_nvm.2 b/doc/man/nvme_format_nvm.2
new file mode 100644
index 0000000..c8615ad
--- /dev/null
+++ b/doc/man/nvme_format_nvm.2
@@ -0,0 +1,17 @@
+.TH "nvme_format_nvm" 9 "nvme_format_nvm" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_format_nvm \- Format nvme namespace(s)
+.SH SYNOPSIS
+.B "int" nvme_format_nvm
+.BI "(struct nvme_format_nvm_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_format_nvme_args\fP argument structure
+.SH "DESCRIPTION"
+The Format NVM command low level formats the NVM media. This command is used
+by the host to change the LBA data size and/or metadata size. A low level
+format may destroy all data and metadata associated with all namespaces or
+only the specific namespace associated with the command
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_format_nvm_compln_event.2 b/doc/man/nvme_format_nvm_compln_event.2
new file mode 100644
index 0000000..261f6d0
--- /dev/null
+++ b/doc/man/nvme_format_nvm_compln_event.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_format_nvm_compln_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_format_nvm_compln_event \- Format NVM Completion Event Data
+.SH SYNOPSIS
+struct nvme_format_nvm_compln_event {
+.br
+.BI " __le32 nsid;"
+.br
+.BI " __u8 smallest_fpi;"
+.br
+.BI " __u8 format_nvm_status;"
+.br
+.BI " __le16 compln_info;"
+.br
+.BI " __le32 status_field;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nsid" 12
+Namespace Identifier
+.IP "smallest_fpi" 12
+Smallest Format Progress Indicator
+.IP "format_nvm_status" 12
+Format NVM Status
+.IP "compln_info" 12
+Completion Information
+.IP "status_field" 12
+Status Field
diff --git a/doc/man/nvme_format_nvm_start_event.2 b/doc/man/nvme_format_nvm_start_event.2
new file mode 100644
index 0000000..8329ffa
--- /dev/null
+++ b/doc/man/nvme_format_nvm_start_event.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_format_nvm_start_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_format_nvm_start_event \- Format NVM Start Event Data
+.SH SYNOPSIS
+struct nvme_format_nvm_start_event {
+.br
+.BI " __le32 nsid;"
+.br
+.BI " __u8 fna;"
+.br
+.BI " __u8 rsvd5[3];"
+.br
+.BI " __le32 format_nvm_cdw10;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nsid" 12
+Namespace Identifier
+.IP "fna" 12
+Format NVM Attributes
+.IP "rsvd5" 12
+Reserved
+.IP "format_nvm_cdw10" 12
+Format NVM CDW10
diff --git a/doc/man/nvme_free_ctrl.2 b/doc/man/nvme_free_ctrl.2
new file mode 100644
index 0000000..f26c491
--- /dev/null
+++ b/doc/man/nvme_free_ctrl.2
@@ -0,0 +1,9 @@
+.TH "nvme_free_ctrl" 9 "nvme_free_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_free_ctrl \- Free controller
+.SH SYNOPSIS
+.B "void" nvme_free_ctrl
+.BI "(struct nvme_ctrl *c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
diff --git a/doc/man/nvme_free_host.2 b/doc/man/nvme_free_host.2
new file mode 100644
index 0000000..8617c5a
--- /dev/null
+++ b/doc/man/nvme_free_host.2
@@ -0,0 +1,9 @@
+.TH "nvme_free_host" 9 "nvme_free_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_free_host \- Free nvme_host_t object
+.SH SYNOPSIS
+.B "void" nvme_free_host
+.BI "(nvme_host_t h " ");"
+.SH ARGUMENTS
+.IP "h" 12
+nvme_host_t object
diff --git a/doc/man/nvme_free_ns.2 b/doc/man/nvme_free_ns.2
new file mode 100644
index 0000000..0befce4
--- /dev/null
+++ b/doc/man/nvme_free_ns.2
@@ -0,0 +1,9 @@
+.TH "nvme_free_ns" 9 "nvme_free_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_free_ns \- Free a namespace object
+.SH SYNOPSIS
+.B "void" nvme_free_ns
+.BI "(struct nvme_ns *n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
diff --git a/doc/man/nvme_free_subsystem.2 b/doc/man/nvme_free_subsystem.2
new file mode 100644
index 0000000..2d7718a
--- /dev/null
+++ b/doc/man/nvme_free_subsystem.2
@@ -0,0 +1,11 @@
+.TH "nvme_free_subsystem" 9 "nvme_free_subsystem" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_free_subsystem \- Free a subsystem
+.SH SYNOPSIS
+.B "void" nvme_free_subsystem
+.BI "(struct nvme_subsystem *s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+subsystem
+.SH "DESCRIPTION"
+Frees \fIs\fP and all related objects.
diff --git a/doc/man/nvme_free_tree.2 b/doc/man/nvme_free_tree.2
new file mode 100644
index 0000000..61cf386
--- /dev/null
+++ b/doc/man/nvme_free_tree.2
@@ -0,0 +1,11 @@
+.TH "nvme_free_tree" 9 "nvme_free_tree" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_free_tree \- Free root object
+.SH SYNOPSIS
+.B "void" nvme_free_tree
+.BI "(nvme_root_t r " ");"
+.SH ARGUMENTS
+.IP "r" 12
+\fInvme_root_t\fP object
+.SH "DESCRIPTION"
+Free an \fInvme_root_t\fP object and all attached objects
diff --git a/doc/man/nvme_fw_commit.2 b/doc/man/nvme_fw_commit.2
new file mode 100644
index 0000000..eae82dc
--- /dev/null
+++ b/doc/man/nvme_fw_commit.2
@@ -0,0 +1,16 @@
+.TH "nvme_fw_commit" 9 "nvme_fw_commit" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_fw_commit \- Commit firmware using the specified action
+.SH SYNOPSIS
+.B "int" nvme_fw_commit
+.BI "(struct nvme_fw_commit_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_fw_commit_args\fP argument structure
+.SH "DESCRIPTION"
+The Firmware Commit command modifies the firmware image or Boot Partitions.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise. The command
+status response may specify additional reset actions required to complete
+the commit process.
diff --git a/doc/man/nvme_fw_commit_ca.2 b/doc/man/nvme_fw_commit_ca.2
new file mode 100644
index 0000000..d3cb31d
--- /dev/null
+++ b/doc/man/nvme_fw_commit_ca.2
@@ -0,0 +1,59 @@
+.TH "libnvme" 9 "enum nvme_fw_commit_ca" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_fw_commit_ca \- Firmware Commit - Commit Action
+.SH SYNOPSIS
+enum nvme_fw_commit_ca {
+.br
+.BI " NVME_FW_COMMIT_CA_REPLACE"
+,
+.br
+.br
+.BI " NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE"
+,
+.br
+.br
+.BI " NVME_FW_COMMIT_CA_SET_ACTIVE"
+,
+.br
+.br
+.BI " NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE"
+,
+.br
+.br
+.BI " NVME_FW_COMMIT_CA_REPLACE_BOOT_PARTITION"
+,
+.br
+.br
+.BI " NVME_FW_COMMIT_CA_ACTIVATE_BOOT_PARTITION"
+
+};
+.SH Constants
+.IP "NVME_FW_COMMIT_CA_REPLACE" 12
+Downloaded image replaces the existing
+image, if any, in the specified Firmware
+Slot. The newly placed image is not
+activated.
+.IP "NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE" 12
+Downloaded image replaces the existing
+image, if any, in the specified Firmware
+Slot. The newly placed image is activated
+at the next Controller Level Reset.
+.IP "NVME_FW_COMMIT_CA_SET_ACTIVE" 12
+The existing image in the specified
+Firmware Slot is activated at the
+next Controller Level Reset.
+.IP "NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE" 12
+Downloaded image replaces the existing
+image, if any, in the specified Firmware
+Slot and is then activated immediately.
+If there is not a newly downloaded image,
+then the existing image in the specified
+firmware slot is activated immediately.
+.IP "NVME_FW_COMMIT_CA_REPLACE_BOOT_PARTITION" 12
+Downloaded image replaces the Boot
+Partition specified by the Boot
+Partition ID field.
+.IP "NVME_FW_COMMIT_CA_ACTIVATE_BOOT_PARTITION" 12
+Mark the Boot Partition specified in
+the BPID field as active and update
+BPINFO.ABPID.
diff --git a/doc/man/nvme_fw_commit_event.2 b/doc/man/nvme_fw_commit_event.2
new file mode 100644
index 0000000..2a688c2
--- /dev/null
+++ b/doc/man/nvme_fw_commit_event.2
@@ -0,0 +1,39 @@
+.TH "libnvme" 9 "struct nvme_fw_commit_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_fw_commit_event \- Firmware Commit Event Data
+.SH SYNOPSIS
+struct nvme_fw_commit_event {
+.br
+.BI " __le64 old_fw_rev;"
+.br
+.BI " __le64 new_fw_rev;"
+.br
+.BI " __u8 fw_commit_action;"
+.br
+.BI " __u8 fw_slot;"
+.br
+.BI " __u8 sct_fw;"
+.br
+.BI " __u8 sc_fw;"
+.br
+.BI " __le16 vndr_assign_fw_commit_rc;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "old_fw_rev" 12
+Old Firmware Revision
+.IP "new_fw_rev" 12
+New Firmware Revision
+.IP "fw_commit_action" 12
+Firmware Commit Action
+.IP "fw_slot" 12
+Firmware Slot
+.IP "sct_fw" 12
+Status Code Type for Firmware Commit Command
+.IP "sc_fw" 12
+Status Returned for Firmware Commit Command
+.IP "vndr_assign_fw_commit_rc" 12
+Vendor Assigned Firmware Commit Result Code
diff --git a/doc/man/nvme_fw_download.2 b/doc/man/nvme_fw_download.2
new file mode 100644
index 0000000..c054797
--- /dev/null
+++ b/doc/man/nvme_fw_download.2
@@ -0,0 +1,25 @@
+.TH "nvme_fw_download" 9 "nvme_fw_download" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_fw_download \- Download part or all of a firmware image to the controller
+.SH SYNOPSIS
+.B "int" nvme_fw_download
+.BI "(struct nvme_fw_download_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_fw_download_args\fP argument structure
+.SH "DESCRIPTION"
+The Firmware Image Download command downloads all or a portion of an image
+for a future update to the controller. The Firmware Image Download command
+downloads a new image (in whole or in part) to the controller.
+
+The image may be constructed of multiple pieces that are individually
+downloaded with separate Firmware Image Download commands. Each Firmware
+Image Download command includes a Dword Offset and Number of Dwords that
+specify a dword range.
+
+The new firmware image is not activated as part of the Firmware Image
+Download command. Use the \fBnvme_fw_commit\fP to activate a newly downloaded
+image.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_fw_download_seq.2 b/doc/man/nvme_fw_download_seq.2
new file mode 100644
index 0000000..217f2ca
--- /dev/null
+++ b/doc/man/nvme_fw_download_seq.2
@@ -0,0 +1,24 @@
+.TH "nvme_fw_download_seq" 9 "nvme_fw_download_seq" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_fw_download_seq \- Firmware download sequence
+.SH SYNOPSIS
+.B "int" nvme_fw_download_seq
+.BI "(int fd " ","
+.BI "__u32 size " ","
+.BI "__u32 xfer " ","
+.BI "__u32 offset " ","
+.BI "void *buf " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "size" 12
+Total size of the firmware image to transfer
+.IP "xfer" 12
+Maximum size to send with each partial transfer
+.IP "offset" 12
+Starting offset to send with this firmware download
+.IP "buf" 12
+Address of buffer containing all or part of the firmware image.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_gen_dhchap_key.2 b/doc/man/nvme_gen_dhchap_key.2
new file mode 100644
index 0000000..5215896
--- /dev/null
+++ b/doc/man/nvme_gen_dhchap_key.2
@@ -0,0 +1,24 @@
+.TH "nvme_gen_dhchap_key" 9 "nvme_gen_dhchap_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_gen_dhchap_key \- DH-HMAC-CHAP key generation
+.SH SYNOPSIS
+.B "int" nvme_gen_dhchap_key
+.BI "(char *hostnqn " ","
+.BI "enum nvme_hmac_alg hmac " ","
+.BI "unsigned int key_len " ","
+.BI "unsigned char *secret " ","
+.BI "unsigned char *key " ");"
+.SH ARGUMENTS
+.IP "hostnqn" 12
+Host NVMe Qualified Name
+.IP "hmac" 12
+HMAC algorithm
+.IP "key_len" 12
+Output key length
+.IP "secret" 12
+Secret to used for digest
+.IP "key" 12
+Generated DH-HMAC-CHAP key
+.SH "RETURN"
+If key generation was successful the function returns 0 or
+-1 with errno set otherwise.
diff --git a/doc/man/nvme_generate_tls_key_identity.2 b/doc/man/nvme_generate_tls_key_identity.2
new file mode 100644
index 0000000..84b5174
--- /dev/null
+++ b/doc/man/nvme_generate_tls_key_identity.2
@@ -0,0 +1,30 @@
+.TH "nvme_generate_tls_key_identity" 9 "nvme_generate_tls_key_identity" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_generate_tls_key_identity \- Generate the TLS key identity
+.SH SYNOPSIS
+.B "char *" nvme_generate_tls_key_identity
+.BI "(const char *hostnqn " ","
+.BI "const char *subsysnqn " ","
+.BI "int version " ","
+.BI "int hmac " ","
+.BI "unsigned char *configured_key " ","
+.BI "int key_len " ");"
+.SH ARGUMENTS
+.IP "hostnqn" 12
+Host NVMe Qualified Name
+.IP "subsysnqn" 12
+Subsystem NVMe Qualified Name
+.IP "version" 12
+Key version to use
+.IP "hmac" 12
+HMAC algorithm
+.IP "configured_key" 12
+Configured key data to derive the key from
+.IP "key_len" 12
+Length of \fIconfigured_key\fP
+.SH "DESCRIPTION"
+Derives a 'retained' TLS key as specified in NVMe TCP and
+generate the corresponding TLs identity.
+.SH "RETURN"
+The string containing the TLS identity. It is the responsibility
+of the caller to free the returned string.
diff --git a/doc/man/nvme_get_ana_log_len.2 b/doc/man/nvme_get_ana_log_len.2
new file mode 100644
index 0000000..0c17419
--- /dev/null
+++ b/doc/man/nvme_get_ana_log_len.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_ana_log_len" 9 "nvme_get_ana_log_len" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_ana_log_len \- Retrieve size of the current ANA log
+.SH SYNOPSIS
+.B "int" nvme_get_ana_log_len
+.BI "(int fd " ","
+.BI "size_t *analen " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "analen" 12
+Pointer to where the length will be set on success
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_attr.2 b/doc/man/nvme_get_attr.2
new file mode 100644
index 0000000..f580ea2
--- /dev/null
+++ b/doc/man/nvme_get_attr.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_attr" 9 "nvme_get_attr" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_attr \- Read sysfs attribute
+.SH SYNOPSIS
+.B "char *" nvme_get_attr
+.BI "(const char *d " ","
+.BI "const char *attr " ");"
+.SH ARGUMENTS
+.IP "d" 12
+sysfs directory
+.IP "attr" 12
+sysfs attribute name
+.SH "RETURN"
+String with the contents of \fIattr\fP or NULL in case of an empty value
+or in case of an error (indicated by non-zero errno code).
diff --git a/doc/man/nvme_get_ctrl_attr.2 b/doc/man/nvme_get_ctrl_attr.2
new file mode 100644
index 0000000..4f4a9ba
--- /dev/null
+++ b/doc/man/nvme_get_ctrl_attr.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_ctrl_attr" 9 "nvme_get_ctrl_attr" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_ctrl_attr \- Read controller sysfs attribute
+.SH SYNOPSIS
+.B "char *" nvme_get_ctrl_attr
+.BI "(nvme_ctrl_t c " ","
+.BI "const char *attr " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "attr" 12
+sysfs attribute name
+.SH "RETURN"
+String with the contents of \fIattr\fP or NULL in case of an empty value
+or in case of an error (indicated by non-zero errno code).
diff --git a/doc/man/nvme_get_ctrl_telemetry.2 b/doc/man/nvme_get_ctrl_telemetry.2
new file mode 100644
index 0000000..c27bf1c
--- /dev/null
+++ b/doc/man/nvme_get_ctrl_telemetry.2
@@ -0,0 +1,27 @@
+.TH "nvme_get_ctrl_telemetry" 9 "nvme_get_ctrl_telemetry" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_ctrl_telemetry \- Get controller telemetry log
+.SH SYNOPSIS
+.B "int" nvme_get_ctrl_telemetry
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_telemetry_log **log " ","
+.BI "enum nvme_telemetry_da da " ","
+.BI "size_t *size " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+On success, set to the value of the allocated and retrieved log.
+.IP "da" 12
+Log page data area, valid values: \fIenum nvme_telemetry_da\fP
+.IP "size" 12
+Ptr to the telemetry log size, so it can be returned
+.SH "DESCRIPTION"
+The total size allocated can be calculated as:
+(nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_directive_receive_length.2 b/doc/man/nvme_get_directive_receive_length.2
new file mode 100644
index 0000000..70e2f93
--- /dev/null
+++ b/doc/man/nvme_get_directive_receive_length.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_directive_receive_length" 9 "nvme_get_directive_receive_length" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_directive_receive_length \- Get directive receive length
+.SH SYNOPSIS
+.B "int" nvme_get_directive_receive_length
+.BI "(enum nvme_directive_dtype dtype " ","
+.BI "enum nvme_directive_receive_doper doper " ","
+.BI "__u32 *len " ");"
+.SH ARGUMENTS
+.IP "dtype" 12
+Directive type, see \fIenum nvme_directive_dtype\fP
+.IP "doper" 12
+Directive receive operation, see \fIenum nvme_directive_receive_doper\fP
+.IP "len" 12
+On success, set to this directives payload length in bytes.
+.SH "RETURN"
+0 on success, -1 with errno set to EINVAL if the function did not
+recognize \fIdtype\fP or \fIdoper\fP.
diff --git a/doc/man/nvme_get_discovery_args.2 b/doc/man/nvme_get_discovery_args.2
new file mode 100644
index 0000000..25ae70e
--- /dev/null
+++ b/doc/man/nvme_get_discovery_args.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_get_discovery_args" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_get_discovery_args \- Arguments for nvmf_get_discovery_wargs()
+.SH SYNOPSIS
+struct nvme_get_discovery_args {
+.br
+.BI " nvme_ctrl_t c;"
+.br
+.BI " int args_size;"
+.br
+.BI " int max_retries;"
+.br
+.BI " __u32 *result;"
+.br
+.BI " __u32 timeout;"
+.br
+.BI " __u8 lsp;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "c" 12
+Discovery controller
+.IP "args_size" 12
+Length of the structure
+.IP "max_retries" 12
+Number of retries in case of failure
+.IP "result" 12
+The command completion result from CQE dword0
+.IP "timeout" 12
+Timeout in ms (default: NVME_DEFAULT_IOCTL_TIMEOUT)
+.IP "lsp" 12
+Log specific field (See enum nvmf_log_discovery_lsp)
diff --git a/doc/man/nvme_get_feature_length.2 b/doc/man/nvme_get_feature_length.2
new file mode 100644
index 0000000..d321a49
--- /dev/null
+++ b/doc/man/nvme_get_feature_length.2
@@ -0,0 +1,19 @@
+.TH "nvme_get_feature_length" 9 "nvme_get_feature_length" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_feature_length \- Retreive the command payload length for a specific feature identifier
+.SH SYNOPSIS
+.B "int" nvme_get_feature_length
+.BI "(int fid " ","
+.BI "__u32 cdw11 " ","
+.BI "__u32 *len " ");"
+.SH ARGUMENTS
+.IP "fid" 12
+Feature identifier, see \fIenum nvme_features_id\fP.
+.IP "cdw11" 12
+The cdw11 value may affect the transfer (only known fid is
+NVME_FEAT_FID_HOST_ID)
+.IP "len" 12
+On success, set to this features payload length in bytes.
+.SH "RETURN"
+0 on success, -1 with errno set to EINVAL if the function did not
+recognize \fIfid\fP.
diff --git a/doc/man/nvme_get_feature_length2.2 b/doc/man/nvme_get_feature_length2.2
new file mode 100644
index 0000000..6498350
--- /dev/null
+++ b/doc/man/nvme_get_feature_length2.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_feature_length2" 9 "nvme_get_feature_length2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_feature_length2 \- Retreive the command payload length for a specific feature identifier
+.SH SYNOPSIS
+.B "int" nvme_get_feature_length2
+.BI "(int fid " ","
+.BI "__u32 cdw11 " ","
+.BI "enum nvme_data_tfr dir " ","
+.BI "__u32 *len " ");"
+.SH ARGUMENTS
+.IP "fid" 12
+Feature identifier, see \fIenum nvme_features_id\fP.
+.IP "cdw11" 12
+The cdw11 value may affect the transfer (only known fid is
+NVME_FEAT_FID_HOST_ID)
+.IP "dir" 12
+Data transfer direction: false - host to controller, true -
+controller to host may affect the transfer (only known fid is
+NVME_FEAT_FID_HOST_MEM_BUF).
+.IP "len" 12
+On success, set to this features payload length in bytes.
+.SH "RETURN"
+0 on success, -1 with errno set to EINVAL if the function did not
+recognize \fIfid\fP.
diff --git a/doc/man/nvme_get_features.2 b/doc/man/nvme_get_features.2
new file mode 100644
index 0000000..a8c86cf
--- /dev/null
+++ b/doc/man/nvme_get_features.2
@@ -0,0 +1,12 @@
+.TH "nvme_get_features" 9 "nvme_get_features" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features \- Retrieve a feature attribute
+.SH SYNOPSIS
+.B "int" nvme_get_features
+.BI "(struct nvme_get_features_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_get_features_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_arbitration.2 b/doc/man/nvme_get_features_arbitration.2
new file mode 100644
index 0000000..1a01c9c
--- /dev/null
+++ b/doc/man/nvme_get_features_arbitration.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_arbitration" 9 "nvme_get_features_arbitration" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_arbitration \- Get arbitration feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_arbitration
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_async_event.2 b/doc/man/nvme_get_features_async_event.2
new file mode 100644
index 0000000..fa2a6c6
--- /dev/null
+++ b/doc/man/nvme_get_features_async_event.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_async_event" 9 "nvme_get_features_async_event" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_async_event \- Get asynchronous event feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_async_event
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_auto_pst.2 b/doc/man/nvme_get_features_auto_pst.2
new file mode 100644
index 0000000..59a41bc
--- /dev/null
+++ b/doc/man/nvme_get_features_auto_pst.2
@@ -0,0 +1,20 @@
+.TH "nvme_get_features_auto_pst" 9 "nvme_get_features_auto_pst" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_auto_pst \- Get autonomous power state feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_auto_pst
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "struct nvme_feat_auto_pst *apst " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "apst" 12
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_data.2 b/doc/man/nvme_get_features_data.2
new file mode 100644
index 0000000..823b0f9
--- /dev/null
+++ b/doc/man/nvme_get_features_data.2
@@ -0,0 +1,27 @@
+.TH "nvme_get_features_data" 9 "nvme_get_features_data" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_data \- Helper function for @nvme_get_features()
+.SH SYNOPSIS
+.B "int" nvme_get_features_data
+.BI "(int fd " ","
+.BI "enum nvme_features_id fid " ","
+.BI "__u32 nsid " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "fid" 12
+Feature identifier
+.IP "nsid" 12
+Namespace ID, if applicable
+.IP "data_len" 12
+Length of feature data, if applicable, in bytes
+.IP "data" 12
+User address of feature data, if applicable
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_endurance_event_cfg.2 b/doc/man/nvme_get_features_endurance_event_cfg.2
new file mode 100644
index 0000000..cabe355
--- /dev/null
+++ b/doc/man/nvme_get_features_endurance_event_cfg.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_endurance_event_cfg" 9 "nvme_get_features_endurance_event_cfg" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_endurance_event_cfg \- Get endurance event config feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_endurance_event_cfg
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u16 endgid " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "endgid" 12
+Endurance Group Identifier
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_err_recovery.2 b/doc/man/nvme_get_features_err_recovery.2
new file mode 100644
index 0000000..f8e14eb
--- /dev/null
+++ b/doc/man/nvme_get_features_err_recovery.2
@@ -0,0 +1,22 @@
+.TH "nvme_get_features_err_recovery" 9 "nvme_get_features_err_recovery" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_err_recovery \- Get error recovery feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_err_recovery
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+
+Deprecated: doesn't support specifying a NSID.
+Use \fBnvme_get_features_err_recovery2\fP instead.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_err_recovery2.2 b/doc/man/nvme_get_features_err_recovery2.2
new file mode 100644
index 0000000..e79833f
--- /dev/null
+++ b/doc/man/nvme_get_features_err_recovery2.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_err_recovery2" 9 "nvme_get_features_err_recovery2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_err_recovery2 \- Get error recovery feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_err_recovery2
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 nsid " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "nsid" 12
+Namespace ID
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_hctm.2 b/doc/man/nvme_get_features_hctm.2
new file mode 100644
index 0000000..ce7b9c1
--- /dev/null
+++ b/doc/man/nvme_get_features_hctm.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_hctm" 9 "nvme_get_features_hctm" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_hctm \- Get thermal management feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_hctm
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_host_behavior.2 b/doc/man/nvme_get_features_host_behavior.2
new file mode 100644
index 0000000..48d333b
--- /dev/null
+++ b/doc/man/nvme_get_features_host_behavior.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_host_behavior" 9 "nvme_get_features_host_behavior" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_host_behavior \- Get host behavior feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_host_behavior
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "struct nvme_feat_host_behavior *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "data" 12
+Pointer to structure nvme_feat_host_behavior
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_host_id.2 b/doc/man/nvme_get_features_host_id.2
new file mode 100644
index 0000000..2f2011a
--- /dev/null
+++ b/doc/man/nvme_get_features_host_id.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_features_host_id" 9 "nvme_get_features_host_id" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_host_id \- Get host id feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_host_id
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "bool exhid " ","
+.BI "__u32 len " ","
+.BI "__u8 *hostid " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "exhid" 12
+Enable Extended Host Identifier
+.IP "len" 12
+Length of \fIhostid\fP
+.IP "hostid" 12
+Buffer for returned host ID
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_host_mem_buf.2 b/doc/man/nvme_get_features_host_mem_buf.2
new file mode 100644
index 0000000..e31e862
--- /dev/null
+++ b/doc/man/nvme_get_features_host_mem_buf.2
@@ -0,0 +1,22 @@
+.TH "nvme_get_features_host_mem_buf" 9 "nvme_get_features_host_mem_buf" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_host_mem_buf \- Get host memory buffer feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_host_mem_buf
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+
+Deprecated: doesn't fetch the Host Memory Buffer Attributes data structure.
+Use \fBnvme_get_features_host_mem_buf2\fP instead.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_host_mem_buf2.2 b/doc/man/nvme_get_features_host_mem_buf2.2
new file mode 100644
index 0000000..6740a8c
--- /dev/null
+++ b/doc/man/nvme_get_features_host_mem_buf2.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_host_mem_buf2" 9 "nvme_get_features_host_mem_buf2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_host_mem_buf2 \- Get host memory buffer feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_host_mem_buf2
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "struct nvme_host_mem_buf_attrs *attrs " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "attrs" 12
+Buffer for returned Host Memory Buffer Attributes
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_iocs_profile.2 b/doc/man/nvme_get_features_iocs_profile.2
new file mode 100644
index 0000000..eb448eb
--- /dev/null
+++ b/doc/man/nvme_get_features_iocs_profile.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_iocs_profile" 9 "nvme_get_features_iocs_profile" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_iocs_profile \- Get IOCS profile feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_iocs_profile
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_irq_coalesce.2 b/doc/man/nvme_get_features_irq_coalesce.2
new file mode 100644
index 0000000..31f03fa
--- /dev/null
+++ b/doc/man/nvme_get_features_irq_coalesce.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_irq_coalesce" 9 "nvme_get_features_irq_coalesce" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_irq_coalesce \- Get IRQ coalesce feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_irq_coalesce
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_irq_config.2 b/doc/man/nvme_get_features_irq_config.2
new file mode 100644
index 0000000..2a7967c
--- /dev/null
+++ b/doc/man/nvme_get_features_irq_config.2
@@ -0,0 +1,20 @@
+.TH "nvme_get_features_irq_config" 9 "nvme_get_features_irq_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_irq_config \- Get IRQ config feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_irq_config
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u16 iv " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "iv" 12
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_kato.2 b/doc/man/nvme_get_features_kato.2
new file mode 100644
index 0000000..fed7ea7
--- /dev/null
+++ b/doc/man/nvme_get_features_kato.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_kato" 9 "nvme_get_features_kato" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_kato \- Get keep alive timeout feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_kato
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_lba_range.2 b/doc/man/nvme_get_features_lba_range.2
new file mode 100644
index 0000000..05686d3
--- /dev/null
+++ b/doc/man/nvme_get_features_lba_range.2
@@ -0,0 +1,25 @@
+.TH "nvme_get_features_lba_range" 9 "nvme_get_features_lba_range" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_lba_range \- Get LBA range feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_lba_range
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "struct nvme_lba_range_type *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "data" 12
+User address of feature data, if applicable
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+
+Deprecated: doesn't support specifying a NSID.
+Use \fBnvme_get_features_lba_range2\fP instead.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_lba_range2.2 b/doc/man/nvme_get_features_lba_range2.2
new file mode 100644
index 0000000..fc6fdfd
--- /dev/null
+++ b/doc/man/nvme_get_features_lba_range2.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_features_lba_range2" 9 "nvme_get_features_lba_range2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_lba_range2 \- Get LBA range feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_lba_range2
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_lba_range_type *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "nsid" 12
+Namespace ID
+.IP "data" 12
+Buffer to receive LBA Range Type data structure
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_lba_sts_interval.2 b/doc/man/nvme_get_features_lba_sts_interval.2
new file mode 100644
index 0000000..0338c16
--- /dev/null
+++ b/doc/man/nvme_get_features_lba_sts_interval.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_lba_sts_interval" 9 "nvme_get_features_lba_sts_interval" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_lba_sts_interval \- Get LBA status information feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_lba_sts_interval
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_nopsc.2 b/doc/man/nvme_get_features_nopsc.2
new file mode 100644
index 0000000..ce6d00c
--- /dev/null
+++ b/doc/man/nvme_get_features_nopsc.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_nopsc" 9 "nvme_get_features_nopsc" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_nopsc \- Get non-operational power state feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_nopsc
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_num_queues.2 b/doc/man/nvme_get_features_num_queues.2
new file mode 100644
index 0000000..ad433b4
--- /dev/null
+++ b/doc/man/nvme_get_features_num_queues.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_num_queues" 9 "nvme_get_features_num_queues" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_num_queues \- Get number of queues feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_num_queues
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_plm_config.2 b/doc/man/nvme_get_features_plm_config.2
new file mode 100644
index 0000000..3683d04
--- /dev/null
+++ b/doc/man/nvme_get_features_plm_config.2
@@ -0,0 +1,23 @@
+.TH "nvme_get_features_plm_config" 9 "nvme_get_features_plm_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_plm_config \- Get predictable latency feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_plm_config
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u16 nvmsetid " ","
+.BI "struct nvme_plm_config *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "nvmsetid" 12
+NVM set id
+.IP "data" 12
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_plm_window.2 b/doc/man/nvme_get_features_plm_window.2
new file mode 100644
index 0000000..0771662
--- /dev/null
+++ b/doc/man/nvme_get_features_plm_window.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_plm_window" 9 "nvme_get_features_plm_window" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_plm_window \- Get window select feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_plm_window
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u16 nvmsetid " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "nvmsetid" 12
+NVM set id
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_power_mgmt.2 b/doc/man/nvme_get_features_power_mgmt.2
new file mode 100644
index 0000000..295c55b
--- /dev/null
+++ b/doc/man/nvme_get_features_power_mgmt.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_power_mgmt" 9 "nvme_get_features_power_mgmt" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_power_mgmt \- Get power management feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_power_mgmt
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_resv_mask.2 b/doc/man/nvme_get_features_resv_mask.2
new file mode 100644
index 0000000..fe25a84
--- /dev/null
+++ b/doc/man/nvme_get_features_resv_mask.2
@@ -0,0 +1,22 @@
+.TH "nvme_get_features_resv_mask" 9 "nvme_get_features_resv_mask" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_resv_mask \- Get reservation mask feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_resv_mask
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+
+Deprecated: doesn't support specifying a NSID.
+Use \fBnvme_get_features_resv_mask2\fP instead.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_resv_mask2.2 b/doc/man/nvme_get_features_resv_mask2.2
new file mode 100644
index 0000000..271efee
--- /dev/null
+++ b/doc/man/nvme_get_features_resv_mask2.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_resv_mask2" 9 "nvme_get_features_resv_mask2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_resv_mask2 \- Get reservation mask feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_resv_mask2
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 nsid " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "nsid" 12
+Namespace ID
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_resv_persist.2 b/doc/man/nvme_get_features_resv_persist.2
new file mode 100644
index 0000000..db4f208
--- /dev/null
+++ b/doc/man/nvme_get_features_resv_persist.2
@@ -0,0 +1,22 @@
+.TH "nvme_get_features_resv_persist" 9 "nvme_get_features_resv_persist" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_resv_persist \- Get reservation persist feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_resv_persist
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+
+Deprecated: doesn't support specifying a NSID.
+Use \fBnvme_get_features_resv_persist2\fP instead.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_resv_persist2.2 b/doc/man/nvme_get_features_resv_persist2.2
new file mode 100644
index 0000000..fb46e77
--- /dev/null
+++ b/doc/man/nvme_get_features_resv_persist2.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_resv_persist2" 9 "nvme_get_features_resv_persist2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_resv_persist2 \- Get reservation persist feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_resv_persist2
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 nsid " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "nsid" 12
+Namespace ID
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_rrl.2 b/doc/man/nvme_get_features_rrl.2
new file mode 100644
index 0000000..672ea0b
--- /dev/null
+++ b/doc/man/nvme_get_features_rrl.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_rrl" 9 "nvme_get_features_rrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_rrl \- Get read recovery level feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_rrl
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_sanitize.2 b/doc/man/nvme_get_features_sanitize.2
new file mode 100644
index 0000000..7db9cec
--- /dev/null
+++ b/doc/man/nvme_get_features_sanitize.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_sanitize" 9 "nvme_get_features_sanitize" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_sanitize \- Get sanitize feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_sanitize
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_sel.2 b/doc/man/nvme_get_features_sel.2
new file mode 100644
index 0000000..84e3853
--- /dev/null
+++ b/doc/man/nvme_get_features_sel.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_get_features_sel" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_get_features_sel \- Get Features - Select
+.SH SYNOPSIS
+enum nvme_get_features_sel {
+.br
+.BI " NVME_GET_FEATURES_SEL_CURRENT"
+,
+.br
+.br
+.BI " NVME_GET_FEATURES_SEL_DEFAULT"
+,
+.br
+.br
+.BI " NVME_GET_FEATURES_SEL_SAVED"
+,
+.br
+.br
+.BI " NVME_GET_FEATURES_SEL_SUPPORTED"
+
+};
+.SH Constants
+.IP "NVME_GET_FEATURES_SEL_CURRENT" 12
+Current value
+.IP "NVME_GET_FEATURES_SEL_DEFAULT" 12
+Default value
+.IP "NVME_GET_FEATURES_SEL_SAVED" 12
+Saved value
+.IP "NVME_GET_FEATURES_SEL_SUPPORTED" 12
+Supported capabilities
diff --git a/doc/man/nvme_get_features_simple.2 b/doc/man/nvme_get_features_simple.2
new file mode 100644
index 0000000..f3701dc
--- /dev/null
+++ b/doc/man/nvme_get_features_simple.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_simple" 9 "nvme_get_features_simple" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_simple \- Helper function for @nvme_get_features()
+.SH SYNOPSIS
+.B "int" nvme_get_features_simple
+.BI "(int fd " ","
+.BI "enum nvme_features_id fid " ","
+.BI "__u32 nsid " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "fid" 12
+Feature identifier
+.IP "nsid" 12
+Namespace ID, if applicable
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_sw_progress.2 b/doc/man/nvme_get_features_sw_progress.2
new file mode 100644
index 0000000..d10df48
--- /dev/null
+++ b/doc/man/nvme_get_features_sw_progress.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_sw_progress" 9 "nvme_get_features_sw_progress" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_sw_progress \- Get software progress feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_sw_progress
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_temp_thresh.2 b/doc/man/nvme_get_features_temp_thresh.2
new file mode 100644
index 0000000..96a95fe
--- /dev/null
+++ b/doc/man/nvme_get_features_temp_thresh.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_temp_thresh" 9 "nvme_get_features_temp_thresh" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_temp_thresh \- Get temperature threshold feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_temp_thresh
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_timestamp.2 b/doc/man/nvme_get_features_timestamp.2
new file mode 100644
index 0000000..b376c48
--- /dev/null
+++ b/doc/man/nvme_get_features_timestamp.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_timestamp" 9 "nvme_get_features_timestamp" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_timestamp \- Get timestamp feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_timestamp
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "struct nvme_timestamp *ts " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "ts" 12
+Current timestamp
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_volatile_wc.2 b/doc/man/nvme_get_features_volatile_wc.2
new file mode 100644
index 0000000..478c5a3
--- /dev/null
+++ b/doc/man/nvme_get_features_volatile_wc.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_volatile_wc" 9 "nvme_get_features_volatile_wc" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_volatile_wc \- Get volatile write cache feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_volatile_wc
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_write_atomic.2 b/doc/man/nvme_get_features_write_atomic.2
new file mode 100644
index 0000000..48b2d62
--- /dev/null
+++ b/doc/man/nvme_get_features_write_atomic.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_features_write_atomic" 9 "nvme_get_features_write_atomic" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_write_atomic \- Get write atomic feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_write_atomic
+.BI "(int fd " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_features_write_protect.2 b/doc/man/nvme_get_features_write_protect.2
new file mode 100644
index 0000000..e0200f2
--- /dev/null
+++ b/doc/man/nvme_get_features_write_protect.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_features_write_protect" 9 "nvme_get_features_write_protect" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_features_write_protect \- Get write protect feature
+.SH SYNOPSIS
+.B "int" nvme_get_features_write_protect
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "enum nvme_get_features_sel sel " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "sel" 12
+Select which type of attribute to return, see \fIenum nvme_get_features_sel\fP
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_host_telemetry.2 b/doc/man/nvme_get_host_telemetry.2
new file mode 100644
index 0000000..b60f685
--- /dev/null
+++ b/doc/man/nvme_get_host_telemetry.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_host_telemetry" 9 "nvme_get_host_telemetry" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_host_telemetry \- Get host telemetry log
+.SH SYNOPSIS
+.B "int" nvme_get_host_telemetry
+.BI "(int fd " ","
+.BI "struct nvme_telemetry_log **log " ","
+.BI "enum nvme_telemetry_da da " ","
+.BI "size_t *size " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "log" 12
+On success, set to the value of the allocated and retrieved log.
+.IP "da" 12
+Log page data area, valid values: \fIenum nvme_telemetry_da\fP
+.IP "size" 12
+Ptr to the telemetry log size, so it can be returned
+.SH "DESCRIPTION"
+The total size allocated can be calculated as:
+(nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_lba_status.2 b/doc/man/nvme_get_lba_status.2
new file mode 100644
index 0000000..c1d39a9
--- /dev/null
+++ b/doc/man/nvme_get_lba_status.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_lba_status" 9 "nvme_get_lba_status" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_lba_status \- Retrieve information on possibly unrecoverable LBAs
+.SH SYNOPSIS
+.B "int" nvme_get_lba_status
+.BI "(struct nvme_get_lba_status_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_get_lba_status_args\fP argument structure
+.SH "DESCRIPTION"
+The Get LBA Status command requests information about Potentially
+Unrecoverable LBAs. Refer to the specification for action type descriptions.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_lba_status_log.2 b/doc/man/nvme_get_lba_status_log.2
new file mode 100644
index 0000000..73a3287
--- /dev/null
+++ b/doc/man/nvme_get_lba_status_log.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_lba_status_log" 9 "nvme_get_lba_status_log" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_lba_status_log \- Retrieve the LBA Status log page
+.SH SYNOPSIS
+.B "int" nvme_get_lba_status_log
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_lba_status_log **log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of the nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+On success, set to the value of the allocated and retrieved log.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log.2 b/doc/man/nvme_get_log.2
new file mode 100644
index 0000000..b64b7d6
--- /dev/null
+++ b/doc/man/nvme_get_log.2
@@ -0,0 +1,12 @@
+.TH "nvme_get_log" 9 "nvme_get_log" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log \- NVMe Admin Get Log command
+.SH SYNOPSIS
+.B "int" nvme_get_log
+.BI "(struct nvme_get_log_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_get_log_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_ana.2 b/doc/man/nvme_get_log_ana.2
new file mode 100644
index 0000000..8dcf428
--- /dev/null
+++ b/doc/man/nvme_get_log_ana.2
@@ -0,0 +1,33 @@
+.TH "nvme_get_log_ana" 9 "nvme_get_log_ana" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_ana \- Retrieve Asymmetric Namespace Access log page
+.SH SYNOPSIS
+.B "int" nvme_get_log_ana
+.BI "(int fd " ","
+.BI "enum nvme_log_ana_lsp lsp " ","
+.BI "bool rae " ","
+.BI "__u64 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "lsp" 12
+Log specific, see \fIenum nvme_get_log_ana_lsp\fP
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset to the start of the log page
+.IP "len" 12
+The allocated length of the log page
+.IP "log" 12
+User address to store the ana log
+.SH "DESCRIPTION"
+This log consists of a header describing the log and descriptors containing
+the asymmetric namespace access information for ANA Groups that contain
+namespaces that are attached to the controller processing the command.
+
+See \fIstruct nvme_ana_rsp_hdr\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_ana_groups.2 b/doc/man/nvme_get_log_ana_groups.2
new file mode 100644
index 0000000..22c7787
--- /dev/null
+++ b/doc/man/nvme_get_log_ana_groups.2
@@ -0,0 +1,23 @@
+.TH "nvme_get_log_ana_groups" 9 "nvme_get_log_ana_groups" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_ana_groups \- Retrieve Asymmetric Namespace Access groups only log page
+.SH SYNOPSIS
+.B "int" nvme_get_log_ana_groups
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "__u32 len " ","
+.BI "struct nvme_ana_group_desc *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "len" 12
+The allocated length of the log page
+.IP "log" 12
+User address to store the ana group log
+.SH "DESCRIPTION"
+See \fIstruct nvme_ana_group_desc\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_boot_partition.2 b/doc/man/nvme_get_log_boot_partition.2
new file mode 100644
index 0000000..e4c5d18
--- /dev/null
+++ b/doc/man/nvme_get_log_boot_partition.2
@@ -0,0 +1,25 @@
+.TH "nvme_get_log_boot_partition" 9 "nvme_get_log_boot_partition" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_boot_partition \- Retrieve Boot Partition
+.SH SYNOPSIS
+.B "int" nvme_get_log_boot_partition
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "__u8 lsp " ","
+.BI "__u32 len " ","
+.BI "struct nvme_boot_partition *part " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "lsp" 12
+The log specified field of LID
+.IP "len" 12
+The allocated size, minimum
+struct nvme_boot_partition
+.IP "part" 12
+User address to store the log page
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise
diff --git a/doc/man/nvme_get_log_changed_ns_list.2 b/doc/man/nvme_get_log_changed_ns_list.2
new file mode 100644
index 0000000..055d26d
--- /dev/null
+++ b/doc/man/nvme_get_log_changed_ns_list.2
@@ -0,0 +1,22 @@
+.TH "nvme_get_log_changed_ns_list" 9 "nvme_get_log_changed_ns_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_changed_ns_list \- Retrieve namespace changed list
+.SH SYNOPSIS
+.B "int" nvme_get_log_changed_ns_list
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_ns_list *ns_log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "ns_log" 12
+User address to store the log page
+.SH "DESCRIPTION"
+This log page describes namespaces attached to this controller that have
+changed since the last time the namespace was identified, been added, or
+deleted.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_cmd_effects.2 b/doc/man/nvme_get_log_cmd_effects.2
new file mode 100644
index 0000000..8fbc4f6
--- /dev/null
+++ b/doc/man/nvme_get_log_cmd_effects.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_log_cmd_effects" 9 "nvme_get_log_cmd_effects" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_cmd_effects \- Retrieve nvme command effects log
+.SH SYNOPSIS
+.B "int" nvme_get_log_cmd_effects
+.BI "(int fd " ","
+.BI "enum nvme_csi csi " ","
+.BI "struct nvme_cmd_effects_log *effects_log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "csi" 12
+Command Set Identifier
+.IP "effects_log" 12
+User address to store the effects log
+.SH "DESCRIPTION"
+This log page describes the commands that the controller supports and the
+effects of those commands on the state of the NVM subsystem.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_create_telemetry_host.2 b/doc/man/nvme_get_log_create_telemetry_host.2
new file mode 100644
index 0000000..145de37
--- /dev/null
+++ b/doc/man/nvme_get_log_create_telemetry_host.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_log_create_telemetry_host" 9 "nvme_get_log_create_telemetry_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_create_telemetry_host \- Create host telemetry log
+.SH SYNOPSIS
+.B "int" nvme_get_log_create_telemetry_host
+.BI "(int fd " ","
+.BI "struct nvme_telemetry_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "log" 12
+Userspace address of the log payload
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_device_self_test.2 b/doc/man/nvme_get_log_device_self_test.2
new file mode 100644
index 0000000..7c6dc37
--- /dev/null
+++ b/doc/man/nvme_get_log_device_self_test.2
@@ -0,0 +1,19 @@
+.TH "nvme_get_log_device_self_test" 9 "nvme_get_log_device_self_test" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_device_self_test \- Retrieve the device self test log
+.SH SYNOPSIS
+.B "int" nvme_get_log_device_self_test
+.BI "(int fd " ","
+.BI "struct nvme_self_test_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "log" 12
+Userspace address of the log payload
+.SH "DESCRIPTION"
+The log page indicates the status of an in progress self test and the
+percent complete of that operation, and the results of the previous 20
+self-test operations.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_discovery.2 b/doc/man/nvme_get_log_discovery.2
new file mode 100644
index 0000000..b5c31aa
--- /dev/null
+++ b/doc/man/nvme_get_log_discovery.2
@@ -0,0 +1,27 @@
+.TH "nvme_get_log_discovery" 9 "nvme_get_log_discovery" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_discovery \- Retrieve Discovery log page
+.SH SYNOPSIS
+.B "int" nvme_get_log_discovery
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset of this log to retrieve
+.IP "len" 12
+The allocated size for this portion of the log
+.IP "log" 12
+User address to store the discovery log
+.SH "DESCRIPTION"
+Supported only by fabrics discovery controllers, returning discovery
+records.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_endurance_group.2 b/doc/man/nvme_get_log_endurance_group.2
new file mode 100644
index 0000000..d1e3074
--- /dev/null
+++ b/doc/man/nvme_get_log_endurance_group.2
@@ -0,0 +1,25 @@
+.TH "nvme_get_log_endurance_group" 9 "nvme_get_log_endurance_group" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_endurance_group \- Get Endurance Group log
+.SH SYNOPSIS
+.B "int" nvme_get_log_endurance_group
+.BI "(int fd " ","
+.BI "__u16 endgid " ","
+.BI "struct nvme_endurance_group_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "endgid" 12
+Starting group identifier to return in the list
+.IP "log" 12
+User address to store the endurance log
+.SH "DESCRIPTION"
+This log page indicates if an Endurance Group Event has occurred for a
+particular Endurance Group. If an Endurance Group Event has occurred, the
+details of the particular event are included in the Endurance Group
+Information log page for that Endurance Group. An asynchronous event is
+generated when an entry for an Endurance Group is newly added to this log
+page.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_endurance_grp_evt.2 b/doc/man/nvme_get_log_endurance_grp_evt.2
new file mode 100644
index 0000000..67c1dd1
--- /dev/null
+++ b/doc/man/nvme_get_log_endurance_grp_evt.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_log_endurance_grp_evt" 9 "nvme_get_log_endurance_grp_evt" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_endurance_grp_evt \- Retrieve Rotational Media Information
+.SH SYNOPSIS
+.B "int" nvme_get_log_endurance_grp_evt
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset to the start of the log page
+.IP "len" 12
+The allocated length of the log page
+.IP "log" 12
+User address to store the log page
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_error.2 b/doc/man/nvme_get_log_error.2
new file mode 100644
index 0000000..abd32df
--- /dev/null
+++ b/doc/man/nvme_get_log_error.2
@@ -0,0 +1,25 @@
+.TH "nvme_get_log_error" 9 "nvme_get_log_error" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_error \- Retrieve nvme error log
+.SH SYNOPSIS
+.B "int" nvme_get_log_error
+.BI "(int fd " ","
+.BI "unsigned int nr_entries " ","
+.BI "bool rae " ","
+.BI "struct nvme_error_log_page *err_log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nr_entries" 12
+Number of error log entries allocated
+.IP "rae" 12
+Retain asynchronous events
+.IP "err_log" 12
+Array of error logs of size 'entries'
+.SH "DESCRIPTION"
+This log page describes extended error information for a command that
+completed with error, or may report an error that is not specific to a
+particular command.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_fdp_configurations.2 b/doc/man/nvme_get_log_fdp_configurations.2
new file mode 100644
index 0000000..0b14306
--- /dev/null
+++ b/doc/man/nvme_get_log_fdp_configurations.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_log_fdp_configurations" 9 "nvme_get_log_fdp_configurations" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_fdp_configurations \- Get list of Flexible Data Placement configurations
+.SH SYNOPSIS
+.B "int" nvme_get_log_fdp_configurations
+.BI "(int fd " ","
+.BI "__u16 egid " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "egid" 12
+Endurance group identifier
+.IP "offset" 12
+Offset into log page
+.IP "len" 12
+Length (in bytes) of provided user buffer to hold the log data
+.IP "log" 12
+Log page data buffer
diff --git a/doc/man/nvme_get_log_fdp_events.2 b/doc/man/nvme_get_log_fdp_events.2
new file mode 100644
index 0000000..1d7834d
--- /dev/null
+++ b/doc/man/nvme_get_log_fdp_events.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_log_fdp_events" 9 "nvme_get_log_fdp_events" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_fdp_events \- Get Flexible Data Placement events
+.SH SYNOPSIS
+.B "int" nvme_get_log_fdp_events
+.BI "(int fd " ","
+.BI "__u16 egid " ","
+.BI "bool host_events " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "egid" 12
+Endurance group identifier
+.IP "host_events" 12
+Whether to report host or controller events
+.IP "offset" 12
+Offset into log page
+.IP "len" 12
+Length (in bytes) of provided user buffer to hold the log data
+.IP "log" 12
+Log page data buffer
diff --git a/doc/man/nvme_get_log_fdp_stats.2 b/doc/man/nvme_get_log_fdp_stats.2
new file mode 100644
index 0000000..7dd19da
--- /dev/null
+++ b/doc/man/nvme_get_log_fdp_stats.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_log_fdp_stats" 9 "nvme_get_log_fdp_stats" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_fdp_stats \- Get Flexible Data Placement statistics
+.SH SYNOPSIS
+.B "int" nvme_get_log_fdp_stats
+.BI "(int fd " ","
+.BI "__u16 egid " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "egid" 12
+Endurance group identifier
+.IP "offset" 12
+Offset into log page
+.IP "len" 12
+Length (in bytes) of provided user buffer to hold the log data
+.IP "log" 12
+Log page data buffer
diff --git a/doc/man/nvme_get_log_fid_supported_effects.2 b/doc/man/nvme_get_log_fid_supported_effects.2
new file mode 100644
index 0000000..300ffff
--- /dev/null
+++ b/doc/man/nvme_get_log_fid_supported_effects.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_log_fid_supported_effects" 9 "nvme_get_log_fid_supported_effects" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_fid_supported_effects \- Retrieve Feature Identifiers Supported and Effects
+.SH SYNOPSIS
+.B "int" nvme_get_log_fid_supported_effects
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_fid_supported_effects_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+FID Supported and Effects data structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise
diff --git a/doc/man/nvme_get_log_fw_slot.2 b/doc/man/nvme_get_log_fw_slot.2
new file mode 100644
index 0000000..3e88526
--- /dev/null
+++ b/doc/man/nvme_get_log_fw_slot.2
@@ -0,0 +1,22 @@
+.TH "nvme_get_log_fw_slot" 9 "nvme_get_log_fw_slot" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_fw_slot \- Retrieves the controller firmware log
+.SH SYNOPSIS
+.B "int" nvme_get_log_fw_slot
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_firmware_slot *fw_log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "fw_log" 12
+User address to store the log page
+.SH "DESCRIPTION"
+This log page describes the firmware revision stored in each firmware slot
+supported. The firmware revision is indicated as an ASCII string. The log
+page also indicates the active slot number.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_lba_status.2 b/doc/man/nvme_get_log_lba_status.2
new file mode 100644
index 0000000..32660c6
--- /dev/null
+++ b/doc/man/nvme_get_log_lba_status.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_log_lba_status" 9 "nvme_get_log_lba_status" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_lba_status \- Retrieve LBA Status
+.SH SYNOPSIS
+.B "int" nvme_get_log_lba_status
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "__u64 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset to the start of the log page
+.IP "len" 12
+The allocated length of the log page
+.IP "log" 12
+User address to store the log page
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_media_unit_stat.2 b/doc/man/nvme_get_log_media_unit_stat.2
new file mode 100644
index 0000000..4c87a68
--- /dev/null
+++ b/doc/man/nvme_get_log_media_unit_stat.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_log_media_unit_stat" 9 "nvme_get_log_media_unit_stat" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_media_unit_stat \- Retrieve Media Unit Status
+.SH SYNOPSIS
+.B "int" nvme_get_log_media_unit_stat
+.BI "(int fd " ","
+.BI "__u16 domid " ","
+.BI "struct nvme_media_unit_stat_log *mus " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "domid" 12
+Domain Identifier selection, if supported
+.IP "mus" 12
+User address to store the Media Unit statistics log
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise
diff --git a/doc/man/nvme_get_log_mi_cmd_supported_effects.2 b/doc/man/nvme_get_log_mi_cmd_supported_effects.2
new file mode 100644
index 0000000..384d9df
--- /dev/null
+++ b/doc/man/nvme_get_log_mi_cmd_supported_effects.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_log_mi_cmd_supported_effects" 9 "nvme_get_log_mi_cmd_supported_effects" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_mi_cmd_supported_effects \- displays the MI Commands Supported by the controller
+.SH SYNOPSIS
+.B "int" nvme_get_log_mi_cmd_supported_effects
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_mi_cmd_supported_effects_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+MI Command Supported and Effects data structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise
diff --git a/doc/man/nvme_get_log_page.2 b/doc/man/nvme_get_log_page.2
new file mode 100644
index 0000000..e3b16a0
--- /dev/null
+++ b/doc/man/nvme_get_log_page.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_log_page" 9 "nvme_get_log_page" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_page \- Get log page data
+.SH SYNOPSIS
+.B "int" nvme_get_log_page
+.BI "(int fd " ","
+.BI "__u32 xfer_len " ","
+.BI "struct nvme_get_log_args *args " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "xfer_len" 12
+Max log transfer size per request to split the total.
+.IP "args" 12
+\fIstruct nvme_get_log_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_persistent_event.2 b/doc/man/nvme_get_log_persistent_event.2
new file mode 100644
index 0000000..4045b53
--- /dev/null
+++ b/doc/man/nvme_get_log_persistent_event.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_log_persistent_event" 9 "nvme_get_log_persistent_event" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_persistent_event \- Retrieve Persistent Event Log
+.SH SYNOPSIS
+.B "int" nvme_get_log_persistent_event
+.BI "(int fd " ","
+.BI "enum nvme_pevent_log_action action " ","
+.BI "__u32 size " ","
+.BI "void *pevent_log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "action" 12
+Action the controller should take during processing this command
+.IP "size" 12
+Size of \fIpevent_log\fP
+.IP "pevent_log" 12
+User address to store the persistent event log
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_phy_rx_eom.2 b/doc/man/nvme_get_log_phy_rx_eom.2
new file mode 100644
index 0000000..845953a
--- /dev/null
+++ b/doc/man/nvme_get_log_phy_rx_eom.2
@@ -0,0 +1,25 @@
+.TH "nvme_get_log_phy_rx_eom" 9 "nvme_get_log_phy_rx_eom" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_phy_rx_eom \- Retrieve Physical Interface Receiver Eye Opening Measurement Log
+.SH SYNOPSIS
+.B "int" nvme_get_log_phy_rx_eom
+.BI "(int fd " ","
+.BI "__u8 lsp " ","
+.BI "__u16 controller " ","
+.BI "__u32 len " ","
+.BI "struct nvme_phy_rx_eom_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "lsp" 12
+Log specific, controls action and measurement quality
+.IP "controller" 12
+Target controller ID
+.IP "len" 12
+The allocated size, minimum
+struct nvme_phy_rx_eom_log
+.IP "log" 12
+User address to store the log page
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise
diff --git a/doc/man/nvme_get_log_predictable_lat_event.2 b/doc/man/nvme_get_log_predictable_lat_event.2
new file mode 100644
index 0000000..e75dc27
--- /dev/null
+++ b/doc/man/nvme_get_log_predictable_lat_event.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_log_predictable_lat_event" 9 "nvme_get_log_predictable_lat_event" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_predictable_lat_event \- Retrieve Predictable Latency Event Aggregate Log Page
+.SH SYNOPSIS
+.B "int" nvme_get_log_predictable_lat_event
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset into the predictable latency event
+.IP "len" 12
+Length of provided user buffer to hold the log data in bytes
+.IP "log" 12
+User address for log page data
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_predictable_lat_nvmset.2 b/doc/man/nvme_get_log_predictable_lat_nvmset.2
new file mode 100644
index 0000000..8ec698e
--- /dev/null
+++ b/doc/man/nvme_get_log_predictable_lat_nvmset.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_log_predictable_lat_nvmset" 9 "nvme_get_log_predictable_lat_nvmset" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_predictable_lat_nvmset \- Predictable Latency Per NVM Set
+.SH SYNOPSIS
+.B "int" nvme_get_log_predictable_lat_nvmset
+.BI "(int fd " ","
+.BI "__u16 nvmsetid " ","
+.BI "struct nvme_nvmset_predictable_lat_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nvmsetid" 12
+NVM set id
+.IP "log" 12
+User address to store the predictable latency log
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_reclaim_unit_handle_usage.2 b/doc/man/nvme_get_log_reclaim_unit_handle_usage.2
new file mode 100644
index 0000000..1d5f274
--- /dev/null
+++ b/doc/man/nvme_get_log_reclaim_unit_handle_usage.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_log_reclaim_unit_handle_usage" 9 "nvme_get_log_reclaim_unit_handle_usage" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_reclaim_unit_handle_usage \- Get reclaim unit handle usage
+.SH SYNOPSIS
+.B "int" nvme_get_log_reclaim_unit_handle_usage
+.BI "(int fd " ","
+.BI "__u16 egid " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "egid" 12
+Endurance group identifier
+.IP "offset" 12
+Offset into log page
+.IP "len" 12
+Length (in bytes) of provided user buffer to hold the log data
+.IP "log" 12
+Log page data buffer
diff --git a/doc/man/nvme_get_log_reservation.2 b/doc/man/nvme_get_log_reservation.2
new file mode 100644
index 0000000..85eeccc
--- /dev/null
+++ b/doc/man/nvme_get_log_reservation.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_log_reservation" 9 "nvme_get_log_reservation" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_reservation \- Retrieve Reservation Notification
+.SH SYNOPSIS
+.B "int" nvme_get_log_reservation
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_resv_notification_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+User address to store the reservation log
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise
diff --git a/doc/man/nvme_get_log_sanitize.2 b/doc/man/nvme_get_log_sanitize.2
new file mode 100644
index 0000000..261bf1b
--- /dev/null
+++ b/doc/man/nvme_get_log_sanitize.2
@@ -0,0 +1,21 @@
+.TH "nvme_get_log_sanitize" 9 "nvme_get_log_sanitize" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_sanitize \- Retrieve Sanitize Status
+.SH SYNOPSIS
+.B "int" nvme_get_log_sanitize
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_sanitize_log_page *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+User address to store the sanitize log
+.SH "DESCRIPTION"
+The Sanitize Status log page reports sanitize operation time estimates and
+information about the most recent sanitize operation.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_smart.2 b/doc/man/nvme_get_log_smart.2
new file mode 100644
index 0000000..578db37
--- /dev/null
+++ b/doc/man/nvme_get_log_smart.2
@@ -0,0 +1,28 @@
+.TH "nvme_get_log_smart" 9 "nvme_get_log_smart" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_smart \- Retrieve nvme smart log
+.SH SYNOPSIS
+.B "int" nvme_get_log_smart
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "bool rae " ","
+.BI "struct nvme_smart_log *smart_log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Optional namespace identifier
+.IP "rae" 12
+Retain asynchronous events
+.IP "smart_log" 12
+User address to store the smart log
+.SH "DESCRIPTION"
+This log page provides SMART and general health information. The information
+provided is over the life of the controller and is retained across power
+cycles. To request the controller log page, the namespace identifier
+specified is FFFFFFFFh. The controller may also support requesting the log
+page on a per namespace basis, as indicated by bit 0 of the LPA field in the
+Identify Controller data structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_support_cap_config_list.2 b/doc/man/nvme_get_log_support_cap_config_list.2
new file mode 100644
index 0000000..662e004
--- /dev/null
+++ b/doc/man/nvme_get_log_support_cap_config_list.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_log_support_cap_config_list" 9 "nvme_get_log_support_cap_config_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_support_cap_config_list \- Retrieve Supported Capacity Configuration List
+.SH SYNOPSIS
+.B "int" nvme_get_log_support_cap_config_list
+.BI "(int fd " ","
+.BI "__u16 domid " ","
+.BI "struct nvme_supported_cap_config_list_log *cap " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "domid" 12
+Domain Identifier selection, if supported
+.IP "cap" 12
+User address to store supported capabilities config list
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise
diff --git a/doc/man/nvme_get_log_supported_log_pages.2 b/doc/man/nvme_get_log_supported_log_pages.2
new file mode 100644
index 0000000..4e2fdb6
--- /dev/null
+++ b/doc/man/nvme_get_log_supported_log_pages.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_log_supported_log_pages" 9 "nvme_get_log_supported_log_pages" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_supported_log_pages \- Retrieve nmve supported log pages
+.SH SYNOPSIS
+.B "int" nvme_get_log_supported_log_pages
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "struct nvme_supported_log_pages *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+Array of LID supported and Effects data structures
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_telemetry_ctrl.2 b/doc/man/nvme_get_log_telemetry_ctrl.2
new file mode 100644
index 0000000..466ec9a
--- /dev/null
+++ b/doc/man/nvme_get_log_telemetry_ctrl.2
@@ -0,0 +1,27 @@
+.TH "nvme_get_log_telemetry_ctrl" 9 "nvme_get_log_telemetry_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_telemetry_ctrl \- Get Telemetry Controller-Initiated log page
+.SH SYNOPSIS
+.B "int" nvme_get_log_telemetry_ctrl
+.BI "(int fd " ","
+.BI "bool rae " ","
+.BI "__u64 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset into the telemetry data
+.IP "len" 12
+Length of provided user buffer to hold the log data in bytes
+.IP "log" 12
+User address for log page data
+.SH "DESCRIPTION"
+Retrieves the Telemetry Controller-Initiated log page at the requested offset
+using the previously existing capture.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_telemetry_host.2 b/doc/man/nvme_get_log_telemetry_host.2
new file mode 100644
index 0000000..8b49a17
--- /dev/null
+++ b/doc/man/nvme_get_log_telemetry_host.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_log_telemetry_host" 9 "nvme_get_log_telemetry_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_telemetry_host \- Get Telemetry Host-Initiated log page
+.SH SYNOPSIS
+.B "int" nvme_get_log_telemetry_host
+.BI "(int fd " ","
+.BI "__u64 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "offset" 12
+Offset into the telemetry data
+.IP "len" 12
+Length of provided user buffer to hold the log data in bytes
+.IP "log" 12
+User address for log page data
+.SH "DESCRIPTION"
+Retrieves the Telemetry Host-Initiated log page at the requested offset
+using the previously existing capture.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_log_zns_changed_zones.2 b/doc/man/nvme_get_log_zns_changed_zones.2
new file mode 100644
index 0000000..f98b6a4
--- /dev/null
+++ b/doc/man/nvme_get_log_zns_changed_zones.2
@@ -0,0 +1,23 @@
+.TH "nvme_get_log_zns_changed_zones" 9 "nvme_get_log_zns_changed_zones" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_log_zns_changed_zones \- Retrieve list of zones that have changed
+.SH SYNOPSIS
+.B "int" nvme_get_log_zns_changed_zones
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "bool rae " ","
+.BI "struct nvme_zns_changed_zone_log *log " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+User address to store the changed zone log
+.SH "DESCRIPTION"
+The list of zones that have changed state due to an exceptional event.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_logical_block_size.2 b/doc/man/nvme_get_logical_block_size.2
new file mode 100644
index 0000000..05992a5
--- /dev/null
+++ b/doc/man/nvme_get_logical_block_size.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_logical_block_size" 9 "nvme_get_logical_block_size" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_logical_block_size \- Retrieve block size
+.SH SYNOPSIS
+.B "int" nvme_get_logical_block_size
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "int *blksize " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace id
+.IP "blksize" 12
+Pointer to where the block size will be set on success
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_new_host_telemetry.2 b/doc/man/nvme_get_new_host_telemetry.2
new file mode 100644
index 0000000..e608b58
--- /dev/null
+++ b/doc/man/nvme_get_new_host_telemetry.2
@@ -0,0 +1,24 @@
+.TH "nvme_get_new_host_telemetry" 9 "nvme_get_new_host_telemetry" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_new_host_telemetry \- Get new host telemetry log
+.SH SYNOPSIS
+.B "int" nvme_get_new_host_telemetry
+.BI "(int fd " ","
+.BI "struct nvme_telemetry_log **log " ","
+.BI "enum nvme_telemetry_da da " ","
+.BI "size_t *size " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "log" 12
+On success, set to the value of the allocated and retrieved log.
+.IP "da" 12
+Log page data area, valid values: \fIenum nvme_telemetry_da\fP
+.IP "size" 12
+Ptr to the telemetry log size, so it can be returned
+.SH "DESCRIPTION"
+The total size allocated can be calculated as:
+(nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_ns_attr.2 b/doc/man/nvme_get_ns_attr.2
new file mode 100644
index 0000000..64a55ea
--- /dev/null
+++ b/doc/man/nvme_get_ns_attr.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_ns_attr" 9 "nvme_get_ns_attr" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_ns_attr \- Read namespace sysfs attribute
+.SH SYNOPSIS
+.B "char *" nvme_get_ns_attr
+.BI "(nvme_ns_t n " ","
+.BI "const char *attr " ");"
+.SH ARGUMENTS
+.IP "n" 12
+nvme_ns_t object
+.IP "attr" 12
+sysfs attribute name
+.SH "RETURN"
+String with the contents of \fIattr\fP or NULL in case of an empty value
+or in case of an error (indicated by non-zero errno code).
diff --git a/doc/man/nvme_get_nsid.2 b/doc/man/nvme_get_nsid.2
new file mode 100644
index 0000000..8b74dfe
--- /dev/null
+++ b/doc/man/nvme_get_nsid.2
@@ -0,0 +1,19 @@
+.TH "nvme_get_nsid" 9 "nvme_get_nsid" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_nsid \- Retrieve the NSID from a namespace file descriptor
+.SH SYNOPSIS
+.B "int" nvme_get_nsid
+.BI "(int fd " ","
+.BI "__u32 *nsid " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme namespace
+.IP "nsid" 12
+User pointer to namespace id
+.SH "DESCRIPTION"
+This should only be sent to namespace handles, not to controllers. The
+kernel's interface returns the nsid as the return value. This is unfortunate
+for many architectures that are incapable of allowing distinguishing a
+namespace id > 0x80000000 from a negative error number.
+.SH "RETURN"
+0 if \fInsid\fP was set successfully or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_path_attr.2 b/doc/man/nvme_get_path_attr.2
new file mode 100644
index 0000000..0ca6ba9
--- /dev/null
+++ b/doc/man/nvme_get_path_attr.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_path_attr" 9 "nvme_get_path_attr" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_path_attr \- Read path sysfs attribute
+.SH SYNOPSIS
+.B "char *" nvme_get_path_attr
+.BI "(nvme_path_t p " ","
+.BI "const char *attr " ");"
+.SH ARGUMENTS
+.IP "p" 12
+nvme_path_t object
+.IP "attr" 12
+sysfs attribute name
+.SH "RETURN"
+String with the contents of \fIattr\fP or NULL in case of an empty value
+or in case of an error (indicated by non-zero errno code).
diff --git a/doc/man/nvme_get_property.2 b/doc/man/nvme_get_property.2
new file mode 100644
index 0000000..9683673
--- /dev/null
+++ b/doc/man/nvme_get_property.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_property" 9 "nvme_get_property" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_property \- Get a controller property
+.SH SYNOPSIS
+.B "int" nvme_get_property
+.BI "(struct nvme_get_property_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_get_propert_args\fP argument structure
+.SH "DESCRIPTION"
+This is an NVMe-over-Fabrics specific command, not applicable to PCIe. These
+properties align to the PCI MMIO controller registers.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_subsys_attr.2 b/doc/man/nvme_get_subsys_attr.2
new file mode 100644
index 0000000..592eb93
--- /dev/null
+++ b/doc/man/nvme_get_subsys_attr.2
@@ -0,0 +1,15 @@
+.TH "nvme_get_subsys_attr" 9 "nvme_get_subsys_attr" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_subsys_attr \- Read subsystem sysfs attribute
+.SH SYNOPSIS
+.B "char *" nvme_get_subsys_attr
+.BI "(nvme_subsystem_t s " ","
+.BI "const char *attr " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.IP "attr" 12
+sysfs attribute name
+.SH "RETURN"
+String with the contents of \fIattr\fP or NULL in case of an empty value
+or in case of an error (indicated by non-zero errno code).
diff --git a/doc/man/nvme_get_telemetry_log.2 b/doc/man/nvme_get_telemetry_log.2
new file mode 100644
index 0000000..4351c55
--- /dev/null
+++ b/doc/man/nvme_get_telemetry_log.2
@@ -0,0 +1,36 @@
+.TH "nvme_get_telemetry_log" 9 "nvme_get_telemetry_log" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_telemetry_log \- Get specified telemetry log
+.SH SYNOPSIS
+.B "int" nvme_get_telemetry_log
+.BI "(int fd " ","
+.BI "bool create " ","
+.BI "bool ctrl " ","
+.BI "bool rae " ","
+.BI "size_t max_data_tx " ","
+.BI "enum nvme_telemetry_da da " ","
+.BI "struct nvme_telemetry_log **log " ","
+.BI "size_t *size " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "create" 12
+Generate new host initated telemetry capture
+.IP "ctrl" 12
+Get controller Initiated log
+.IP "rae" 12
+Retain asynchronous events
+.IP "max_data_tx" 12
+Set the max data transfer size to be used retrieving telemetry.
+.IP "da" 12
+Log page data area, valid values: \fIenum nvme_telemetry_da\fP.
+.IP "log" 12
+On success, set to the value of the allocated and retrieved log.
+.IP "size" 12
+Ptr to the telemetry log size, so it can be returned
+.SH "DESCRIPTION"
+The total size allocated can be calculated as:
+(nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_get_telemetry_max.2 b/doc/man/nvme_get_telemetry_max.2
new file mode 100644
index 0000000..d2b61cf
--- /dev/null
+++ b/doc/man/nvme_get_telemetry_max.2
@@ -0,0 +1,18 @@
+.TH "nvme_get_telemetry_max" 9 "nvme_get_telemetry_max" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_get_telemetry_max \- Get telemetry limits
+.SH SYNOPSIS
+.B "int" nvme_get_telemetry_max
+.BI "(int fd " ","
+.BI "enum nvme_telemetry_da *da " ","
+.BI "size_t *max_data_tx " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "da" 12
+On success return max supported data area
+.IP "max_data_tx" 12
+On success set to max transfer chunk supported by the controller
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_hmac_alg.2 b/doc/man/nvme_hmac_alg.2
new file mode 100644
index 0000000..af8f783
--- /dev/null
+++ b/doc/man/nvme_hmac_alg.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_hmac_alg" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_hmac_alg \- HMAC algorithm
+.SH SYNOPSIS
+enum nvme_hmac_alg {
+.br
+.BI " NVME_HMAC_ALG_NONE"
+,
+.br
+.br
+.BI " NVME_HMAC_ALG_SHA2_256"
+,
+.br
+.br
+.BI " NVME_HMAC_ALG_SHA2_384"
+,
+.br
+.br
+.BI " NVME_HMAC_ALG_SHA2_512"
+
+};
+.SH Constants
+.IP "NVME_HMAC_ALG_NONE" 12
+No HMAC algorithm
+.IP "NVME_HMAC_ALG_SHA2_256" 12
+SHA2-256
+.IP "NVME_HMAC_ALG_SHA2_384" 12
+SHA2-384
+.IP "NVME_HMAC_ALG_SHA2_512" 12
+SHA2-512
diff --git a/doc/man/nvme_host_behavior_support.2 b/doc/man/nvme_host_behavior_support.2
new file mode 100644
index 0000000..f13a90c
--- /dev/null
+++ b/doc/man/nvme_host_behavior_support.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nvme_host_behavior_support" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_host_behavior_support \- Enable Advanced Command
+.SH SYNOPSIS
+enum nvme_host_behavior_support {
+.br
+.BI " NVME_ENABLE_ACRE"
+
+};
+.SH Constants
+.IP "NVME_ENABLE_ACRE" 12
+Enable Advanced Command Retry Enable
diff --git a/doc/man/nvme_host_get_dhchap_key.2 b/doc/man/nvme_host_get_dhchap_key.2
new file mode 100644
index 0000000..59ec840
--- /dev/null
+++ b/doc/man/nvme_host_get_dhchap_key.2
@@ -0,0 +1,11 @@
+.TH "nvme_host_get_dhchap_key" 9 "nvme_host_get_dhchap_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_get_dhchap_key \- Return host key
+.SH SYNOPSIS
+.B "const char *" nvme_host_get_dhchap_key
+.BI "(nvme_host_t h " ");"
+.SH ARGUMENTS
+.IP "h" 12
+Host for which the key should be returned
+.SH "RETURN"
+DH-HMAC-CHAP host key or NULL if not set
diff --git a/doc/man/nvme_host_get_hostid.2 b/doc/man/nvme_host_get_hostid.2
new file mode 100644
index 0000000..94d3e72
--- /dev/null
+++ b/doc/man/nvme_host_get_hostid.2
@@ -0,0 +1,11 @@
+.TH "nvme_host_get_hostid" 9 "nvme_host_get_hostid" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_get_hostid \- Host ID of an nvme_host_t object
+.SH SYNOPSIS
+.B "const char *" nvme_host_get_hostid
+.BI "(nvme_host_t h " ");"
+.SH ARGUMENTS
+.IP "h" 12
+nvme_host_t object
+.SH "RETURN"
+Host ID of \fIh\fP
diff --git a/doc/man/nvme_host_get_hostnqn.2 b/doc/man/nvme_host_get_hostnqn.2
new file mode 100644
index 0000000..5b51b79
--- /dev/null
+++ b/doc/man/nvme_host_get_hostnqn.2
@@ -0,0 +1,11 @@
+.TH "nvme_host_get_hostnqn" 9 "nvme_host_get_hostnqn" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_get_hostnqn \- Host NQN of an nvme_host_t object
+.SH SYNOPSIS
+.B "const char *" nvme_host_get_hostnqn
+.BI "(nvme_host_t h " ");"
+.SH ARGUMENTS
+.IP "h" 12
+nvme_host_t object
+.SH "RETURN"
+Host NQN of \fIh\fP
diff --git a/doc/man/nvme_host_get_hostsymname.2 b/doc/man/nvme_host_get_hostsymname.2
new file mode 100644
index 0000000..39c8d3d
--- /dev/null
+++ b/doc/man/nvme_host_get_hostsymname.2
@@ -0,0 +1,12 @@
+.TH "nvme_host_get_hostsymname" 9 "nvme_host_get_hostsymname" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_get_hostsymname \- Get the host's symbolic name
+.SH SYNOPSIS
+.B "const char *" nvme_host_get_hostsymname
+.BI "(nvme_host_t h " ");"
+.SH ARGUMENTS
+.IP "h" 12
+Host for which the symbolic name should be returned.
+.SH "RETURN"
+The symbolic name or NULL if a symbolic name hasn't been
+configure.
diff --git a/doc/man/nvme_host_get_root.2 b/doc/man/nvme_host_get_root.2
new file mode 100644
index 0000000..fb929d6
--- /dev/null
+++ b/doc/man/nvme_host_get_root.2
@@ -0,0 +1,11 @@
+.TH "nvme_host_get_root" 9 "nvme_host_get_root" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_get_root \- Returns nvme_root_t object
+.SH SYNOPSIS
+.B "nvme_root_t" nvme_host_get_root
+.BI "(nvme_host_t h " ");"
+.SH ARGUMENTS
+.IP "h" 12
+\fInvme_host_t\fP object
+.SH "RETURN"
+\fInvme_root_t\fP object from \fIh\fP
diff --git a/doc/man/nvme_host_is_pdc_enabled.2 b/doc/man/nvme_host_is_pdc_enabled.2
new file mode 100644
index 0000000..406dca4
--- /dev/null
+++ b/doc/man/nvme_host_is_pdc_enabled.2
@@ -0,0 +1,16 @@
+.TH "nvme_host_is_pdc_enabled" 9 "nvme_host_is_pdc_enabled" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_is_pdc_enabled \- Is Persistenct Discovery Controller enabled
+.SH SYNOPSIS
+.B "bool" nvme_host_is_pdc_enabled
+.BI "(nvme_host_t h " ","
+.BI "bool fallback " ");"
+.SH ARGUMENTS
+.IP "h" 12
+Host which to check if PDC is enabled
+.IP "fallback" 12
+The fallback default value of the flag when
+\fInvme_host_set_pdc_enabled\fP has not be used
+to set the flag.
+.SH "RETURN"
+true if PDC is enabled for \fIh\fP, else false
diff --git a/doc/man/nvme_host_mem_buf_attrs.2 b/doc/man/nvme_host_mem_buf_attrs.2
new file mode 100644
index 0000000..b4a99f4
--- /dev/null
+++ b/doc/man/nvme_host_mem_buf_attrs.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_host_mem_buf_attrs" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_host_mem_buf_attrs \- Host Memory Buffer - Attributes Data Structure
+.SH SYNOPSIS
+struct nvme_host_mem_buf_attrs {
+.br
+.BI " __le32 hsize;"
+.br
+.BI " __le32 hmdlal;"
+.br
+.BI " __le32 hmdlau;"
+.br
+.BI " __le32 hmdlec;"
+.br
+.BI " __u8 rsvd16[4080];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "hsize" 12
+Host Memory Buffer Size
+.IP "hmdlal" 12
+Host Memory Descriptor List Lower Address
+.IP "hmdlau" 12
+Host Memory Descriptor List Upper Address
+.IP "hmdlec" 12
+Host Memory Descriptor List Entry Count
+.IP "rsvd16" 12
+Reserved
diff --git a/doc/man/nvme_host_metadata.2 b/doc/man/nvme_host_metadata.2
new file mode 100644
index 0000000..662811a
--- /dev/null
+++ b/doc/man/nvme_host_metadata.2
@@ -0,0 +1,33 @@
+.TH "libnvme" 9 "struct nvme_host_metadata" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_host_metadata \- Host Metadata Data Structure
+.SH SYNOPSIS
+struct nvme_host_metadata {
+.br
+.BI " __u8 ndesc;"
+.br
+.BI " __u8 rsvd1;"
+.br
+.BI " union {"
+.br
+.BI " struct nvme_metadata_element_desc descs[0];"
+.br
+.BI " __u8 descs_buf[4094];"
+.br
+.BI " };"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "ndesc" 12
+Number of metadata element descriptors
+.IP "rsvd1" 12
+Reserved
+.IP "{unnamed_union}" 12
+anonymous
+.IP "descs" 12
+Metadata element descriptors
+.IP "descs_buf" 12
+Metadata element descriptor buffer
diff --git a/doc/man/nvme_host_release_fds.2 b/doc/man/nvme_host_release_fds.2
new file mode 100644
index 0000000..02772a9
--- /dev/null
+++ b/doc/man/nvme_host_release_fds.2
@@ -0,0 +1,13 @@
+.TH "nvme_host_release_fds" 9 "nvme_host_release_fds" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_release_fds \- Close all opened file descriptors under host
+.SH SYNOPSIS
+.B "void" nvme_host_release_fds
+.BI "(struct nvme_host *h " ");"
+.SH ARGUMENTS
+.IP "h" 12
+nvme_host_t object
+.SH "DESCRIPTION"
+Controller and Namespace objects cache the file descriptors
+of opened nvme devices. This API can be used to close and
+clear all cached fds under this host.
diff --git a/doc/man/nvme_host_set_dhchap_key.2 b/doc/man/nvme_host_set_dhchap_key.2
new file mode 100644
index 0000000..f6e3018
--- /dev/null
+++ b/doc/man/nvme_host_set_dhchap_key.2
@@ -0,0 +1,12 @@
+.TH "nvme_host_set_dhchap_key" 9 "nvme_host_set_dhchap_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_set_dhchap_key \- set host key
+.SH SYNOPSIS
+.B "void" nvme_host_set_dhchap_key
+.BI "(nvme_host_t h " ","
+.BI "const char *key " ");"
+.SH ARGUMENTS
+.IP "h" 12
+Host for which the key should be set
+.IP "key" 12
+DH-HMAC-CHAP Key to set or NULL to clear existing key
diff --git a/doc/man/nvme_host_set_hostsymname.2 b/doc/man/nvme_host_set_hostsymname.2
new file mode 100644
index 0000000..4fb979e
--- /dev/null
+++ b/doc/man/nvme_host_set_hostsymname.2
@@ -0,0 +1,12 @@
+.TH "nvme_host_set_hostsymname" 9 "nvme_host_set_hostsymname" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_set_hostsymname \- Set the host's symbolic name
+.SH SYNOPSIS
+.B "void" nvme_host_set_hostsymname
+.BI "(nvme_host_t h " ","
+.BI "const char *hostsymname " ");"
+.SH ARGUMENTS
+.IP "h" 12
+Host for which the symbolic name should be set.
+.IP "hostsymname" 12
+Symbolic name
diff --git a/doc/man/nvme_host_set_pdc_enabled.2 b/doc/man/nvme_host_set_pdc_enabled.2
new file mode 100644
index 0000000..43fce53
--- /dev/null
+++ b/doc/man/nvme_host_set_pdc_enabled.2
@@ -0,0 +1,16 @@
+.TH "nvme_host_set_pdc_enabled" 9 "nvme_host_set_pdc_enabled" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_host_set_pdc_enabled \- Set Persistent Discovery Controller flag
+.SH SYNOPSIS
+.B "void" nvme_host_set_pdc_enabled
+.BI "(nvme_host_t h " ","
+.BI "bool enabled " ");"
+.SH ARGUMENTS
+.IP "h" 12
+Host for which the falg should be set
+.IP "enabled" 12
+The bool to set the enabled flag
+.SH "DESCRIPTION"
+When \fBnvme_host_set_pdc_enabled\fP is not used to set the PDC flag,
+\fBnvme_host_is_pdc_enabled\fP will return the default value which was
+passed into the function and not the undefined flag value.
diff --git a/doc/man/nvme_id_ctrl.2 b/doc/man/nvme_id_ctrl.2
new file mode 100644
index 0000000..c627f87
--- /dev/null
+++ b/doc/man/nvme_id_ctrl.2
@@ -0,0 +1,511 @@
+.TH "libnvme" 9 "struct nvme_id_ctrl" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_ctrl \- Identify Controller data structure
+.SH SYNOPSIS
+struct nvme_id_ctrl {
+.br
+.BI " __le16 vid;"
+.br
+.BI " __le16 ssvid;"
+.br
+.BI " char sn[20];"
+.br
+.BI " char mn[40];"
+.br
+.BI " char fr[8];"
+.br
+.BI " __u8 rab;"
+.br
+.BI " __u8 ieee[3];"
+.br
+.BI " __u8 cmic;"
+.br
+.BI " __u8 mdts;"
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " __le32 ver;"
+.br
+.BI " __le32 rtd3r;"
+.br
+.BI " __le32 rtd3e;"
+.br
+.BI " __le32 oaes;"
+.br
+.BI " __le32 ctratt;"
+.br
+.BI " __le16 rrls;"
+.br
+.BI " __u8 rsvd102[9];"
+.br
+.BI " __u8 cntrltype;"
+.br
+.BI " __u8 fguid[16];"
+.br
+.BI " __le16 crdt1;"
+.br
+.BI " __le16 crdt2;"
+.br
+.BI " __le16 crdt3;"
+.br
+.BI " __u8 rsvd134[119];"
+.br
+.BI " __u8 nvmsr;"
+.br
+.BI " __u8 vwci;"
+.br
+.BI " __u8 mec;"
+.br
+.BI " __le16 oacs;"
+.br
+.BI " __u8 acl;"
+.br
+.BI " __u8 aerl;"
+.br
+.BI " __u8 frmw;"
+.br
+.BI " __u8 lpa;"
+.br
+.BI " __u8 elpe;"
+.br
+.BI " __u8 npss;"
+.br
+.BI " __u8 avscc;"
+.br
+.BI " __u8 apsta;"
+.br
+.BI " __le16 wctemp;"
+.br
+.BI " __le16 cctemp;"
+.br
+.BI " __le16 mtfa;"
+.br
+.BI " __le32 hmpre;"
+.br
+.BI " __le32 hmmin;"
+.br
+.BI " __u8 tnvmcap[16];"
+.br
+.BI " __u8 unvmcap[16];"
+.br
+.BI " __le32 rpmbs;"
+.br
+.BI " __le16 edstt;"
+.br
+.BI " __u8 dsto;"
+.br
+.BI " __u8 fwug;"
+.br
+.BI " __le16 kas;"
+.br
+.BI " __le16 hctma;"
+.br
+.BI " __le16 mntmt;"
+.br
+.BI " __le16 mxtmt;"
+.br
+.BI " __le32 sanicap;"
+.br
+.BI " __le32 hmminds;"
+.br
+.BI " __le16 hmmaxd;"
+.br
+.BI " __le16 nsetidmax;"
+.br
+.BI " __le16 endgidmax;"
+.br
+.BI " __u8 anatt;"
+.br
+.BI " __u8 anacap;"
+.br
+.BI " __le32 anagrpmax;"
+.br
+.BI " __le32 nanagrpid;"
+.br
+.BI " __le32 pels;"
+.br
+.BI " __le16 domainid;"
+.br
+.BI " __u8 rsvd358[10];"
+.br
+.BI " __u8 megcap[16];"
+.br
+.BI " __u8 rsvd384[128];"
+.br
+.BI " __u8 sqes;"
+.br
+.BI " __u8 cqes;"
+.br
+.BI " __le16 maxcmd;"
+.br
+.BI " __le32 nn;"
+.br
+.BI " __le16 oncs;"
+.br
+.BI " __le16 fuses;"
+.br
+.BI " __u8 fna;"
+.br
+.BI " __u8 vwc;"
+.br
+.BI " __le16 awun;"
+.br
+.BI " __le16 awupf;"
+.br
+.BI " __u8 icsvscc;"
+.br
+.BI " __u8 nwpc;"
+.br
+.BI " __le16 acwu;"
+.br
+.BI " __le16 ocfs;"
+.br
+.BI " __le32 sgls;"
+.br
+.BI " __le32 mnan;"
+.br
+.BI " __u8 maxdna[16];"
+.br
+.BI " __le32 maxcna;"
+.br
+.BI " __le32 oaqd;"
+.br
+.BI " __u8 rsvd568[200];"
+.br
+.BI " char subnqn[NVME_NQN_LENGTH];"
+.br
+.BI " __u8 rsvd1024[768];"
+.br
+.BI " __le32 ioccsz;"
+.br
+.BI " __le32 iorcsz;"
+.br
+.BI " __le16 icdoff;"
+.br
+.BI " __u8 fcatt;"
+.br
+.BI " __u8 msdbd;"
+.br
+.BI " __le16 ofcs;"
+.br
+.BI " __u8 dctype;"
+.br
+.BI " __u8 rsvd1807[241];"
+.br
+.BI " struct nvme_id_psd psd[32];"
+.br
+.BI " __u8 vs[1024];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "vid" 12
+PCI Vendor ID, the company vendor identifier that is assigned by
+the PCI SIG.
+.IP "ssvid" 12
+PCI Subsystem Vendor ID, the company vendor identifier that is
+assigned by the PCI SIG for the subsystem.
+.IP "sn" 12
+Serial Number in ASCII
+.IP "mn" 12
+Model Number in ASCII
+.IP "fr" 12
+Firmware Revision in ASCII, the currently active firmware
+revision for the NVM subsystem
+.IP "rab" 12
+Recommended Arbitration Burst, reported as a power of two
+.IP "ieee" 12
+IEEE assigned Organization Unique Identifier
+.IP "cmic" 12
+Controller Multipath IO and Namespace Sharing Capabilities of
+the controller and NVM subsystem. See \fIenum nvme_id_ctrl_cmic\fP.
+.IP "mdts" 12
+Max Data Transfer Size is the largest data transfer size. The
+host should not submit a command that exceeds this maximum data
+transfer size. The value is in units of the minimum memory page
+size (CAP.MPSMIN) and is reported as a power of two
+.IP "cntlid" 12
+Controller ID, the NVM subsystem unique controller identifier
+associated with the controller.
+.IP "ver" 12
+Version, this field contains the value reported in the Version
+register, or property (see \fIenum nvme_registers\fP NVME_REG_VS).
+.IP "rtd3r" 12
+RTD3 Resume Latency, the expected latency in microseconds to resume
+from Runtime D3
+.IP "rtd3e" 12
+RTD3 Exit Latency, the typical latency in microseconds to enter
+Runtime D3.
+.IP "oaes" 12
+Optional Async Events Supported, see \fIenum\fP nvme_id_ctrl_oaes.
+.IP "ctratt" 12
+Controller Attributes, see \fIenum\fP nvme_id_ctrl_ctratt.
+.IP "rrls" 12
+Read Recovery Levels. If a bit is set, then the corresponding
+Read Recovery Level is supported. If a bit is cleared, then the
+corresponding Read Recovery Level is not supported.
+.IP "rsvd102" 12
+Reserved
+.IP "cntrltype" 12
+Controller Type, see \fIenum nvme_id_ctrl_cntrltype\fP
+.IP "fguid" 12
+FRU GUID, a 128-bit value that is globally unique for a given
+Field Replaceable Unit
+.IP "crdt1" 12
+Controller Retry Delay time in 100 millisecond units if CQE CRD
+field is 1
+.IP "crdt2" 12
+Controller Retry Delay time in 100 millisecond units if CQE CRD
+field is 2
+.IP "crdt3" 12
+Controller Retry Delay time in 100 millisecond units if CQE CRD
+field is 3
+.IP "rsvd134" 12
+Reserved
+.IP "nvmsr" 12
+NVM Subsystem Report, see \fIenum nvme_id_ctrl_nvmsr\fP
+.IP "vwci" 12
+VPD Write Cycle Information, see \fIenum nvme_id_ctrl_vwci\fP
+.IP "mec" 12
+Management Endpoint Capabilities, see \fIenum nvme_id_ctrl_mec\fP
+.IP "oacs" 12
+Optional Admin Command Support,the optional Admin commands and
+features supported by the controller, see \fIenum nvme_id_ctrl_oacs\fP.
+.IP "acl" 12
+Abort Command Limit, the maximum number of concurrently
+executing Abort commands supported by the controller. This is a
+0's based value.
+.IP "aerl" 12
+Async Event Request Limit, the maximum number of concurrently
+outstanding Asynchronous Event Request commands supported by the
+controller This is a 0's based value.
+.IP "frmw" 12
+Firmware Updates indicates capabilities regarding firmware
+updates. See \fIenum nvme_id_ctrl_frmw\fP.
+.IP "lpa" 12
+Log Page Attributes, see \fIenum nvme_id_ctrl_lpa\fP.
+.IP "elpe" 12
+Error Log Page Entries, the maximum number of Error Information
+log entries that are stored by the controller. This field is a
+0's based value.
+.IP "npss" 12
+Number of Power States Supported, the number of NVM Express
+power states supported by the controller, indicating the number
+of valid entries in \fIstruct nvme_id_ctrl\fP.psd. This is a 0's
+based value.
+.IP "avscc" 12
+Admin Vendor Specific Command Configuration, see
+\fIenum nvme_id_ctrl_avscc\fP.
+.IP "apsta" 12
+Autonomous Power State Transition Attributes, see
+\fIenum nvme_id_ctrl_apsta\fP.
+.IP "wctemp" 12
+Warning Composite Temperature Threshold indicates
+the minimum Composite Temperature field value (see \fIstruct
+nvme_smart_log\fP.critical_comp_time) that indicates an overheating
+condition during which controller operation continues.
+.IP "cctemp" 12
+Critical Composite Temperature Threshold, field indicates the
+minimum Composite Temperature field value (see \fIstruct
+nvme_smart_log\fP.critical_comp_time) that indicates a critical
+overheating condition.
+.IP "mtfa" 12
+Maximum Time for Firmware Activation indicates the maximum time
+the controller temporarily stops processing commands to activate
+the firmware image, specified in 100 millisecond units. This
+field is always valid if the controller supports firmware
+activation without a reset.
+.IP "hmpre" 12
+Host Memory Buffer Preferred Size indicates the preferred size
+that the host is requested to allocate for the Host Memory
+Buffer feature in 4 KiB units.
+.IP "hmmin" 12
+Host Memory Buffer Minimum Size indicates the minimum size that
+the host is requested to allocate for the Host Memory Buffer
+feature in 4 KiB units.
+.IP "tnvmcap" 12
+Total NVM Capacity, the total NVM capacity in the NVM subsystem.
+The value is in bytes.
+.IP "unvmcap" 12
+Unallocated NVM Capacity, the unallocated NVM capacity in the
+NVM subsystem. The value is in bytes.
+.IP "rpmbs" 12
+Replay Protected Memory Block Support, see
+\fIenum nvme_id_ctrl_rpmbs\fP.
+.IP "edstt" 12
+Extended Device Self-test Time, if Device Self-test command is
+supported (see \fIstruct nvme_id_ctrl\fP.oacs, NVME_CTRL_OACS_SELF_TEST),
+then this field indicates the nominal amount of time in one
+minute units that the controller takes to complete an extended
+device self-test operation when in power state 0.
+.IP "dsto" 12
+Device Self-test Options, see \fIenum nvme_id_ctrl_dsto\fP.
+.IP "fwug" 12
+Firmware Update Granularity indicates the granularity and
+alignment requirement of the firmware image being updated by the
+Firmware Image Download command. The value is reported in 4 KiB
+units. A value of 0h indicates no information on granularity is
+provided. A value of FFh indicates no restriction
+.IP "kas" 12
+Keep Alive Support indicates the granularity of the Keep Alive
+Timer in 100 millisecond units.
+.IP "hctma" 12
+Host Controlled Thermal Management Attributes, see
+\fIenum nvme_id_ctrl_hctm\fP.
+.IP "mntmt" 12
+Minimum Thermal Management Temperature indicates the minimum
+temperature, in degrees Kelvin, that the host may request in the
+Thermal Management Temperature 1 field and Thermal Management
+Temperature 2 field of a Set Features command with the Feature
+Identifier field set to NVME_FEAT_FID_HCTM.
+.IP "mxtmt" 12
+Maximum Thermal Management Temperature indicates the maximum
+temperature, in degrees Kelvin, that the host may request in the
+Thermal Management Temperature 1 field and Thermal Management
+Temperature 2 field of the Set Features command with the Feature
+Identifier set to NVME_FEAT_FID_HCTM.
+.IP "sanicap" 12
+Sanitize Capabilities, see \fIenum nvme_id_ctrl_sanicap\fP
+.IP "hmminds" 12
+Host Memory Buffer Minimum Descriptor Entry Size indicates the
+minimum usable size of a Host Memory Buffer Descriptor Entry in
+4 KiB units.
+.IP "hmmaxd" 12
+Host Memory Maximum Descriptors Entries indicates the number of
+usable Host Memory Buffer Descriptor Entries.
+.IP "nsetidmax" 12
+NVM Set Identifier Maximum, defines the maximum value of a valid
+NVM Set Identifier for any controller in the NVM subsystem.
+.IP "endgidmax" 12
+Endurance Group Identifier Maximum, defines the maximum value of
+a valid Endurance Group Identifier for any controller in the NVM
+subsystem.
+.IP "anatt" 12
+ANA Transition Time indicates the maximum amount of time, in
+seconds, for a transition between ANA states or the maximum
+amount of time, in seconds, that the controller reports the ANA
+change state.
+.IP "anacap" 12
+Asymmetric Namespace Access Capabilities, see
+\fIenum nvme_id_ctrl_anacap\fP.
+.IP "anagrpmax" 12
+ANA Group Identifier Maximum indicates the maximum value of a
+valid ANA Group Identifier for any controller in the NVM
+subsystem.
+.IP "nanagrpid" 12
+Number of ANA Group Identifiers indicates the number of ANA
+groups supported by this controller.
+.IP "pels" 12
+Persistent Event Log Size indicates the maximum reportable size
+for the Persistent Event Log.
+.IP "domainid" 12
+Domain Identifier indicates the identifier of the domain
+that contains this controller.
+.IP "rsvd358" 12
+Reserved
+.IP "megcap" 12
+Max Endurance Group Capacity indicates the maximum capacity
+of a single Endurance Group.
+.IP "rsvd384" 12
+Reserved
+.IP "sqes" 12
+Submission Queue Entry Size, see \fIenum nvme_id_ctrl_sqes\fP.
+.IP "cqes" 12
+Completion Queue Entry Size, see \fIenum nvme_id_ctrl_cqes\fP.
+.IP "maxcmd" 12
+Maximum Outstanding Commands indicates the maximum number of
+commands that the controller processes at one time for a
+particular queue.
+.IP "nn" 12
+Number of Namespaces indicates the maximum value of a valid
+nsid for the NVM subsystem. If the MNAN (\fIstruct nvme_id_ctrl\fP.mnan
+field is cleared to 0h, then this field also indicates the
+maximum number of namespaces supported by the NVM subsystem.
+.IP "oncs" 12
+Optional NVM Command Support, see \fIenum nvme_id_ctrl_oncs\fP.
+.IP "fuses" 12
+Fused Operation Support, see \fIenum nvme_id_ctrl_fuses\fP.
+.IP "fna" 12
+Format NVM Attributes, see \fIenum nvme_id_ctrl_fna\fP.
+.IP "vwc" 12
+Volatile Write Cache, see \fIenum nvme_id_ctrl_vwc\fP.
+.IP "awun" 12
+Atomic Write Unit Normal indicates the size of the write
+operation guaranteed to be written atomically to the NVM across
+all namespaces with any supported namespace format during normal
+operation. This field is specified in logical blocks and is a
+0's based value.
+.IP "awupf" 12
+Atomic Write Unit Power Fail indicates the size of the write
+operation guaranteed to be written atomically to the NVM across
+all namespaces with any supported namespace format during a
+power fail or error condition. This field is specified in
+logical blocks and is a 0’s based value.
+.IP "icsvscc" 12
+NVM Vendor Specific Command Configuration, see
+\fIenum nvme_id_ctrl_nvscc\fP.
+.IP "nwpc" 12
+Namespace Write Protection Capabilities, see
+\fIenum nvme_id_ctrl_nwpc\fP.
+.IP "acwu" 12
+Atomic Compare & Write Unit indicates the size of the write
+operation guaranteed to be written atomically to the NVM across
+all namespaces with any supported namespace format for a Compare
+and Write fused operation. This field is specified in logical
+blocks and is a 0’s based value.
+.IP "ocfs" 12
+Optional Copy Formats Supported, each bit n means controller
+supports Copy Format n.
+.IP "sgls" 12
+SGL Support, see \fIenum nvme_id_ctrl_sgls\fP
+.IP "mnan" 12
+Maximum Number of Allowed Namespaces indicates the maximum
+number of namespaces supported by the NVM subsystem.
+.IP "maxdna" 12
+Maximum Domain Namespace Attachments indicates the maximum
+of the sum of the number of namespaces attached to each I/O
+controller in the Domain.
+.IP "maxcna" 12
+Maximum I/O Controller Namespace Attachments indicates the
+maximum number of namespaces that are allowed to be attached to
+this I/O controller.
+.IP "oaqd" 12
+Optimal Aggregated Queue Depth indicates the recommended maximum
+total number of outstanding I/O commands across all I/O queues
+on the controller for optimal operation.
+.IP "rsvd568" 12
+Reserved
+.IP "subnqn" 12
+NVM Subsystem NVMe Qualified Name, UTF-8 null terminated string
+.IP "rsvd1024" 12
+Reserved
+.IP "ioccsz" 12
+I/O Queue Command Capsule Supported Size, defines the maximum
+I/O command capsule size in 16 byte units.
+.IP "iorcsz" 12
+I/O Queue Response Capsule Supported Size, defines the maximum
+I/O response capsule size in 16 byte units.
+.IP "icdoff" 12
+In Capsule Data Offset, defines the offset where data starts
+within a capsule. This value is applicable to I/O Queues only.
+.IP "fcatt" 12
+Fabrics Controller Attributes, see \fIenum nvme_id_ctrl_fcatt\fP.
+.IP "msdbd" 12
+Maximum SGL Data Block Descriptors indicates the maximum
+number of SGL Data Block or Keyed SGL Data Block descriptors
+that a host is allowed to place in a capsule. A value of 0h
+indicates no limit.
+.IP "ofcs" 12
+Optional Fabric Commands Support, see \fIenum nvme_id_ctrl_ofcs\fP.
+.IP "dctype" 12
+Discovery Controller Type (DCTYPE). This field indicates what
+type of Discovery controller the controller is (see enum
+nvme_id_ctrl_dctype)
+.IP "rsvd1807" 12
+Reserved
+.IP "psd" 12
+Power State Descriptors, see \fIstruct nvme_id_psd\fP.
+.IP "vs" 12
+Vendor Specific
diff --git a/doc/man/nvme_id_ctrl_anacap.2 b/doc/man/nvme_id_ctrl_anacap.2
new file mode 100644
index 0000000..df17a10
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_anacap.2
@@ -0,0 +1,59 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_anacap" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_anacap \- This field indicates the capabilities associated with Asymmetric Namespace Access Reporting.
+.SH SYNOPSIS
+enum nvme_id_ctrl_anacap {
+.br
+.BI " NVME_CTRL_ANACAP_OPT"
+,
+.br
+.br
+.BI " NVME_CTRL_ANACAP_NON_OPT"
+,
+.br
+.br
+.BI " NVME_CTRL_ANACAP_INACCESSIBLE"
+,
+.br
+.br
+.BI " NVME_CTRL_ANACAP_PERSISTENT_LOSS"
+,
+.br
+.br
+.BI " NVME_CTRL_ANACAP_CHANGE"
+,
+.br
+.br
+.BI " NVME_CTRL_ANACAP_GRPID_NO_CHG"
+,
+.br
+.br
+.BI " NVME_CTRL_ANACAP_GRPID_MGMT"
+
+};
+.SH Constants
+.IP "NVME_CTRL_ANACAP_OPT" 12
+If set, then the controller is able to
+report ANA Optimized state.
+.IP "NVME_CTRL_ANACAP_NON_OPT" 12
+If set, then the controller is able to
+report ANA Non-Optimized state.
+.IP "NVME_CTRL_ANACAP_INACCESSIBLE" 12
+If set, then the controller is able to
+report ANA Inaccessible state.
+.IP "NVME_CTRL_ANACAP_PERSISTENT_LOSS" 12
+If set, then the controller is able to
+report ANA Persistent Loss state.
+.IP "NVME_CTRL_ANACAP_CHANGE" 12
+If set, then the controller is able to
+report ANA Change state.
+.IP "NVME_CTRL_ANACAP_GRPID_NO_CHG" 12
+If set, then the ANAGRPID field in the
+Identify Namespace data structure
+(\fIstruct nvme_id_ns\fP.anagrpid), does not
+change while the namespace is attached to
+any controller.
+.IP "NVME_CTRL_ANACAP_GRPID_MGMT" 12
+If set, then the controller supports a
+non-zero value in the ANAGRPID field of
+the Namespace Management command.
diff --git a/doc/man/nvme_id_ctrl_apsta.2 b/doc/man/nvme_id_ctrl_apsta.2
new file mode 100644
index 0000000..e322d97
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_apsta.2
@@ -0,0 +1,13 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_apsta" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_apsta \- Flags indicating the attributes of the autonomous power state transition feature.
+.SH SYNOPSIS
+enum nvme_id_ctrl_apsta {
+.br
+.BI " NVME_CTRL_APSTA_APST"
+
+};
+.SH Constants
+.IP "NVME_CTRL_APSTA_APST" 12
+If set, then the controller supports autonomous power
+state transitions.
diff --git a/doc/man/nvme_id_ctrl_avscc.2 b/doc/man/nvme_id_ctrl_avscc.2
new file mode 100644
index 0000000..a7a1e34
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_avscc.2
@@ -0,0 +1,14 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_avscc" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_avscc \- Flags indicating the configuration settings for Admin Vendor Specific command handling.
+.SH SYNOPSIS
+enum nvme_id_ctrl_avscc {
+.br
+.BI " NVME_CTRL_AVSCC_AVS"
+
+};
+.SH Constants
+.IP "NVME_CTRL_AVSCC_AVS" 12
+If set, all Admin Vendor Specific Commands use the
+optional vendor specific command format with NDT and
+NDM fields.
diff --git a/doc/man/nvme_id_ctrl_cmic.2 b/doc/man/nvme_id_ctrl_cmic.2
new file mode 100644
index 0000000..2c5e43d
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_cmic.2
@@ -0,0 +1,43 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_cmic" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_cmic \- Controller Multipath IO and Namespace Sharing Capabilities of the controller and NVM subsystem.
+.SH SYNOPSIS
+enum nvme_id_ctrl_cmic {
+.br
+.BI " NVME_CTRL_CMIC_MULTI_PORT"
+,
+.br
+.br
+.BI " NVME_CTRL_CMIC_MULTI_CTRL"
+,
+.br
+.br
+.BI " NVME_CTRL_CMIC_MULTI_SRIOV"
+,
+.br
+.br
+.BI " NVME_CTRL_CMIC_MULTI_ANA_REPORTING"
+
+};
+.SH Constants
+.IP "NVME_CTRL_CMIC_MULTI_PORT" 12
+If set, then the NVM subsystem may contain
+more than one NVM subsystem port, otherwise
+the NVM subsystem contains only a single
+NVM subsystem port.
+.IP "NVME_CTRL_CMIC_MULTI_CTRL" 12
+If set, then the NVM subsystem may contain
+two or more controllers, otherwise the
+NVM subsystem contains only a single
+controller. An NVM subsystem that contains
+multiple controllers may be used by
+multiple hosts, or may provide multiple
+paths for a single host.
+.IP "NVME_CTRL_CMIC_MULTI_SRIOV" 12
+If set, then the controller is associated
+with an SR-IOV Virtual Function, otherwise
+it is associated with a PCI Function
+or a Fabrics connection.
+.IP "NVME_CTRL_CMIC_MULTI_ANA_REPORTING" 12
+If set, then the NVM subsystem supports
+Asymmetric Namespace Access Reporting.
diff --git a/doc/man/nvme_id_ctrl_cntrltype.2 b/doc/man/nvme_id_ctrl_cntrltype.2
new file mode 100644
index 0000000..5d21b8d
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_cntrltype.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_cntrltype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_cntrltype \- Controller types
+.SH SYNOPSIS
+enum nvme_id_ctrl_cntrltype {
+.br
+.BI " NVME_CTRL_CNTRLTYPE_IO"
+,
+.br
+.br
+.BI " NVME_CTRL_CNTRLTYPE_DISCOVERY"
+,
+.br
+.br
+.BI " NVME_CTRL_CNTRLTYPE_ADMIN"
+
+};
+.SH Constants
+.IP "NVME_CTRL_CNTRLTYPE_IO" 12
+NVM I/O controller
+.IP "NVME_CTRL_CNTRLTYPE_DISCOVERY" 12
+Discovery controller
+.IP "NVME_CTRL_CNTRLTYPE_ADMIN" 12
+Admin controller
diff --git a/doc/man/nvme_id_ctrl_cqes.2 b/doc/man/nvme_id_ctrl_cqes.2
new file mode 100644
index 0000000..5d7faaf
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_cqes.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_cqes" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_cqes \- Defines the required and maximum Completion Queue entry size when using the NVM Command Set.
+.SH SYNOPSIS
+enum nvme_id_ctrl_cqes {
+.br
+.BI " NVME_CTRL_CQES_MIN"
+,
+.br
+.br
+.BI " NVME_CTRL_CQES_MAX"
+
+};
+.SH Constants
+.IP "NVME_CTRL_CQES_MIN" 12
+Mask to get the value of the required Completion Queue
+Entry size when using the NVM Command Set.
+.IP "NVME_CTRL_CQES_MAX" 12
+Mask to get the value of the maximum Completion Queue
+entry size when using the NVM Command Set.
diff --git a/doc/man/nvme_id_ctrl_ctratt.2 b/doc/man/nvme_id_ctrl_ctratt.2
new file mode 100644
index 0000000..c03bba6
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_ctratt.2
@@ -0,0 +1,110 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_ctratt" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_ctratt \- Controller attributes
+.SH SYNOPSIS
+enum nvme_id_ctrl_ctratt {
+.br
+.BI " NVME_CTRL_CTRATT_128_ID"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_NON_OP_PSP"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_NVM_SETS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_READ_RECV_LVLS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_ENDURANCE_GROUPS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_PREDICTABLE_LAT"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_TBKAS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_SQ_ASSOCIATIONS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_UUID_LIST"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_MDS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_FIXED_CAP"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_VARIABLE_CAP"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_DEL_NVM_SETS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_ELBAS"
+,
+.br
+.br
+.BI " NVME_CTRL_CTRATT_FDPS"
+
+};
+.SH Constants
+.IP "NVME_CTRL_CTRATT_128_ID" 12
+128-bit Host Identifier supported
+.IP "NVME_CTRL_CTRATT_NON_OP_PSP" 12
+Non-Operational Poser State Permissive Mode
+supported
+.IP "NVME_CTRL_CTRATT_NVM_SETS" 12
+NVM Sets supported
+.IP "NVME_CTRL_CTRATT_READ_RECV_LVLS" 12
+Read Recovery Levels supported
+.IP "NVME_CTRL_CTRATT_ENDURANCE_GROUPS" 12
+Endurance Groups supported
+.IP "NVME_CTRL_CTRATT_PREDICTABLE_LAT" 12
+Predictable Latency Mode supported
+.IP "NVME_CTRL_CTRATT_TBKAS" 12
+Traffic Based Keep Alive Support
+.IP "NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY" 12
+Namespace Granularity reporting
+supported
+.IP "NVME_CTRL_CTRATT_SQ_ASSOCIATIONS" 12
+SQ Associations supported
+.IP "NVME_CTRL_CTRATT_UUID_LIST" 12
+UUID List reporting supported
+.IP "NVME_CTRL_CTRATT_MDS" 12
+Multi-Domain Subsystem supported
+.IP "NVME_CTRL_CTRATT_FIXED_CAP" 12
+Fixed Capacity Management supported
+.IP "NVME_CTRL_CTRATT_VARIABLE_CAP" 12
+Variable Capacity Management supported
+.IP "NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS" 12
+Delete Endurance Groups supported
+.IP "NVME_CTRL_CTRATT_DEL_NVM_SETS" 12
+Delete NVM Sets supported
+.IP "NVME_CTRL_CTRATT_ELBAS" 12
+Extended LBA Formats supported
+.IP "NVME_CTRL_CTRATT_FDPS" 12
+Flexible Data Placement supported
diff --git a/doc/man/nvme_id_ctrl_dctype.2 b/doc/man/nvme_id_ctrl_dctype.2
new file mode 100644
index 0000000..747aee1
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_dctype.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_dctype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_dctype \- Discovery Controller types
+.SH SYNOPSIS
+enum nvme_id_ctrl_dctype {
+.br
+.BI " NVME_CTRL_DCTYPE_NOT_REPORTED"
+,
+.br
+.br
+.BI " NVME_CTRL_DCTYPE_DDC"
+,
+.br
+.br
+.BI " NVME_CTRL_DCTYPE_CDC"
+
+};
+.SH Constants
+.IP "NVME_CTRL_DCTYPE_NOT_REPORTED" 12
+Not reported (I/O, Admin, and pre-TP8010)
+.IP "NVME_CTRL_DCTYPE_DDC" 12
+Direct Discovery controller
+.IP "NVME_CTRL_DCTYPE_CDC" 12
+Central Discovery controller
diff --git a/doc/man/nvme_id_ctrl_dsto.2 b/doc/man/nvme_id_ctrl_dsto.2
new file mode 100644
index 0000000..4c759a1
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_dsto.2
@@ -0,0 +1,13 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_dsto" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_dsto \- Flags indicating the optional Device Self-test command or operation behaviors supported by the controller or NVM subsystem.
+.SH SYNOPSIS
+enum nvme_id_ctrl_dsto {
+.br
+.BI " NVME_CTRL_DSTO_ONE_DST"
+
+};
+.SH Constants
+.IP "NVME_CTRL_DSTO_ONE_DST" 12
+If set, then the NVM subsystem supports only one
+device self-test operation in progress at a time.
diff --git a/doc/man/nvme_id_ctrl_fcatt.2 b/doc/man/nvme_id_ctrl_fcatt.2
new file mode 100644
index 0000000..3fa95da
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_fcatt.2
@@ -0,0 +1,14 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_fcatt" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_fcatt \- This field indicates attributes of the controller that are specific to NVMe over Fabrics.
+.SH SYNOPSIS
+enum nvme_id_ctrl_fcatt {
+.br
+.BI " NVME_CTRL_FCATT_DYNAMIC"
+
+};
+.SH Constants
+.IP "NVME_CTRL_FCATT_DYNAMIC" 12
+If cleared, then the NVM subsystem uses a dynamic
+controller model. If set, then the NVM subsystem
+uses a static controller model.
diff --git a/doc/man/nvme_id_ctrl_fna.2 b/doc/man/nvme_id_ctrl_fna.2
new file mode 100644
index 0000000..1220a11
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_fna.2
@@ -0,0 +1,48 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_fna" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_fna \- This field indicates attributes for the Format NVM command.
+.SH SYNOPSIS
+enum nvme_id_ctrl_fna {
+.br
+.BI " NVME_CTRL_FNA_FMT_ALL_NAMESPACES"
+,
+.br
+.br
+.BI " NVME_CTRL_FNA_SEC_ALL_NAMESPACES"
+,
+.br
+.br
+.BI " NVME_CTRL_FNA_CRYPTO_ERASE"
+,
+.br
+.br
+.BI " NVME_CTRL_FNA_NSID_FFFFFFFF"
+
+};
+.SH Constants
+.IP "NVME_CTRL_FNA_FMT_ALL_NAMESPACES" 12
+If set, then all namespaces in an NVM
+subsystem shall be configured with the
+same attributes and a format (excluding
+secure erase) of any namespace results in
+a format of all namespaces in an NVM
+subsystem. If cleared, then the
+controller supports format on a per
+namespace basis.
+.IP "NVME_CTRL_FNA_SEC_ALL_NAMESPACES" 12
+If set, then any secure erase performed
+as part of a format operation results in
+a secure erase of all namespaces in the
+NVM subsystem. If cleared, then any
+secure erase performed as part of a
+format results in a secure erase of the
+particular namespace specified.
+.IP "NVME_CTRL_FNA_CRYPTO_ERASE" 12
+If set, then cryptographic erase is
+supported. If cleared, then cryptographic
+erase is not supported.
+.IP "NVME_CTRL_FNA_NSID_FFFFFFFF" 12
+If set, then format does not support
+nsid value set to FFFFFFFFh. If cleared,
+format supports nsid value set to
+FFFFFFFFh.
diff --git a/doc/man/nvme_id_ctrl_frmw.2 b/doc/man/nvme_id_ctrl_frmw.2
new file mode 100644
index 0000000..4f9029c
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_frmw.2
@@ -0,0 +1,34 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_frmw" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_frmw \- Flags and values indicates capabilities regarding firmware updates from &struct nvme_id_ctrl.frmw.
+.SH SYNOPSIS
+enum nvme_id_ctrl_frmw {
+.br
+.BI " NVME_CTRL_FRMW_1ST_RO"
+,
+.br
+.br
+.BI " NVME_CTRL_FRMW_NR_SLOTS"
+,
+.br
+.br
+.BI " NVME_CTRL_FRMW_FW_ACT_NO_RESET"
+,
+.br
+.br
+.BI " NVME_CTRL_FRMW_MP_UP_DETECTION"
+
+};
+.SH Constants
+.IP "NVME_CTRL_FRMW_1ST_RO" 12
+If set, the first firmware slot is readonly
+.IP "NVME_CTRL_FRMW_NR_SLOTS" 12
+Mask to get the value of the number of
+firmware slots that the controller supports.
+.IP "NVME_CTRL_FRMW_FW_ACT_NO_RESET" 12
+If set, the controller supports firmware
+activation without a reset.
+.IP "NVME_CTRL_FRMW_MP_UP_DETECTION" 12
+If set, the controller is able to detect
+overlapping firmware/boot partition
+image update.
diff --git a/doc/man/nvme_id_ctrl_fuses.2 b/doc/man/nvme_id_ctrl_fuses.2
new file mode 100644
index 0000000..c32976d
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_fuses.2
@@ -0,0 +1,13 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_fuses" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_fuses \- This field indicates the fused operations that the controller supports.
+.SH SYNOPSIS
+enum nvme_id_ctrl_fuses {
+.br
+.BI " NVME_CTRL_FUSES_COMPARE_AND_WRITE"
+
+};
+.SH Constants
+.IP "NVME_CTRL_FUSES_COMPARE_AND_WRITE" 12
+If set, then the controller supports the
+Compare and Write fused operation.
diff --git a/doc/man/nvme_id_ctrl_hctm.2 b/doc/man/nvme_id_ctrl_hctm.2
new file mode 100644
index 0000000..5d05ed3
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_hctm.2
@@ -0,0 +1,15 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_hctm" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_hctm \- Flags indicate the attributes of the host controlled thermal management feature
+.SH SYNOPSIS
+enum nvme_id_ctrl_hctm {
+.br
+.BI " NVME_CTRL_HCTMA_HCTM"
+
+};
+.SH Constants
+.IP "NVME_CTRL_HCTMA_HCTM" 12
+then the controller supports host controlled thermal
+management, and the Set Features command and Get
+Features command with the Feature Identifier field
+set to NVME_FEAT_FID_HCTM.
diff --git a/doc/man/nvme_id_ctrl_lpa.2 b/doc/man/nvme_id_ctrl_lpa.2
new file mode 100644
index 0000000..f32680d
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_lpa.2
@@ -0,0 +1,65 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_lpa" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_lpa \- Flags indicating optional attributes for log pages that are accessed via the Get Log Page command.
+.SH SYNOPSIS
+enum nvme_id_ctrl_lpa {
+.br
+.BI " NVME_CTRL_LPA_SMART_PER_NS"
+,
+.br
+.br
+.BI " NVME_CTRL_LPA_CMD_EFFECTS"
+,
+.br
+.br
+.BI " NVME_CTRL_LPA_EXTENDED"
+,
+.br
+.br
+.BI " NVME_CTRL_LPA_TELEMETRY"
+,
+.br
+.br
+.BI " NVME_CTRL_LPA_PERSETENT_EVENT"
+,
+.br
+.br
+.BI " NVME_CTRL_LPA_LI0_LI5_LI12_LI13"
+,
+.br
+.br
+.BI " NVME_CTRL_LPA_DA4_TELEMETRY"
+
+};
+.SH Constants
+.IP "NVME_CTRL_LPA_SMART_PER_NS" 12
+If set, controller supports SMART/Health log
+page on a per namespace basis.
+.IP "NVME_CTRL_LPA_CMD_EFFECTS" 12
+If Set, the controller supports the commands
+supported and effects log page.
+.IP "NVME_CTRL_LPA_EXTENDED" 12
+If set, the controller supports extended data
+for log page command including extended number
+of dwords and log page offset fields.
+.IP "NVME_CTRL_LPA_TELEMETRY" 12
+If set, the controller supports the telemetry
+host-initiated and telemetry controller-initiated
+log pages and sending telemetry log notices.
+.IP "NVME_CTRL_LPA_PERSETENT_EVENT" 12
+If set, the controller supports
+persistent event log.
+.IP "NVME_CTRL_LPA_LI0_LI5_LI12_LI13" 12
+If set, the controller supports
+- log pages log page.
+- returning scope of each command in
+commands supported and effects log
+page.
+- feature identifiers supported and
+effects log page.
+- NVMe-MI commands supported and
+effects log page.
+.IP "NVME_CTRL_LPA_DA4_TELEMETRY" 12
+If set, the controller supports data
+area 4 for telemetry host-initiated and
+telemetry.
diff --git a/doc/man/nvme_id_ctrl_mec.2 b/doc/man/nvme_id_ctrl_mec.2
new file mode 100644
index 0000000..9fa960c
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_mec.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_mec" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_mec \- Flags indicating the capabilities of the Management Endpoint in the Controller, &struct nvme_id_ctrl.mec.
+.SH SYNOPSIS
+enum nvme_id_ctrl_mec {
+.br
+.BI " NVME_CTRL_MEC_SMBUSME"
+,
+.br
+.br
+.BI " NVME_CTRL_MEC_PCIEME"
+
+};
+.SH Constants
+.IP "NVME_CTRL_MEC_SMBUSME" 12
+If set, then the NVM Subsystem contains a Management
+Endpoint on an SMBus/I2C port.
+.IP "NVME_CTRL_MEC_PCIEME" 12
+If set, then the NVM Subsystem contains a Management
+Endpoint on a PCIe port.
diff --git a/doc/man/nvme_id_ctrl_nvm.2 b/doc/man/nvme_id_ctrl_nvm.2
new file mode 100644
index 0000000..c4ebbf4
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_nvm.2
@@ -0,0 +1,39 @@
+.TH "libnvme" 9 "struct nvme_id_ctrl_nvm" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_ctrl_nvm \- I/O Command Set Specific Identify Controller data structure
+.SH SYNOPSIS
+struct nvme_id_ctrl_nvm {
+.br
+.BI " __u8 vsl;"
+.br
+.BI " __u8 wzsl;"
+.br
+.BI " __u8 wusl;"
+.br
+.BI " __u8 dmrl;"
+.br
+.BI " __le32 dmrsl;"
+.br
+.BI " __le64 dmsl;"
+.br
+.BI " __u8 rsvd16[4080];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "vsl" 12
+Verify Size Limit
+.IP "wzsl" 12
+Write Zeroes Size Limit
+.IP "wusl" 12
+Write Uncorrectable Size Limit
+.IP "dmrl" 12
+Dataset Management Ranges Limit
+.IP "dmrsl" 12
+Dataset Management Range Size Limit
+.IP "dmsl" 12
+Dataset Management Size Limit
+.IP "rsvd16" 12
+reserved
diff --git a/doc/man/nvme_id_ctrl_nvmsr.2 b/doc/man/nvme_id_ctrl_nvmsr.2
new file mode 100644
index 0000000..64e6f3e
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_nvmsr.2
@@ -0,0 +1,22 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_nvmsr" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_nvmsr \- This field reports information associated with the NVM Subsystem, see &struct nvme_id_ctrl.nvmsr.
+.SH SYNOPSIS
+enum nvme_id_ctrl_nvmsr {
+.br
+.BI " NVME_CTRL_NVMSR_NVMESD"
+,
+.br
+.br
+.BI " NVME_CTRL_NVMSR_NVMEE"
+
+};
+.SH Constants
+.IP "NVME_CTRL_NVMSR_NVMESD" 12
+If set, then the NVM Subsystem is part of an NVMe
+Storage Device; if cleared, then the NVM Subsystem
+is not part of an NVMe Storage Device.
+.IP "NVME_CTRL_NVMSR_NVMEE" 12
+If set’, then the NVM Subsystem is part of an NVMe
+Enclosure; if cleared, then the NVM Subsystem is
+not part of an NVMe Enclosure.
diff --git a/doc/man/nvme_id_ctrl_nvscc.2 b/doc/man/nvme_id_ctrl_nvscc.2
new file mode 100644
index 0000000..975554f
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_nvscc.2
@@ -0,0 +1,13 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_nvscc" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_nvscc \- This field indicates the configuration settings for NVM Vendor Specific command handling.
+.SH SYNOPSIS
+enum nvme_id_ctrl_nvscc {
+.br
+.BI " NVME_CTRL_NVSCC_FMT"
+
+};
+.SH Constants
+.IP "NVME_CTRL_NVSCC_FMT" 12
+If set, all NVM Vendor Specific Commands use the
+format with NDT and NDM fields.
diff --git a/doc/man/nvme_id_ctrl_nwpc.2 b/doc/man/nvme_id_ctrl_nwpc.2
new file mode 100644
index 0000000..0479ea4
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_nwpc.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_nwpc" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_nwpc \- This field indicates the optional namespace write protection capabilities supported by the controller.
+.SH SYNOPSIS
+enum nvme_id_ctrl_nwpc {
+.br
+.BI " NVME_CTRL_NWPC_WRITE_PROTECT"
+,
+.br
+.br
+.BI " NVME_CTRL_NWPC_WRITE_PROTECT_POWER_CYCLE"
+,
+.br
+.br
+.BI " NVME_CTRL_NWPC_WRITE_PROTECT_PERMANENT"
+
+};
+.SH Constants
+.IP "NVME_CTRL_NWPC_WRITE_PROTECT" 12
+If set, then the controller shall
+support the No Write Protect and
+Write Protect namespace write
+protection states and may support
+the Write Protect Until Power
+Cycle state and Permanent Write
+Protect namespace write
+protection states.
+.IP "NVME_CTRL_NWPC_WRITE_PROTECT_POWER_CYCLE" 12
+If set, then the controller
+supports the Write Protect Until
+Power Cycle state.
+.IP "NVME_CTRL_NWPC_WRITE_PROTECT_PERMANENT" 12
+If set, then the controller
+supports the Permanent Write
+Protect state.
diff --git a/doc/man/nvme_id_ctrl_oacs.2 b/doc/man/nvme_id_ctrl_oacs.2
new file mode 100644
index 0000000..762f93a
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_oacs.2
@@ -0,0 +1,84 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_oacs" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_oacs \- Flags indicating the optional Admin commands and features supported by the controller, see &struct nvme_id_ctrl.oacs.
+.SH SYNOPSIS
+enum nvme_id_ctrl_oacs {
+.br
+.BI " NVME_CTRL_OACS_SECURITY"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_FORMAT"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_FW"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_NS_MGMT"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_SELF_TEST"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_DIRECTIVES"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_NVME_MI"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_VIRT_MGMT"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_DBBUF_CFG"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_LBA_STATUS"
+,
+.br
+.br
+.BI " NVME_CTRL_OACS_CMD_FEAT_LD"
+
+};
+.SH Constants
+.IP "NVME_CTRL_OACS_SECURITY" 12
+If set, then the controller supports the
+Security Send and Security Receive commands.
+.IP "NVME_CTRL_OACS_FORMAT" 12
+If set then the controller supports the Format
+NVM command.
+.IP "NVME_CTRL_OACS_FW" 12
+If set, then the controller supports the
+Firmware Commit and Firmware Image Download commands.
+.IP "NVME_CTRL_OACS_NS_MGMT" 12
+If set, then the controller supports the
+Namespace Management capability
+.IP "NVME_CTRL_OACS_SELF_TEST" 12
+If set, then the controller supports the Device
+Self-test command.
+.IP "NVME_CTRL_OACS_DIRECTIVES" 12
+If set, then the controller supports Directives
+and the Directive Send and Directive Receive
+commands.
+.IP "NVME_CTRL_OACS_NVME_MI" 12
+If set, then the controller supports the NVMe-MI
+Send and NVMe-MI Receive commands.
+.IP "NVME_CTRL_OACS_VIRT_MGMT" 12
+If set, then the controller supports the
+Virtualization Management command.
+.IP "NVME_CTRL_OACS_DBBUF_CFG" 12
+If set, then the controller supports the
+Doorbell Buffer Config command.
+.IP "NVME_CTRL_OACS_LBA_STATUS" 12
+If set, then the controller supports the Get LBA
+Status capability.
+.IP "NVME_CTRL_OACS_CMD_FEAT_LD" 12
+If set, then the controller supports the command
+and feature lockdown capability.
diff --git a/doc/man/nvme_id_ctrl_oaes.2 b/doc/man/nvme_id_ctrl_oaes.2
new file mode 100644
index 0000000..6395687
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_oaes.2
@@ -0,0 +1,62 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_oaes" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_oaes \- Optional Asynchronous Events Supported
+.SH SYNOPSIS
+enum nvme_id_ctrl_oaes {
+.br
+.BI " NVME_CTRL_OAES_NA"
+,
+.br
+.br
+.BI " NVME_CTRL_OAES_FA"
+,
+.br
+.br
+.BI " NVME_CTRL_OAES_ANA"
+,
+.br
+.br
+.BI " NVME_CTRL_OAES_PLEA"
+,
+.br
+.br
+.BI " NVME_CTRL_OAES_LBAS"
+,
+.br
+.br
+.BI " NVME_CTRL_OAES_EGE"
+,
+.br
+.br
+.BI " NVME_CTRL_OAES_NS"
+,
+.br
+.br
+.BI " NVME_CTRL_OAES_ZD"
+,
+.br
+.br
+.BI " NVME_CTRL_OAES_DL"
+
+};
+.SH Constants
+.IP "NVME_CTRL_OAES_NA" 12
+Namespace Attribute Notices event supported
+.IP "NVME_CTRL_OAES_FA" 12
+Firmware Activation Notices event supported
+.IP "NVME_CTRL_OAES_ANA" 12
+ANA Change Notices supported
+.IP "NVME_CTRL_OAES_PLEA" 12
+Predictable Latency Event Aggregate Log
+Change Notices event supported
+.IP "NVME_CTRL_OAES_LBAS" 12
+LBA Status Information Notices event supported
+.IP "NVME_CTRL_OAES_EGE" 12
+Endurance Group Events Aggregate Log Change
+Notices event supported
+.IP "NVME_CTRL_OAES_NS" 12
+Normal NVM Subsystem Shutdown event supported
+.IP "NVME_CTRL_OAES_ZD" 12
+Zone Descriptor Change Notifications supported
+.IP "NVME_CTRL_OAES_DL" 12
+Discover Log Page Change Notifications supported
diff --git a/doc/man/nvme_id_ctrl_ofcs.2 b/doc/man/nvme_id_ctrl_ofcs.2
new file mode 100644
index 0000000..2e5a52c
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_ofcs.2
@@ -0,0 +1,14 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_ofcs" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_ofcs \- Indicate whether the controller supports optional fabric commands.
+.SH SYNOPSIS
+enum nvme_id_ctrl_ofcs {
+.br
+.BI " NVME_CTRL_OFCS_DISCONNECT"
+
+};
+.SH Constants
+.IP "NVME_CTRL_OFCS_DISCONNECT" 12
+If set, then the controller supports the
+Disconnect command and deletion of individual
+I/O Queues.
diff --git a/doc/man/nvme_id_ctrl_oncs.2 b/doc/man/nvme_id_ctrl_oncs.2
new file mode 100644
index 0000000..0045929
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_oncs.2
@@ -0,0 +1,90 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_oncs" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_oncs \- This field indicates the optional NVM commands and features supported by the controller.
+.SH SYNOPSIS
+enum nvme_id_ctrl_oncs {
+.br
+.BI " NVME_CTRL_ONCS_COMPARE"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_WRITE_UNCORRECTABLE"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_DSM"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_WRITE_ZEROES"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_SAVE_FEATURES"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_RESERVATIONS"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_TIMESTAMP"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_VERIFY"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_COPY"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_COPY_SINGLE_ATOMICITY"
+,
+.br
+.br
+.BI " NVME_CTRL_ONCS_ALL_FAST_COPY"
+
+};
+.SH Constants
+.IP "NVME_CTRL_ONCS_COMPARE" 12
+If set, then the controller supports
+the Compare command.
+.IP "NVME_CTRL_ONCS_WRITE_UNCORRECTABLE" 12
+If set, then the controller supports
+the Write Uncorrectable command.
+.IP "NVME_CTRL_ONCS_DSM" 12
+If set, then the controller supports
+the Dataset Management command.
+.IP "NVME_CTRL_ONCS_WRITE_ZEROES" 12
+If set, then the controller supports
+the Write Zeroes command.
+.IP "NVME_CTRL_ONCS_SAVE_FEATURES" 12
+If set, then the controller supports
+the Save field set to a non-zero value
+in the Set Features command and the
+Select field set to a non-zero value in
+the Get Features command.
+.IP "NVME_CTRL_ONCS_RESERVATIONS" 12
+If set, then the controller supports
+reservations.
+.IP "NVME_CTRL_ONCS_TIMESTAMP" 12
+If set, then the controller supports
+the Timestamp feature.
+.IP "NVME_CTRL_ONCS_VERIFY" 12
+If set, then the controller supports
+the Verify command.
+.IP "NVME_CTRL_ONCS_COPY" 12
+If set, then the controller supports
+the copy command.
+.IP "NVME_CTRL_ONCS_COPY_SINGLE_ATOMICITY" 12
+If set, then the write portion of a
+Copy command is performed as a single
+write command to which the same
+atomicity requirements that apply to
+a write command apply.
+.IP "NVME_CTRL_ONCS_ALL_FAST_COPY" 12
+If set, then all copy operations for
+the Copy command are fast copy
+operations.
diff --git a/doc/man/nvme_id_ctrl_rpmbs.2 b/doc/man/nvme_id_ctrl_rpmbs.2
new file mode 100644
index 0000000..bd11228
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_rpmbs.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_rpmbs" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_rpmbs \- This field indicates if the controller supports one or more Replay Protected Memory Blocks, from &struct nvme_id_ctrl.rpmbs.
+.SH SYNOPSIS
+enum nvme_id_ctrl_rpmbs {
+.br
+.BI " NVME_CTRL_RPMBS_NR_UNITS"
+,
+.br
+.br
+.BI " NVME_CTRL_RPMBS_AUTH_METHOD"
+,
+.br
+.br
+.BI " NVME_CTRL_RPMBS_TOTAL_SIZE"
+,
+.br
+.br
+.BI " NVME_CTRL_RPMBS_ACCESS_SIZE"
+
+};
+.SH Constants
+.IP "NVME_CTRL_RPMBS_NR_UNITS" 12
+Mask to get the value of the Number of RPMB Units
+.IP "NVME_CTRL_RPMBS_AUTH_METHOD" 12
+Mask to get the value of the Authentication Method
+.IP "NVME_CTRL_RPMBS_TOTAL_SIZE" 12
+Mask to get the value of Total Size
+.IP "NVME_CTRL_RPMBS_ACCESS_SIZE" 12
+Mask to get the value of Access Size
diff --git a/doc/man/nvme_id_ctrl_sanicap.2 b/doc/man/nvme_id_ctrl_sanicap.2
new file mode 100644
index 0000000..a6af345
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_sanicap.2
@@ -0,0 +1,44 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_sanicap" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_sanicap \- Indicates attributes for sanitize operations.
+.SH SYNOPSIS
+enum nvme_id_ctrl_sanicap {
+.br
+.BI " NVME_CTRL_SANICAP_CES"
+,
+.br
+.br
+.BI " NVME_CTRL_SANICAP_BES"
+,
+.br
+.br
+.BI " NVME_CTRL_SANICAP_OWS"
+,
+.br
+.br
+.BI " NVME_CTRL_SANICAP_NDI"
+,
+.br
+.br
+.BI " NVME_CTRL_SANICAP_NODMMAS"
+
+};
+.SH Constants
+.IP "NVME_CTRL_SANICAP_CES" 12
+Crypto Erase Support. If set, then the
+controller supports the Crypto Erase sanitize operation.
+.IP "NVME_CTRL_SANICAP_BES" 12
+Block Erase Support. If set, then the controller
+supports the Block Erase sanitize operation.
+.IP "NVME_CTRL_SANICAP_OWS" 12
+Overwrite Support. If set, then the controller
+supports the Overwrite sanitize operation.
+.IP "NVME_CTRL_SANICAP_NDI" 12
+No-Deallocate Inhibited. If set and the No-
+Deallocate Response Mode bit is set, then the
+controller deallocates after the sanitize
+operation even if the No-Deallocate After
+Sanitize bit is set in a Sanitize command.
+.IP "NVME_CTRL_SANICAP_NODMMAS" 12
+No-Deallocate Modifies Media After Sanitize,
+mask to extract value.
diff --git a/doc/man/nvme_id_ctrl_sgls.2 b/doc/man/nvme_id_ctrl_sgls.2
new file mode 100644
index 0000000..92b074d
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_sgls.2
@@ -0,0 +1,46 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_sgls" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_sgls \- This field indicates if SGLs are supported for the NVM Command Set and the particular SGL types supported.
+.SH SYNOPSIS
+enum nvme_id_ctrl_sgls {
+.br
+.BI " NVME_CTRL_SGLS_SUPPORTED"
+,
+.br
+.br
+.BI " NVME_CTRL_SGLS_KEYED"
+,
+.br
+.br
+.BI " NVME_CTRL_SGLS_BIT_BUCKET"
+,
+.br
+.br
+.BI " NVME_CTRL_SGLS_MPTR_BYTE_ALIGNED"
+,
+.br
+.br
+.BI " NVME_CTRL_SGLS_OVERSIZE"
+,
+.br
+.br
+.BI " NVME_CTRL_SGLS_MPTR_SGL"
+,
+.br
+.br
+.BI " NVME_CTRL_SGLS_OFFSET"
+,
+.br
+.br
+.BI " NVME_CTRL_SGLS_TPORT"
+
+};
+.SH Constants
+.IP "NVME_CTRL_SGLS_SUPPORTED" 12
+.IP "NVME_CTRL_SGLS_KEYED" 12
+.IP "NVME_CTRL_SGLS_BIT_BUCKET" 12
+.IP "NVME_CTRL_SGLS_MPTR_BYTE_ALIGNED" 12
+.IP "NVME_CTRL_SGLS_OVERSIZE" 12
+.IP "NVME_CTRL_SGLS_MPTR_SGL" 12
+.IP "NVME_CTRL_SGLS_OFFSET" 12
+.IP "NVME_CTRL_SGLS_TPORT" 12
diff --git a/doc/man/nvme_id_ctrl_sqes.2 b/doc/man/nvme_id_ctrl_sqes.2
new file mode 100644
index 0000000..bd48447
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_sqes.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_sqes" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_sqes \- Defines the required and maximum Submission Queue entry size when using the NVM Command Set.
+.SH SYNOPSIS
+enum nvme_id_ctrl_sqes {
+.br
+.BI " NVME_CTRL_SQES_MIN"
+,
+.br
+.br
+.BI " NVME_CTRL_SQES_MAX"
+
+};
+.SH Constants
+.IP "NVME_CTRL_SQES_MIN" 12
+Mask to get the value of the required Submission Queue
+Entry size when using the NVM Command Set.
+.IP "NVME_CTRL_SQES_MAX" 12
+Mask to get the value of the maximum Submission Queue
+entry size when using the NVM Command Set.
diff --git a/doc/man/nvme_id_ctrl_vwc.2 b/doc/man/nvme_id_ctrl_vwc.2
new file mode 100644
index 0000000..89fd427
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_vwc.2
@@ -0,0 +1,22 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_vwc" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_vwc \- Volatile write cache
+.SH SYNOPSIS
+enum nvme_id_ctrl_vwc {
+.br
+.BI " NVME_CTRL_VWC_PRESENT"
+,
+.br
+.br
+.BI " NVME_CTRL_VWC_FLUSH"
+
+};
+.SH Constants
+.IP "NVME_CTRL_VWC_PRESENT" 12
+If set, indicates a volatile write cache is present.
+If a volatile write cache is present, then the host
+controls whether the volatile write cache is enabled
+with a Set Features command specifying the value
+NVME_FEAT_FID_VOLATILE_WC.
+.IP "NVME_CTRL_VWC_FLUSH" 12
+Mask to get the value of the flush command behavior.
diff --git a/doc/man/nvme_id_ctrl_vwci.2 b/doc/man/nvme_id_ctrl_vwci.2
new file mode 100644
index 0000000..db81959
--- /dev/null
+++ b/doc/man/nvme_id_ctrl_vwci.2
@@ -0,0 +1,28 @@
+.TH "libnvme" 9 "enum nvme_id_ctrl_vwci" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ctrl_vwci \- This field indicates information about remaining number of times that VPD contents are able to be updated using the VPD Write command, see &struct nvme_id_ctrl.vwci.
+.SH SYNOPSIS
+enum nvme_id_ctrl_vwci {
+.br
+.BI " NVME_CTRL_VWCI_VWCR"
+,
+.br
+.br
+.BI " NVME_CTRL_VWCI_VWCRV"
+
+};
+.SH Constants
+.IP "NVME_CTRL_VWCI_VWCR" 12
+Mask to get value of VPD Write Cycles Remaining. If
+the VPD Write Cycle Remaining Valid bit is set, then
+this field contains a value indicating the remaining
+number of times that VPD contents are able to be
+updated using the VPD Write command. If this field is
+set to 7Fh, then the remaining number of times that
+VPD contents are able to be updated using the VPD
+Write command is greater than or equal to 7Fh.
+.IP "NVME_CTRL_VWCI_VWCRV" 12
+VPD Write Cycle Remaining Valid. If this bit is set,
+then the VPD Write Cycle Remaining field is valid. If
+this bit is cleared, then the VPD Write Cycles
+Remaining field is invalid and cleared to 0h.
diff --git a/doc/man/nvme_id_directives.2 b/doc/man/nvme_id_directives.2
new file mode 100644
index 0000000..9350a49
--- /dev/null
+++ b/doc/man/nvme_id_directives.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_id_directives" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_directives \- Identify Directive - Return Parameters Data Structure
+.SH SYNOPSIS
+struct nvme_id_directives {
+.br
+.BI " __u8 supported[32];"
+.br
+.BI " __u8 enabled[32];"
+.br
+.BI " __u8 rsvd64[4032];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "supported" 12
+Identify directive is supported
+.IP "enabled" 12
+Identify directive is Enabled
+.IP "rsvd64" 12
+Reserved
diff --git a/doc/man/nvme_id_domain_attr.2 b/doc/man/nvme_id_domain_attr.2
new file mode 100644
index 0000000..76f56fe
--- /dev/null
+++ b/doc/man/nvme_id_domain_attr.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_id_domain_attr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_domain_attr \- Domain Attributes Entry
+.SH SYNOPSIS
+struct nvme_id_domain_attr {
+.br
+.BI " __le16 dom_id;"
+.br
+.BI " __u8 rsvd2[14];"
+.br
+.BI " __u8 dom_cap[16];"
+.br
+.BI " __u8 unalloc_dom_cap[16];"
+.br
+.BI " __u8 max_egrp_dom_cap[16];"
+.br
+.BI " __u8 rsvd64[64];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "dom_id" 12
+Domain Identifier
+.IP "rsvd2" 12
+Reserved
+.IP "dom_cap" 12
+Total Domain Capacity
+.IP "unalloc_dom_cap" 12
+Unallocated Domain Capacity
+.IP "max_egrp_dom_cap" 12
+Max Endurance Group Domain Capacity
+.IP "rsvd64" 12
+Reserved
diff --git a/doc/man/nvme_id_domain_list.2 b/doc/man/nvme_id_domain_list.2
new file mode 100644
index 0000000..1bf634e
--- /dev/null
+++ b/doc/man/nvme_id_domain_list.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_id_domain_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_domain_list \- Domain List
+.SH SYNOPSIS
+struct nvme_id_domain_list {
+.br
+.BI " __u8 num;"
+.br
+.BI " __u8 rsvd[127];"
+.br
+.BI " struct nvme_id_domain_attr domain_attr[NVME_ID_DOMAIN_LIST_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "num" 12
+Number of domain attributes
+.IP "rsvd" 12
+Reserved
+.IP "domain_attr" 12
+List of domain attributes
diff --git a/doc/man/nvme_id_endurance_group_list.2 b/doc/man/nvme_id_endurance_group_list.2
new file mode 100644
index 0000000..0d63953
--- /dev/null
+++ b/doc/man/nvme_id_endurance_group_list.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_id_endurance_group_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_endurance_group_list \- Endurance Group List
+.SH SYNOPSIS
+struct nvme_id_endurance_group_list {
+.br
+.BI " __le16 num;"
+.br
+.BI " __le16 identifier[NVME_ID_ENDURANCE_GROUP_LIST_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "num" 12
+Number of Identifiers
+.IP "identifier" 12
+Endurance Group Identifier
diff --git a/doc/man/nvme_id_independent_id_ns.2 b/doc/man/nvme_id_independent_id_ns.2
new file mode 100644
index 0000000..d5995f9
--- /dev/null
+++ b/doc/man/nvme_id_independent_id_ns.2
@@ -0,0 +1,56 @@
+.TH "libnvme" 9 "struct nvme_id_independent_id_ns" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_independent_id_ns \- Identify - I/O Command Set Independent Identify Namespace Data Structure
+.SH SYNOPSIS
+struct nvme_id_independent_id_ns {
+.br
+.BI " __u8 nsfeat;"
+.br
+.BI " __u8 nmic;"
+.br
+.BI " __u8 rescap;"
+.br
+.BI " __u8 fpi;"
+.br
+.BI " __le32 anagrpid;"
+.br
+.BI " __u8 nsattr;"
+.br
+.BI " __u8 rsvd9;"
+.br
+.BI " __le16 nvmsetid;"
+.br
+.BI " __le16 endgid;"
+.br
+.BI " __u8 nstat;"
+.br
+.BI " __u8 rsvd15[4081];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nsfeat" 12
+common namespace features
+.IP "nmic" 12
+Namespace Multi-path I/O and Namespace
+Sharing Capabilities
+.IP "rescap" 12
+Reservation Capabilities
+.IP "fpi" 12
+Format Progress Indicator
+.IP "anagrpid" 12
+ANA Group Identifier
+.IP "nsattr" 12
+Namespace Attributes
+.IP "rsvd9" 12
+reserved
+.IP "nvmsetid" 12
+NVM Set Identifier
+.IP "endgid" 12
+Endurance Group Identifier
+.IP "nstat" 12
+Namespace Status
+.IP "rsvd15" 12
+reserved
diff --git a/doc/man/nvme_id_iocs.2 b/doc/man/nvme_id_iocs.2
new file mode 100644
index 0000000..a9a91aa
--- /dev/null
+++ b/doc/man/nvme_id_iocs.2
@@ -0,0 +1,15 @@
+.TH "libnvme" 9 "struct nvme_id_iocs" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_iocs \- NVMe Identify IO Command Set data structure
+.SH SYNOPSIS
+struct nvme_id_iocs {
+.br
+.BI " __le64 iocsc[512];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "iocsc" 12
+List of supported IO Command Set Combination vectors
diff --git a/doc/man/nvme_id_ns.2 b/doc/man/nvme_id_ns.2
new file mode 100644
index 0000000..689f722
--- /dev/null
+++ b/doc/man/nvme_id_ns.2
@@ -0,0 +1,233 @@
+.TH "libnvme" 9 "struct nvme_id_ns" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_ns \- Identify Namespace data structure
+.SH SYNOPSIS
+struct nvme_id_ns {
+.br
+.BI " __le64 nsze;"
+.br
+.BI " __le64 ncap;"
+.br
+.BI " __le64 nuse;"
+.br
+.BI " __u8 nsfeat;"
+.br
+.BI " __u8 nlbaf;"
+.br
+.BI " __u8 flbas;"
+.br
+.BI " __u8 mc;"
+.br
+.BI " __u8 dpc;"
+.br
+.BI " __u8 dps;"
+.br
+.BI " __u8 nmic;"
+.br
+.BI " __u8 rescap;"
+.br
+.BI " __u8 fpi;"
+.br
+.BI " __u8 dlfeat;"
+.br
+.BI " __le16 nawun;"
+.br
+.BI " __le16 nawupf;"
+.br
+.BI " __le16 nacwu;"
+.br
+.BI " __le16 nabsn;"
+.br
+.BI " __le16 nabo;"
+.br
+.BI " __le16 nabspf;"
+.br
+.BI " __le16 noiob;"
+.br
+.BI " __u8 nvmcap[16];"
+.br
+.BI " __le16 npwg;"
+.br
+.BI " __le16 npwa;"
+.br
+.BI " __le16 npdg;"
+.br
+.BI " __le16 npda;"
+.br
+.BI " __le16 nows;"
+.br
+.BI " __le16 mssrl;"
+.br
+.BI " __le32 mcl;"
+.br
+.BI " __u8 msrc;"
+.br
+.BI " __u8 rsvd81;"
+.br
+.BI " __u8 nulbaf;"
+.br
+.BI " __u8 rsvd83[9];"
+.br
+.BI " __le32 anagrpid;"
+.br
+.BI " __u8 rsvd96[3];"
+.br
+.BI " __u8 nsattr;"
+.br
+.BI " __le16 nvmsetid;"
+.br
+.BI " __le16 endgid;"
+.br
+.BI " __u8 nguid[16];"
+.br
+.BI " __u8 eui64[8];"
+.br
+.BI " struct nvme_lbaf lbaf[64];"
+.br
+.BI " __u8 vs[3712];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nsze" 12
+Namespace Size indicates the total size of the namespace in
+logical blocks. The number of logical blocks is based on the
+formatted LBA size.
+.IP "ncap" 12
+Namespace Capacity indicates the maximum number of logical blocks
+that may be allocated in the namespace at any point in time. The
+number of logical blocks is based on the formatted LBA size.
+.IP "nuse" 12
+Namespace Utilization indicates the current number of logical
+blocks allocated in the namespace. This field is smaller than or
+equal to the Namespace Capacity. The number of logical blocks is
+based on the formatted LBA size.
+.IP "nsfeat" 12
+Namespace Features, see \fIenum nvme_id_nsfeat\fP.
+.IP "nlbaf" 12
+Number of LBA Formats defines the number of supported LBA data
+size and metadata size combinations supported by the namespace
+and the highest possible index to \fIstruct nvme_id_ns\fP.lbaf.
+.IP "flbas" 12
+Formatted LBA Size, see \fIenum nvme_id_ns_flbas\fP.
+.IP "mc" 12
+Metadata Capabilities, see \fIenum nvme_id_ns_mc\fP.
+.IP "dpc" 12
+End-to-end Data Protection Capabilities, see
+\fIenum nvme_id_ns_dpc\fP.
+.IP "dps" 12
+End-to-end Data Protection Type Settings, see
+\fIenum nvme_id_ns_dps\fP.
+.IP "nmic" 12
+Namespace Multi-path I/O and Namespace Sharing Capabilities, see
+\fIenum nvme_id_ns_nmic\fP.
+.IP "rescap" 12
+Reservation Capabilities, see \fIenum nvme_id_ns_rescap\fP.
+.IP "fpi" 12
+Format Progress Indicator, see \fIenum nvme_nd_ns_fpi\fP.
+.IP "dlfeat" 12
+Deallocate Logical Block Features, see \fIenum nvme_id_ns_dlfeat\fP.
+.IP "nawun" 12
+Namespace Atomic Write Unit Normal indicates the
+namespace specific size of the write operation guaranteed to be
+written atomically to the NVM during normal operation.
+.IP "nawupf" 12
+Namespace Atomic Write Unit Power Fail indicates the
+namespace specific size of the write operation guaranteed to be
+written atomically to the NVM during a power fail or error
+condition.
+.IP "nacwu" 12
+Namespace Atomic Compare & Write Unit indicates the namespace
+specific size of the write operation guaranteed to be written
+atomically to the NVM for a Compare and Write fused command.
+.IP "nabsn" 12
+Namespace Atomic Boundary Size Normal indicates the atomic
+boundary size for this namespace for the NAWUN value. This field
+is specified in logical blocks.
+.IP "nabo" 12
+Namespace Atomic Boundary Offset indicates the LBA on this
+namespace where the first atomic boundary starts.
+.IP "nabspf" 12
+Namespace Atomic Boundary Size Power Fail indicates the atomic
+boundary size for this namespace specific to the Namespace Atomic
+Write Unit Power Fail value. This field is specified in logical
+blocks.
+.IP "noiob" 12
+Namespace Optimal I/O Boundary indicates the optimal I/O boundary
+for this namespace. This field is specified in logical blocks.
+The host should construct Read and Write commands that do not
+cross the I/O boundary to achieve optimal performance.
+.IP "nvmcap" 12
+NVM Capacity indicates the total size of the NVM allocated to
+this namespace. The value is in bytes.
+.IP "npwg" 12
+Namespace Preferred Write Granularity indicates the smallest
+recommended write granularity in logical blocks for this
+namespace. This is a 0's based value.
+.IP "npwa" 12
+Namespace Preferred Write Alignment indicates the recommended
+write alignment in logical blocks for this namespace. This is a
+0's based value.
+.IP "npdg" 12
+Namespace Preferred Deallocate Granularity indicates the
+recommended granularity in logical blocks for the Dataset
+Management command with the Attribute - Deallocate bit.
+.IP "npda" 12
+Namespace Preferred Deallocate Alignment indicates the
+recommended alignment in logical blocks for the Dataset
+Management command with the Attribute - Deallocate bit
+.IP "nows" 12
+Namespace Optimal Write Size indicates the size in logical blocks
+for optimal write performance for this namespace. This is a 0's
+based value.
+.IP "mssrl" 12
+Maximum Single Source Range Length indicates the maximum number
+of logical blocks that may be specified in each valid Source Range
+field of a Copy command.
+.IP "mcl" 12
+Maximum Copy Length indicates the maximum number of logical
+blocks that may be specified in a Copy command.
+.IP "msrc" 12
+Maximum Source Range Count indicates the maximum number of Source
+Range entries that may be used to specify source data in a Copy
+command. This is a 0’s based value.
+.IP "rsvd81" 12
+Reserved
+.IP "nulbaf" 12
+Number of Unique Capability LBA Formats defines the number of
+supported user data size and metadata size combinations supported
+by the namespace that may not share the same capabilities. LBA
+formats shall be allocated in order and packed sequentially.
+.IP "rsvd83" 12
+Reserved
+.IP "anagrpid" 12
+ANA Group Identifier indicates the ANA Group Identifier of the
+ANA group of which the namespace is a member.
+.IP "rsvd96" 12
+Reserved
+.IP "nsattr" 12
+Namespace Attributes, see \fIenum nvme_id_ns_attr\fP.
+.IP "nvmsetid" 12
+NVM Set Identifier indicates the NVM Set with which this
+namespace is associated.
+.IP "endgid" 12
+Endurance Group Identifier indicates the Endurance Group with
+which this namespace is associated.
+.IP "nguid" 12
+Namespace Globally Unique Identifier contains a 128-bit value
+that is globally unique and assigned to the namespace when the
+namespace is created. This field remains fixed throughout the
+life of the namespace and is preserved across namespace and
+controller operations
+.IP "eui64" 12
+IEEE Extended Unique Identifier contains a 64-bit IEEE Extended
+Unique Identifier (EUI-64) that is globally unique and assigned
+to the namespace when the namespace is created. This field
+remains fixed throughout the life of the namespace and is
+preserved across namespace and controller operations
+.IP "lbaf" 12
+LBA Format, see \fIstruct nvme_lbaf\fP.
+.IP "vs" 12
+Vendor Specific
diff --git a/doc/man/nvme_id_ns_attr.2 b/doc/man/nvme_id_ns_attr.2
new file mode 100644
index 0000000..15bd317
--- /dev/null
+++ b/doc/man/nvme_id_ns_attr.2
@@ -0,0 +1,14 @@
+.TH "libnvme" 9 "enum nvme_id_ns_attr" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ns_attr \- Specifies attributes of the namespace.
+.SH SYNOPSIS
+enum nvme_id_ns_attr {
+.br
+.BI " NVME_NS_NSATTR_WRITE_PROTECTED"
+
+};
+.SH Constants
+.IP "NVME_NS_NSATTR_WRITE_PROTECTED" 12
+If set, then the namespace is currently
+write protected and all write access to the
+namespace shall fail.
diff --git a/doc/man/nvme_id_ns_dlfeat.2 b/doc/man/nvme_id_ns_dlfeat.2
new file mode 100644
index 0000000..248c7ac
--- /dev/null
+++ b/doc/man/nvme_id_ns_dlfeat.2
@@ -0,0 +1,50 @@
+.TH "libnvme" 9 "enum nvme_id_ns_dlfeat" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ns_dlfeat \- This field indicates information about features that affect deallocating logical blocks for this namespace.
+.SH SYNOPSIS
+enum nvme_id_ns_dlfeat {
+.br
+.BI " NVME_NS_DLFEAT_RB"
+,
+.br
+.br
+.BI " NVME_NS_DLFEAT_RB_NR"
+,
+.br
+.br
+.BI " NVME_NS_DLFEAT_RB_ALL_0S"
+,
+.br
+.br
+.BI " NVME_NS_DLFEAT_RB_ALL_FS"
+,
+.br
+.br
+.BI " NVME_NS_DLFEAT_WRITE_ZEROES"
+,
+.br
+.br
+.BI " NVME_NS_DLFEAT_CRC_GUARD"
+
+};
+.SH Constants
+.IP "NVME_NS_DLFEAT_RB" 12
+Mask to get the value of the read behavior
+.IP "NVME_NS_DLFEAT_RB_NR" 12
+Read behvaior is not reported
+.IP "NVME_NS_DLFEAT_RB_ALL_0S" 12
+A deallocated logical block returns all bytes
+cleared to 0h.
+.IP "NVME_NS_DLFEAT_RB_ALL_FS" 12
+A deallocated logical block returns all bytes
+set to FFh.
+.IP "NVME_NS_DLFEAT_WRITE_ZEROES" 12
+If set, indicates that the controller supports
+the Deallocate bit in the Write Zeroes command
+for this namespace.
+.IP "NVME_NS_DLFEAT_CRC_GUARD" 12
+If set, indicates that the Guard field for
+deallocated logical blocks that contain
+protection information is set to the CRC for
+the value read from the deallocated logical
+block and its metadata
diff --git a/doc/man/nvme_id_ns_dpc.2 b/doc/man/nvme_id_ns_dpc.2
new file mode 100644
index 0000000..6186d66
--- /dev/null
+++ b/doc/man/nvme_id_ns_dpc.2
@@ -0,0 +1,43 @@
+.TH "libnvme" 9 "enum nvme_id_ns_dpc" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ns_dpc \- This field indicates the capabilities for the end-to-end data protection feature.
+.SH SYNOPSIS
+enum nvme_id_ns_dpc {
+.br
+.BI " NVME_NS_DPC_PI_TYPE1"
+,
+.br
+.br
+.BI " NVME_NS_DPC_PI_TYPE2"
+,
+.br
+.br
+.BI " NVME_NS_DPC_PI_TYPE3"
+,
+.br
+.br
+.BI " NVME_NS_DPC_PI_FIRST"
+,
+.br
+.br
+.BI " NVME_NS_DPC_PI_LAST"
+
+};
+.SH Constants
+.IP "NVME_NS_DPC_PI_TYPE1" 12
+If set, indicates that the namespace supports
+Protection Information Type 1.
+.IP "NVME_NS_DPC_PI_TYPE2" 12
+If set, indicates that the namespace supports
+Protection Information Type 2.
+.IP "NVME_NS_DPC_PI_TYPE3" 12
+If set, indicates that the namespace supports
+Protection Information Type 3.
+.IP "NVME_NS_DPC_PI_FIRST" 12
+If set, indicates that the namespace supports
+protection information transferred as the first eight
+bytes of metadata.
+.IP "NVME_NS_DPC_PI_LAST" 12
+If set, indicates that the namespace supports
+protection information transferred as the last eight
+bytes of metadata.
diff --git a/doc/man/nvme_id_ns_dps.2 b/doc/man/nvme_id_ns_dps.2
new file mode 100644
index 0000000..1f61b8e
--- /dev/null
+++ b/doc/man/nvme_id_ns_dps.2
@@ -0,0 +1,44 @@
+.TH "libnvme" 9 "enum nvme_id_ns_dps" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ns_dps \- This field indicates the Type settings for the end-to-end data protection feature.
+.SH SYNOPSIS
+enum nvme_id_ns_dps {
+.br
+.BI " NVME_NS_DPS_PI_NONE"
+,
+.br
+.br
+.BI " NVME_NS_DPS_PI_TYPE1"
+,
+.br
+.br
+.BI " NVME_NS_DPS_PI_TYPE2"
+,
+.br
+.br
+.BI " NVME_NS_DPS_PI_TYPE3"
+,
+.br
+.br
+.BI " NVME_NS_DPS_PI_MASK"
+,
+.br
+.br
+.BI " NVME_NS_DPS_PI_FIRST"
+
+};
+.SH Constants
+.IP "NVME_NS_DPS_PI_NONE" 12
+Protection information is not enabled
+.IP "NVME_NS_DPS_PI_TYPE1" 12
+Protection information is enabled, Type 1
+.IP "NVME_NS_DPS_PI_TYPE2" 12
+Protection information is enabled, Type 2
+.IP "NVME_NS_DPS_PI_TYPE3" 12
+Protection information is enabled, Type 3
+.IP "NVME_NS_DPS_PI_MASK" 12
+Mask to get the value of the PI type
+.IP "NVME_NS_DPS_PI_FIRST" 12
+If set, indicates that the protection information, if
+enabled, is transferred as the first eight bytes of
+metadata.
diff --git a/doc/man/nvme_id_ns_flbas.2 b/doc/man/nvme_id_ns_flbas.2
new file mode 100644
index 0000000..68be9cf
--- /dev/null
+++ b/doc/man/nvme_id_ns_flbas.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "enum nvme_id_ns_flbas" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ns_flbas \- This field indicates the LBA data size & metadata size combination that the namespace has been formatted with
+.SH SYNOPSIS
+enum nvme_id_ns_flbas {
+.br
+.BI " NVME_NS_FLBAS_LOWER_MASK"
+,
+.br
+.br
+.BI " NVME_NS_FLBAS_META_EXT"
+,
+.br
+.br
+.BI " NVME_NS_FLBAS_HIGHER_MASK"
+
+};
+.SH Constants
+.IP "NVME_NS_FLBAS_LOWER_MASK" 12
+Mask to get the index of one of the supported
+LBA Formats's least significant
+4bits indicated in
+:c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf.
+.IP "NVME_NS_FLBAS_META_EXT" 12
+Applicable only if format contains metadata. If
+this bit is set, indicates that the metadata is
+transferred at the end of the data LBA, creating an
+extended data LBA. If cleared, indicates that all
+of the metadata for a command is transferred as a
+separate contiguous buffer of data.
+.IP "NVME_NS_FLBAS_HIGHER_MASK" 12
+Mask to get the index of one of
+the supported LBA Formats's most significant
+2bits indicated in
+:c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf.
diff --git a/doc/man/nvme_id_ns_granularity_desc.2 b/doc/man/nvme_id_ns_granularity_desc.2
new file mode 100644
index 0000000..669d26c
--- /dev/null
+++ b/doc/man/nvme_id_ns_granularity_desc.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_id_ns_granularity_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_ns_granularity_desc \- Namespace Granularity Descriptor
+.SH SYNOPSIS
+struct nvme_id_ns_granularity_desc {
+.br
+.BI " __le64 nszegran;"
+.br
+.BI " __le64 ncapgran;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nszegran" 12
+Namespace Size Granularity
+.IP "ncapgran" 12
+Namespace Capacity Granularity
diff --git a/doc/man/nvme_id_ns_granularity_list.2 b/doc/man/nvme_id_ns_granularity_list.2
new file mode 100644
index 0000000..d501d56
--- /dev/null
+++ b/doc/man/nvme_id_ns_granularity_list.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_id_ns_granularity_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_ns_granularity_list \- Namespace Granularity List
+.SH SYNOPSIS
+struct nvme_id_ns_granularity_list {
+.br
+.BI " __le32 attributes;"
+.br
+.BI " __u8 num_descriptors;"
+.br
+.BI " __u8 rsvd5[27];"
+.br
+.BI " struct nvme_id_ns_granularity_desc entry[NVME_ID_ND_DESCRIPTOR_MAX];"
+.br
+.BI " __u8 rsvd288[3808];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "attributes" 12
+Namespace Granularity Attributes
+.IP "num_descriptors" 12
+Number of Descriptors
+.IP "rsvd5" 12
+reserved
+.IP "entry" 12
+Namespace Granularity Descriptor
+.IP "rsvd288" 12
+reserved
diff --git a/doc/man/nvme_id_ns_mc.2 b/doc/man/nvme_id_ns_mc.2
new file mode 100644
index 0000000..c7ccb04
--- /dev/null
+++ b/doc/man/nvme_id_ns_mc.2
@@ -0,0 +1,21 @@
+.TH "libnvme" 9 "enum nvme_id_ns_mc" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ns_mc \- This field indicates the capabilities for metadata.
+.SH SYNOPSIS
+enum nvme_id_ns_mc {
+.br
+.BI " NVME_NS_MC_EXTENDED"
+,
+.br
+.br
+.BI " NVME_NS_MC_SEPARATE"
+
+};
+.SH Constants
+.IP "NVME_NS_MC_EXTENDED" 12
+If set, indicates the namespace supports the metadata
+being transferred as part of a separate buffer that is
+specified in the Metadata Pointer.
+.IP "NVME_NS_MC_SEPARATE" 12
+If set, indicates that the namespace supports the
+metadata being transferred as part of an extended data LBA.
diff --git a/doc/man/nvme_id_ns_nmic.2 b/doc/man/nvme_id_ns_nmic.2
new file mode 100644
index 0000000..faee303
--- /dev/null
+++ b/doc/man/nvme_id_ns_nmic.2
@@ -0,0 +1,13 @@
+.TH "libnvme" 9 "enum nvme_id_ns_nmic" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ns_nmic \- This field specifies multi-path I/O and namespace sharing capabilities of the namespace.
+.SH SYNOPSIS
+enum nvme_id_ns_nmic {
+.br
+.BI " NVME_NS_NMIC_SHARED"
+
+};
+.SH Constants
+.IP "NVME_NS_NMIC_SHARED" 12
+If set, then the namespace may be attached to two or
+more controllers in the NVM subsystem concurrently
diff --git a/doc/man/nvme_id_ns_rescap.2 b/doc/man/nvme_id_ns_rescap.2
new file mode 100644
index 0000000..301882f
--- /dev/null
+++ b/doc/man/nvme_id_ns_rescap.2
@@ -0,0 +1,62 @@
+.TH "libnvme" 9 "enum nvme_id_ns_rescap" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_ns_rescap \- This field indicates the reservation capabilities of the namespace.
+.SH SYNOPSIS
+enum nvme_id_ns_rescap {
+.br
+.BI " NVME_NS_RESCAP_PTPL"
+,
+.br
+.br
+.BI " NVME_NS_RESCAP_WE"
+,
+.br
+.br
+.BI " NVME_NS_RESCAP_EA"
+,
+.br
+.br
+.BI " NVME_NS_RESCAP_WERO"
+,
+.br
+.br
+.BI " NVME_NS_RESCAP_EARO"
+,
+.br
+.br
+.BI " NVME_NS_RESCAP_WEAR"
+,
+.br
+.br
+.BI " NVME_NS_RESCAP_EAAR"
+,
+.br
+.br
+.BI " NVME_NS_RESCAP_IEK_13"
+
+};
+.SH Constants
+.IP "NVME_NS_RESCAP_PTPL" 12
+If set, indicates that the namespace supports the
+Persist Through Power Loss capability.
+.IP "NVME_NS_RESCAP_WE" 12
+If set, indicates that the namespace supports the
+Write Exclusive reservation type.
+.IP "NVME_NS_RESCAP_EA" 12
+If set, indicates that the namespace supports the
+Exclusive Access reservation type.
+.IP "NVME_NS_RESCAP_WERO" 12
+If set, indicates that the namespace supports the
+Write Exclusive - Registrants Only reservation type.
+.IP "NVME_NS_RESCAP_EARO" 12
+If set, indicates that the namespace supports the
+Exclusive Access - Registrants Only reservation type.
+.IP "NVME_NS_RESCAP_WEAR" 12
+If set, indicates that the namespace supports the
+Write Exclusive - All Registrants reservation type.
+.IP "NVME_NS_RESCAP_EAAR" 12
+If set, indicates that the namespace supports the
+Exclusive Access - All Registrants reservation type.
+.IP "NVME_NS_RESCAP_IEK_13" 12
+If set, indicates that Ignore Existing Key is used
+as defined in revision 1.3 or later of this specification.
diff --git a/doc/man/nvme_id_nsfeat.2 b/doc/man/nvme_id_nsfeat.2
new file mode 100644
index 0000000..fb78c04
--- /dev/null
+++ b/doc/man/nvme_id_nsfeat.2
@@ -0,0 +1,50 @@
+.TH "libnvme" 9 "enum nvme_id_nsfeat" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_nsfeat \- This field defines features of the namespace.
+.SH SYNOPSIS
+enum nvme_id_nsfeat {
+.br
+.BI " NVME_NS_FEAT_THIN"
+,
+.br
+.br
+.BI " NVME_NS_FEAT_NATOMIC"
+,
+.br
+.br
+.BI " NVME_NS_FEAT_DULBE"
+,
+.br
+.br
+.BI " NVME_NS_FEAT_ID_REUSE"
+,
+.br
+.br
+.BI " NVME_NS_FEAT_IO_OPT"
+
+};
+.SH Constants
+.IP "NVME_NS_FEAT_THIN" 12
+If set, indicates that the namespace supports thin
+provisioning. Specifically, the Namespace Capacity
+reported may be less than the Namespace Size.
+.IP "NVME_NS_FEAT_NATOMIC" 12
+If set, indicates that the fields NAWUN, NAWUPF, and
+NACWU are defined for this namespace and should be
+used by the host for this namespace instead of the
+AWUN, AWUPF, and ACWU fields in the Identify
+Controller data structure.
+.IP "NVME_NS_FEAT_DULBE" 12
+If set, indicates that the controller supports the
+Deallocated or Unwritten Logical Block error for
+this namespace.
+.IP "NVME_NS_FEAT_ID_REUSE" 12
+If set, indicates that the value in the NGUID field
+for this namespace, if non- zero, is never reused by
+the controller and that the value in the EUI64 field
+for this namespace, if non-zero, is never reused by
+the controller.
+.IP "NVME_NS_FEAT_IO_OPT" 12
+If set, indicates that the fields NPWG, NPWA, NPDG,
+NPDA, and NOWS are defined for this namespace and
+should be used by the host for I/O optimization
diff --git a/doc/man/nvme_id_nvmset_list.2 b/doc/man/nvme_id_nvmset_list.2
new file mode 100644
index 0000000..6b53ed5
--- /dev/null
+++ b/doc/man/nvme_id_nvmset_list.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_id_nvmset_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_nvmset_list \- NVM set list
+.SH SYNOPSIS
+struct nvme_id_nvmset_list {
+.br
+.BI " __u8 nid;"
+.br
+.BI " __u8 rsvd1[127];"
+.br
+.BI " struct nvme_nvmset_attr ent[NVME_ID_NVMSET_LIST_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nid" 12
+Nvmset id
+.IP "rsvd1" 12
+Reserved
+.IP "ent" 12
+nvmset id list
diff --git a/doc/man/nvme_id_psd.2 b/doc/man/nvme_id_psd.2
new file mode 100644
index 0000000..2a377ff
--- /dev/null
+++ b/doc/man/nvme_id_psd.2
@@ -0,0 +1,95 @@
+.TH "libnvme" 9 "struct nvme_id_psd" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_psd \- Power Management data structure
+.SH SYNOPSIS
+struct nvme_id_psd {
+.br
+.BI " __le16 mp;"
+.br
+.BI " __u8 rsvd2;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __le32 enlat;"
+.br
+.BI " __le32 exlat;"
+.br
+.BI " __u8 rrt;"
+.br
+.BI " __u8 rrl;"
+.br
+.BI " __u8 rwt;"
+.br
+.BI " __u8 rwl;"
+.br
+.BI " __le16 idlp;"
+.br
+.BI " __u8 ips;"
+.br
+.BI " __u8 rsvd19;"
+.br
+.BI " __le16 actp;"
+.br
+.BI " __u8 apws;"
+.br
+.BI " __u8 rsvd23[9];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "mp" 12
+Maximum Power indicates the sustained maximum power consumed by the
+NVM subsystem in this power state. The power in Watts is equal to
+the value in this field multiplied by the scale specified in the Max
+Power Scale bit (see \fIenum nvme_psd_flags\fP). A value of 0 indicates
+Maximum Power is not reported.
+.IP "rsvd2" 12
+Reserved
+.IP "flags" 12
+Additional decoding flags, see \fIenum nvme_psd_flags\fP.
+.IP "enlat" 12
+Entry Latency indicates the maximum latency in microseconds
+associated with entering this power state. A value of 0 indicates
+Entry Latency is not reported.
+.IP "exlat" 12
+Exit Latency indicates the maximum latency in microseconds
+associated with exiting this power state. A value of 0 indicates
+Exit Latency is not reported.
+.IP "rrt" 12
+Relative Read Throughput indicates the read throughput rank
+associated with this power state relative to others. The value in
+this is less than the number of supported power states.
+.IP "rrl" 12
+Relative Read Latency indicates the read latency rank associated
+with this power state relative to others. The value in this field is
+less than the number of supported power states.
+.IP "rwt" 12
+Relative Write Throughput indicates write throughput rank associated
+with this power state relative to others. The value in this field is
+less than the number of supported power states
+.IP "rwl" 12
+Relative Write Latency indicates the write latency rank associated
+with this power state relative to others. The value in this field is
+less than the number of supported power states
+.IP "idlp" 12
+Idle Power indicates the typical power consumed by the NVM
+subsystem over 30 seconds in this power state when idle.
+.IP "ips" 12
+Idle Power Scale indicates the scale for \fIstruct nvme_id_psd\fP.idlp,
+see \fIenum nvme_psd_ps\fP for decoding this field.
+.IP "rsvd19" 12
+Reserved
+.IP "actp" 12
+Active Power indicates the largest average power consumed by the
+NVM subsystem over a 10 second period in this power state with
+the workload indicated in the Active Power Workload field.
+.IP "apws" 12
+Bits 7-6: Active Power Scale(APS) indicates the scale for the \fIstruct
+nvme_id_psd\fP.actp, see \fIenum nvme_psd_ps\fP for decoding this value.
+Bits 2-0: Active Power Workload(APW) indicates the workload
+used to calculate maximum power for this power state.
+See \fIenum nvme_psd_workload\fP for decoding this field.
+.IP "rsvd23" 12
+Reserved
diff --git a/doc/man/nvme_id_uuid.2 b/doc/man/nvme_id_uuid.2
new file mode 100644
index 0000000..2be553d
--- /dev/null
+++ b/doc/man/nvme_id_uuid.2
@@ -0,0 +1,26 @@
+.TH "libnvme" 9 "enum nvme_id_uuid" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_id_uuid \- Identifier Association
+.SH SYNOPSIS
+enum nvme_id_uuid {
+.br
+.BI " NVME_ID_UUID_HDR_ASSOCIATION_MASK"
+,
+.br
+.br
+.BI " NVME_ID_UUID_ASSOCIATION_NONE"
+,
+.br
+.br
+.BI " NVME_ID_UUID_ASSOCIATION_VENDOR"
+,
+.br
+.br
+.BI " NVME_ID_UUID_ASSOCIATION_SUBSYSTEM_VENDOR"
+
+};
+.SH Constants
+.IP "NVME_ID_UUID_HDR_ASSOCIATION_MASK" 12
+.IP "NVME_ID_UUID_ASSOCIATION_NONE" 12
+.IP "NVME_ID_UUID_ASSOCIATION_VENDOR" 12
+.IP "NVME_ID_UUID_ASSOCIATION_SUBSYSTEM_VENDOR" 12
diff --git a/doc/man/nvme_id_uuid_list.2 b/doc/man/nvme_id_uuid_list.2
new file mode 100644
index 0000000..f3630f4
--- /dev/null
+++ b/doc/man/nvme_id_uuid_list.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_id_uuid_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_uuid_list \- UUID list
+.SH SYNOPSIS
+struct nvme_id_uuid_list {
+.br
+.BI " __u8 rsvd0[32];"
+.br
+.BI " struct nvme_id_uuid_list_entry entry[NVME_ID_UUID_LIST_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "rsvd0" 12
+reserved
+.IP "entry" 12
+UUID list entry
diff --git a/doc/man/nvme_id_uuid_list_entry.2 b/doc/man/nvme_id_uuid_list_entry.2
new file mode 100644
index 0000000..db887c4
--- /dev/null
+++ b/doc/man/nvme_id_uuid_list_entry.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_id_uuid_list_entry" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_id_uuid_list_entry \- UUID List Entry
+.SH SYNOPSIS
+struct nvme_id_uuid_list_entry {
+.br
+.BI " __u8 header;"
+.br
+.BI " __u8 rsvd1[15];"
+.br
+.BI " __u8 uuid[16];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "header" 12
+UUID Lists Entry Header
+.IP "rsvd1" 12
+reserved
+.IP "uuid" 12
+128-bit Universally Unique Identifier
diff --git a/doc/man/nvme_identify.2 b/doc/man/nvme_identify.2
new file mode 100644
index 0000000..332826a
--- /dev/null
+++ b/doc/man/nvme_identify.2
@@ -0,0 +1,15 @@
+.TH "nvme_identify" 9 "nvme_identify" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify \- Send the NVMe Identify command
+.SH SYNOPSIS
+.B "int" nvme_identify
+.BI "(struct nvme_identify_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_identify_args\fP argument structure
+.SH "DESCRIPTION"
+The Identify command returns a data buffer that describes information about
+the NVM subsystem, the controller or the namespace(s).
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_active_ns_list.2 b/doc/man/nvme_identify_active_ns_list.2
new file mode 100644
index 0000000..a47d827
--- /dev/null
+++ b/doc/man/nvme_identify_active_ns_list.2
@@ -0,0 +1,24 @@
+.TH "nvme_identify_active_ns_list" 9 "nvme_identify_active_ns_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_active_ns_list \- Retrieves active namespaces id list
+.SH SYNOPSIS
+.B "int" nvme_identify_active_ns_list
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ns_list *list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Return namespaces greater than this identifier
+.IP "list" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+A list of 1024 namespace IDs is returned to the host containing NSIDs in
+increasing order that are greater than the value specified in the Namespace
+Identifier (nsid) field of the command.
+
+See \fIstruct nvme_ns_list\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_active_ns_list_csi.2 b/doc/man/nvme_identify_active_ns_list_csi.2
new file mode 100644
index 0000000..1330390
--- /dev/null
+++ b/doc/man/nvme_identify_active_ns_list_csi.2
@@ -0,0 +1,28 @@
+.TH "nvme_identify_active_ns_list_csi" 9 "nvme_identify_active_ns_list_csi" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_active_ns_list_csi \- Active namespace ID list associated with a specified I/O command set
+.SH SYNOPSIS
+.B "int" nvme_identify_active_ns_list_csi
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "enum nvme_csi csi " ","
+.BI "struct nvme_ns_list *ns_list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Return namespaces greater than this identifier
+.IP "csi" 12
+Command Set Identifier
+.IP "ns_list" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+A list of 1024 namespace IDs is returned to the host containing active
+NSIDs in increasing order that are greater than the value specified in
+the Namespace Identifier (nsid) field of the command and matching the
+I/O Command Set specified in the \fIcsi\fP argument.
+
+See \fIstruct nvme_ns_list\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_allocated_ns.2 b/doc/man/nvme_identify_allocated_ns.2
new file mode 100644
index 0000000..6b3e2b2
--- /dev/null
+++ b/doc/man/nvme_identify_allocated_ns.2
@@ -0,0 +1,18 @@
+.TH "nvme_identify_allocated_ns" 9 "nvme_identify_allocated_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_allocated_ns \- Same as nvme_identify_ns, but only for allocated namespaces
+.SH SYNOPSIS
+.B "int" nvme_identify_allocated_ns
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_id_ns *ns " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace to identify
+.IP "ns" 12
+User space destination address to transfer the data
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_allocated_ns_list.2 b/doc/man/nvme_identify_allocated_ns_list.2
new file mode 100644
index 0000000..eb295cb
--- /dev/null
+++ b/doc/man/nvme_identify_allocated_ns_list.2
@@ -0,0 +1,24 @@
+.TH "nvme_identify_allocated_ns_list" 9 "nvme_identify_allocated_ns_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_allocated_ns_list \- Retrieves allocated namespace id list
+.SH SYNOPSIS
+.B "int" nvme_identify_allocated_ns_list
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ns_list *list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Return namespaces greater than this identifier
+.IP "list" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+A list of 1024 namespace IDs is returned to the host containing NSIDs in
+increasing order that are greater than the value specified in the Namespace
+Identifier (nsid) field of the command.
+
+See \fIstruct nvme_ns_list\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_allocated_ns_list_csi.2 b/doc/man/nvme_identify_allocated_ns_list_csi.2
new file mode 100644
index 0000000..07f9820
--- /dev/null
+++ b/doc/man/nvme_identify_allocated_ns_list_csi.2
@@ -0,0 +1,28 @@
+.TH "nvme_identify_allocated_ns_list_csi" 9 "nvme_identify_allocated_ns_list_csi" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_allocated_ns_list_csi \- Allocated namespace ID list associated with a specified I/O command set
+.SH SYNOPSIS
+.B "int" nvme_identify_allocated_ns_list_csi
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "enum nvme_csi csi " ","
+.BI "struct nvme_ns_list *ns_list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Return namespaces greater than this identifier
+.IP "csi" 12
+Command Set Identifier
+.IP "ns_list" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+A list of 1024 namespace IDs is returned to the host containing allocated
+NSIDs in increasing order that are greater than the value specified in
+the \fInsid\fP field of the command and matching the I/O Command Set
+specified in the \fIcsi\fP argument.
+
+See \fIstruct nvme_ns_list\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_cns.2 b/doc/man/nvme_identify_cns.2
new file mode 100644
index 0000000..7fc4dab
--- /dev/null
+++ b/doc/man/nvme_identify_cns.2
@@ -0,0 +1,163 @@
+.TH "libnvme" 9 "enum nvme_identify_cns" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_identify_cns \- Identify - CNS Values
+.SH SYNOPSIS
+enum nvme_identify_cns {
+.br
+.BI " NVME_IDENTIFY_CNS_NS"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CTRL"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_NS_ACTIVE_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_NS_DESC_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_NVMSET_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CSI_NS"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CSI_CTRL"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_ALLOCATED_NS"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_NS_CTRL_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CTRL_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_NS_GRANULARITY"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_UUID_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_DOMAIN_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE"
+,
+.br
+.br
+.BI " NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE"
+
+};
+.SH Constants
+.IP "NVME_IDENTIFY_CNS_NS" 12
+Identify Namespace data structure
+.IP "NVME_IDENTIFY_CNS_CTRL" 12
+Identify Controller data structure
+.IP "NVME_IDENTIFY_CNS_NS_ACTIVE_LIST" 12
+Active Namespace ID list
+.IP "NVME_IDENTIFY_CNS_NS_DESC_LIST" 12
+Namespace Identification Descriptor list
+.IP "NVME_IDENTIFY_CNS_NVMSET_LIST" 12
+NVM Set List
+.IP "NVME_IDENTIFY_CNS_CSI_NS" 12
+I/O Command Set specific Identify
+Namespace data structure
+.IP "NVME_IDENTIFY_CNS_CSI_CTRL" 12
+I/O Command Set specific Identify
+Controller data structure
+.IP "NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST" 12
+Active Namespace ID list associated
+with the specified I/O Command Set
+.IP "NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS" 12
+I/O Command Set Independent Identify
+.IP "NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT" 12
+Namespace user data format
+.IP "NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT" 12
+I/O Command Set specific user data
+format
+Namespace data structure
+.IP "NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST" 12
+Allocated Namespace ID list
+.IP "NVME_IDENTIFY_CNS_ALLOCATED_NS" 12
+Identify Namespace data structure for
+the specified allocated NSID
+.IP "NVME_IDENTIFY_CNS_NS_CTRL_LIST" 12
+Controller List of controllers attached
+to the specified NSID
+.IP "NVME_IDENTIFY_CNS_CTRL_LIST" 12
+Controller List of controllers that exist
+in the NVM subsystem
+.IP "NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP" 12
+Primary Controller Capabilities data
+structure for the specified primary controller
+.IP "NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST" 12
+Secondary Controller list of controllers
+associated with the primary controller
+processing the command
+.IP "NVME_IDENTIFY_CNS_NS_GRANULARITY" 12
+A Namespace Granularity List
+.IP "NVME_IDENTIFY_CNS_UUID_LIST" 12
+A UUID List
+.IP "NVME_IDENTIFY_CNS_DOMAIN_LIST" 12
+Domain List
+.IP "NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID" 12
+Endurance Group List
+.IP "NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST" 12
+I/O Command Set specific Allocated Namespace
+ID list
+.IP "NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE" 12
+I/O Command Set specific ID Namespace
+Data Structure for Allocated Namespace ID
+.IP "NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE" 12
+Base Specification 2.0a section 5.17.2.21
diff --git a/doc/man/nvme_identify_ctrl.2 b/doc/man/nvme_identify_ctrl.2
new file mode 100644
index 0000000..a935da9
--- /dev/null
+++ b/doc/man/nvme_identify_ctrl.2
@@ -0,0 +1,19 @@
+.TH "nvme_identify_ctrl" 9 "nvme_identify_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_ctrl \- Retrieves nvme identify controller
+.SH SYNOPSIS
+.B "int" nvme_identify_ctrl
+.BI "(int fd " ","
+.BI "struct nvme_id_ctrl *id " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "id" 12
+User space destination address to transfer the data,
+.SH "DESCRIPTION"
+Sends nvme identify with CNS value NVME_IDENTIFY_CNS_CTRL.
+
+See \fIstruct nvme_id_ctrl\fP for details on the data returned.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_ctrl_csi.2 b/doc/man/nvme_identify_ctrl_csi.2
new file mode 100644
index 0000000..f0e8bd4
--- /dev/null
+++ b/doc/man/nvme_identify_ctrl_csi.2
@@ -0,0 +1,22 @@
+.TH "nvme_identify_ctrl_csi" 9 "nvme_identify_ctrl_csi" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_ctrl_csi \- I/O command set specific Identify Controller data
+.SH SYNOPSIS
+.B "int" nvme_identify_ctrl_csi
+.BI "(int fd " ","
+.BI "enum nvme_csi csi " ","
+.BI "void *data " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "csi" 12
+Command Set Identifier
+.IP "data" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+An I/O Command Set specific Identify Controller data structure is returned
+to the host for the controller processing the command. The specific Identify
+Controller data structure to be returned is specified by \fIcsi\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_ctrl_list.2 b/doc/man/nvme_identify_ctrl_list.2
new file mode 100644
index 0000000..60cf67d
--- /dev/null
+++ b/doc/man/nvme_identify_ctrl_list.2
@@ -0,0 +1,24 @@
+.TH "nvme_identify_ctrl_list" 9 "nvme_identify_ctrl_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_ctrl_list \- Retrieves identify controller list
+.SH SYNOPSIS
+.B "int" nvme_identify_ctrl_list
+.BI "(int fd " ","
+.BI "__u16 cntid " ","
+.BI "struct nvme_ctrl_list *cntlist " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "cntid" 12
+Starting CNTLID to return in the list
+.IP "cntlist" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+Up to 2047 controller identifiers is returned containing a controller
+identifier greater than or equal to the controller identifier specified in
+\fIcntid\fP.
+
+See \fIstruct nvme_ctrl_list\fP for a definition of the structure returned.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_domain_list.2 b/doc/man/nvme_identify_domain_list.2
new file mode 100644
index 0000000..c5e729b
--- /dev/null
+++ b/doc/man/nvme_identify_domain_list.2
@@ -0,0 +1,25 @@
+.TH "nvme_identify_domain_list" 9 "nvme_identify_domain_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_domain_list \- Domain list data
+.SH SYNOPSIS
+.B "int" nvme_identify_domain_list
+.BI "(int fd " ","
+.BI "__u16 domid " ","
+.BI "struct nvme_id_domain_list *list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "domid" 12
+Domain ID
+.IP "list" 12
+User space destination address to transfer data
+.SH "DESCRIPTION"
+A list of 31 domain IDs is returned to the host containing domain
+attributes in increasing order that are greater than the value
+specified in the \fIdomid\fP field.
+
+See \fIstruct nvme_identify_domain_attr\fP for the definition of the
+returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_endurance_group_list.2 b/doc/man/nvme_identify_endurance_group_list.2
new file mode 100644
index 0000000..7e0f9b2
--- /dev/null
+++ b/doc/man/nvme_identify_endurance_group_list.2
@@ -0,0 +1,18 @@
+.TH "nvme_identify_endurance_group_list" 9 "nvme_identify_endurance_group_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_endurance_group_list \- Endurance group list data
+.SH SYNOPSIS
+.B "int" nvme_identify_endurance_group_list
+.BI "(int fd " ","
+.BI "__u16 endgrp_id " ","
+.BI "struct nvme_id_endurance_group_list *list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "endgrp_id" 12
+Endurance group identifier
+.IP "list" 12
+Array of endurance group identifiers
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_independent_identify_ns.2 b/doc/man/nvme_identify_independent_identify_ns.2
new file mode 100644
index 0000000..53269f3
--- /dev/null
+++ b/doc/man/nvme_identify_independent_identify_ns.2
@@ -0,0 +1,22 @@
+.TH "nvme_identify_independent_identify_ns" 9 "nvme_identify_independent_identify_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_independent_identify_ns \- I/O command set independent Identify namespace data
+.SH SYNOPSIS
+.B "int" nvme_identify_independent_identify_ns
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_id_independent_id_ns *ns " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Return namespaces greater than this identifier
+.IP "ns" 12
+I/O Command Set Independent Identify Namespace data
+structure
+.SH "DESCRIPTION"
+The I/O command set independent Identify namespace data structure for
+the namespace identified with \fIns\fP is returned to the host.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_iocs.2 b/doc/man/nvme_identify_iocs.2
new file mode 100644
index 0000000..ac4c8a6
--- /dev/null
+++ b/doc/man/nvme_identify_iocs.2
@@ -0,0 +1,21 @@
+.TH "nvme_identify_iocs" 9 "nvme_identify_iocs" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_iocs \- I/O command set data structure
+.SH SYNOPSIS
+.B "int" nvme_identify_iocs
+.BI "(int fd " ","
+.BI "__u16 cntlid " ","
+.BI "struct nvme_id_iocs *iocs " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "cntlid" 12
+Controller ID
+.IP "iocs" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+Retrieves list of the controller's supported io command set vectors. See
+\fIstruct nvme_id_iocs\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_iocs_ns_csi_user_data_format.2 b/doc/man/nvme_identify_iocs_ns_csi_user_data_format.2
new file mode 100644
index 0000000..c1fe93c
--- /dev/null
+++ b/doc/man/nvme_identify_iocs_ns_csi_user_data_format.2
@@ -0,0 +1,28 @@
+.TH "nvme_identify_iocs_ns_csi_user_data_format" 9 "nvme_identify_iocs_ns_csi_user_data_format" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_iocs_ns_csi_user_data_format \- Identify I/O command set namespace data structure
+.SH SYNOPSIS
+.B "int" nvme_identify_iocs_ns_csi_user_data_format
+.BI "(int fd " ","
+.BI "__u16 user_data_format " ","
+.BI "__u8 uuidx " ","
+.BI "enum nvme_csi csi " ","
+.BI "void *data " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "user_data_format" 12
+Return namespaces capability of identifier
+.IP "uuidx" 12
+UUID selection, if supported
+.IP "csi" 12
+Command Set Identifier
+.IP "data" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+I/O Command Set specific Identify Namespace data structure for
+the specified User Data Format index containing the namespace
+capabilities for the I/O Command Set specified in the CSI field.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_ns.2 b/doc/man/nvme_identify_ns.2
new file mode 100644
index 0000000..7f49f72
--- /dev/null
+++ b/doc/man/nvme_identify_ns.2
@@ -0,0 +1,29 @@
+.TH "nvme_identify_ns" 9 "nvme_identify_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_ns \- Retrieves nvme identify namespace
+.SH SYNOPSIS
+.B "int" nvme_identify_ns
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_id_ns *ns " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace to identify
+.IP "ns" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+If the Namespace Identifier (NSID) field specifies an active NSID, then the
+Identify Namespace data structure is returned to the host for that specified
+namespace.
+
+If the controller supports the Namespace Management capability and the NSID
+field is set to NVME_NSID_ALL, then the controller returns an Identify Namespace
+data structure that specifies capabilities that are common across namespaces
+for this controller.
+
+See \fIstruct nvme_id_ns\fP for details on the structure returned.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_ns_csi.2 b/doc/man/nvme_identify_ns_csi.2
new file mode 100644
index 0000000..572cab3
--- /dev/null
+++ b/doc/man/nvme_identify_ns_csi.2
@@ -0,0 +1,27 @@
+.TH "nvme_identify_ns_csi" 9 "nvme_identify_ns_csi" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_ns_csi \- I/O command set specific identify namespace data
+.SH SYNOPSIS
+.B "int" nvme_identify_ns_csi
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u8 uuidx " ","
+.BI "enum nvme_csi csi " ","
+.BI "void *data " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace to identify
+.IP "uuidx" 12
+UUID Index for differentiating vendor specific encoding
+.IP "csi" 12
+Command Set Identifier
+.IP "data" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+An I/O Command Set specific Identify Namespace data structure is returned
+for the namespace specified in \fInsid\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_ns_csi_user_data_format.2 b/doc/man/nvme_identify_ns_csi_user_data_format.2
new file mode 100644
index 0000000..3adaebb
--- /dev/null
+++ b/doc/man/nvme_identify_ns_csi_user_data_format.2
@@ -0,0 +1,27 @@
+.TH "nvme_identify_ns_csi_user_data_format" 9 "nvme_identify_ns_csi_user_data_format" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_ns_csi_user_data_format \- Identify namespace user data format
+.SH SYNOPSIS
+.B "int" nvme_identify_ns_csi_user_data_format
+.BI "(int fd " ","
+.BI "__u16 user_data_format " ","
+.BI "__u8 uuidx " ","
+.BI "enum nvme_csi csi " ","
+.BI "void *data " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "user_data_format" 12
+Return namespaces capability of identifier
+.IP "uuidx" 12
+UUID selection, if supported
+.IP "csi" 12
+Command Set Identifier
+.IP "data" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+Identify Namespace data structure for the specified User Data Format
+index containing the namespace capabilities for the NVM Command Set.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_ns_descs.2 b/doc/man/nvme_identify_ns_descs.2
new file mode 100644
index 0000000..ff8ea83
--- /dev/null
+++ b/doc/man/nvme_identify_ns_descs.2
@@ -0,0 +1,26 @@
+.TH "nvme_identify_ns_descs" 9 "nvme_identify_ns_descs" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_ns_descs \- Retrieves namespace descriptor list
+.SH SYNOPSIS
+.B "int" nvme_identify_ns_descs
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ns_id_desc *descs " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+The namespace id to retrieve descriptors
+.IP "descs" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+A list of Namespace Identification Descriptor structures is returned to the
+host for the namespace specified in the Namespace Identifier (NSID) field if
+it is an active NSID.
+
+The data returned is in the form of an array of 'struct nvme_ns_id_desc'.
+
+See \fIstruct nvme_ns_id_desc\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_ns_granularity.2 b/doc/man/nvme_identify_ns_granularity.2
new file mode 100644
index 0000000..ad351b0
--- /dev/null
+++ b/doc/man/nvme_identify_ns_granularity.2
@@ -0,0 +1,22 @@
+.TH "nvme_identify_ns_granularity" 9 "nvme_identify_ns_granularity" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_ns_granularity \- Retrieves namespace granularity identification
+.SH SYNOPSIS
+.B "int" nvme_identify_ns_granularity
+.BI "(int fd " ","
+.BI "struct nvme_id_ns_granularity_list *gr_list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "gr_list" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+If the controller supports reporting of Namespace Granularity, then a
+Namespace Granularity List is returned to the host for up to sixteen
+namespace granularity descriptors
+
+See \fIstruct nvme_id_ns_granularity_list\fP for the definition of the returned
+structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_nsid_ctrl_list.2 b/doc/man/nvme_identify_nsid_ctrl_list.2
new file mode 100644
index 0000000..9667f9c
--- /dev/null
+++ b/doc/man/nvme_identify_nsid_ctrl_list.2
@@ -0,0 +1,27 @@
+.TH "nvme_identify_nsid_ctrl_list" 9 "nvme_identify_nsid_ctrl_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_nsid_ctrl_list \- Retrieves controller list attached to an nsid
+.SH SYNOPSIS
+.B "int" nvme_identify_nsid_ctrl_list
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u16 cntid " ","
+.BI "struct nvme_ctrl_list *cntlist " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Return controllers that are attached to this nsid
+.IP "cntid" 12
+Starting CNTLID to return in the list
+.IP "cntlist" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+Up to 2047 controller identifiers are returned containing a controller
+identifier greater than or equal to the controller identifier specified in
+\fIcntid\fP attached to \fInsid\fP.
+
+See \fIstruct nvme_ctrl_list\fP for a definition of the structure returned.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1
diff --git a/doc/man/nvme_identify_nvmset_list.2 b/doc/man/nvme_identify_nvmset_list.2
new file mode 100644
index 0000000..7ae5f3c
--- /dev/null
+++ b/doc/man/nvme_identify_nvmset_list.2
@@ -0,0 +1,25 @@
+.TH "nvme_identify_nvmset_list" 9 "nvme_identify_nvmset_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_nvmset_list \- Retrieves NVM Set List
+.SH SYNOPSIS
+.B "int" nvme_identify_nvmset_list
+.BI "(int fd " ","
+.BI "__u16 nvmsetid " ","
+.BI "struct nvme_id_nvmset_list *nvmset " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nvmsetid" 12
+NVM Set Identifier
+.IP "nvmset" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+Retrieves an NVM Set List, \fIstruct nvme_id_nvmset_list\fP. The data structure
+is an ordered list by NVM Set Identifier, starting with the first NVM Set
+Identifier supported by the NVM subsystem that is equal to or greater than
+the NVM Set Identifier.
+
+See \fIstruct nvme_id_nvmset_list\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_primary_ctrl.2 b/doc/man/nvme_identify_primary_ctrl.2
new file mode 100644
index 0000000..a3010cc
--- /dev/null
+++ b/doc/man/nvme_identify_primary_ctrl.2
@@ -0,0 +1,20 @@
+.TH "nvme_identify_primary_ctrl" 9 "nvme_identify_primary_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_primary_ctrl \- Retrieve NVMe Primary Controller identification
+.SH SYNOPSIS
+.B "int" nvme_identify_primary_ctrl
+.BI "(int fd " ","
+.BI "__u16 cntid " ","
+.BI "struct nvme_primary_ctrl_cap *cap " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "cntid" 12
+Return controllers starting at this identifier
+.IP "cap" 12
+User space destination buffer address to transfer the data
+.SH "DESCRIPTION"
+See \fIstruct nvme_primary_ctrl_cap\fP for the definition of the returned structure, \fIcap\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_secondary_ctrl_list.2 b/doc/man/nvme_identify_secondary_ctrl_list.2
new file mode 100644
index 0000000..c35f474
--- /dev/null
+++ b/doc/man/nvme_identify_secondary_ctrl_list.2
@@ -0,0 +1,26 @@
+.TH "nvme_identify_secondary_ctrl_list" 9 "nvme_identify_secondary_ctrl_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_secondary_ctrl_list \- Retrieves secondary controller list
+.SH SYNOPSIS
+.B "int" nvme_identify_secondary_ctrl_list
+.BI "(int fd " ","
+.BI "__u16 cntid " ","
+.BI "struct nvme_secondary_ctrl_list *sc_list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "cntid" 12
+Return controllers starting at this identifier
+.IP "sc_list" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+A Secondary Controller List is returned to the host for up to 127 secondary
+controllers associated with the primary controller processing this command.
+The list contains entries for controller identifiers greater than or equal
+to the value specified in the Controller Identifier (cntid).
+
+See \fIstruct nvme_secondary_ctrls_list\fP for a definition of the returned
+structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_identify_uuid.2 b/doc/man/nvme_identify_uuid.2
new file mode 100644
index 0000000..2bcad5b
--- /dev/null
+++ b/doc/man/nvme_identify_uuid.2
@@ -0,0 +1,20 @@
+.TH "nvme_identify_uuid" 9 "nvme_identify_uuid" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_identify_uuid \- Retrieves device's UUIDs
+.SH SYNOPSIS
+.B "int" nvme_identify_uuid
+.BI "(int fd " ","
+.BI "struct nvme_id_uuid_list *uuid_list " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "uuid_list" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+Each UUID List entry is either 0h, the NVMe Invalid UUID, or a valid UUID.
+Valid UUIDs are those which are non-zero and are not the NVMe Invalid UUID.
+
+See \fIstruct nvme_id_uuid_list\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_init_copy_range.2 b/doc/man/nvme_init_copy_range.2
new file mode 100644
index 0000000..36e3dfb
--- /dev/null
+++ b/doc/man/nvme_init_copy_range.2
@@ -0,0 +1,27 @@
+.TH "nvme_init_copy_range" 9 "nvme_init_copy_range" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_init_copy_range \- Constructs a copy range structure
+.SH SYNOPSIS
+.B "void" nvme_init_copy_range
+.BI "(struct nvme_copy_range *copy " ","
+.BI "__u16 *nlbs " ","
+.BI "__u64 *slbas " ","
+.BI "__u32 *eilbrts " ","
+.BI "__u32 *elbatms " ","
+.BI "__u32 *elbats " ","
+.BI "__u16 nr " ");"
+.SH ARGUMENTS
+.IP "copy" 12
+Copy range array
+.IP "nlbs" 12
+Number of logical blocks
+.IP "slbas" 12
+Starting LBA
+.IP "eilbrts" 12
+Expected initial logical block reference tag
+.IP "elbatms" 12
+Expected logical block application tag mask
+.IP "elbats" 12
+Expected logical block application tag
+.IP "nr" 12
+Number of descriptors to construct
diff --git a/doc/man/nvme_init_copy_range_f1.2 b/doc/man/nvme_init_copy_range_f1.2
new file mode 100644
index 0000000..54423e8
--- /dev/null
+++ b/doc/man/nvme_init_copy_range_f1.2
@@ -0,0 +1,27 @@
+.TH "nvme_init_copy_range_f1" 9 "nvme_init_copy_range_f1" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_init_copy_range_f1 \- Constructs a copy range f1 structure
+.SH SYNOPSIS
+.B "void" nvme_init_copy_range_f1
+.BI "(struct nvme_copy_range_f1 *copy " ","
+.BI "__u16 *nlbs " ","
+.BI "__u64 *slbas " ","
+.BI "__u64 *eilbrts " ","
+.BI "__u32 *elbatms " ","
+.BI "__u32 *elbats " ","
+.BI "__u16 nr " ");"
+.SH ARGUMENTS
+.IP "copy" 12
+Copy range array
+.IP "nlbs" 12
+Number of logical blocks
+.IP "slbas" 12
+Starting LBA
+.IP "eilbrts" 12
+Expected initial logical block reference tag
+.IP "elbatms" 12
+Expected logical block application tag mask
+.IP "elbats" 12
+Expected logical block application tag
+.IP "nr" 12
+Number of descriptors to construct
diff --git a/doc/man/nvme_init_copy_range_f2.2 b/doc/man/nvme_init_copy_range_f2.2
new file mode 100644
index 0000000..8c0a469
--- /dev/null
+++ b/doc/man/nvme_init_copy_range_f2.2
@@ -0,0 +1,33 @@
+.TH "nvme_init_copy_range_f2" 9 "nvme_init_copy_range_f2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_init_copy_range_f2 \- Constructs a copy range f2 structure
+.SH SYNOPSIS
+.B "void" nvme_init_copy_range_f2
+.BI "(struct nvme_copy_range_f2 *copy " ","
+.BI "__u32 *snsids " ","
+.BI "__u16 *nlbs " ","
+.BI "__u64 *slbas " ","
+.BI "__u16 *sopts " ","
+.BI "__u32 *eilbrts " ","
+.BI "__u32 *elbatms " ","
+.BI "__u32 *elbats " ","
+.BI "__u16 nr " ");"
+.SH ARGUMENTS
+.IP "copy" 12
+Copy range array
+.IP "snsids" 12
+Source namespace identifier
+.IP "nlbs" 12
+Number of logical blocks
+.IP "slbas" 12
+Starting LBA
+.IP "sopts" 12
+Source options
+.IP "eilbrts" 12
+Expected initial logical block reference tag
+.IP "elbatms" 12
+Expected logical block application tag mask
+.IP "elbats" 12
+Expected logical block application tag
+.IP "nr" 12
+Number of descriptors to construct
diff --git a/doc/man/nvme_init_copy_range_f3.2 b/doc/man/nvme_init_copy_range_f3.2
new file mode 100644
index 0000000..398f474
--- /dev/null
+++ b/doc/man/nvme_init_copy_range_f3.2
@@ -0,0 +1,33 @@
+.TH "nvme_init_copy_range_f3" 9 "nvme_init_copy_range_f3" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_init_copy_range_f3 \- Constructs a copy range f3 structure
+.SH SYNOPSIS
+.B "void" nvme_init_copy_range_f3
+.BI "(struct nvme_copy_range_f3 *copy " ","
+.BI "__u32 *snsids " ","
+.BI "__u16 *nlbs " ","
+.BI "__u64 *slbas " ","
+.BI "__u16 *sopts " ","
+.BI "__u64 *eilbrts " ","
+.BI "__u32 *elbatms " ","
+.BI "__u32 *elbats " ","
+.BI "__u16 nr " ");"
+.SH ARGUMENTS
+.IP "copy" 12
+Copy range array
+.IP "snsids" 12
+Source namespace identifier
+.IP "nlbs" 12
+Number of logical blocks
+.IP "slbas" 12
+Starting LBA
+.IP "sopts" 12
+Source options
+.IP "eilbrts" 12
+Expected initial logical block reference tag
+.IP "elbatms" 12
+Expected logical block application tag mask
+.IP "elbats" 12
+Expected logical block application tag
+.IP "nr" 12
+Number of descriptors to construct
diff --git a/doc/man/nvme_init_ctrl.2 b/doc/man/nvme_init_ctrl.2
new file mode 100644
index 0000000..4b3e34a
--- /dev/null
+++ b/doc/man/nvme_init_ctrl.2
@@ -0,0 +1,17 @@
+.TH "nvme_init_ctrl" 9 "nvme_init_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_init_ctrl \- Initialize nvme_ctrl_t object for an existing controller.
+.SH SYNOPSIS
+.B "int" nvme_init_ctrl
+.BI "(nvme_host_t h " ","
+.BI "nvme_ctrl_t c " ","
+.BI "int instance " ");"
+.SH ARGUMENTS
+.IP "h" 12
+nvme_host_t object
+.IP "c" 12
+nvme_ctrl_t object
+.IP "instance" 12
+Instance number (e.g. 1 for nvme1)
+.SH "RETURN"
+The \fBioctl\fP return code. Typically 0 on success.
diff --git a/doc/man/nvme_init_ctrl_list.2 b/doc/man/nvme_init_ctrl_list.2
new file mode 100644
index 0000000..92fadd0
--- /dev/null
+++ b/doc/man/nvme_init_ctrl_list.2
@@ -0,0 +1,18 @@
+.TH "nvme_init_ctrl_list" 9 "nvme_init_ctrl_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_init_ctrl_list \- Initialize an nvme_ctrl_list structure from an array.
+.SH SYNOPSIS
+.B "void" nvme_init_ctrl_list
+.BI "(struct nvme_ctrl_list *cntlist " ","
+.BI "__u16 num_ctrls " ","
+.BI "__u16 *ctrlist " ");"
+.SH ARGUMENTS
+.IP "cntlist" 12
+The controller list structure to initialize
+.IP "num_ctrls" 12
+The number of controllers in the array, \fIctrlist\fP.
+.IP "ctrlist" 12
+An array of controller identifiers in CPU native endian.
+.SH "DESCRIPTION"
+This is intended to be used with any command that takes a controller list
+argument. See \fBnvme_ns_attach_ctrls\fP and \fBnvme_ns_detach\fP.
diff --git a/doc/man/nvme_init_dsm_range.2 b/doc/man/nvme_init_dsm_range.2
new file mode 100644
index 0000000..9ca2dc0
--- /dev/null
+++ b/doc/man/nvme_init_dsm_range.2
@@ -0,0 +1,27 @@
+.TH "nvme_init_dsm_range" 9 "nvme_init_dsm_range" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_init_dsm_range \- Constructs a data set range structure
+.SH SYNOPSIS
+.B "void" nvme_init_dsm_range
+.BI "(struct nvme_dsm_range *dsm " ","
+.BI "__u32 *ctx_attrs " ","
+.BI "__u32 *llbas " ","
+.BI "__u64 *slbas " ","
+.BI "__u16 nr_ranges " ");"
+.SH ARGUMENTS
+.IP "dsm" 12
+DSM range array
+.IP "ctx_attrs" 12
+Array of context attributes
+.IP "llbas" 12
+Array of length in logical blocks
+.IP "slbas" 12
+Array of starting logical blocks
+.IP "nr_ranges" 12
+The size of the dsm arrays
+.SH "DESCRIPTION"
+Each array must be the same size of size 'nr_ranges'. This is intended to be
+used with constructing a payload for \fBnvme_dsm\fP.
+.SH "RETURN"
+The nvme command status if a response was received or -errno
+otherwise.
diff --git a/doc/man/nvme_init_logging.2 b/doc/man/nvme_init_logging.2
new file mode 100644
index 0000000..e20c965
--- /dev/null
+++ b/doc/man/nvme_init_logging.2
@@ -0,0 +1,20 @@
+.TH "nvme_init_logging" 9 "nvme_init_logging" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_init_logging \- Initialize logging
+.SH SYNOPSIS
+.B "void" nvme_init_logging
+.BI "(nvme_root_t r " ","
+.BI "int lvl " ","
+.BI "bool log_pid " ","
+.BI "bool log_tstamp " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t context
+.IP "lvl" 12
+Logging level to set
+.IP "log_pid" 12
+Boolean to enable logging of the PID
+.IP "log_tstamp" 12
+Boolean to enable logging of the timestamp
+.SH "DESCRIPTION"
+Sets the default logging variables for the library.
diff --git a/doc/man/nvme_insert_tls_key.2 b/doc/man/nvme_insert_tls_key.2
new file mode 100644
index 0000000..7d9b3bf
--- /dev/null
+++ b/doc/man/nvme_insert_tls_key.2
@@ -0,0 +1,33 @@
+.TH "nvme_insert_tls_key" 9 "nvme_insert_tls_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_insert_tls_key \- Derive and insert TLS key
+.SH SYNOPSIS
+.B "long" nvme_insert_tls_key
+.BI "(const char *keyring " ","
+.BI "const char *key_type " ","
+.BI "const char *hostnqn " ","
+.BI "const char *subsysnqn " ","
+.BI "int hmac " ","
+.BI "unsigned char *configured_key " ","
+.BI "int key_len " ");"
+.SH ARGUMENTS
+.IP "keyring" 12
+Keyring to use
+.IP "key_type" 12
+Type of the resulting key
+.IP "hostnqn" 12
+Host NVMe Qualified Name
+.IP "subsysnqn" 12
+Subsystem NVMe Qualified Name
+.IP "hmac" 12
+HMAC algorithm
+.IP "configured_key" 12
+Configured key data to derive the key from
+.IP "key_len" 12
+Length of \fIconfigured_key\fP
+.SH "DESCRIPTION"
+Derives a 'retained' TLS key as specified in NVMe TCP 1.0a and
+stores it as type \fIkey_type\fP in the keyring specified by \fIkeyring\fP.
+.SH "RETURN"
+The key serial number if the key could be inserted into
+the keyring or 0 with errno otherwise.
diff --git a/doc/man/nvme_insert_tls_key_versioned.2 b/doc/man/nvme_insert_tls_key_versioned.2
new file mode 100644
index 0000000..4381914
--- /dev/null
+++ b/doc/man/nvme_insert_tls_key_versioned.2
@@ -0,0 +1,37 @@
+.TH "nvme_insert_tls_key_versioned" 9 "nvme_insert_tls_key_versioned" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_insert_tls_key_versioned \- Derive and insert TLS key
+.SH SYNOPSIS
+.B "long" nvme_insert_tls_key_versioned
+.BI "(const char *keyring " ","
+.BI "const char *key_type " ","
+.BI "const char *hostnqn " ","
+.BI "const char *subsysnqn " ","
+.BI "int version " ","
+.BI "int hmac " ","
+.BI "unsigned char *configured_key " ","
+.BI "int key_len " ");"
+.SH ARGUMENTS
+.IP "keyring" 12
+Keyring to use
+.IP "key_type" 12
+Type of the resulting key
+.IP "hostnqn" 12
+Host NVMe Qualified Name
+.IP "subsysnqn" 12
+Subsystem NVMe Qualified Name
+.IP "version" 12
+Key version to use
+.IP "hmac" 12
+HMAC algorithm
+.IP "configured_key" 12
+Configured key data to derive the key from
+.IP "key_len" 12
+Length of \fIconfigured_key\fP
+.SH "DESCRIPTION"
+Derives a 'retained' TLS key as specified in NVMe TCP 1.0a (if
+\fIversion\fP s set to '0') or NVMe TP8028 (if \fIversion\fP is set to '1) and
+stores it as type \fIkey_type\fP in the keyring specified by \fIkeyring\fP.
+.SH "RETURN"
+The key serial number if the key could be inserted into
+the keyring or 0 with errno otherwise.
diff --git a/doc/man/nvme_io.2 b/doc/man/nvme_io.2
new file mode 100644
index 0000000..3af5d96
--- /dev/null
+++ b/doc/man/nvme_io.2
@@ -0,0 +1,15 @@
+.TH "nvme_io" 9 "nvme_io" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_io \- Submit an nvme user I/O command
+.SH SYNOPSIS
+.B "int" nvme_io
+.BI "(struct nvme_io_args *args " ","
+.BI "__u8 opcode " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_args\fP argument structure
+.IP "opcode" 12
+Opcode to execute
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_io_control_flags.2 b/doc/man/nvme_io_control_flags.2
new file mode 100644
index 0000000..98129ca
--- /dev/null
+++ b/doc/man/nvme_io_control_flags.2
@@ -0,0 +1,66 @@
+.TH "libnvme" 9 "enum nvme_io_control_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_io_control_flags \- I/O control flags
+.SH SYNOPSIS
+enum nvme_io_control_flags {
+.br
+.BI " NVME_IO_DTYPE_STREAMS"
+,
+.br
+.br
+.BI " NVME_IO_STC"
+,
+.br
+.br
+.BI " NVME_IO_DEAC"
+,
+.br
+.br
+.BI " NVME_IO_ZNS_APPEND_PIREMAP"
+,
+.br
+.br
+.BI " NVME_IO_PRINFO_PRCHK_REF"
+,
+.br
+.br
+.BI " NVME_IO_PRINFO_PRCHK_APP"
+,
+.br
+.br
+.BI " NVME_IO_PRINFO_PRCHK_GUARD"
+,
+.br
+.br
+.BI " NVME_IO_PRINFO_PRACT"
+,
+.br
+.br
+.BI " NVME_IO_FUA"
+,
+.br
+.br
+.BI " NVME_IO_LR"
+
+};
+.SH Constants
+.IP "NVME_IO_DTYPE_STREAMS" 12
+Directive Type Streams
+.IP "NVME_IO_STC" 12
+Storage Tag Check
+.IP "NVME_IO_DEAC" 12
+Deallocate
+.IP "NVME_IO_ZNS_APPEND_PIREMAP" 12
+Protection Information Remap
+.IP "NVME_IO_PRINFO_PRCHK_REF" 12
+Protection Information Check Reference Tag
+.IP "NVME_IO_PRINFO_PRCHK_APP" 12
+Protection Information Check Application Tag
+.IP "NVME_IO_PRINFO_PRCHK_GUARD" 12
+Protection Information Check Guard field
+.IP "NVME_IO_PRINFO_PRACT" 12
+Protection Information Action
+.IP "NVME_IO_FUA" 12
+Force Unit Access
+.IP "NVME_IO_LR" 12
+Limited Retry
diff --git a/doc/man/nvme_io_dsm_flags.2 b/doc/man/nvme_io_dsm_flags.2
new file mode 100644
index 0000000..811f9c0
--- /dev/null
+++ b/doc/man/nvme_io_dsm_flags.2
@@ -0,0 +1,96 @@
+.TH "libnvme" 9 "enum nvme_io_dsm_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_io_dsm_flags \- Dataset Management flags
+.SH SYNOPSIS
+enum nvme_io_dsm_flags {
+.br
+.BI " NVME_IO_DSM_FREQ_UNSPEC"
+,
+.br
+.br
+.BI " NVME_IO_DSM_FREQ_TYPICAL"
+,
+.br
+.br
+.BI " NVME_IO_DSM_FREQ_RARE"
+,
+.br
+.br
+.BI " NVME_IO_DSM_FREQ_READS"
+,
+.br
+.br
+.BI " NVME_IO_DSM_FREQ_WRITES"
+,
+.br
+.br
+.BI " NVME_IO_DSM_FREQ_RW"
+,
+.br
+.br
+.BI " NVME_IO_DSM_FREQ_ONCE"
+,
+.br
+.br
+.BI " NVME_IO_DSM_FREQ_PREFETCH"
+,
+.br
+.br
+.BI " NVME_IO_DSM_FREQ_TEMP"
+,
+.br
+.br
+.BI " NVME_IO_DSM_LATENCY_NONE"
+,
+.br
+.br
+.BI " NVME_IO_DSM_LATENCY_IDLE"
+,
+.br
+.br
+.BI " NVME_IO_DSM_LATENCY_NORM"
+,
+.br
+.br
+.BI " NVME_IO_DSM_LATENCY_LOW"
+,
+.br
+.br
+.BI " NVME_IO_DSM_SEQ_REQ"
+,
+.br
+.br
+.BI " NVME_IO_DSM_COMPRESSED"
+
+};
+.SH Constants
+.IP "NVME_IO_DSM_FREQ_UNSPEC" 12
+No frequency information provided
+.IP "NVME_IO_DSM_FREQ_TYPICAL" 12
+Typical number of reads and writes
+expected for this LBA range
+.IP "NVME_IO_DSM_FREQ_RARE" 12
+Infrequent writes and infrequent
+reads to the LBA range indicated
+.IP "NVME_IO_DSM_FREQ_READS" 12
+Infrequent writes and frequent
+reads to the LBA range indicated
+.IP "NVME_IO_DSM_FREQ_WRITES" 12
+Frequent writes and infrequent
+reads to the LBA range indicated
+.IP "NVME_IO_DSM_FREQ_RW" 12
+Frequent writes and frequent reads
+to the LBA range indicated
+.IP "NVME_IO_DSM_FREQ_ONCE" 12
+.IP "NVME_IO_DSM_FREQ_PREFETCH" 12
+.IP "NVME_IO_DSM_FREQ_TEMP" 12
+.IP "NVME_IO_DSM_LATENCY_NONE" 12
+No latency information provided
+.IP "NVME_IO_DSM_LATENCY_IDLE" 12
+Longer latency acceptable
+.IP "NVME_IO_DSM_LATENCY_NORM" 12
+Typical latency
+.IP "NVME_IO_DSM_LATENCY_LOW" 12
+Smallest possible latency
+.IP "NVME_IO_DSM_SEQ_REQ" 12
+.IP "NVME_IO_DSM_COMPRESSED" 12
diff --git a/doc/man/nvme_io_mgmt_recv.2 b/doc/man/nvme_io_mgmt_recv.2
new file mode 100644
index 0000000..83bdaa2
--- /dev/null
+++ b/doc/man/nvme_io_mgmt_recv.2
@@ -0,0 +1,12 @@
+.TH "nvme_io_mgmt_recv" 9 "nvme_io_mgmt_recv" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_io_mgmt_recv \- I/O Management Receive command
+.SH SYNOPSIS
+.B "int" nvme_io_mgmt_recv
+.BI "(struct nvme_io_mgmt_recv_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_mgmt_recv_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_io_mgmt_recv_mo.2 b/doc/man/nvme_io_mgmt_recv_mo.2
new file mode 100644
index 0000000..b38dc0b
--- /dev/null
+++ b/doc/man/nvme_io_mgmt_recv_mo.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nvme_io_mgmt_recv_mo" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_io_mgmt_recv_mo \- I/O Management Receive - Management Operation
+.SH SYNOPSIS
+enum nvme_io_mgmt_recv_mo {
+.br
+.BI " NVME_IO_MGMT_RECV_RUH_STATUS"
+
+};
+.SH Constants
+.IP "NVME_IO_MGMT_RECV_RUH_STATUS" 12
+Reclaim Unit Handle Status
diff --git a/doc/man/nvme_io_mgmt_send.2 b/doc/man/nvme_io_mgmt_send.2
new file mode 100644
index 0000000..43f2c7f
--- /dev/null
+++ b/doc/man/nvme_io_mgmt_send.2
@@ -0,0 +1,12 @@
+.TH "nvme_io_mgmt_send" 9 "nvme_io_mgmt_send" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_io_mgmt_send \- I/O Management Send command
+.SH SYNOPSIS
+.B "int" nvme_io_mgmt_send
+.BI "(struct nvme_io_mgmt_send_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_mgmt_send_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_io_mgmt_send_mo.2 b/doc/man/nvme_io_mgmt_send_mo.2
new file mode 100644
index 0000000..0cb8504
--- /dev/null
+++ b/doc/man/nvme_io_mgmt_send_mo.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nvme_io_mgmt_send_mo" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_io_mgmt_send_mo \- I/O Management Send - Management Operation
+.SH SYNOPSIS
+enum nvme_io_mgmt_send_mo {
+.br
+.BI " NVME_IO_MGMT_SEND_RUH_UPDATE"
+
+};
+.SH Constants
+.IP "NVME_IO_MGMT_SEND_RUH_UPDATE" 12
+Reclaim Unit Handle Update
diff --git a/doc/man/nvme_io_opcode.2 b/doc/man/nvme_io_opcode.2
new file mode 100644
index 0000000..409edfa
--- /dev/null
+++ b/doc/man/nvme_io_opcode.2
@@ -0,0 +1,114 @@
+.TH "libnvme" 9 "enum nvme_io_opcode" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_io_opcode \- Opcodes for I/O Commands
+.SH SYNOPSIS
+enum nvme_io_opcode {
+.br
+.BI " nvme_cmd_flush"
+,
+.br
+.br
+.BI " nvme_cmd_write"
+,
+.br
+.br
+.BI " nvme_cmd_read"
+,
+.br
+.br
+.BI " nvme_cmd_write_uncor"
+,
+.br
+.br
+.BI " nvme_cmd_compare"
+,
+.br
+.br
+.BI " nvme_cmd_write_zeroes"
+,
+.br
+.br
+.BI " nvme_cmd_dsm"
+,
+.br
+.br
+.BI " nvme_cmd_verify"
+,
+.br
+.br
+.BI " nvme_cmd_resv_register"
+,
+.br
+.br
+.BI " nvme_cmd_resv_report"
+,
+.br
+.br
+.BI " nvme_cmd_resv_acquire"
+,
+.br
+.br
+.BI " nvme_cmd_io_mgmt_recv"
+,
+.br
+.br
+.BI " nvme_cmd_resv_release"
+,
+.br
+.br
+.BI " nvme_cmd_copy"
+,
+.br
+.br
+.BI " nvme_cmd_io_mgmt_send"
+,
+.br
+.br
+.BI " nvme_zns_cmd_mgmt_send"
+,
+.br
+.br
+.BI " nvme_zns_cmd_mgmt_recv"
+,
+.br
+.br
+.BI " nvme_zns_cmd_append"
+
+};
+.SH Constants
+.IP "nvme_cmd_flush" 12
+Flush
+.IP "nvme_cmd_write" 12
+Write
+.IP "nvme_cmd_read" 12
+Read
+.IP "nvme_cmd_write_uncor" 12
+Write Uncorrectable
+.IP "nvme_cmd_compare" 12
+Compare
+.IP "nvme_cmd_write_zeroes" 12
+write Zeros
+.IP "nvme_cmd_dsm" 12
+Dataset Management
+.IP "nvme_cmd_verify" 12
+Verify
+.IP "nvme_cmd_resv_register" 12
+Reservation Register
+.IP "nvme_cmd_resv_report" 12
+Reservation Report
+.IP "nvme_cmd_resv_acquire" 12
+Reservation Acquire
+.IP "nvme_cmd_io_mgmt_recv" 12
+I/O Management Receive
+.IP "nvme_cmd_resv_release" 12
+Reservation Release
+.IP "nvme_cmd_copy" 12
+Copy
+.IP "nvme_cmd_io_mgmt_send" 12
+I/O Management Send
+.IP "nvme_zns_cmd_mgmt_send" 12
+Zone Management Send
+.IP "nvme_zns_cmd_mgmt_recv" 12
+Zone Management Receive
+.IP "nvme_zns_cmd_append" 12
+Zone Append
diff --git a/doc/man/nvme_io_passthru.2 b/doc/man/nvme_io_passthru.2
new file mode 100644
index 0000000..8e2d54d
--- /dev/null
+++ b/doc/man/nvme_io_passthru.2
@@ -0,0 +1,71 @@
+.TH "nvme_io_passthru" 9 "nvme_io_passthru" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_io_passthru \- Submit an nvme io passthrough command
+.SH SYNOPSIS
+.B "int" nvme_io_passthru
+.BI "(int fd " ","
+.BI "__u8 opcode " ","
+.BI "__u8 flags " ","
+.BI "__u16 rsvd " ","
+.BI "__u32 nsid " ","
+.BI "__u32 cdw2 " ","
+.BI "__u32 cdw3 " ","
+.BI "__u32 cdw10 " ","
+.BI "__u32 cdw11 " ","
+.BI "__u32 cdw12 " ","
+.BI "__u32 cdw13 " ","
+.BI "__u32 cdw14 " ","
+.BI "__u32 cdw15 " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 metadata_len " ","
+.BI "void *metadata " ","
+.BI "__u32 timeout_ms " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "opcode" 12
+The nvme io command to send
+.IP "flags" 12
+NVMe command flags (not used)
+.IP "rsvd" 12
+Reserved for future use
+.IP "nsid" 12
+Namespace identifier
+.IP "cdw2" 12
+Command dword 2
+.IP "cdw3" 12
+Command dword 3
+.IP "cdw10" 12
+Command dword 10
+.IP "cdw11" 12
+Command dword 11
+.IP "cdw12" 12
+Command dword 12
+.IP "cdw13" 12
+Command dword 13
+.IP "cdw14" 12
+Command dword 14
+.IP "cdw15" 12
+Command dword 15
+.IP "data_len" 12
+Length of the data transferred in this command in bytes
+.IP "data" 12
+Pointer to user address of the data buffer
+.IP "metadata_len" 12
+Length of metadata transferred in this command
+.IP "metadata" 12
+Pointer to user address of the metadata buffer
+.IP "timeout_ms" 12
+How long the kernel waits for the command to complete
+.IP "result" 12
+Optional field to return the result from the CQE dword 0
+.SH "DESCRIPTION"
+Parameterized form of \fBnvme_submit_io_passthru\fP. This sets up and submits
+a \fIstruct nvme_passthru_cmd\fP.
+
+Known values for \fIopcode\fP are defined in \fIenum nvme_io_opcode\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_io_passthru64.2 b/doc/man/nvme_io_passthru64.2
new file mode 100644
index 0000000..de03474
--- /dev/null
+++ b/doc/man/nvme_io_passthru64.2
@@ -0,0 +1,71 @@
+.TH "nvme_io_passthru64" 9 "nvme_io_passthru64" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_io_passthru64 \- Submit an nvme io passthrough command
+.SH SYNOPSIS
+.B "int" nvme_io_passthru64
+.BI "(int fd " ","
+.BI "__u8 opcode " ","
+.BI "__u8 flags " ","
+.BI "__u16 rsvd " ","
+.BI "__u32 nsid " ","
+.BI "__u32 cdw2 " ","
+.BI "__u32 cdw3 " ","
+.BI "__u32 cdw10 " ","
+.BI "__u32 cdw11 " ","
+.BI "__u32 cdw12 " ","
+.BI "__u32 cdw13 " ","
+.BI "__u32 cdw14 " ","
+.BI "__u32 cdw15 " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 metadata_len " ","
+.BI "void *metadata " ","
+.BI "__u32 timeout_ms " ","
+.BI "__u64 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "opcode" 12
+The nvme io command to send
+.IP "flags" 12
+NVMe command flags (not used)
+.IP "rsvd" 12
+Reserved for future use
+.IP "nsid" 12
+Namespace identifier
+.IP "cdw2" 12
+Command dword 2
+.IP "cdw3" 12
+Command dword 3
+.IP "cdw10" 12
+Command dword 10
+.IP "cdw11" 12
+Command dword 11
+.IP "cdw12" 12
+Command dword 12
+.IP "cdw13" 12
+Command dword 13
+.IP "cdw14" 12
+Command dword 14
+.IP "cdw15" 12
+Command dword 15
+.IP "data_len" 12
+Length of the data transferred in this command in bytes
+.IP "data" 12
+Pointer to user address of the data buffer
+.IP "metadata_len" 12
+Length of metadata transferred in this command
+.IP "metadata" 12
+Pointer to user address of the metadata buffer
+.IP "timeout_ms" 12
+How long the kernel waits for the command to complete
+.IP "result" 12
+Optional field to return the result from the CQE dword 0
+.SH "DESCRIPTION"
+Parameterized form of \fBnvme_submit_io_passthru64\fP. This sets up and submits
+a \fIstruct nvme_passthru_cmd64\fP.
+
+Known values for \fIopcode\fP are defined in \fIenum nvme_io_opcode\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_is_64bit_reg.2 b/doc/man/nvme_is_64bit_reg.2
new file mode 100644
index 0000000..ecebc87
--- /dev/null
+++ b/doc/man/nvme_is_64bit_reg.2
@@ -0,0 +1,16 @@
+.TH "nvme_is_64bit_reg" 9 "nvme_is_64bit_reg" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_is_64bit_reg \- Checks if offset of the controller register is a know 64bit value.
+.SH SYNOPSIS
+.B "bool" nvme_is_64bit_reg
+.BI "(__u32 offset " ");"
+.SH ARGUMENTS
+.IP "offset" 12
+Offset of controller register field in bytes
+.SH "DESCRIPTION"
+This function does not care about transport so that the offset is not going
+to be checked inside of this function for the unsupported fields in a
+specific transport. For example, BPMBL(Boot Partition Memory Buffer
+Location) register is not supported by fabrics, but it can be checked here.
+.SH "RETURN"
+true if given offset is 64bit register, otherwise it returns false.
diff --git a/doc/man/nvme_lba_range_type.2 b/doc/man/nvme_lba_range_type.2
new file mode 100644
index 0000000..b6f934b
--- /dev/null
+++ b/doc/man/nvme_lba_range_type.2
@@ -0,0 +1,15 @@
+.TH "libnvme" 9 "struct nvme_lba_range_type" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_lba_range_type \- LBA Range Type
+.SH SYNOPSIS
+struct nvme_lba_range_type {
+.br
+.BI " struct nvme_lba_range_type_entry entry[NVME_FEAT_LBA_RANGE_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "entry" 12
+LBA range type entry. See \fIstruct\fP nvme_lba_range_type_entry
diff --git a/doc/man/nvme_lba_range_type_entry.2 b/doc/man/nvme_lba_range_type_entry.2
new file mode 100644
index 0000000..25ad9d8
--- /dev/null
+++ b/doc/man/nvme_lba_range_type_entry.2
@@ -0,0 +1,39 @@
+.TH "libnvme" 9 "struct nvme_lba_range_type_entry" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_lba_range_type_entry \- LBA Range Type - Data Structure Entry
+.SH SYNOPSIS
+struct nvme_lba_range_type_entry {
+.br
+.BI " __u8 type;"
+.br
+.BI " __u8 attributes;"
+.br
+.BI " __u8 rsvd2[14];"
+.br
+.BI " __le64 slba;"
+.br
+.BI " __le64 nlb;"
+.br
+.BI " __u8 guid[16];"
+.br
+.BI " __u8 rsvd48[16];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "type" 12
+Specifies the Type of the LBA range
+.IP "attributes" 12
+Specifies attributes of the LBA range
+.IP "rsvd2" 12
+Reserved
+.IP "slba" 12
+Starting LBA
+.IP "nlb" 12
+Number of Logical Blocks
+.IP "guid" 12
+Unique Identifier
+.IP "rsvd48" 12
+Reserved
diff --git a/doc/man/nvme_lba_rd.2 b/doc/man/nvme_lba_rd.2
new file mode 100644
index 0000000..77a8d4d
--- /dev/null
+++ b/doc/man/nvme_lba_rd.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_lba_rd" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_lba_rd \- LBA Range Descriptor
+.SH SYNOPSIS
+struct nvme_lba_rd {
+.br
+.BI " __le64 rslba;"
+.br
+.BI " __le32 rnlb;"
+.br
+.BI " __u8 rsvd12[4];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "rslba" 12
+Range Starting LBA
+.IP "rnlb" 12
+Range Number of Logical Blocks
+.IP "rsvd12" 12
+Reserved
diff --git a/doc/man/nvme_lba_status.2 b/doc/man/nvme_lba_status.2
new file mode 100644
index 0000000..7fb6a55
--- /dev/null
+++ b/doc/man/nvme_lba_status.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_lba_status" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_lba_status \- LBA Status Descriptor List
+.SH SYNOPSIS
+struct nvme_lba_status {
+.br
+.BI " __le32 nlsd;"
+.br
+.BI " __u8 cmpc;"
+.br
+.BI " __u8 rsvd5[3];"
+.br
+.BI " struct nvme_lba_status_desc descs[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nlsd" 12
+Number of LBA Status Descriptors
+.IP "cmpc" 12
+Completion Condition
+.IP "rsvd5" 12
+Reserved
+.IP "descs" 12
+LBA status descriptor Entry
diff --git a/doc/man/nvme_lba_status_atype.2 b/doc/man/nvme_lba_status_atype.2
new file mode 100644
index 0000000..66ae259
--- /dev/null
+++ b/doc/man/nvme_lba_status_atype.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "enum nvme_lba_status_atype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_lba_status_atype \- Potentially Unrecoverable LBAs
+.SH SYNOPSIS
+enum nvme_lba_status_atype {
+.br
+.BI " NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED"
+,
+.br
+.br
+.BI " NVME_LBA_STATUS_ATYPE_SCAN_TRACKED"
+
+};
+.SH Constants
+.IP "NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED" 12
+Potentially Unrecoverable LBAs
+.IP "NVME_LBA_STATUS_ATYPE_SCAN_TRACKED" 12
+Potentially Unrecoverable LBAs
+associated with physical storage
diff --git a/doc/man/nvme_lba_status_desc.2 b/doc/man/nvme_lba_status_desc.2
new file mode 100644
index 0000000..deb31d7
--- /dev/null
+++ b/doc/man/nvme_lba_status_desc.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_lba_status_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_lba_status_desc \- LBA Status Descriptor Entry
+.SH SYNOPSIS
+struct nvme_lba_status_desc {
+.br
+.BI " __le64 dslba;"
+.br
+.BI " __le32 nlb;"
+.br
+.BI " __u8 rsvd12;"
+.br
+.BI " __u8 status;"
+.br
+.BI " __u8 rsvd14[2];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "dslba" 12
+Descriptor Starting LBA
+.IP "nlb" 12
+Number of Logical Blocks
+.IP "rsvd12" 12
+Reserved
+.IP "status" 12
+Additional status about this LBA range
+.IP "rsvd14" 12
+Reserved
diff --git a/doc/man/nvme_lba_status_log.2 b/doc/man/nvme_lba_status_log.2
new file mode 100644
index 0000000..92a4ea7
--- /dev/null
+++ b/doc/man/nvme_lba_status_log.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_lba_status_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_lba_status_log \- LBA Status Information Log
+.SH SYNOPSIS
+struct nvme_lba_status_log {
+.br
+.BI " __le32 lslplen;"
+.br
+.BI " __le32 nlslne;"
+.br
+.BI " __le32 estulb;"
+.br
+.BI " __u8 rsvd12[2];"
+.br
+.BI " __le16 lsgc;"
+.br
+.BI " struct nvme_lbas_ns_element elements[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "lslplen" 12
+LBA Status Log Page Length
+.IP "nlslne" 12
+Number of LBA Status Log Namespace Elements
+.IP "estulb" 12
+Estimate of Unrecoverable Logical Blocks
+.IP "rsvd12" 12
+Reserved
+.IP "lsgc" 12
+LBA Status Generation Counter
+.IP "elements" 12
+LBA Status Log Namespace Element List
diff --git a/doc/man/nvme_lbaf.2 b/doc/man/nvme_lbaf.2
new file mode 100644
index 0000000..2b699b5
--- /dev/null
+++ b/doc/man/nvme_lbaf.2
@@ -0,0 +1,25 @@
+.TH "libnvme" 9 "struct nvme_lbaf" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_lbaf \- LBA Format Data Structure
+.SH SYNOPSIS
+struct nvme_lbaf {
+.br
+.BI " __le16 ms;"
+.br
+.BI " __u8 ds;"
+.br
+.BI " __u8 rp;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "ms" 12
+Metadata Size indicates the number of metadata bytes provided per LBA
+based on the LBA Data Size indicated.
+.IP "ds" 12
+LBA Data Size indicates the LBA data size supported, reported as a
+power of two.
+.IP "rp" 12
+Relative Performance, see \fIenum nvme_lbaf_rp\fP.
diff --git a/doc/man/nvme_lbaf_rp.2 b/doc/man/nvme_lbaf_rp.2
new file mode 100644
index 0000000..d6511d8
--- /dev/null
+++ b/doc/man/nvme_lbaf_rp.2
@@ -0,0 +1,37 @@
+.TH "libnvme" 9 "enum nvme_lbaf_rp" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_lbaf_rp \- This field indicates the relative performance of the LBA format indicated relative to other LBA formats supported by the controller.
+.SH SYNOPSIS
+enum nvme_lbaf_rp {
+.br
+.BI " NVME_LBAF_RP_BEST"
+,
+.br
+.br
+.BI " NVME_LBAF_RP_BETTER"
+,
+.br
+.br
+.BI " NVME_LBAF_RP_GOOD"
+,
+.br
+.br
+.BI " NVME_LBAF_RP_DEGRADED"
+,
+.br
+.br
+.BI " NVME_LBAF_RP_MASK"
+
+};
+.SH Constants
+.IP "NVME_LBAF_RP_BEST" 12
+Best performance
+.IP "NVME_LBAF_RP_BETTER" 12
+Better performance
+.IP "NVME_LBAF_RP_GOOD" 12
+Good performance
+.IP "NVME_LBAF_RP_DEGRADED" 12
+Degraded performance
+.IP "NVME_LBAF_RP_MASK" 12
+Mask to get the relative performance value from the
+field
diff --git a/doc/man/nvme_lbart.2 b/doc/man/nvme_lbart.2
new file mode 100644
index 0000000..b8319d0
--- /dev/null
+++ b/doc/man/nvme_lbart.2
@@ -0,0 +1,48 @@
+.TH "libnvme" 9 "enum nvme_lbart" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_lbart \- LBA Range Type - Data Structure Entry
+.SH SYNOPSIS
+enum nvme_lbart {
+.br
+.BI " NVME_LBART_TYPE_GP"
+,
+.br
+.br
+.BI " NVME_LBART_TYPE_FS"
+,
+.br
+.br
+.BI " NVME_LBART_TYPE_RAID"
+,
+.br
+.br
+.BI " NVME_LBART_TYPE_CACHE"
+,
+.br
+.br
+.BI " NVME_LBART_TYPE_SWAP"
+,
+.br
+.br
+.BI " NVME_LBART_ATTRIB_TEMP"
+,
+.br
+.br
+.BI " NVME_LBART_ATTRIB_HIDE"
+
+};
+.SH Constants
+.IP "NVME_LBART_TYPE_GP" 12
+General Purpose
+.IP "NVME_LBART_TYPE_FS" 12
+Filesystem
+.IP "NVME_LBART_TYPE_RAID" 12
+RAID
+.IP "NVME_LBART_TYPE_CACHE" 12
+Cache
+.IP "NVME_LBART_TYPE_SWAP" 12
+Page / swap file
+.IP "NVME_LBART_ATTRIB_TEMP" 12
+Temp
+.IP "NVME_LBART_ATTRIB_HIDE" 12
+Hidden
diff --git a/doc/man/nvme_lbas_ns_element.2 b/doc/man/nvme_lbas_ns_element.2
new file mode 100644
index 0000000..abaf8bf
--- /dev/null
+++ b/doc/man/nvme_lbas_ns_element.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_lbas_ns_element" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_lbas_ns_element \- LBA Status Log Namespace Element
+.SH SYNOPSIS
+struct nvme_lbas_ns_element {
+.br
+.BI " __le32 neid;"
+.br
+.BI " __le32 nlrd;"
+.br
+.BI " __u8 ratype;"
+.br
+.BI " __u8 rsvd8[7];"
+.br
+.BI " struct nvme_lba_rd lba_rd[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "neid" 12
+Namespace Element Identifier
+.IP "nlrd" 12
+Number of LBA Range Descriptors
+.IP "ratype" 12
+Recommended Action Type. see \fIenum\fP nvme_lba_status_atype
+.IP "rsvd8" 12
+Reserved
+.IP "lba_rd" 12
+LBA Range Descriptor
diff --git a/doc/man/nvme_lockdown.2 b/doc/man/nvme_lockdown.2
new file mode 100644
index 0000000..a71f6dc
--- /dev/null
+++ b/doc/man/nvme_lockdown.2
@@ -0,0 +1,12 @@
+.TH "nvme_lockdown" 9 "nvme_lockdown" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_lockdown \- Issue lockdown command
+.SH SYNOPSIS
+.B "int" nvme_lockdown
+.BI "(struct nvme_lockdown_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_lockdown_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_log_ana_lsp.2 b/doc/man/nvme_log_ana_lsp.2
new file mode 100644
index 0000000..618e156
--- /dev/null
+++ b/doc/man/nvme_log_ana_lsp.2
@@ -0,0 +1,16 @@
+.TH "libnvme" 9 "enum nvme_log_ana_lsp" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_log_ana_lsp \- Asymmetric Namespace Access - Return Groups Only
+.SH SYNOPSIS
+enum nvme_log_ana_lsp {
+.br
+.BI " NVME_LOG_ANA_LSP_RGO_NAMESPACES"
+,
+.br
+.br
+.BI " NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY"
+
+};
+.SH Constants
+.IP "NVME_LOG_ANA_LSP_RGO_NAMESPACES" 12
+.IP "NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY" 12
diff --git a/doc/man/nvme_log_phy_rx_eom_action.2 b/doc/man/nvme_log_phy_rx_eom_action.2
new file mode 100644
index 0000000..b0f3ae1
--- /dev/null
+++ b/doc/man/nvme_log_phy_rx_eom_action.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_log_phy_rx_eom_action" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_log_phy_rx_eom_action \- Physical Interface Receiver Eye Opening Measurement Action
+.SH SYNOPSIS
+enum nvme_log_phy_rx_eom_action {
+.br
+.BI " NVME_LOG_PHY_RX_EOM_READ"
+,
+.br
+.br
+.BI " NVME_LOG_PHY_RX_EOM_START_READ"
+,
+.br
+.br
+.BI " NVME_LOG_PHY_RX_EOM_ABORT_CLEAR"
+
+};
+.SH Constants
+.IP "NVME_LOG_PHY_RX_EOM_READ" 12
+Read Log Data
+.IP "NVME_LOG_PHY_RX_EOM_START_READ" 12
+Start Measurement and Read Log Data
+.IP "NVME_LOG_PHY_RX_EOM_ABORT_CLEAR" 12
+Abort Measurement and Clear Log Data
diff --git a/doc/man/nvme_log_phy_rx_eom_quality.2 b/doc/man/nvme_log_phy_rx_eom_quality.2
new file mode 100644
index 0000000..c0e0ff3
--- /dev/null
+++ b/doc/man/nvme_log_phy_rx_eom_quality.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_log_phy_rx_eom_quality" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_log_phy_rx_eom_quality \- Physical Interface Receiver Eye Opening Measurement Quality
+.SH SYNOPSIS
+enum nvme_log_phy_rx_eom_quality {
+.br
+.BI " NVME_LOG_PHY_RX_EOM_GOOD"
+,
+.br
+.br
+.BI " NVME_LOG_PHY_RX_EOM_BETTER"
+,
+.br
+.br
+.BI " NVME_LOG_PHY_RX_EOM_BEST"
+
+};
+.SH Constants
+.IP "NVME_LOG_PHY_RX_EOM_GOOD" 12
+<= Better Quality
+.IP "NVME_LOG_PHY_RX_EOM_BETTER" 12
+<= Best Quality, >= Good Quality
+.IP "NVME_LOG_PHY_RX_EOM_BEST" 12
+>= Better Quality
diff --git a/doc/man/nvme_lookup_ctrl.2 b/doc/man/nvme_lookup_ctrl.2
new file mode 100644
index 0000000..d68e4da
--- /dev/null
+++ b/doc/man/nvme_lookup_ctrl.2
@@ -0,0 +1,35 @@
+.TH "nvme_lookup_ctrl" 9 "nvme_lookup_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_lookup_ctrl \- Lookup nvme_ctrl_t object
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvme_lookup_ctrl
+.BI "(nvme_subsystem_t s " ","
+.BI "const char *transport " ","
+.BI "const char *traddr " ","
+.BI "const char *host_traddr " ","
+.BI "const char *host_iface " ","
+.BI "const char *trsvcid " ","
+.BI "nvme_ctrl_t p " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "transport" 12
+Transport name
+.IP "traddr" 12
+Transport address
+.IP "host_traddr" 12
+Host transport address
+.IP "host_iface" 12
+Host interface name
+.IP "trsvcid" 12
+Transport service identifier
+.IP "p" 12
+Previous controller instance
+.SH "DESCRIPTION"
+Lookup a controller in \fIs\fP based on \fItransport\fP, \fItraddr\fP,
+\fIhost_traddr\fP, \fIhost_iface\fP, and \fItrsvcid\fP. \fItransport\fP must be specified,
+other fields may be required depending on the transport. A new
+object is created if none is found. If \fIp\fP is specified the lookup
+will start at \fIp\fP instead of the first controller.
+.SH "RETURN"
+Controller instance
diff --git a/doc/man/nvme_lookup_host.2 b/doc/man/nvme_lookup_host.2
new file mode 100644
index 0000000..13644b0
--- /dev/null
+++ b/doc/man/nvme_lookup_host.2
@@ -0,0 +1,20 @@
+.TH "nvme_lookup_host" 9 "nvme_lookup_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_lookup_host \- Lookup nvme_host_t object
+.SH SYNOPSIS
+.B "nvme_host_t" nvme_lookup_host
+.BI "(nvme_root_t r " ","
+.BI "const char *hostnqn " ","
+.BI "const char *hostid " ");"
+.SH ARGUMENTS
+.IP "r" 12
+\fInvme_root_t\fP object
+.IP "hostnqn" 12
+Host NQN
+.IP "hostid" 12
+Host ID
+.SH "DESCRIPTION"
+Lookup a nvme_host_t object based on \fIhostnqn\fP and \fIhostid\fP
+or create one if not found.
+.SH "RETURN"
+\fInvme_host_t\fP object
diff --git a/doc/man/nvme_lookup_key.2 b/doc/man/nvme_lookup_key.2
new file mode 100644
index 0000000..745ba37
--- /dev/null
+++ b/doc/man/nvme_lookup_key.2
@@ -0,0 +1,18 @@
+.TH "nvme_lookup_key" 9 "nvme_lookup_key" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_lookup_key \- Lookup key serial number
+.SH SYNOPSIS
+.B "long" nvme_lookup_key
+.BI "(const char *type " ","
+.BI "const char *identity " ");"
+.SH ARGUMENTS
+.IP "type" 12
+Key type
+.IP "identity" 12
+Key description
+.SH "DESCRIPTION"
+Looks up the serial number of the key \fIidentity\fP
+with type type in the current session keyring.
+.SH "RETURN"
+The key serial number of the key
+or 0 with errno set otherwise.
diff --git a/doc/man/nvme_lookup_keyring.2 b/doc/man/nvme_lookup_keyring.2
new file mode 100644
index 0000000..591a6d7
--- /dev/null
+++ b/doc/man/nvme_lookup_keyring.2
@@ -0,0 +1,14 @@
+.TH "nvme_lookup_keyring" 9 "nvme_lookup_keyring" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_lookup_keyring \- Lookup keyring serial number
+.SH SYNOPSIS
+.B "long" nvme_lookup_keyring
+.BI "(const char *keyring " ");"
+.SH ARGUMENTS
+.IP "keyring" 12
+Keyring name
+.SH "DESCRIPTION"
+Looks up the serial number of the keyring \fIkeyring\fP.
+.SH "RETURN"
+The key serial number of the keyring
+or 0 with errno set otherwise.
diff --git a/doc/man/nvme_lookup_subsystem.2 b/doc/man/nvme_lookup_subsystem.2
new file mode 100644
index 0000000..ef376f9
--- /dev/null
+++ b/doc/man/nvme_lookup_subsystem.2
@@ -0,0 +1,20 @@
+.TH "nvme_lookup_subsystem" 9 "nvme_lookup_subsystem" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_lookup_subsystem \- Lookup nvme_subsystem_t object
+.SH SYNOPSIS
+.B "nvme_subsystem_t" nvme_lookup_subsystem
+.BI "(struct nvme_host *h " ","
+.BI "const char *name " ","
+.BI "const char *subsysnqn " ");"
+.SH ARGUMENTS
+.IP "h" 12
+\fInvme_host_t\fP object
+.IP "name" 12
+Name of the subsystem (may be NULL)
+.IP "subsysnqn" 12
+Subsystem NQN
+.SH "DESCRIPTION"
+Lookup a \fInvme_subsystem_t\fP object in \fIh\fP base on \fIname\fP (if present)
+and \fIsubsysnqn\fP or create one if not found.
+.SH "RETURN"
+nvme_subsystem_t object
diff --git a/doc/man/nvme_media_unit_config_desc.2 b/doc/man/nvme_media_unit_config_desc.2
new file mode 100644
index 0000000..319aac1
--- /dev/null
+++ b/doc/man/nvme_media_unit_config_desc.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_media_unit_config_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_media_unit_config_desc \- Media Unit Configuration Descriptor
+.SH SYNOPSIS
+struct nvme_media_unit_config_desc {
+.br
+.BI " __le16 muid;"
+.br
+.BI " __u8 rsvd2[4];"
+.br
+.BI " __le16 mudl;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "muid" 12
+Media Unit Identifier
+.IP "rsvd2" 12
+Reserved
+.IP "mudl" 12
+Media Unit Descriptor Length
diff --git a/doc/man/nvme_media_unit_stat_desc.2 b/doc/man/nvme_media_unit_stat_desc.2
new file mode 100644
index 0000000..7e85fbf
--- /dev/null
+++ b/doc/man/nvme_media_unit_stat_desc.2
@@ -0,0 +1,47 @@
+.TH "libnvme" 9 "struct nvme_media_unit_stat_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_media_unit_stat_desc \- Media Unit Status Descriptor
+.SH SYNOPSIS
+struct nvme_media_unit_stat_desc {
+.br
+.BI " __le16 muid;"
+.br
+.BI " __le16 domainid;"
+.br
+.BI " __le16 endgid;"
+.br
+.BI " __le16 nvmsetid;"
+.br
+.BI " __le16 cap_adj_fctr;"
+.br
+.BI " __u8 avl_spare;"
+.br
+.BI " __u8 percent_used;"
+.br
+.BI " __u8 mucs;"
+.br
+.BI " __u8 cio;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "muid" 12
+Media Unit Identifier
+.IP "domainid" 12
+Domain Identifier
+.IP "endgid" 12
+Endurance Group Identifier
+.IP "nvmsetid" 12
+NVM Set Identifier
+.IP "cap_adj_fctr" 12
+Capacity Adjustment Factor
+.IP "avl_spare" 12
+Available Spare
+.IP "percent_used" 12
+Percentage Used
+.IP "mucs" 12
+Number of Channels attached to media units
+.IP "cio" 12
+Channel Identifiers Offset
diff --git a/doc/man/nvme_media_unit_stat_log.2 b/doc/man/nvme_media_unit_stat_log.2
new file mode 100644
index 0000000..c231923
--- /dev/null
+++ b/doc/man/nvme_media_unit_stat_log.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_media_unit_stat_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_media_unit_stat_log \- Media Unit Status
+.SH SYNOPSIS
+struct nvme_media_unit_stat_log {
+.br
+.BI " __le16 nmu;"
+.br
+.BI " __le16 cchans;"
+.br
+.BI " __le16 sel_config;"
+.br
+.BI " __u8 rsvd6[10];"
+.br
+.BI " struct nvme_media_unit_stat_desc mus_desc[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nmu" 12
+Number unit status descriptor
+.IP "cchans" 12
+Number of Channels
+.IP "sel_config" 12
+Selected Configuration
+.IP "rsvd6" 12
+Reserved
+.IP "mus_desc" 12
+Media unit statistic descriptors
diff --git a/doc/man/nvme_metadata_element_desc.2 b/doc/man/nvme_metadata_element_desc.2
new file mode 100644
index 0000000..461524c
--- /dev/null
+++ b/doc/man/nvme_metadata_element_desc.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_metadata_element_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_metadata_element_desc \- Metadata Element Descriptor
+.SH SYNOPSIS
+struct nvme_metadata_element_desc {
+.br
+.BI " __u8 type;"
+.br
+.BI " __u8 rev;"
+.br
+.BI " __le16 len;"
+.br
+.BI " __u8 val[0];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "type" 12
+Element Type (ET)
+.IP "rev" 12
+Element Revision (ER)
+.IP "len" 12
+Element Length (ELEN)
+.IP "val" 12
+Element Value (EVAL), UTF-8 string
diff --git a/doc/man/nvme_mi_admin_admin_passthru.2 b/doc/man/nvme_mi_admin_admin_passthru.2
new file mode 100644
index 0000000..1b84014
--- /dev/null
+++ b/doc/man/nvme_mi_admin_admin_passthru.2
@@ -0,0 +1,74 @@
+.TH "nvme_mi_admin_admin_passthru" 9 "nvme_mi_admin_admin_passthru" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_admin_passthru \- Submit an nvme admin passthrough command
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_admin_passthru
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u8 opcode " ","
+.BI "__u8 flags " ","
+.BI "__u16 rsvd " ","
+.BI "__u32 nsid " ","
+.BI "__u32 cdw2 " ","
+.BI "__u32 cdw3 " ","
+.BI "__u32 cdw10 " ","
+.BI "__u32 cdw11 " ","
+.BI "__u32 cdw12 " ","
+.BI "__u32 cdw13 " ","
+.BI "__u32 cdw14 " ","
+.BI "__u32 cdw15 " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 metadata_len " ","
+.BI "void *metadata " ","
+.BI "__u32 timeout_ms " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "opcode" 12
+The nvme admin command to send
+.IP "flags" 12
+NVMe command flags (not used)
+.IP "rsvd" 12
+Reserved for future use
+.IP "nsid" 12
+Namespace identifier
+.IP "cdw2" 12
+Command dword 2
+.IP "cdw3" 12
+Command dword 3
+.IP "cdw10" 12
+Command dword 10
+.IP "cdw11" 12
+Command dword 11
+.IP "cdw12" 12
+Command dword 12
+.IP "cdw13" 12
+Command dword 13
+.IP "cdw14" 12
+Command dword 14
+.IP "cdw15" 12
+Command dword 15
+.IP "data_len" 12
+Length of the data transferred in this command in bytes
+.IP "data" 12
+Pointer to user address of the data buffer
+.IP "metadata_len" 12
+Length of metadata transferred in this command(not used)
+.IP "metadata" 12
+Pointer to user address of the metadata buffer(not used)
+.IP "timeout_ms" 12
+How long to wait for the command to complete
+.IP "result" 12
+Optional field to return the result from the CQE dword 0
+.SH "DESCRIPTION"
+Send a customized NVMe Admin command request message and get the corresponding
+response message.
+
+This interface supports no data, host to controller and controller to
+host but it doesn't support bidirectional data transfer.
+Also this interface only supports data transfer size range [0, 4096] (bytes)
+so the & data_len parameter must be less than 4097.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_format_nvm.2 b/doc/man/nvme_mi_admin_format_nvm.2
new file mode 100644
index 0000000..12041e6
--- /dev/null
+++ b/doc/man/nvme_mi_admin_format_nvm.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_format_nvm" 9 "nvme_mi_admin_format_nvm" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_format_nvm \- Format NVMe namespace
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_format_nvm
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_format_nvm_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "args" 12
+Format NVM command arguments
+.SH "DESCRIPTION"
+Perform a low-level format to set the LBA data & metadata size. May destroy
+data & metadata on the specified namespaces
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_fw_commit.2 b/doc/man/nvme_mi_admin_fw_commit.2
new file mode 100644
index 0000000..2d13667
--- /dev/null
+++ b/doc/man/nvme_mi_admin_fw_commit.2
@@ -0,0 +1,16 @@
+.TH "nvme_mi_admin_fw_commit" 9 "nvme_mi_admin_fw_commit" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_fw_commit \- Commit firmware using the specified action
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_fw_commit
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_fw_commit_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send firmware data to
+.IP "args" 12
+\fIstruct nvme_fw_download_args\fP argument structure
+.SH "DESCRIPTION"
+The Firmware Commit command modifies the firmware image or Boot Partitions.
+.SH "RETURN"
+0 on success, non-zero on failure
diff --git a/doc/man/nvme_mi_admin_fw_download.2 b/doc/man/nvme_mi_admin_fw_download.2
new file mode 100644
index 0000000..e73d41d
--- /dev/null
+++ b/doc/man/nvme_mi_admin_fw_download.2
@@ -0,0 +1,27 @@
+.TH "nvme_mi_admin_fw_download" 9 "nvme_mi_admin_fw_download" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_fw_download \- Download part or all of a firmware image to the controller
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_fw_download
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_fw_download_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send firmware data to
+.IP "args" 12
+\fIstruct nvme_fw_download_args\fP argument structure
+.SH "DESCRIPTION"
+The Firmware Image Download command downloads all or a portion of an image
+for a future update to the controller. The Firmware Image Download command
+downloads a new image (in whole or in part) to the controller.
+
+The image may be constructed of multiple pieces that are individually
+downloaded with separate Firmware Image Download commands. Each Firmware
+Image Download command includes a Dword Offset and Number of Dwords that
+specify a dword range.
+
+The new firmware image is not activated as part of the Firmware Image
+Download command. Use the \fBnvme_mi_admin_fw_commit\fP to activate a newly
+downloaded image.
+.SH "RETURN"
+0 on success, non-zero on failure
diff --git a/doc/man/nvme_mi_admin_get_features_data.2 b/doc/man/nvme_mi_admin_get_features_data.2
new file mode 100644
index 0000000..fdde194
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_features_data.2
@@ -0,0 +1,30 @@
+.TH "nvme_mi_admin_get_features_data" 9 "nvme_mi_admin_get_features_data" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_features_data \- Helper function for &nvme_mi_admin_get_features()
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_features_data
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "enum nvme_features_id fid " ","
+.BI "__u32 nsid " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "fid" 12
+Feature identifier
+.IP "nsid" 12
+Namespace ID, if applicable for \fIfid\fP
+.IP "data_len" 12
+Length of feature data, if applicable for \fIfid\fP, in bytes
+.IP "data" 12
+User address of feature data, if applicable
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+Helper for optionally features that optionally return data, using the
+SEL_CURRENT selector value.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log.2 b/doc/man/nvme_mi_admin_get_log.2
new file mode 100644
index 0000000..68c3af8
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log.2
@@ -0,0 +1,25 @@
+.TH "nvme_mi_admin_get_log" 9 "nvme_mi_admin_get_log" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log \- Retrieve log page data from controller
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_get_log_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "args" 12
+Get Log Page command arguments
+.SH "DESCRIPTION"
+Performs a Get Log Page Admin command as specified by \fIargs\fP. Response data
+is stored in \fIargs->data\fP, which should be a buffer of \fIargs->data_len\fP bytes.
+Resulting data length is stored in \fIargs->data_len\fP on successful
+command completion.
+
+This request may be implemented as multiple log page commands, in order
+to fit within MI message-size limits.
+
+See: \fIstruct nvme_get_log_args\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_ana.2 b/doc/man/nvme_mi_admin_get_log_ana.2
new file mode 100644
index 0000000..5b7402f
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_ana.2
@@ -0,0 +1,33 @@
+.TH "nvme_mi_admin_get_log_ana" 9 "nvme_mi_admin_get_log_ana" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_ana \- Retrieve Asymmetric Namespace Access log page
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_ana
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "enum nvme_log_ana_lsp lsp " ","
+.BI "bool rae " ","
+.BI "__u64 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "lsp" 12
+Log specific, see \fIenum nvme_get_log_ana_lsp\fP
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset to the start of the log page
+.IP "len" 12
+The allocated length of the log page
+.IP "log" 12
+User address to store the ana log
+.SH "DESCRIPTION"
+This log consists of a header describing the log and descriptors containing
+the asymmetric namespace access information for ANA Groups that contain
+namespaces that are attached to the controller processing the command.
+
+See \fIstruct nvme_ana_rsp_hdr\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_ana_groups.2 b/doc/man/nvme_mi_admin_get_log_ana_groups.2
new file mode 100644
index 0000000..92dd182
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_ana_groups.2
@@ -0,0 +1,23 @@
+.TH "nvme_mi_admin_get_log_ana_groups" 9 "nvme_mi_admin_get_log_ana_groups" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_ana_groups \- Retrieve Asymmetric Namespace Access groups only log page
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_ana_groups
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "__u32 len " ","
+.BI "struct nvme_ana_group_desc *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "len" 12
+The allocated length of the log page
+.IP "log" 12
+User address to store the ana group log
+.SH "DESCRIPTION"
+See \fIstruct nvme_ana_group_desc\fP for the definition of the returned structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_boot_partition.2 b/doc/man/nvme_mi_admin_get_log_boot_partition.2
new file mode 100644
index 0000000..f952cc4
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_boot_partition.2
@@ -0,0 +1,25 @@
+.TH "nvme_mi_admin_get_log_boot_partition" 9 "nvme_mi_admin_get_log_boot_partition" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_boot_partition \- Retrieve Boot Partition
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_boot_partition
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "__u8 lsp " ","
+.BI "__u32 len " ","
+.BI "struct nvme_boot_partition *part " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "lsp" 12
+The log specified field of LID
+.IP "len" 12
+The allocated size, minimum
+struct nvme_boot_partition
+.IP "part" 12
+User address to store the log page
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_changed_ns_list.2 b/doc/man/nvme_mi_admin_get_log_changed_ns_list.2
new file mode 100644
index 0000000..6663d45
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_changed_ns_list.2
@@ -0,0 +1,22 @@
+.TH "nvme_mi_admin_get_log_changed_ns_list" 9 "nvme_mi_admin_get_log_changed_ns_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_changed_ns_list \- Retrieve namespace changed list
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_changed_ns_list
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "struct nvme_ns_list *ns_log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "ns_log" 12
+User address to store the log page
+.SH "DESCRIPTION"
+This log page describes namespaces attached to this controller that have
+changed since the last time the namespace was identified, been added, or
+deleted.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_cmd_effects.2 b/doc/man/nvme_mi_admin_get_log_cmd_effects.2
new file mode 100644
index 0000000..3f00c88
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_cmd_effects.2
@@ -0,0 +1,21 @@
+.TH "nvme_mi_admin_get_log_cmd_effects" 9 "nvme_mi_admin_get_log_cmd_effects" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_cmd_effects \- Retrieve nvme command effects log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_cmd_effects
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "enum nvme_csi csi " ","
+.BI "struct nvme_cmd_effects_log *effects_log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "csi" 12
+Command Set Identifier
+.IP "effects_log" 12
+User address to store the effects log
+.SH "DESCRIPTION"
+This log page describes the commands that the controller supports and the
+effects of those commands on the state of the NVM subsystem.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_create_telemetry_host.2 b/doc/man/nvme_mi_admin_get_log_create_telemetry_host.2
new file mode 100644
index 0000000..659910e
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_create_telemetry_host.2
@@ -0,0 +1,15 @@
+.TH "nvme_mi_admin_get_log_create_telemetry_host" 9 "nvme_mi_admin_get_log_create_telemetry_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_create_telemetry_host \- Create host telemetry log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_create_telemetry_host
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_telemetry_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "log" 12
+Userspace address of the log payload
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_device_self_test.2 b/doc/man/nvme_mi_admin_get_log_device_self_test.2
new file mode 100644
index 0000000..682b625
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_device_self_test.2
@@ -0,0 +1,19 @@
+.TH "nvme_mi_admin_get_log_device_self_test" 9 "nvme_mi_admin_get_log_device_self_test" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_device_self_test \- Retrieve the device self test log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_device_self_test
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_self_test_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "log" 12
+Userspace address of the log payload
+.SH "DESCRIPTION"
+The log page indicates the status of an in progress self test and the
+percent complete of that operation, and the results of the previous 20
+self-test operations.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_discovery.2 b/doc/man/nvme_mi_admin_get_log_discovery.2
new file mode 100644
index 0000000..bb10fb6
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_discovery.2
@@ -0,0 +1,27 @@
+.TH "nvme_mi_admin_get_log_discovery" 9 "nvme_mi_admin_get_log_discovery" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_discovery \- Retrieve Discovery log page
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_discovery
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset of this log to retrieve
+.IP "len" 12
+The allocated size for this portion of the log
+.IP "log" 12
+User address to store the discovery log
+.SH "DESCRIPTION"
+Supported only by fabrics discovery controllers, returning discovery
+records.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_endurance_group.2 b/doc/man/nvme_mi_admin_get_log_endurance_group.2
new file mode 100644
index 0000000..60d0988
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_endurance_group.2
@@ -0,0 +1,25 @@
+.TH "nvme_mi_admin_get_log_endurance_group" 9 "nvme_mi_admin_get_log_endurance_group" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_endurance_group \- Get Endurance Group log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_endurance_group
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u16 endgid " ","
+.BI "struct nvme_endurance_group_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "endgid" 12
+Starting group identifier to return in the list
+.IP "log" 12
+User address to store the endurance log
+.SH "DESCRIPTION"
+This log page indicates if an Endurance Group Event has occurred for a
+particular Endurance Group. If an Endurance Group Event has occurred, the
+details of the particular event are included in the Endurance Group
+Information log page for that Endurance Group. An asynchronous event is
+generated when an entry for an Endurance Group is newly added to this log
+page.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_endurance_grp_evt.2 b/doc/man/nvme_mi_admin_get_log_endurance_grp_evt.2
new file mode 100644
index 0000000..d9d5edc
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_endurance_grp_evt.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_admin_get_log_endurance_grp_evt" 9 "nvme_mi_admin_get_log_endurance_grp_evt" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_endurance_grp_evt \- Retrieve Rotational Media Information
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_endurance_grp_evt
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset to the start of the log page
+.IP "len" 12
+The allocated length of the log page
+.IP "log" 12
+User address to store the log page
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_error.2 b/doc/man/nvme_mi_admin_get_log_error.2
new file mode 100644
index 0000000..2f41599
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_error.2
@@ -0,0 +1,25 @@
+.TH "nvme_mi_admin_get_log_error" 9 "nvme_mi_admin_get_log_error" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_error \- Retrieve nvme error log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_error
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "unsigned int nr_entries " ","
+.BI "bool rae " ","
+.BI "struct nvme_error_log_page *err_log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "nr_entries" 12
+Number of error log entries allocated
+.IP "rae" 12
+Retain asynchronous events
+.IP "err_log" 12
+Array of error logs of size 'entries'
+.SH "DESCRIPTION"
+This log page describes extended error information for a command that
+completed with error, or may report an error that is not specific to a
+particular command.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_fid_supported_effects.2 b/doc/man/nvme_mi_admin_get_log_fid_supported_effects.2
new file mode 100644
index 0000000..a04e839
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_fid_supported_effects.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_get_log_fid_supported_effects" 9 "nvme_mi_admin_get_log_fid_supported_effects" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_fid_supported_effects \- Retrieve Feature Identifiers Supported and Effects
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_fid_supported_effects
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "struct nvme_fid_supported_effects_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+FID Supported and Effects data structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_fw_slot.2 b/doc/man/nvme_mi_admin_get_log_fw_slot.2
new file mode 100644
index 0000000..d6710a2
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_fw_slot.2
@@ -0,0 +1,22 @@
+.TH "nvme_mi_admin_get_log_fw_slot" 9 "nvme_mi_admin_get_log_fw_slot" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_fw_slot \- Retrieves the controller firmware log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_fw_slot
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "struct nvme_firmware_slot *fw_log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "fw_log" 12
+User address to store the log page
+.SH "DESCRIPTION"
+This log page describes the firmware revision stored in each firmware slot
+supported. The firmware revision is indicated as an ASCII string. The log
+page also indicates the active slot number.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_lba_status.2 b/doc/man/nvme_mi_admin_get_log_lba_status.2
new file mode 100644
index 0000000..862e63a
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_lba_status.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_admin_get_log_lba_status" 9 "nvme_mi_admin_get_log_lba_status" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_lba_status \- Retrieve LBA Status
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_lba_status
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "__u64 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset to the start of the log page
+.IP "len" 12
+The allocated length of the log page
+.IP "log" 12
+User address to store the log page
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_media_unit_stat.2 b/doc/man/nvme_mi_admin_get_log_media_unit_stat.2
new file mode 100644
index 0000000..ef225fd
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_media_unit_stat.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_get_log_media_unit_stat" 9 "nvme_mi_admin_get_log_media_unit_stat" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_media_unit_stat \- Retrieve Media Unit Status
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_media_unit_stat
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u16 domid " ","
+.BI "struct nvme_media_unit_stat_log *mus " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "domid" 12
+Domain Identifier selection, if supported
+.IP "mus" 12
+User address to store the Media Unit statistics log
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_mi_cmd_supported_effects.2 b/doc/man/nvme_mi_admin_get_log_mi_cmd_supported_effects.2
new file mode 100644
index 0000000..15dfd48
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_mi_cmd_supported_effects.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_get_log_mi_cmd_supported_effects" 9 "nvme_mi_admin_get_log_mi_cmd_supported_effects" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_mi_cmd_supported_effects \- displays the MI Commands Supported by the controller
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_mi_cmd_supported_effects
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "struct nvme_mi_cmd_supported_effects_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+MI Command Supported and Effects data structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_page.2 b/doc/man/nvme_mi_admin_get_log_page.2
new file mode 100644
index 0000000..f016429
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_page.2
@@ -0,0 +1,28 @@
+.TH "nvme_mi_admin_get_log_page" 9 "nvme_mi_admin_get_log_page" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_page \- Retrieve log page data from controller
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_page
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 xfer_len " ","
+.BI "struct nvme_get_log_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "xfer_len" 12
+The chunk size of the read
+.IP "args" 12
+Get Log Page command arguments
+.SH "DESCRIPTION"
+Performs a Get Log Page Admin command as specified by \fIargs\fP. Response data
+is stored in \fIargs->data\fP, which should be a buffer of \fIargs->data_len\fP bytes.
+Resulting data length is stored in \fIargs->data_len\fP on successful
+command completion.
+
+This request may be implemented as multiple log page commands, in order
+to fit within MI message-size limits.
+
+See: \fIstruct nvme_get_log_args\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_persistent_event.2 b/doc/man/nvme_mi_admin_get_log_persistent_event.2
new file mode 100644
index 0000000..fcdc3f4
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_persistent_event.2
@@ -0,0 +1,21 @@
+.TH "nvme_mi_admin_get_log_persistent_event" 9 "nvme_mi_admin_get_log_persistent_event" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_persistent_event \- Retrieve Persistent Event Log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_persistent_event
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "enum nvme_pevent_log_action action " ","
+.BI "__u32 size " ","
+.BI "void *pevent_log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "action" 12
+Action the controller should take during processing this command
+.IP "size" 12
+Size of \fIpevent_log\fP
+.IP "pevent_log" 12
+User address to store the persistent event log
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_phy_rx_eom.2 b/doc/man/nvme_mi_admin_get_log_phy_rx_eom.2
new file mode 100644
index 0000000..87917a6
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_phy_rx_eom.2
@@ -0,0 +1,25 @@
+.TH "nvme_mi_admin_get_log_phy_rx_eom" 9 "nvme_mi_admin_get_log_phy_rx_eom" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_phy_rx_eom \- Retrieve Physical Interface Receiver Eye Opening Measurement Log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_phy_rx_eom
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u8 lsp " ","
+.BI "__u16 controller " ","
+.BI "__u32 len " ","
+.BI "struct nvme_phy_rx_eom_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "lsp" 12
+Log specific, controls action and measurement quality
+.IP "controller" 12
+Target controller ID
+.IP "len" 12
+The allocated size, minimum
+struct nvme_phy_rx_eom_log
+.IP "log" 12
+User address to store the log page
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise
diff --git a/doc/man/nvme_mi_admin_get_log_predictable_lat_event.2 b/doc/man/nvme_mi_admin_get_log_predictable_lat_event.2
new file mode 100644
index 0000000..20cf516
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_predictable_lat_event.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_admin_get_log_predictable_lat_event" 9 "nvme_mi_admin_get_log_predictable_lat_event" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_predictable_lat_event \- Retrieve Predictable Latency Event Aggregate Log Page
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_predictable_lat_event
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "__u32 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset into the predictable latency event
+.IP "len" 12
+Length of provided user buffer to hold the log data in bytes
+.IP "log" 12
+User address for log page data
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_predictable_lat_nvmset.2 b/doc/man/nvme_mi_admin_get_log_predictable_lat_nvmset.2
new file mode 100644
index 0000000..756613b
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_predictable_lat_nvmset.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_get_log_predictable_lat_nvmset" 9 "nvme_mi_admin_get_log_predictable_lat_nvmset" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_predictable_lat_nvmset \- Predictable Latency Per NVM Set
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_predictable_lat_nvmset
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u16 nvmsetid " ","
+.BI "struct nvme_nvmset_predictable_lat_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "nvmsetid" 12
+NVM set id
+.IP "log" 12
+User address to store the predictable latency log
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_reservation.2 b/doc/man/nvme_mi_admin_get_log_reservation.2
new file mode 100644
index 0000000..239d425
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_reservation.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_get_log_reservation" 9 "nvme_mi_admin_get_log_reservation" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_reservation \- Retrieve Reservation Notification
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_reservation
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "struct nvme_resv_notification_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+User address to store the reservation log
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_sanitize.2 b/doc/man/nvme_mi_admin_get_log_sanitize.2
new file mode 100644
index 0000000..4994b42
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_sanitize.2
@@ -0,0 +1,21 @@
+.TH "nvme_mi_admin_get_log_sanitize" 9 "nvme_mi_admin_get_log_sanitize" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_sanitize \- Retrieve Sanitize Status
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_sanitize
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "struct nvme_sanitize_log_page *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+User address to store the sanitize log
+.SH "DESCRIPTION"
+The Sanitize Status log page reports sanitize operation time estimates and
+information about the most recent sanitize operation.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_simple.2 b/doc/man/nvme_mi_admin_get_log_simple.2
new file mode 100644
index 0000000..337e32a
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_simple.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_admin_get_log_simple" 9 "nvme_mi_admin_get_log_simple" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_simple \- Helper for Get Log Page functions with no NSID or RAE requirements
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_simple
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "enum nvme_cmd_get_log_lid lid " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "lid" 12
+Log identifier
+.IP "len" 12
+length of log buffer
+.IP "log" 12
+pointer for resulting log data
+.SH "DESCRIPTION"
+Performs a Get Log Page Admin command for a specific log ID \fIlid\fP, using
+NVME_NSID_ALL for the namespace identifier, and rae set to false.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_smart.2 b/doc/man/nvme_mi_admin_get_log_smart.2
new file mode 100644
index 0000000..3807875
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_smart.2
@@ -0,0 +1,28 @@
+.TH "nvme_mi_admin_get_log_smart" 9 "nvme_mi_admin_get_log_smart" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_smart \- Retrieve nvme smart log
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_smart
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "bool rae " ","
+.BI "struct nvme_smart_log *smart_log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "nsid" 12
+Optional namespace identifier
+.IP "rae" 12
+Retain asynchronous events
+.IP "smart_log" 12
+User address to store the smart log
+.SH "DESCRIPTION"
+This log page provides SMART and general health information. The information
+provided is over the life of the controller and is retained across power
+cycles. To request the controller log page, the namespace identifier
+specified is FFFFFFFFh. The controller may also support requesting the log
+page on a per namespace basis, as indicated by bit 0 of the LPA field in the
+Identify Controller data structure.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_support_cap_config_list.2 b/doc/man/nvme_mi_admin_get_log_support_cap_config_list.2
new file mode 100644
index 0000000..43956c8
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_support_cap_config_list.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_get_log_support_cap_config_list" 9 "nvme_mi_admin_get_log_support_cap_config_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_support_cap_config_list \- Retrieve Supported Capacity Configuration List
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_support_cap_config_list
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u16 domid " ","
+.BI "struct nvme_supported_cap_config_list_log *cap " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "domid" 12
+Domain Identifier selection, if supported
+.IP "cap" 12
+User address to store supported capabilities config list
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_supported_log_pages.2 b/doc/man/nvme_mi_admin_get_log_supported_log_pages.2
new file mode 100644
index 0000000..49df890
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_supported_log_pages.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_get_log_supported_log_pages" 9 "nvme_mi_admin_get_log_supported_log_pages" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_supported_log_pages \- Retrieve nmve supported log pages
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_supported_log_pages
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "struct nvme_supported_log_pages *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+Array of LID supported and Effects data structures
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_telemetry_ctrl.2 b/doc/man/nvme_mi_admin_get_log_telemetry_ctrl.2
new file mode 100644
index 0000000..e2e10f1
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_telemetry_ctrl.2
@@ -0,0 +1,27 @@
+.TH "nvme_mi_admin_get_log_telemetry_ctrl" 9 "nvme_mi_admin_get_log_telemetry_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_telemetry_ctrl \- Get Telemetry Controller-Initiated log page
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_telemetry_ctrl
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "__u64 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain asynchronous events
+.IP "offset" 12
+Offset into the telemetry data
+.IP "len" 12
+Length of provided user buffer to hold the log data in bytes
+.IP "log" 12
+User address for log page data
+.SH "DESCRIPTION"
+Retrieves the Telemetry Controller-Initiated log page at the requested offset
+using the previously existing capture.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_telemetry_host.2 b/doc/man/nvme_mi_admin_get_log_telemetry_host.2
new file mode 100644
index 0000000..92d83fe
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_telemetry_host.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_admin_get_log_telemetry_host" 9 "nvme_mi_admin_get_log_telemetry_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_telemetry_host \- Get Telemetry Host-Initiated log page
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_telemetry_host
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u64 offset " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "offset" 12
+Offset into the telemetry data
+.IP "len" 12
+Length of provided user buffer to hold the log data in bytes
+.IP "log" 12
+User address for log page data
+.SH "DESCRIPTION"
+Retrieves the Telemetry Host-Initiated log page at the requested offset
+using the previously existing capture.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_log_zns_changed_zones.2 b/doc/man/nvme_mi_admin_get_log_zns_changed_zones.2
new file mode 100644
index 0000000..7e10c44
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_log_zns_changed_zones.2
@@ -0,0 +1,23 @@
+.TH "nvme_mi_admin_get_log_zns_changed_zones" 9 "nvme_mi_admin_get_log_zns_changed_zones" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_log_zns_changed_zones \- Retrieve list of zones that have changed
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_log_zns_changed_zones
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "bool rae " ","
+.BI "struct nvme_zns_changed_zone_log *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "nsid" 12
+Namespace ID
+.IP "rae" 12
+Retain asynchronous events
+.IP "log" 12
+User address to store the changed zone log
+.SH "DESCRIPTION"
+The list of zones that have changed state due to an exceptional event.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_get_nsid_log.2 b/doc/man/nvme_mi_admin_get_nsid_log.2
new file mode 100644
index 0000000..7408a50
--- /dev/null
+++ b/doc/man/nvme_mi_admin_get_nsid_log.2
@@ -0,0 +1,32 @@
+.TH "nvme_mi_admin_get_nsid_log" 9 "nvme_mi_admin_get_nsid_log" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_get_nsid_log \- Helper for Get Log Page functions
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_get_nsid_log
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "bool rae " ","
+.BI "enum nvme_cmd_get_log_lid lid " ","
+.BI "__u32 nsid " ","
+.BI "__u32 len " ","
+.BI "void *log " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to query
+.IP "rae" 12
+Retain Asynchronous Events
+.IP "lid" 12
+Log identifier
+.IP "nsid" 12
+Namespace ID
+.IP "len" 12
+length of log buffer
+.IP "log" 12
+pointer for resulting log data
+.SH "DESCRIPTION"
+Performs a Get Log Page Admin command for a specific log ID \fIlid\fP and
+namespace ID \fInsid\fP. Log data is expected to be \fIlen\fP bytes, and is stored
+in \fIlog\fP on success. The \fIrae\fP flag is passed as-is to the Get Log Page
+command, and is specific to the Log Page requested.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify.2 b/doc/man/nvme_mi_admin_identify.2
new file mode 100644
index 0000000..3af75ee
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_admin_identify" 9 "nvme_mi_admin_identify" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify \- Perform an Admin identify command.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_identify_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "args" 12
+Identify command arguments
+.SH "DESCRIPTION"
+Perform an Identify command, using the Identify command parameters in \fIargs\fP.
+Stores the identify data in ->data, and (if set) the result from cdw0
+into args->result.
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP.
+
+See: \fIstruct nvme_identify_args\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_active_ns_list.2 b/doc/man/nvme_mi_admin_identify_active_ns_list.2
new file mode 100644
index 0000000..eabc99c
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_active_ns_list.2
@@ -0,0 +1,28 @@
+.TH "nvme_mi_admin_identify_active_ns_list" 9 "nvme_mi_admin_identify_active_ns_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_active_ns_list \- Perform an Admin identify for an active namespace list
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_active_ns_list
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ns_list *list " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "nsid" 12
+Namespace ID to specify list start
+.IP "list" 12
+List data to populate
+.SH "DESCRIPTION"
+Perform an Identify command, for the active namespace list starting with
+IDs greater than or equal to \fInsid\fP. Specify \fINVME_NSID_NONE\fP for the start
+of the list.
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP, so \fIlist\fP will be
+be fully populated on success.
+
+See: \fIstruct nvme_ns_list\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_allocated_ns.2 b/doc/man/nvme_mi_admin_identify_allocated_ns.2
new file mode 100644
index 0000000..2e5dd91
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_allocated_ns.2
@@ -0,0 +1,21 @@
+.TH "nvme_mi_admin_identify_allocated_ns" 9 "nvme_mi_admin_identify_allocated_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_allocated_ns \- Perform an Admin identify command for an allocated namespace
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_allocated_ns
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_id_ns *ns " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "nsid" 12
+namespace ID
+.IP "ns" 12
+Namespace identification to populate
+.SH "DESCRIPTION"
+Perform an Identify (namespace) command, setting the namespace id data
+in \fIns\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_allocated_ns_list.2 b/doc/man/nvme_mi_admin_identify_allocated_ns_list.2
new file mode 100644
index 0000000..b847d6e
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_allocated_ns_list.2
@@ -0,0 +1,28 @@
+.TH "nvme_mi_admin_identify_allocated_ns_list" 9 "nvme_mi_admin_identify_allocated_ns_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_allocated_ns_list \- Perform an Admin identify for an allocated namespace list
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_allocated_ns_list
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ns_list *list " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "nsid" 12
+Namespace ID to specify list start
+.IP "list" 12
+List data to populate
+.SH "DESCRIPTION"
+Perform an Identify command, for the allocated namespace list starting with
+IDs greater than or equal to \fInsid\fP. Specify \fINVME_NSID_NONE\fP for the start
+of the list.
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP, so \fIlist\fP will be
+be fully populated on success.
+
+See: \fIstruct nvme_ns_list\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_cns_nsid.2 b/doc/man/nvme_mi_admin_identify_cns_nsid.2
new file mode 100644
index 0000000..d6cc88a
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_cns_nsid.2
@@ -0,0 +1,30 @@
+.TH "nvme_mi_admin_identify_cns_nsid" 9 "nvme_mi_admin_identify_cns_nsid" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_cns_nsid \- Perform an Admin identify command using specific CNS/NSID parameters.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_cns_nsid
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "enum nvme_identify_cns cns " ","
+.BI "__u32 nsid " ","
+.BI "void *data " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "cns" 12
+Controller or Namespace Structure, specifying identified object
+.IP "nsid" 12
+namespace ID
+.IP "data" 12
+buffer for identify data response
+.SH "DESCRIPTION"
+Perform an Identify command, using the CNS specifier \fIcns\fP, and the
+namespace ID \fInsid\fP if required by the CNS type.
+
+Stores the identify data in \fIdata\fP, which is expected to be a buffer of
+\fINVME_IDENTIFY_DATA_SIZE\fP bytes.
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_ctrl.2 b/doc/man/nvme_mi_admin_identify_ctrl.2
new file mode 100644
index 0000000..3fe3f01
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_ctrl.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_admin_identify_ctrl" 9 "nvme_mi_admin_identify_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_ctrl \- Perform an Admin identify for a controller
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_ctrl
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_id_ctrl *id " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "id" 12
+Controller identify data to populate
+.SH "DESCRIPTION"
+Perform an Identify command, for the controller specified by \fIctrl\fP,
+writing identify data to \fIid\fP.
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP, so \fIid\fP will be
+fully populated on success.
+
+See: \fIstruct nvme_id_ctrl\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_ctrl_list.2 b/doc/man/nvme_mi_admin_identify_ctrl_list.2
new file mode 100644
index 0000000..04379b4
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_ctrl_list.2
@@ -0,0 +1,27 @@
+.TH "nvme_mi_admin_identify_ctrl_list" 9 "nvme_mi_admin_identify_ctrl_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_ctrl_list \- Perform an Admin identify for a controller list.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_ctrl_list
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u16 cntid " ","
+.BI "struct nvme_ctrl_list *list " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "cntid" 12
+Controller ID to specify list start
+.IP "list" 12
+List data to populate
+.SH "DESCRIPTION"
+Perform an Identify command, for the controller list starting with
+IDs greater than or equal to \fIcntid\fP.
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP, so \fIid\fP will be
+fully populated on success.
+
+See: \fIstruct nvme_ctrl_list\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_ns.2 b/doc/man/nvme_mi_admin_identify_ns.2
new file mode 100644
index 0000000..1aee01e
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_ns.2
@@ -0,0 +1,21 @@
+.TH "nvme_mi_admin_identify_ns" 9 "nvme_mi_admin_identify_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_ns \- Perform an Admin identify command for a namespace
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_ns
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_id_ns *ns " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "nsid" 12
+namespace ID
+.IP "ns" 12
+Namespace identification to populate
+.SH "DESCRIPTION"
+Perform an Identify (namespace) command, setting the namespace id data
+in \fIns\fP. The namespace is expected to active and allocated.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_ns_descs.2 b/doc/man/nvme_mi_admin_identify_ns_descs.2
new file mode 100644
index 0000000..3788ef0
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_ns_descs.2
@@ -0,0 +1,21 @@
+.TH "nvme_mi_admin_identify_ns_descs" 9 "nvme_mi_admin_identify_ns_descs" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_ns_descs \- Perform an Admin identify Namespace Identification Descriptor list command for a namespace
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_ns_descs
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ns_id_desc *descs " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "nsid" 12
+Namespace ID
+.IP "descs" 12
+Namespace Identification Descriptor list to populate
+.SH "DESCRIPTION"
+Perform an Identify namespace identification description list command,
+setting the namespace identification description list in \fIdescs\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_nsid_ctrl_list.2 b/doc/man/nvme_mi_admin_identify_nsid_ctrl_list.2
new file mode 100644
index 0000000..e10dfbe
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_nsid_ctrl_list.2
@@ -0,0 +1,30 @@
+.TH "nvme_mi_admin_identify_nsid_ctrl_list" 9 "nvme_mi_admin_identify_nsid_ctrl_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_nsid_ctrl_list \- Perform an Admin identify for a controller list with specific namespace ID
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_nsid_ctrl_list
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "__u16 cntid " ","
+.BI "struct nvme_ctrl_list *list " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "nsid" 12
+Namespace identifier
+.IP "cntid" 12
+Controller ID to specify list start
+.IP "list" 12
+List data to populate
+.SH "DESCRIPTION"
+Perform an Identify command, for the controller list for \fInsid\fP, starting
+with IDs greater than or equal to \fIcntid\fP.
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP, so \fIid\fP will be
+fully populated on success.
+
+See: \fIstruct nvme_ctrl_list\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_partial.2 b/doc/man/nvme_mi_admin_identify_partial.2
new file mode 100644
index 0000000..bddc694
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_partial.2
@@ -0,0 +1,37 @@
+.TH "nvme_mi_admin_identify_partial" 9 "nvme_mi_admin_identify_partial" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_partial \- Perform an Admin identify command, and retrieve partial response data.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_partial
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_identify_args *args " ","
+.BI "off_t offset " ","
+.BI "size_t size " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "args" 12
+Identify command arguments
+.IP "offset" 12
+offset of identify data to retrieve from response
+.IP "size" 12
+size of identify data to return
+.SH "DESCRIPTION"
+Perform an Identify command, using the Identify command parameters in \fIargs\fP.
+The \fIoffset\fP and \fIsize\fP arguments allow the caller to retrieve part of
+the identify response. See NVMe-MI section 6.2 for the semantics (and some
+handy diagrams) of the offset & size parameters.
+
+Will return an error if the length of the response data (from the controller)
+did not match \fIsize\fP.
+
+Unless you're performing a vendor-unique identify command, You'll probably
+want to use one of the identify helpers (nvme_mi_admin_identify,
+nvme_mi_admin_identify_cns_nsid, or nvme_mi_admin_identify_<type>) instead
+of this. If the type of your identify command is standardized but not
+yet supported by libnvme-mi, please contact the maintainers.
+
+See: \fIstruct nvme_identify_args\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_identify_primary_ctrl.2 b/doc/man/nvme_mi_admin_identify_primary_ctrl.2
new file mode 100644
index 0000000..f595e14
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_primary_ctrl.2
@@ -0,0 +1,26 @@
+.TH "nvme_mi_admin_identify_primary_ctrl" 9 "nvme_mi_admin_identify_primary_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_primary_ctrl \- Perform an Admin identify for primary controller capabilities data structure.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_primary_ctrl
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u16 cntid " ","
+.BI "struct nvme_primary_ctrl_cap *cap " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "cntid" 12
+Controller ID to specify
+.IP "cap" 12
+Primary Controller Capabilities data structure to populate
+.SH "DESCRIPTION"
+Perform an Identify command to get the Primary Controller Capabilities data
+for the controller specified by \fIcntid\fP
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP, so \fIcap\fP will be
+be fully populated on success.
+
+See: \fIstruct nvme_primary_ctrl_cap\fP
+.SH "RETURN"
+0 on success, non-zero on failure
diff --git a/doc/man/nvme_mi_admin_identify_secondary_ctrl_list.2 b/doc/man/nvme_mi_admin_identify_secondary_ctrl_list.2
new file mode 100644
index 0000000..21c76e7
--- /dev/null
+++ b/doc/man/nvme_mi_admin_identify_secondary_ctrl_list.2
@@ -0,0 +1,27 @@
+.TH "nvme_mi_admin_identify_secondary_ctrl_list" 9 "nvme_mi_admin_identify_secondary_ctrl_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_identify_secondary_ctrl_list \- Perform an Admin identify for a secondary controller list.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_identify_secondary_ctrl_list
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u16 cntid " ","
+.BI "struct nvme_secondary_ctrl_list *list " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to process identify command
+.IP "cntid" 12
+Controller ID to specify list start
+.IP "list" 12
+List data to populate
+.SH "DESCRIPTION"
+Perform an Identify command, for the secondary controllers associated with
+the current primary controller. Only entries with IDs greater than or
+equal to \fIcntid\fP are returned.
+
+Will return an error if the length of the response data (from the
+controller) is not a full \fINVME_IDENTIFY_DATA_SIZE\fP, so \fIlist\fP will be
+be fully populated on success.
+
+See: \fIstruct nvme_secondary_ctrl_list\fP
+.SH "RETURN"
+0 on success, non-zero on failure
diff --git a/doc/man/nvme_mi_admin_ns_attach.2 b/doc/man/nvme_mi_admin_ns_attach.2
new file mode 100644
index 0000000..80164f3
--- /dev/null
+++ b/doc/man/nvme_mi_admin_ns_attach.2
@@ -0,0 +1,15 @@
+.TH "nvme_mi_admin_ns_attach" 9 "nvme_mi_admin_ns_attach" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_ns_attach \- Attach or detach namespace to controller(s)
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_ns_attach
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_ns_attach_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "args" 12
+Namespace Attach command arguments
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_ns_attach_ctrls.2 b/doc/man/nvme_mi_admin_ns_attach_ctrls.2
new file mode 100644
index 0000000..f41dfdb
--- /dev/null
+++ b/doc/man/nvme_mi_admin_ns_attach_ctrls.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_ns_attach_ctrls" 9 "nvme_mi_admin_ns_attach_ctrls" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_ns_attach_ctrls \- Attach namespace to controllers
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_ns_attach_ctrls
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ctrl_list *ctrlist " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "nsid" 12
+Namespace ID to attach
+.IP "ctrlist" 12
+Controller list to modify attachment state of nsid
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_ns_detach_ctrls.2 b/doc/man/nvme_mi_admin_ns_detach_ctrls.2
new file mode 100644
index 0000000..6965810
--- /dev/null
+++ b/doc/man/nvme_mi_admin_ns_detach_ctrls.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_admin_ns_detach_ctrls" 9 "nvme_mi_admin_ns_detach_ctrls" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_ns_detach_ctrls \- Detach namespace from controllers
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_ns_detach_ctrls
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ctrl_list *ctrlist " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "nsid" 12
+Namespace ID to detach
+.IP "ctrlist" 12
+Controller list to modify attachment state of nsid
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_req_hdr.2 b/doc/man/nvme_mi_admin_req_hdr.2
new file mode 100644
index 0000000..95aac5f
--- /dev/null
+++ b/doc/man/nvme_mi_admin_req_hdr.2
@@ -0,0 +1,72 @@
+.TH "libnvme" 9 "struct nvme_mi_admin_req_hdr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_admin_req_hdr \- Admin command request header.
+.SH SYNOPSIS
+struct nvme_mi_admin_req_hdr {
+.br
+.BI " struct nvme_mi_msg_hdr hdr;"
+.br
+.BI " __u8 opcode;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __le16 ctrl_id;"
+.br
+.BI " __le32 cdw1, cdw2, cdw3, cdw4, cdw5;"
+.br
+.BI " __le32 doff;"
+.br
+.BI " __le32 dlen;"
+.br
+.BI " __le32 rsvd0, rsvd1;"
+.br
+.BI " __le32 cdw10, cdw11, cdw12, cdw13, cdw14, cdw15;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "hdr" 12
+Generic MI message header
+.IP "opcode" 12
+Admin command opcode (using enum nvme_admin_opcode)
+.IP "flags" 12
+Command Flags, indicating dlen and doff validity; Only defined in
+NVMe-MI version 1.1, no fields defined in 1.2 (where the dlen/doff
+are always considered valid).
+.IP "ctrl_id" 12
+Controller ID target of command
+.IP "cdw1" 12
+Submission Queue Entry doubleword 1
+.IP "cdw2" 12
+Submission Queue Entry doubleword 2
+.IP "cdw3" 12
+Submission Queue Entry doubleword 3
+.IP "cdw4" 12
+Submission Queue Entry doubleword 4
+.IP "cdw5" 12
+Submission Queue Entry doubleword 5
+.IP "doff" 12
+Offset of data to return from command
+.IP "dlen" 12
+Length of sent/returned data
+.IP "rsvd0" 12
+Reserved
+.IP "rsvd1" 12
+Reserved
+.IP "cdw10" 12
+Submission Queue Entry doubleword 10
+.IP "cdw11" 12
+Submission Queue Entry doubleword 11
+.IP "cdw12" 12
+Submission Queue Entry doubleword 12
+.IP "cdw13" 12
+Submission Queue Entry doubleword 13
+.IP "cdw14" 12
+Submission Queue Entry doubleword 14
+.IP "cdw15" 12
+Submission Queue Entry doubleword 15
+.SH "Description"
+Wire format for Admin command message headers, defined in section 6 of
+NVMe-MI.
diff --git a/doc/man/nvme_mi_admin_resp_hdr.2 b/doc/man/nvme_mi_admin_resp_hdr.2
new file mode 100644
index 0000000..c26cbd9
--- /dev/null
+++ b/doc/man/nvme_mi_admin_resp_hdr.2
@@ -0,0 +1,34 @@
+.TH "libnvme" 9 "struct nvme_mi_admin_resp_hdr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_admin_resp_hdr \- Admin command response header.
+.SH SYNOPSIS
+struct nvme_mi_admin_resp_hdr {
+.br
+.BI " struct nvme_mi_msg_hdr hdr;"
+.br
+.BI " __u8 status;"
+.br
+.BI " __u8 rsvd0[3];"
+.br
+.BI " __le32 cdw0, cdw1, cdw3;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "hdr" 12
+Generic MI message header
+.IP "status" 12
+Generic response code, non-zero on failure
+.IP "rsvd0" 12
+Reserved
+.IP "cdw0" 12
+Completion Queue Entry doubleword 0
+.IP "cdw1" 12
+Completion Queue Entry doubleword 1
+.IP "cdw3" 12
+Completion Queue Entry doubleword 3
+.SH "Description"
+This is the generic response format with the three doublewords of completion
+queue data, plus optional response data.
diff --git a/doc/man/nvme_mi_admin_sanitize_nvm.2 b/doc/man/nvme_mi_admin_sanitize_nvm.2
new file mode 100644
index 0000000..2902275
--- /dev/null
+++ b/doc/man/nvme_mi_admin_sanitize_nvm.2
@@ -0,0 +1,25 @@
+.TH "nvme_mi_admin_sanitize_nvm" 9 "nvme_mi_admin_sanitize_nvm" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_sanitize_nvm \- Start a subsystem Sanitize operation
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_sanitize_nvm
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_sanitize_nvm_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "args" 12
+Sanitize command arguments
+.SH "DESCRIPTION"
+A sanitize operation alters all user data in the NVM subsystem such that
+recovery of any previous user data from any cache, the non-volatile media,
+or any Controller Memory Buffer is not possible.
+
+The Sanitize command starts a sanitize operation or to recover from a
+previously failed sanitize operation. The sanitize operation types that may
+be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize
+operations are processed in the background, i.e., completion of the sanitize
+command does not indicate completion of the sanitize operation.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_security_recv.2 b/doc/man/nvme_mi_admin_security_recv.2
new file mode 100644
index 0000000..804d40c
--- /dev/null
+++ b/doc/man/nvme_mi_admin_security_recv.2
@@ -0,0 +1,25 @@
+.TH "nvme_mi_admin_security_recv" 9 "nvme_mi_admin_security_recv" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_security_recv \- Perform a Security Receive command on a controller.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_security_recv
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_security_receive_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "args" 12
+Security Receive command arguments
+.SH "DESCRIPTION"
+Performs a Security Receive Admin command as specified by \fIargs\fP. Response
+data is stored in \fIargs->data\fP, which should be a buffer of \fIargs->data_len\fP
+bytes. Resulting data length is stored in \fIargs->data_len\fP on successful
+command completion.
+
+Security Receive data length should not be greater than 4096 bytes to
+comply with specification limits.
+
+See: \fIstruct nvme_get_log_args\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_security_send.2 b/doc/man/nvme_mi_admin_security_send.2
new file mode 100644
index 0000000..61da5f3
--- /dev/null
+++ b/doc/man/nvme_mi_admin_security_send.2
@@ -0,0 +1,25 @@
+.TH "nvme_mi_admin_security_send" 9 "nvme_mi_admin_security_send" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_security_send \- Perform a Security Send command on a controller.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_security_send
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_security_send_args *args " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+Controller to send command to
+.IP "args" 12
+Security Send command arguments
+.SH "DESCRIPTION"
+Performs a Security Send Admin command as specified by \fIargs\fP. Response data
+is stored in \fIargs->data\fP, which should be a buffer of \fIargs->data_len\fP bytes.
+Resulting data length is stored in \fIargs->data_len\fP on successful
+command completion.
+
+Security Send data length should not be greater than 4096 bytes to
+comply with specification limits.
+
+See: \fIstruct nvme_get_log_args\fP
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_mi_admin_xfer.2 b/doc/man/nvme_mi_admin_xfer.2
new file mode 100644
index 0000000..a76189c
--- /dev/null
+++ b/doc/man/nvme_mi_admin_xfer.2
@@ -0,0 +1,42 @@
+.TH "nvme_mi_admin_xfer" 9 "nvme_mi_admin_xfer" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_admin_xfer \- Raw admin transfer interface.
+.SH SYNOPSIS
+.B "int" nvme_mi_admin_xfer
+.BI "(nvme_mi_ctrl_t ctrl " ","
+.BI "struct nvme_mi_admin_req_hdr *admin_req " ","
+.BI "size_t req_data_size " ","
+.BI "struct nvme_mi_admin_resp_hdr *admin_resp " ","
+.BI "off_t resp_data_offset " ","
+.BI "size_t *resp_data_size " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+controller to send the admin command to
+.IP "admin_req" 12
+request data
+.IP "req_data_size" 12
+size of request data payload
+.IP "admin_resp" 12
+buffer for response data
+.IP "resp_data_offset" 12
+offset into request data to retrieve from controller
+.IP "resp_data_size" 12
+size of response data buffer, updated to received size
+.SH "DESCRIPTION"
+Performs an arbitrary NVMe Admin command, using the provided request data,
+in \fIadmin_req\fP. The size of the request data *payload* is specified in
+\fIreq_data_size\fP - this does not include the standard header length (so a
+header-only request would have a size of 0).
+
+On success, response data is stored in \fIadmin_resp\fP, which has an optional
+appended payload buffer of \fIresp_data_size\fP bytes. The actual payload
+transferred will be stored in \fIresp_data_size\fP. These sizes do not include
+the Admin request header, so 0 represents no payload.
+
+As with all Admin commands, we can request partial data from the Admin
+Response payload, offset by \fIresp_data_offset\fP.
+
+See: \fIstruct nvme_mi_admin_req_hdr\fP and \fIstruct nvme_mi_admin_resp_hdr\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise..
diff --git a/doc/man/nvme_mi_ccs.2 b/doc/man/nvme_mi_ccs.2
new file mode 100644
index 0000000..565b96d
--- /dev/null
+++ b/doc/man/nvme_mi_ccs.2
@@ -0,0 +1,78 @@
+.TH "libnvme" 9 "enum nvme_mi_ccs" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_ccs \- Get State Control Primitive Success Response Fields - Control Primitive Specific Response
+.SH SYNOPSIS
+enum nvme_mi_ccs {
+.br
+.BI " NVME_MI_CCS_RDY"
+,
+.br
+.br
+.BI " NVME_MI_CCS_CFS"
+,
+.br
+.br
+.BI " NVME_MI_CCS_SHST"
+,
+.br
+.br
+.BI " NVME_MI_CCS_NSSRO"
+,
+.br
+.br
+.BI " NVME_MI_CCS_CECO"
+,
+.br
+.br
+.BI " NVME_MI_CCS_NAC"
+,
+.br
+.br
+.BI " NVME_MI_CCS_FA"
+,
+.br
+.br
+.BI " NVME_MI_CCS_CSTS"
+,
+.br
+.br
+.BI " NVME_MI_CCS_CTEMP"
+,
+.br
+.br
+.BI " NVME_MI_CCS_PDLU"
+,
+.br
+.br
+.BI " NVME_MI_CCS_SPARE"
+,
+.br
+.br
+.BI " NVME_MI_CCS_CCWARN"
+
+};
+.SH Constants
+.IP "NVME_MI_CCS_RDY" 12
+Ready
+.IP "NVME_MI_CCS_CFS" 12
+Controller Fatal Status
+.IP "NVME_MI_CCS_SHST" 12
+Shutdown Status
+.IP "NVME_MI_CCS_NSSRO" 12
+NVM Subsystem Reset Occurred
+.IP "NVME_MI_CCS_CECO" 12
+Controller Enable Change Occurred
+.IP "NVME_MI_CCS_NAC" 12
+Namespace Attribute Changed
+.IP "NVME_MI_CCS_FA" 12
+Firmware Activated
+.IP "NVME_MI_CCS_CSTS" 12
+Controller Status Change
+.IP "NVME_MI_CCS_CTEMP" 12
+Composite Temperature Change
+.IP "NVME_MI_CCS_PDLU" 12
+Percentage Used
+.IP "NVME_MI_CCS_SPARE" 12
+Available Spare
+.IP "NVME_MI_CCS_CCWARN" 12
+Critical Warning
diff --git a/doc/man/nvme_mi_close.2 b/doc/man/nvme_mi_close.2
new file mode 100644
index 0000000..d380e4d
--- /dev/null
+++ b/doc/man/nvme_mi_close.2
@@ -0,0 +1,9 @@
+.TH "nvme_mi_close" 9 "nvme_mi_close" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_close \- Close an endpoint connection and release resources, including controller objects.
+.SH SYNOPSIS
+.B "void" nvme_mi_close
+.BI "(nvme_mi_ep_t ep " ");"
+.SH ARGUMENTS
+.IP "ep" 12
+Endpoint object to close
diff --git a/doc/man/nvme_mi_close_ctrl.2 b/doc/man/nvme_mi_close_ctrl.2
new file mode 100644
index 0000000..605a6af
--- /dev/null
+++ b/doc/man/nvme_mi_close_ctrl.2
@@ -0,0 +1,9 @@
+.TH "nvme_mi_close_ctrl" 9 "nvme_mi_close_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_close_ctrl \- free a controller
+.SH SYNOPSIS
+.B "void" nvme_mi_close_ctrl
+.BI "(nvme_mi_ctrl_t ctrl " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+controller to free
diff --git a/doc/man/nvme_mi_cmd_supported_effects.2 b/doc/man/nvme_mi_cmd_supported_effects.2
new file mode 100644
index 0000000..502bff6
--- /dev/null
+++ b/doc/man/nvme_mi_cmd_supported_effects.2
@@ -0,0 +1,84 @@
+.TH "libnvme" 9 "enum nvme_mi_cmd_supported_effects" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_cmd_supported_effects \- MI Command Supported and Effects Data Structure
+.SH SYNOPSIS
+enum nvme_mi_cmd_supported_effects {
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_CSUPP"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_UDCC"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_NCC"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_NIC"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_CCC"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_SHIFT"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_MASK"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NS"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_CTRL"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NVM_SET"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_ENDGRP"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_DOMAIN"
+,
+.br
+.br
+.BI " NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NSS"
+
+};
+.SH Constants
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_CSUPP" 12
+Command Supported
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_UDCC" 12
+User Data Content Change
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_NCC" 12
+Namespace Capability Change
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_NIC" 12
+Namespace Inventory Change
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_CCC" 12
+Controller Capability Change
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_SHIFT" 12
+20 bit shift
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_MASK" 12
+12 bit mask - 0xfff
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NS" 12
+Namespace Scope
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_CTRL" 12
+Controller Scope
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NVM_SET" 12
+NVM Set Scope
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_ENDGRP" 12
+Endurance Group Scope
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_DOMAIN" 12
+Domain Scope
+.IP "NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NSS" 12
+NVM Subsystem Scope
diff --git a/doc/man/nvme_mi_cmd_supported_effects_log.2 b/doc/man/nvme_mi_cmd_supported_effects_log.2
new file mode 100644
index 0000000..df5678c
--- /dev/null
+++ b/doc/man/nvme_mi_cmd_supported_effects_log.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_mi_cmd_supported_effects_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_cmd_supported_effects_log \- NVMe-MI Commands Supported and Effects Log
+.SH SYNOPSIS
+struct nvme_mi_cmd_supported_effects_log {
+.br
+.BI " __le32 mi_cmd_support[NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX];"
+.br
+.BI " __le32 reserved1[NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "mi_cmd_support" 12
+NVMe-MI Commands Supported
+.IP "reserved1" 12
+Reserved
diff --git a/doc/man/nvme_mi_config_id.2 b/doc/man/nvme_mi_config_id.2
new file mode 100644
index 0000000..fa1512f
--- /dev/null
+++ b/doc/man/nvme_mi_config_id.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "enum nvme_mi_config_id" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_config_id \- NVMe-MI Configuration identifier.
+.SH SYNOPSIS
+enum nvme_mi_config_id {
+.br
+.BI " NVME_MI_CONFIG_SMBUS_FREQ"
+,
+.br
+.br
+.BI " NVME_MI_CONFIG_HEALTH_STATUS_CHANGE"
+,
+.br
+.br
+.BI " NVME_MI_CONFIG_MCTP_MTU"
+
+};
+.SH Constants
+.IP "NVME_MI_CONFIG_SMBUS_FREQ" 12
+Current SMBus/I2C frequency
+.IP "NVME_MI_CONFIG_HEALTH_STATUS_CHANGE" 12
+Health Status change - used to clear
+health status bits in CCS bits of
+status poll. Only for Set ops.
+.IP "NVME_MI_CONFIG_MCTP_MTU" 12
+MCTP maximum transmission unit size of port
+specified in dw 0
+.SH "Description"
+Configuration parameters for the MI Get/Set Configuration commands.
+
+See &\fBnvme_mi_mi_config_get\fP and &\fBnvme_mi_config_set\fP.
diff --git a/doc/man/nvme_mi_config_smbus_freq.2 b/doc/man/nvme_mi_config_smbus_freq.2
new file mode 100644
index 0000000..c6cab1e
--- /dev/null
+++ b/doc/man/nvme_mi_config_smbus_freq.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "enum nvme_mi_config_smbus_freq" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_config_smbus_freq \- SMBus/I2C frequency values
+.SH SYNOPSIS
+enum nvme_mi_config_smbus_freq {
+.br
+.BI " NVME_MI_CONFIG_SMBUS_FREQ_100kHz"
+,
+.br
+.br
+.BI " NVME_MI_CONFIG_SMBUS_FREQ_400kHz"
+,
+.br
+.br
+.BI " NVME_MI_CONFIG_SMBUS_FREQ_1MHz"
+
+};
+.SH Constants
+.IP "NVME_MI_CONFIG_SMBUS_FREQ_100kHz" 12
+100kHz
+.IP "NVME_MI_CONFIG_SMBUS_FREQ_400kHz" 12
+400kHz
+.IP "NVME_MI_CONFIG_SMBUS_FREQ_1MHz" 12
+1MHz
+.SH "Description"
+Values used in the SMBus Frequency device configuration. See
+&\fBnvme_mi_mi_config_get_smbus_freq\fP and &\fBnvme_mi_mi_config_set_smbus_freq\fP.
diff --git a/doc/man/nvme_mi_create_root.2 b/doc/man/nvme_mi_create_root.2
new file mode 100644
index 0000000..e11912b
--- /dev/null
+++ b/doc/man/nvme_mi_create_root.2
@@ -0,0 +1,20 @@
+.TH "nvme_mi_create_root" 9 "nvme_mi_create_root" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_create_root \- Create top-level MI (root) handle.
+.SH SYNOPSIS
+.B "nvme_root_t" nvme_mi_create_root
+.BI "(FILE *fp " ","
+.BI "int log_level " ");"
+.SH ARGUMENTS
+.IP "fp" 12
+File descriptor for logging messages
+.IP "log_level" 12
+Logging level to use
+.SH "DESCRIPTION"
+Create the top-level (library) handle for creating subsequent endpoint
+objects. Similar to \fBnvme_create_root\fP, but we provide this to allow linking
+without the core libnvme.
+
+See \fInvme_create_root\fP.
+.SH "RETURN"
+new root object, or NULL on failure.
diff --git a/doc/man/nvme_mi_csts.2 b/doc/man/nvme_mi_csts.2
new file mode 100644
index 0000000..5eb19a1
--- /dev/null
+++ b/doc/man/nvme_mi_csts.2
@@ -0,0 +1,48 @@
+.TH "libnvme" 9 "enum nvme_mi_csts" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_csts \- Controller Health Data Structure (CHDS) - Controller Status (CSTS)
+.SH SYNOPSIS
+enum nvme_mi_csts {
+.br
+.BI " NVME_MI_CSTS_RDY"
+,
+.br
+.br
+.BI " NVME_MI_CSTS_CFS"
+,
+.br
+.br
+.BI " NVME_MI_CSTS_SHST"
+,
+.br
+.br
+.BI " NVME_MI_CSTS_NSSRO"
+,
+.br
+.br
+.BI " NVME_MI_CSTS_CECO"
+,
+.br
+.br
+.BI " NVME_MI_CSTS_NAC"
+,
+.br
+.br
+.BI " NVME_MI_CSTS_FA"
+
+};
+.SH Constants
+.IP "NVME_MI_CSTS_RDY" 12
+Ready
+.IP "NVME_MI_CSTS_CFS" 12
+Controller Fatal Status
+.IP "NVME_MI_CSTS_SHST" 12
+Shutdown Status
+.IP "NVME_MI_CSTS_NSSRO" 12
+NVM Subsystem Reset Occurred
+.IP "NVME_MI_CSTS_CECO" 12
+Controller Enable Change Occurred
+.IP "NVME_MI_CSTS_NAC" 12
+Namespace Attribute Changed
+.IP "NVME_MI_CSTS_FA" 12
+Firmware Activated
diff --git a/doc/man/nvme_mi_ctrl_health_status.2 b/doc/man/nvme_mi_ctrl_health_status.2
new file mode 100644
index 0000000..4483462
--- /dev/null
+++ b/doc/man/nvme_mi_ctrl_health_status.2
@@ -0,0 +1,39 @@
+.TH "libnvme" 9 "struct nvme_mi_ctrl_health_status" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_ctrl_health_status \- Controller Health Data Structure (CHDS)
+.SH SYNOPSIS
+struct nvme_mi_ctrl_health_status {
+.br
+.BI " __le16 ctlid;"
+.br
+.BI " __le16 csts;"
+.br
+.BI " __le16 ctemp;"
+.br
+.BI " __u8 pdlu;"
+.br
+.BI " __u8 spare;"
+.br
+.BI " __u8 cwarn;"
+.br
+.BI " __u8 rsvd9[7];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "ctlid" 12
+Controller Identifier
+.IP "csts" 12
+Controller Status
+.IP "ctemp" 12
+Composite Temperature
+.IP "pdlu" 12
+Percentage Used
+.IP "spare" 12
+Available Spare
+.IP "cwarn" 12
+Critical Warning
+.IP "rsvd9" 12
+Reserved
diff --git a/doc/man/nvme_mi_ctrl_id.2 b/doc/man/nvme_mi_ctrl_id.2
new file mode 100644
index 0000000..c07633f
--- /dev/null
+++ b/doc/man/nvme_mi_ctrl_id.2
@@ -0,0 +1,16 @@
+.TH "nvme_mi_ctrl_id" 9 "nvme_mi_ctrl_id" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_ctrl_id \- get the ID of a controller
+.SH SYNOPSIS
+.B "__u16" nvme_mi_ctrl_id
+.BI "(nvme_mi_ctrl_t ctrl " ");"
+.SH ARGUMENTS
+.IP "ctrl" 12
+controller to query
+.SH "DESCRIPTION"
+Retrieve the ID of the controller, as defined by hardware, and available
+in the Identify (Controller List) data. This is the value passed to
+\fInvme_mi_init_ctrl\fP, but may have been created internally via
+\fInvme_mi_scan_ep\fP.
+.SH "RETURN"
+the (locally-stored) ID of this controller.
diff --git a/doc/man/nvme_mi_cwarn.2 b/doc/man/nvme_mi_cwarn.2
new file mode 100644
index 0000000..fac6e60
--- /dev/null
+++ b/doc/man/nvme_mi_cwarn.2
@@ -0,0 +1,36 @@
+.TH "libnvme" 9 "enum nvme_mi_cwarn" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_cwarn \- Controller Health Data Structure (CHDS) - Critical Warning (CWARN)
+.SH SYNOPSIS
+enum nvme_mi_cwarn {
+.br
+.BI " NVME_MI_CWARN_ST"
+,
+.br
+.br
+.BI " NVME_MI_CWARN_TAUT"
+,
+.br
+.br
+.BI " NVME_MI_CWARN_RD"
+,
+.br
+.br
+.BI " NVME_MI_CWARN_RO"
+,
+.br
+.br
+.BI " NVME_MI_CWARN_VMBF"
+
+};
+.SH Constants
+.IP "NVME_MI_CWARN_ST" 12
+Spare Threshold
+.IP "NVME_MI_CWARN_TAUT" 12
+Temperature Above or Under Threshold
+.IP "NVME_MI_CWARN_RD" 12
+Reliability Degraded
+.IP "NVME_MI_CWARN_RO" 12
+Read Only
+.IP "NVME_MI_CWARN_VMBF" 12
+Volatile Memory Backup Failed
diff --git a/doc/man/nvme_mi_dtyp.2 b/doc/man/nvme_mi_dtyp.2
new file mode 100644
index 0000000..88e7fab
--- /dev/null
+++ b/doc/man/nvme_mi_dtyp.2
@@ -0,0 +1,45 @@
+.TH "libnvme" 9 "enum nvme_mi_dtyp" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_dtyp \- Data Structure Type field.
+.SH SYNOPSIS
+enum nvme_mi_dtyp {
+.br
+.BI " nvme_mi_dtyp_subsys_info"
+,
+.br
+.br
+.BI " nvme_mi_dtyp_port_info"
+,
+.br
+.br
+.BI " nvme_mi_dtyp_ctrl_list"
+,
+.br
+.br
+.BI " nvme_mi_dtyp_ctrl_info"
+,
+.br
+.br
+.BI " nvme_mi_dtyp_opt_cmd_support"
+,
+.br
+.br
+.BI " nvme_mi_dtyp_meb_support"
+
+};
+.SH Constants
+.IP "nvme_mi_dtyp_subsys_info" 12
+NVM Subsystem Information
+.IP "nvme_mi_dtyp_port_info" 12
+Port information
+.IP "nvme_mi_dtyp_ctrl_list" 12
+Controller List
+.IP "nvme_mi_dtyp_ctrl_info" 12
+Controller Information
+.IP "nvme_mi_dtyp_opt_cmd_support" 12
+Optionally Supported Command List
+.IP "nvme_mi_dtyp_meb_support" 12
+Management Endpoint Buffer Command Support List
+.SH "Description"
+Data Structure Type field for Read NVMe-MI Data Structure command, used to
+indicate the particular structure to query from the endpoint.
diff --git a/doc/man/nvme_mi_elem.2 b/doc/man/nvme_mi_elem.2
new file mode 100644
index 0000000..20ed699
--- /dev/null
+++ b/doc/man/nvme_mi_elem.2
@@ -0,0 +1,48 @@
+.TH "libnvme" 9 "enum nvme_mi_elem" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_elem \- Element Descriptor Types
+.SH SYNOPSIS
+enum nvme_mi_elem {
+.br
+.BI " NVME_MI_ELEM_EED"
+,
+.br
+.br
+.BI " NVME_MI_ELEM_USCE"
+,
+.br
+.br
+.BI " NVME_MI_ELEM_ECED"
+,
+.br
+.br
+.BI " NVME_MI_ELEM_LED"
+,
+.br
+.br
+.BI " NVME_MI_ELEM_SMBMED"
+,
+.br
+.br
+.BI " NVME_MI_ELEM_PCIESED"
+,
+.br
+.br
+.BI " NVME_MI_ELEM_NVMED"
+
+};
+.SH Constants
+.IP "NVME_MI_ELEM_EED" 12
+Extended Element Descriptor
+.IP "NVME_MI_ELEM_USCE" 12
+Upstream Connector Element Descriptor
+.IP "NVME_MI_ELEM_ECED" 12
+Expansion Connector Element Descriptor
+.IP "NVME_MI_ELEM_LED" 12
+Label Element Descriptor
+.IP "NVME_MI_ELEM_SMBMED" 12
+SMBus/I2C Mux Element Descriptor
+.IP "NVME_MI_ELEM_PCIESED" 12
+PCIe Switch Element Descriptor
+.IP "NVME_MI_ELEM_NVMED" 12
+NVM Subsystem Element Descriptor
diff --git a/doc/man/nvme_mi_free_root.2 b/doc/man/nvme_mi_free_root.2
new file mode 100644
index 0000000..b8ab29f
--- /dev/null
+++ b/doc/man/nvme_mi_free_root.2
@@ -0,0 +1,9 @@
+.TH "nvme_mi_free_root" 9 "nvme_mi_free_root" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_free_root \- Free root object.
+.SH SYNOPSIS
+.B "void" nvme_mi_free_root
+.BI "(nvme_root_t root " ");"
+.SH ARGUMENTS
+.IP "root" 12
+root to free
diff --git a/doc/man/nvme_mi_init_ctrl.2 b/doc/man/nvme_mi_init_ctrl.2
new file mode 100644
index 0000000..a0f6d44
--- /dev/null
+++ b/doc/man/nvme_mi_init_ctrl.2
@@ -0,0 +1,20 @@
+.TH "nvme_mi_init_ctrl" 9 "nvme_mi_init_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_init_ctrl \- initialise a NVMe controller.
+.SH SYNOPSIS
+.B "nvme_mi_ctrl_t" nvme_mi_init_ctrl
+.BI "(nvme_mi_ep_t ep " ","
+.BI "__u16 ctrl_id " ");"
+.SH ARGUMENTS
+.IP "ep" 12
+Endpoint to create under
+.IP "ctrl_id" 12
+ID of controller to initialize.
+.SH "DESCRIPTION"
+Create a connection to a controller behind the endpoint specified in \fIep\fP.
+Controller IDs may be queried from the endpoint through
+\fInvme_mi_mi_read_mi_data_ctrl_list\fP.
+
+See \fInvme_mi_close_ctrl\fP
+.SH "RETURN"
+New controller object, or NULL on failure.
diff --git a/doc/man/nvme_mi_message_type.2 b/doc/man/nvme_mi_message_type.2
new file mode 100644
index 0000000..ba3249f
--- /dev/null
+++ b/doc/man/nvme_mi_message_type.2
@@ -0,0 +1,33 @@
+.TH "libnvme" 9 "enum nvme_mi_message_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_message_type \- NVMe-MI message type field.
+.SH SYNOPSIS
+enum nvme_mi_message_type {
+.br
+.BI " NVME_MI_MT_CONTROL"
+,
+.br
+.br
+.BI " NVME_MI_MT_MI"
+,
+.br
+.br
+.BI " NVME_MI_MT_ADMIN"
+,
+.br
+.br
+.BI " NVME_MI_MT_PCIE"
+
+};
+.SH Constants
+.IP "NVME_MI_MT_CONTROL" 12
+NVME-MI Control Primitive
+.IP "NVME_MI_MT_MI" 12
+NVMe-MI command
+.IP "NVME_MI_MT_ADMIN" 12
+NVMe Admin command
+.IP "NVME_MI_MT_PCIE" 12
+PCIe command
+.SH "Description"
+Used as byte 1 of both request and response messages (NMIMT bits of NMP
+byte). Not to be confused with the MCTP message type in byte 0.
diff --git a/doc/man/nvme_mi_mi_opcode.2 b/doc/man/nvme_mi_mi_opcode.2
new file mode 100644
index 0000000..c884079
--- /dev/null
+++ b/doc/man/nvme_mi_mi_opcode.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_mi_mi_opcode" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_mi_opcode \- Operation code for supported NVMe-MI commands.
+.SH SYNOPSIS
+enum nvme_mi_mi_opcode {
+.br
+.BI " nvme_mi_mi_opcode_mi_data_read"
+,
+.br
+.br
+.BI " nvme_mi_mi_opcode_subsys_health_status_poll"
+,
+.br
+.br
+.BI " nvme_mi_mi_opcode_configuration_set"
+,
+.br
+.br
+.BI " nvme_mi_mi_opcode_configuration_get"
+
+};
+.SH Constants
+.IP "nvme_mi_mi_opcode_mi_data_read" 12
+Read NVMe-MI Data Structure
+.IP "nvme_mi_mi_opcode_subsys_health_status_poll" 12
+Subsystem Health Status Poll
+.IP "nvme_mi_mi_opcode_configuration_set" 12
+MI Configuration Set
+.IP "nvme_mi_mi_opcode_configuration_get" 12
+MI Configuration Get
diff --git a/doc/man/nvme_mi_mi_read_mi_data_ctrl.2 b/doc/man/nvme_mi_mi_read_mi_data_ctrl.2
new file mode 100644
index 0000000..36ee870
--- /dev/null
+++ b/doc/man/nvme_mi_mi_read_mi_data_ctrl.2
@@ -0,0 +1,23 @@
+.TH "nvme_mi_mi_read_mi_data_ctrl" 9 "nvme_mi_mi_read_mi_data_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_mi_read_mi_data_ctrl \- Perform a Read MI Data Structure command, retrieving controller information
+.SH SYNOPSIS
+.B "int" nvme_mi_mi_read_mi_data_ctrl
+.BI "(nvme_mi_ep_t ep " ","
+.BI "__u16 ctrl_id " ","
+.BI "struct nvme_mi_read_ctrl_info *ctrl " ");"
+.SH ARGUMENTS
+.IP "ep" 12
+endpoint for MI communication
+.IP "ctrl_id" 12
+ID of controller to query
+.IP "ctrl" 12
+controller data to populate
+.SH "DESCRIPTION"
+Retrieves the Controller Information Data Structure for the attached
+controller with ID \fIctrlid\fP.
+
+See \fIstruct nvme_mi_read_ctrl_info\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise..
diff --git a/doc/man/nvme_mi_mi_read_mi_data_ctrl_list.2 b/doc/man/nvme_mi_mi_read_mi_data_ctrl_list.2
new file mode 100644
index 0000000..265ded1
--- /dev/null
+++ b/doc/man/nvme_mi_mi_read_mi_data_ctrl_list.2
@@ -0,0 +1,23 @@
+.TH "nvme_mi_mi_read_mi_data_ctrl_list" 9 "nvme_mi_mi_read_mi_data_ctrl_list" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_mi_read_mi_data_ctrl_list \- Perform a Read MI Data Structure command, retrieving the list of attached controllers.
+.SH SYNOPSIS
+.B "int" nvme_mi_mi_read_mi_data_ctrl_list
+.BI "(nvme_mi_ep_t ep " ","
+.BI "__u8 start_ctrlid " ","
+.BI "struct nvme_ctrl_list *list " ");"
+.SH ARGUMENTS
+.IP "ep" 12
+endpoint for MI communication
+.IP "start_ctrlid" 12
+starting controller ID
+.IP "list" 12
+controller list to populate
+.SH "DESCRIPTION"
+Retrieves the list of attached controllers, with IDs greater than or
+equal to \fIstart_ctrlid\fP.
+
+See \fIstruct nvme_ctrl_list\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise..
diff --git a/doc/man/nvme_mi_mi_read_mi_data_port.2 b/doc/man/nvme_mi_mi_read_mi_data_port.2
new file mode 100644
index 0000000..775b3d8
--- /dev/null
+++ b/doc/man/nvme_mi_mi_read_mi_data_port.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_mi_read_mi_data_port" 9 "nvme_mi_mi_read_mi_data_port" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_mi_read_mi_data_port \- Perform a Read MI Data Structure command, retrieving port data.
+.SH SYNOPSIS
+.B "int" nvme_mi_mi_read_mi_data_port
+.BI "(nvme_mi_ep_t ep " ","
+.BI "__u8 portid " ","
+.BI "struct nvme_mi_read_port_info *p " ");"
+.SH ARGUMENTS
+.IP "ep" 12
+endpoint for MI communication
+.IP "portid" 12
+id of port data to retrieve
+.IP "p" 12
+port information to populate
+.SH "DESCRIPTION"
+Retrieves the Port information, for the specified port ID. The subsystem
+data (from \fInvme_mi_mi_read_mi_data_subsys\fP) nmp field contains the allowed
+range of port IDs.
+
+See \fIstruct nvme_mi_read_port_info\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise..
diff --git a/doc/man/nvme_mi_mi_read_mi_data_subsys.2 b/doc/man/nvme_mi_mi_read_mi_data_subsys.2
new file mode 100644
index 0000000..b8a7ad2
--- /dev/null
+++ b/doc/man/nvme_mi_mi_read_mi_data_subsys.2
@@ -0,0 +1,18 @@
+.TH "nvme_mi_mi_read_mi_data_subsys" 9 "nvme_mi_mi_read_mi_data_subsys" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_mi_read_mi_data_subsys \- Perform a Read MI Data Structure command, retrieving subsystem data.
+.SH SYNOPSIS
+.B "int" nvme_mi_mi_read_mi_data_subsys
+.BI "(nvme_mi_ep_t ep " ","
+.BI "struct nvme_mi_read_nvm_ss_info *s " ");"
+.SH ARGUMENTS
+.IP "ep" 12
+endpoint for MI communication
+.IP "s" 12
+subsystem information to populate
+.SH "DESCRIPTION"
+Retrieves the Subsystem information - number of external ports and
+NVMe version information. See \fIstruct nvme_mi_read_nvm_ss_info\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise..
diff --git a/doc/man/nvme_mi_mi_req_hdr.2 b/doc/man/nvme_mi_mi_req_hdr.2
new file mode 100644
index 0000000..fa49be1
--- /dev/null
+++ b/doc/man/nvme_mi_mi_req_hdr.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_mi_mi_req_hdr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_mi_req_hdr \- MI request message header.
+.SH SYNOPSIS
+struct nvme_mi_mi_req_hdr {
+.br
+.BI " struct nvme_mi_msg_hdr hdr;"
+.br
+.BI " __u8 opcode;"
+.br
+.BI " __u8 rsvd0[3];"
+.br
+.BI " __le32 cdw0, cdw1;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "hdr" 12
+generic MI message header
+.IP "opcode" 12
+opcode (OPC) for the specific MI command
+.IP "rsvd0" 12
+reserved bytes
+.IP "cdw0" 12
+Management Request Doubleword 0 - command specific usage
+.IP "cdw1" 12
+Management Request Doubleword 1 - command specific usage
+.SH "Description"
+Wire format for MI request message headers, defined in section 5 of NVMe-MI.
diff --git a/doc/man/nvme_mi_mi_resp_hdr.2 b/doc/man/nvme_mi_mi_resp_hdr.2
new file mode 100644
index 0000000..974ec3c
--- /dev/null
+++ b/doc/man/nvme_mi_mi_resp_hdr.2
@@ -0,0 +1,25 @@
+.TH "libnvme" 9 "struct nvme_mi_mi_resp_hdr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_mi_resp_hdr \- MI response message header.
+.SH SYNOPSIS
+struct nvme_mi_mi_resp_hdr {
+.br
+.BI " struct nvme_mi_msg_hdr hdr;"
+.br
+.BI " __u8 status;"
+.br
+.BI " __u8 nmresp[3];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "hdr" 12
+generic MI message header
+.IP "status" 12
+generic response status from command; non-zero on failure.
+.IP "nmresp" 12
+NVMe Management Response: command-type-specific response data
+.SH "Description"
+Wire format for MI response message header, defined in section 5 of NVMe-MI.
diff --git a/doc/man/nvme_mi_mi_subsystem_health_status_poll.2 b/doc/man/nvme_mi_mi_subsystem_health_status_poll.2
new file mode 100644
index 0000000..f2e2d6d
--- /dev/null
+++ b/doc/man/nvme_mi_mi_subsystem_health_status_poll.2
@@ -0,0 +1,24 @@
+.TH "nvme_mi_mi_subsystem_health_status_poll" 9 "nvme_mi_mi_subsystem_health_status_poll" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_mi_subsystem_health_status_poll \- Read the Subsystem Health Data Structure from the NVM subsystem
+.SH SYNOPSIS
+.B "int" nvme_mi_mi_subsystem_health_status_poll
+.BI "(nvme_mi_ep_t ep " ","
+.BI "bool clear " ","
+.BI "struct nvme_mi_nvm_ss_health_status *nshds " ");"
+.SH ARGUMENTS
+.IP "ep" 12
+endpoint for MI communication
+.IP "clear" 12
+flag to clear the Composite Controller Status state
+.IP "nshds" 12
+subsystem health status data to populate
+.SH "DESCRIPTION"
+Retrieves the Subsystem Health Data Structure into \fInshds\fP. If \fIclear\fP is
+set, requests that the Composite Controller Status bits are cleared after
+the read. See NVMe-MI section 5.6 for details on the CCS bits.
+
+See \fIstruct nvme_mi_nvm_ss_health_status\fP.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise..
diff --git a/doc/man/nvme_mi_msg_hdr.2 b/doc/man/nvme_mi_msg_hdr.2
new file mode 100644
index 0000000..e8f70e6
--- /dev/null
+++ b/doc/man/nvme_mi_msg_hdr.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "struct nvme_mi_msg_hdr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_msg_hdr \- General MI message header.
+.SH SYNOPSIS
+struct nvme_mi_msg_hdr {
+.br
+.BI " __u8 type;"
+.br
+.BI " __u8 nmp;"
+.br
+.BI " __u8 meb;"
+.br
+.BI " __u8 rsvd0;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "type" 12
+MCTP message type, will always be NVME_MI_MSGTYPE_NVME
+.IP "nmp" 12
+NVMe-MI message parameters (including MI message type)
+.IP "meb" 12
+Management Endpoint Buffer flag; unused for libnvme-mi implementation
+.IP "rsvd0" 12
+currently reserved
+.SH "Description"
+Wire format shared by both request and response messages, per NVMe-MI
+section 3.1. This is used for all message types, MI and Admin.
diff --git a/doc/man/nvme_mi_msg_resp.2 b/doc/man/nvme_mi_msg_resp.2
new file mode 100644
index 0000000..32baa77
--- /dev/null
+++ b/doc/man/nvme_mi_msg_resp.2
@@ -0,0 +1,26 @@
+.TH "libnvme" 9 "struct nvme_mi_msg_resp" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_msg_resp \- Generic response type.
+.SH SYNOPSIS
+struct nvme_mi_msg_resp {
+.br
+.BI " struct nvme_mi_msg_hdr hdr;"
+.br
+.BI " __u8 status;"
+.br
+.BI " __u8 rsvd0[3];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "hdr" 12
+the general request/response message header
+.IP "status" 12
+response status value (see \fIenum nvme_mi_resp_status\fP)
+.IP "rsvd0" 12
+reserved data, may be defined by specific response
+.SH "Description"
+Every response will start with one of these; command-specific responses
+will define parts of the reserved data, and may add further fields.
diff --git a/doc/man/nvme_mi_nvm_ss_health_status.2 b/doc/man/nvme_mi_nvm_ss_health_status.2
new file mode 100644
index 0000000..b10ea76
--- /dev/null
+++ b/doc/man/nvme_mi_nvm_ss_health_status.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_mi_nvm_ss_health_status" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_nvm_ss_health_status \- Subsystem Management Data Structure
+.SH SYNOPSIS
+struct nvme_mi_nvm_ss_health_status {
+.br
+.BI " __u8 nss;"
+.br
+.BI " __u8 sw;"
+.br
+.BI " __u8 ctemp;"
+.br
+.BI " __u8 pdlu;"
+.br
+.BI " __le16 ccs;"
+.br
+.BI " __u8 rsvd8[2];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nss" 12
+NVM Subsystem Status
+.IP "sw" 12
+Smart Warnings
+.IP "ctemp" 12
+Composite Temperature
+.IP "pdlu" 12
+Percentage Drive Life Used
+.IP "ccs" 12
+Composite Controller Status
+.IP "rsvd8" 12
+Reserved
diff --git a/doc/man/nvme_mi_open_mctp.2 b/doc/man/nvme_mi_open_mctp.2
new file mode 100644
index 0000000..2f99512
--- /dev/null
+++ b/doc/man/nvme_mi_open_mctp.2
@@ -0,0 +1,22 @@
+.TH "nvme_mi_open_mctp" 9 "nvme_mi_open_mctp" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_open_mctp \- Create an endpoint using a MCTP connection.
+.SH SYNOPSIS
+.B "nvme_mi_ep_t" nvme_mi_open_mctp
+.BI "(nvme_root_t root " ","
+.BI "unsigned int netid " ","
+.BI "uint8_t eid " ");"
+.SH ARGUMENTS
+.IP "root" 12
+root object to create under
+.IP "netid" 12
+MCTP network ID on this system
+.IP "eid" 12
+MCTP endpoint ID
+.SH "DESCRIPTION"
+Transport-specific endpoint initialization for MI-connected endpoints. Once
+an endpoint is created, the rest of the API is transport-independent.
+
+See \fInvme_mi_close\fP
+.SH "RETURN"
+New endpoint object for \fInetid\fP & \fIeid\fP, or NULL on failure.
diff --git a/doc/man/nvme_mi_osc.2 b/doc/man/nvme_mi_osc.2
new file mode 100644
index 0000000..8d47c5d
--- /dev/null
+++ b/doc/man/nvme_mi_osc.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_mi_osc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_osc \- Optionally Supported Command Data Structure
+.SH SYNOPSIS
+struct nvme_mi_osc {
+.br
+.BI " __u8 type;"
+.br
+.BI " __u8 opc;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "type" 12
+Command Type
+.IP "opc" 12
+Opcode
diff --git a/doc/man/nvme_mi_port_pcie.2 b/doc/man/nvme_mi_port_pcie.2
new file mode 100644
index 0000000..e99e772
--- /dev/null
+++ b/doc/man/nvme_mi_port_pcie.2
@@ -0,0 +1,39 @@
+.TH "libnvme" 9 "struct nvme_mi_port_pcie" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_port_pcie \- PCIe Port Specific Data
+.SH SYNOPSIS
+struct nvme_mi_port_pcie {
+.br
+.BI " __u8 mps;"
+.br
+.BI " __u8 sls;"
+.br
+.BI " __u8 cls;"
+.br
+.BI " __u8 mlw;"
+.br
+.BI " __u8 nlw;"
+.br
+.BI " __u8 pn;"
+.br
+.BI " __u8 rsvd14[18];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "mps" 12
+PCIe Maximum Payload Size
+.IP "sls" 12
+PCIe Supported Link Speeds Vector
+.IP "cls" 12
+PCIe Current Link Speed
+.IP "mlw" 12
+PCIe Maximum Link Width
+.IP "nlw" 12
+PCIe Negotiated Link Width
+.IP "pn" 12
+PCIe Port Number
+.IP "rsvd14" 12
+Reserved
diff --git a/doc/man/nvme_mi_port_smb.2 b/doc/man/nvme_mi_port_smb.2
new file mode 100644
index 0000000..95e2238
--- /dev/null
+++ b/doc/man/nvme_mi_port_smb.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_mi_port_smb" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_port_smb \- SMBus Port Specific Data
+.SH SYNOPSIS
+struct nvme_mi_port_smb {
+.br
+.BI " __u8 vpd_addr;"
+.br
+.BI " __u8 mvpd_freq;"
+.br
+.BI " __u8 mme_addr;"
+.br
+.BI " __u8 mme_freq;"
+.br
+.BI " __u8 nvmebm;"
+.br
+.BI " __u8 rsvd13[19];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "vpd_addr" 12
+Current VPD SMBus/I2C Address
+.IP "mvpd_freq" 12
+Maximum VPD Access SMBus/I2C Frequency
+.IP "mme_addr" 12
+Current Management Endpoint SMBus/I2C Address
+.IP "mme_freq" 12
+Maximum Management Endpoint SMBus/I2C Frequency
+.IP "nvmebm" 12
+NVMe Basic Management
+.IP "rsvd13" 12
+Reserved
diff --git a/doc/man/nvme_mi_read_ctrl_info.2 b/doc/man/nvme_mi_read_ctrl_info.2
new file mode 100644
index 0000000..c340978
--- /dev/null
+++ b/doc/man/nvme_mi_read_ctrl_info.2
@@ -0,0 +1,47 @@
+.TH "libnvme" 9 "struct nvme_mi_read_ctrl_info" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_read_ctrl_info \- Controller Information Data Structure
+.SH SYNOPSIS
+struct nvme_mi_read_ctrl_info {
+.br
+.BI " __u8 portid;"
+.br
+.BI " __u8 rsvd1[4];"
+.br
+.BI " __u8 prii;"
+.br
+.BI " __le16 pri;"
+.br
+.BI " __le16 vid;"
+.br
+.BI " __le16 did;"
+.br
+.BI " __le16 ssvid;"
+.br
+.BI " __le16 ssid;"
+.br
+.BI " __u8 rsvd16[16];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "portid" 12
+Port Identifier
+.IP "rsvd1" 12
+Reserved
+.IP "prii" 12
+PCIe Routing ID Information
+.IP "pri" 12
+PCIe Routing ID
+.IP "vid" 12
+PCI Vendor ID
+.IP "did" 12
+PCI Device ID
+.IP "ssvid" 12
+PCI Subsystem Vendor ID
+.IP "ssid" 12
+PCI Subsystem Device ID
+.IP "rsvd16" 12
+Reserved
diff --git a/doc/man/nvme_mi_read_nvm_ss_info.2 b/doc/man/nvme_mi_read_nvm_ss_info.2
new file mode 100644
index 0000000..9f1d40a
--- /dev/null
+++ b/doc/man/nvme_mi_read_nvm_ss_info.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_mi_read_nvm_ss_info" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_read_nvm_ss_info \- NVM Subsystem Information Data Structure
+.SH SYNOPSIS
+struct nvme_mi_read_nvm_ss_info {
+.br
+.BI " __u8 nump;"
+.br
+.BI " __u8 mjr;"
+.br
+.BI " __u8 mnr;"
+.br
+.BI " __u8 rsvd3[29];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nump" 12
+Number of Ports
+.IP "mjr" 12
+NVMe-MI Major Version Number
+.IP "mnr" 12
+NVMe-MI Minor Version Number
+.IP "rsvd3" 12
+Reserved
diff --git a/doc/man/nvme_mi_read_port_info.2 b/doc/man/nvme_mi_read_port_info.2
new file mode 100644
index 0000000..6e21e75
--- /dev/null
+++ b/doc/man/nvme_mi_read_port_info.2
@@ -0,0 +1,41 @@
+.TH "libnvme" 9 "struct nvme_mi_read_port_info" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_read_port_info \- Port Information Data Structure
+.SH SYNOPSIS
+struct nvme_mi_read_port_info {
+.br
+.BI " __u8 portt;"
+.br
+.BI " __u8 rsvd1;"
+.br
+.BI " __le16 mmctptus;"
+.br
+.BI " __le32 meb;"
+.br
+.BI " union {"
+.br
+.BI " struct nvme_mi_port_pcie pcie;"
+.br
+.BI " struct nvme_mi_port_smb smb;"
+.br
+.BI " };"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "portt" 12
+Port Type
+.IP "rsvd1" 12
+Reserved
+.IP "mmctptus" 12
+Maximum MCTP Transmission Unit Size
+.IP "meb" 12
+Management Endpoint Buffer Size
+.IP "{unnamed_union}" 12
+anonymous
+.IP "pcie" 12
+PCIe Port Specific Data
+.IP "smb" 12
+SMBus Port Specific Data
diff --git a/doc/man/nvme_mi_read_sc_list.2 b/doc/man/nvme_mi_read_sc_list.2
new file mode 100644
index 0000000..3d84219
--- /dev/null
+++ b/doc/man/nvme_mi_read_sc_list.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "struct nvme_mi_read_sc_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_read_sc_list \- Management Endpoint Buffer Supported Command List Data Structure
+.SH SYNOPSIS
+struct nvme_mi_read_sc_list {
+.br
+.BI " __le16 numcmd;"
+.br
+.BI " struct nvme_mi_osc cmds[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "numcmd" 12
+Number of Commands
+.IP "cmds" 12
+MEB supported Command Data Structure.
+See \fIstruct\fP nvme_mi_osc
diff --git a/doc/man/nvme_mi_resp_status.2 b/doc/man/nvme_mi_resp_status.2
new file mode 100644
index 0000000..73a1e9c
--- /dev/null
+++ b/doc/man/nvme_mi_resp_status.2
@@ -0,0 +1,120 @@
+.TH "libnvme" 9 "enum nvme_mi_resp_status" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_mi_resp_status \- values for the response status field
+.SH SYNOPSIS
+enum nvme_mi_resp_status {
+.br
+.BI " NVME_MI_RESP_SUCCESS"
+,
+.br
+.br
+.BI " NVME_MI_RESP_MPR"
+,
+.br
+.br
+.BI " NVME_MI_RESP_INTERNAL_ERR"
+,
+.br
+.br
+.BI " NVME_MI_RESP_INVALID_OPCODE"
+,
+.br
+.br
+.BI " NVME_MI_RESP_INVALID_PARAM"
+,
+.br
+.br
+.BI " NVME_MI_RESP_INVALID_CMD_SIZE"
+,
+.br
+.br
+.BI " NVME_MI_RESP_INVALID_INPUT_SIZE"
+,
+.br
+.br
+.BI " NVME_MI_RESP_ACCESS_DENIED"
+,
+.br
+.br
+.BI " NVME_MI_RESP_VPD_UPDATES_EXCEEDED"
+,
+.br
+.br
+.BI " NVME_MI_RESP_PCIE_INACCESSIBLE"
+,
+.br
+.br
+.BI " NVME_MI_RESP_MEB_SANITIZED"
+,
+.br
+.br
+.BI " NVME_MI_RESP_ENC_SERV_FAILURE"
+,
+.br
+.br
+.BI " NVME_MI_RESP_ENC_SERV_XFER_FAILURE"
+,
+.br
+.br
+.BI " NVME_MI_RESP_ENC_FAILURE"
+,
+.br
+.br
+.BI " NVME_MI_RESP_ENC_XFER_REFUSED"
+,
+.br
+.br
+.BI " NVME_MI_RESP_ENC_FUNC_UNSUP"
+,
+.br
+.br
+.BI " NVME_MI_RESP_ENC_SERV_UNAVAIL"
+,
+.br
+.br
+.BI " NVME_MI_RESP_ENC_DEGRADED"
+,
+.br
+.br
+.BI " NVME_MI_RESP_SANITIZE_IN_PROGRESS"
+
+};
+.SH Constants
+.IP "NVME_MI_RESP_SUCCESS" 12
+success
+.IP "NVME_MI_RESP_MPR" 12
+More Processing Required
+.IP "NVME_MI_RESP_INTERNAL_ERR" 12
+Internal Error
+.IP "NVME_MI_RESP_INVALID_OPCODE" 12
+Invalid command opcode
+.IP "NVME_MI_RESP_INVALID_PARAM" 12
+Invalid command parameter
+.IP "NVME_MI_RESP_INVALID_CMD_SIZE" 12
+Invalid command size
+.IP "NVME_MI_RESP_INVALID_INPUT_SIZE" 12
+Invalid command input data size
+.IP "NVME_MI_RESP_ACCESS_DENIED" 12
+Access Denied
+.IP "NVME_MI_RESP_VPD_UPDATES_EXCEEDED" 12
+More VPD updates than allowed
+.IP "NVME_MI_RESP_PCIE_INACCESSIBLE" 12
+PCIe functionality currently unavailable
+.IP "NVME_MI_RESP_MEB_SANITIZED" 12
+MEB has been cleared due to sanitize
+.IP "NVME_MI_RESP_ENC_SERV_FAILURE" 12
+Enclosure services process failed
+.IP "NVME_MI_RESP_ENC_SERV_XFER_FAILURE" 12
+Transfer with enclosure services failed
+.IP "NVME_MI_RESP_ENC_FAILURE" 12
+Unreoverable enclosure failure
+.IP "NVME_MI_RESP_ENC_XFER_REFUSED" 12
+Enclosure services transfer refused
+.IP "NVME_MI_RESP_ENC_FUNC_UNSUP" 12
+Unsupported enclosure services function
+.IP "NVME_MI_RESP_ENC_SERV_UNAVAIL" 12
+Enclosure services unavailable
+.IP "NVME_MI_RESP_ENC_DEGRADED" 12
+Noncritical failure detected by enc. services
+.IP "NVME_MI_RESP_SANITIZE_IN_PROGRESS" 12
+Command prohibited during sanitize
diff --git a/doc/man/nvme_mi_set_probe_enabled.2 b/doc/man/nvme_mi_set_probe_enabled.2
new file mode 100644
index 0000000..bc40f19
--- /dev/null
+++ b/doc/man/nvme_mi_set_probe_enabled.2
@@ -0,0 +1,16 @@
+.TH "nvme_mi_set_probe_enabled" 9 "nvme_mi_set_probe_enabled" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_set_probe_enabled \- enable/disable the probe for new endpoints
+.SH SYNOPSIS
+.B "void" nvme_mi_set_probe_enabled
+.BI "(nvme_root_t root " ","
+.BI "bool enabled " ");"
+.SH ARGUMENTS
+.IP "root" 12
+\fInvme_root_t\fP object
+.IP "enabled" 12
+whether to probe new endpoints
+.SH "DESCRIPTION"
+Controls whether newly-created endpoints are probed for quirks on creation.
+Defaults to enabled, which results in some initial messaging with the
+endpoint to determine model-specific details.
diff --git a/doc/man/nvme_mi_status_to_string.2 b/doc/man/nvme_mi_status_to_string.2
new file mode 100644
index 0000000..bb4ba45
--- /dev/null
+++ b/doc/man/nvme_mi_status_to_string.2
@@ -0,0 +1,17 @@
+.TH "nvme_mi_status_to_string" 9 "nvme_mi_status_to_string" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_mi_status_to_string \- return a string representation of the MI status.
+.SH SYNOPSIS
+.B "const char *" nvme_mi_status_to_string
+.BI "(int status " ");"
+.SH ARGUMENTS
+.IP "status" 12
+MI response status
+.SH "DESCRIPTION"
+Gives a string description of \fIstatus\fP, as per section 4.1.2 of the NVMe-MI
+spec. The status value should be of type NVME_STATUS_MI, and extracted
+from the return value using \fBnvme_status_get_value\fP.
+
+Returned string is const, and should not be \fBfree\fPed.
+.SH "RETURN"
+A string representing the status value
diff --git a/doc/man/nvme_mi_vpd_hdr.2 b/doc/man/nvme_mi_vpd_hdr.2
new file mode 100644
index 0000000..ff51fe1
--- /dev/null
+++ b/doc/man/nvme_mi_vpd_hdr.2
@@ -0,0 +1,47 @@
+.TH "libnvme" 9 "struct nvme_mi_vpd_hdr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_vpd_hdr \- Vital Product Data Common Header
+.SH SYNOPSIS
+struct nvme_mi_vpd_hdr {
+.br
+.BI " __u8 ipmiver;"
+.br
+.BI " __u8 iuaoff;"
+.br
+.BI " __u8 ciaoff;"
+.br
+.BI " __u8 biaoff;"
+.br
+.BI " __u8 piaoff;"
+.br
+.BI " __u8 mrioff;"
+.br
+.BI " __u8 rsvd6;"
+.br
+.BI " __u8 chchk;"
+.br
+.BI " __u8 vpd[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "ipmiver" 12
+IPMI Format Version Number
+.IP "iuaoff" 12
+Internal Use Area Starting Offset
+.IP "ciaoff" 12
+Chassis Info Area Starting Offset
+.IP "biaoff" 12
+Board Info Area Starting Offset
+.IP "piaoff" 12
+Product Info Area Starting Offset
+.IP "mrioff" 12
+MultiRecord Info Area Starting Offset
+.IP "rsvd6" 12
+Reserved
+.IP "chchk" 12
+Common Header Checksum
+.IP "vpd" 12
+Vital Product Data
diff --git a/doc/man/nvme_mi_vpd_mr_common.2 b/doc/man/nvme_mi_vpd_mr_common.2
new file mode 100644
index 0000000..47ff9a4
--- /dev/null
+++ b/doc/man/nvme_mi_vpd_mr_common.2
@@ -0,0 +1,49 @@
+.TH "libnvme" 9 "struct nvme_mi_vpd_mr_common" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_vpd_mr_common \- NVMe MultiRecord Area
+.SH SYNOPSIS
+struct nvme_mi_vpd_mr_common {
+.br
+.BI " __u8 type;"
+.br
+.BI " __u8 rf;"
+.br
+.BI " __u8 rlen;"
+.br
+.BI " __u8 rchksum;"
+.br
+.BI " __u8 hchksum;"
+.br
+.BI " union {"
+.br
+.BI " struct nvme_mi_vpd_mra nmra;"
+.br
+.BI " struct nvme_mi_vpd_ppmra ppmra;"
+.br
+.BI " struct nvme_mi_vpd_tra tmra;"
+.br
+.BI " };"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "type" 12
+NVMe Record Type ID
+.IP "rf" 12
+Record Format
+.IP "rlen" 12
+Record Length
+.IP "rchksum" 12
+Record Checksum
+.IP "hchksum" 12
+Header Checksum
+.IP "{unnamed_union}" 12
+anonymous
+.IP "nmra" 12
+NVMe MultiRecord Area
+.IP "ppmra" 12
+NVMe PCIe Port MultiRecord Area
+.IP "tmra" 12
+Topology MultiRecord Area
diff --git a/doc/man/nvme_mi_vpd_mra.2 b/doc/man/nvme_mi_vpd_mra.2
new file mode 100644
index 0000000..190a1d0
--- /dev/null
+++ b/doc/man/nvme_mi_vpd_mra.2
@@ -0,0 +1,75 @@
+.TH "libnvme" 9 "struct nvme_mi_vpd_mra" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_vpd_mra \- NVMe MultiRecord Area
+.SH SYNOPSIS
+struct nvme_mi_vpd_mra {
+.br
+.BI " __u8 nmravn;"
+.br
+.BI " __u8 ff;"
+.br
+.BI " __u8 rsvd7[6];"
+.br
+.BI " __u8 i18vpwr;"
+.br
+.BI " __u8 m18vpwr;"
+.br
+.BI " __u8 i33vpwr;"
+.br
+.BI " __u8 m33vpwr;"
+.br
+.BI " __u8 rsvd17;"
+.br
+.BI " __u8 m33vapsr;"
+.br
+.BI " __u8 i5vapsr;"
+.br
+.BI " __u8 m5vapsr;"
+.br
+.BI " __u8 i12vapsr;"
+.br
+.BI " __u8 m12vapsr;"
+.br
+.BI " __u8 mtl;"
+.br
+.BI " __u8 tnvmcap[16];"
+.br
+.BI " __u8 rsvd37[27];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nmravn" 12
+NVMe MultiRecord Area Version Number
+.IP "ff" 12
+Form Factor
+.IP "rsvd7" 12
+Reserved
+.IP "i18vpwr" 12
+Initial 1.8 V Power Supply Requirements
+.IP "m18vpwr" 12
+Maximum 1.8 V Power Supply Requirements
+.IP "i33vpwr" 12
+Initial 3.3 V Power Supply Requirements
+.IP "m33vpwr" 12
+Maximum 3.3 V Power Supply Requirements
+.IP "rsvd17" 12
+Reserved
+.IP "m33vapsr" 12
+Maximum 3.3 Vi aux Power Supply Requirements
+.IP "i5vapsr" 12
+Initial 5 V Power Supply Requirements
+.IP "m5vapsr" 12
+Maximum 5 V Power Supply Requirements
+.IP "i12vapsr" 12
+Initial 12 V Power Supply Requirements
+.IP "m12vapsr" 12
+Maximum 12 V Power Supply Requirements
+.IP "mtl" 12
+Maximum Thermal Load
+.IP "tnvmcap" 12
+Total NVM Capacity
+.IP "rsvd37" 12
+Reserved
diff --git a/doc/man/nvme_mi_vpd_ppmra.2 b/doc/man/nvme_mi_vpd_ppmra.2
new file mode 100644
index 0000000..9085fba
--- /dev/null
+++ b/doc/man/nvme_mi_vpd_ppmra.2
@@ -0,0 +1,47 @@
+.TH "libnvme" 9 "struct nvme_mi_vpd_ppmra" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_vpd_ppmra \- NVMe PCIe Port MultiRecord Area
+.SH SYNOPSIS
+struct nvme_mi_vpd_ppmra {
+.br
+.BI " __u8 nppmravn;"
+.br
+.BI " __u8 pn;"
+.br
+.BI " __u8 ppi;"
+.br
+.BI " __u8 ls;"
+.br
+.BI " __u8 mlw;"
+.br
+.BI " __u8 mctp;"
+.br
+.BI " __u8 refccap;"
+.br
+.BI " __u8 pi;"
+.br
+.BI " __u8 rsvd13[3];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nppmravn" 12
+NVMe PCIe Port MultiRecord Area Version Number
+.IP "pn" 12
+PCIe Port Number
+.IP "ppi" 12
+Port Information
+.IP "ls" 12
+PCIe Link Speed
+.IP "mlw" 12
+PCIe Maximum Link Width
+.IP "mctp" 12
+MCTP Support
+.IP "refccap" 12
+Ref Clk Capability
+.IP "pi" 12
+Port Identifier
+.IP "rsvd13" 12
+Reserved
diff --git a/doc/man/nvme_mi_vpd_telem.2 b/doc/man/nvme_mi_vpd_telem.2
new file mode 100644
index 0000000..d282e3c
--- /dev/null
+++ b/doc/man/nvme_mi_vpd_telem.2
@@ -0,0 +1,28 @@
+.TH "libnvme" 9 "struct nvme_mi_vpd_telem" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_vpd_telem \- Vital Product Data Element Descriptor
+.SH SYNOPSIS
+struct nvme_mi_vpd_telem {
+.br
+.BI " __u8 type;"
+.br
+.BI " __u8 rev;"
+.br
+.BI " __u8 len;"
+.br
+.BI " __u8 data[0];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "type" 12
+Type of the Element Descriptor
+.IP "rev" 12
+Revision of the Element Descriptor
+.IP "len" 12
+Number of bytes in the Element Descriptor
+.IP "data" 12
+Type-specific information associated with
+the Element Descriptor
diff --git a/doc/man/nvme_mi_vpd_tra.2 b/doc/man/nvme_mi_vpd_tra.2
new file mode 100644
index 0000000..73aec0f
--- /dev/null
+++ b/doc/man/nvme_mi_vpd_tra.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_mi_vpd_tra" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_mi_vpd_tra \- Vital Product Data Topology MultiRecord
+.SH SYNOPSIS
+struct nvme_mi_vpd_tra {
+.br
+.BI " __u8 vn;"
+.br
+.BI " __u8 rsvd6;"
+.br
+.BI " __u8 ec;"
+.br
+.BI " struct nvme_mi_vpd_telem elems[0];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "vn" 12
+Version Number
+.IP "rsvd6" 12
+Reserved
+.IP "ec" 12
+Element Count
+.IP "elems" 12
+Element Descriptor
diff --git a/doc/man/nvme_namespace_attach_ctrls.2 b/doc/man/nvme_namespace_attach_ctrls.2
new file mode 100644
index 0000000..4093c9b
--- /dev/null
+++ b/doc/man/nvme_namespace_attach_ctrls.2
@@ -0,0 +1,21 @@
+.TH "nvme_namespace_attach_ctrls" 9 "nvme_namespace_attach_ctrls" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_namespace_attach_ctrls \- Attach namespace to controller(s)
+.SH SYNOPSIS
+.B "int" nvme_namespace_attach_ctrls
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u16 num_ctrls " ","
+.BI "__u16 *ctrlist " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID to attach
+.IP "num_ctrls" 12
+Number of controllers in ctrlist
+.IP "ctrlist" 12
+List of controller IDs to perform the attach action
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_namespace_detach_ctrls.2 b/doc/man/nvme_namespace_detach_ctrls.2
new file mode 100644
index 0000000..59e15dd
--- /dev/null
+++ b/doc/man/nvme_namespace_detach_ctrls.2
@@ -0,0 +1,21 @@
+.TH "nvme_namespace_detach_ctrls" 9 "nvme_namespace_detach_ctrls" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_namespace_detach_ctrls \- Detach namespace from controller(s)
+.SH SYNOPSIS
+.B "int" nvme_namespace_detach_ctrls
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u16 num_ctrls " ","
+.BI "__u16 *ctrlist " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID to detach
+.IP "num_ctrls" 12
+Number of controllers in ctrlist
+.IP "ctrlist" 12
+List of controller IDs to perform the detach action
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_namespace_filter.2 b/doc/man/nvme_namespace_filter.2
new file mode 100644
index 0000000..09156bf
--- /dev/null
+++ b/doc/man/nvme_namespace_filter.2
@@ -0,0 +1,11 @@
+.TH "nvme_namespace_filter" 9 "nvme_namespace_filter" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_namespace_filter \- Filter for namespaces
+.SH SYNOPSIS
+.B "int" nvme_namespace_filter
+.BI "(const struct dirent *d " ");"
+.SH ARGUMENTS
+.IP "d" 12
+dirent to check
+.SH "RETURN"
+1 if \fId\fP matches, 0 otherwise
diff --git a/doc/man/nvme_namespace_first_path.2 b/doc/man/nvme_namespace_first_path.2
new file mode 100644
index 0000000..3df6ead
--- /dev/null
+++ b/doc/man/nvme_namespace_first_path.2
@@ -0,0 +1,11 @@
+.TH "nvme_namespace_first_path" 9 "nvme_namespace_first_path" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_namespace_first_path \- Start path iterator
+.SH SYNOPSIS
+.B "nvme_path_t" nvme_namespace_first_path
+.BI "(nvme_ns_t ns " ");"
+.SH ARGUMENTS
+.IP "ns" 12
+Namespace instance
+.SH "RETURN"
+First \fInvme_path_t\fP object of an \fIns\fP iterator
diff --git a/doc/man/nvme_namespace_for_each_path.2 b/doc/man/nvme_namespace_for_each_path.2
new file mode 100644
index 0000000..d181d07
--- /dev/null
+++ b/doc/man/nvme_namespace_for_each_path.2
@@ -0,0 +1,12 @@
+.TH "nvme_namespace_for_each_path" 9 "nvme_namespace_for_each_path" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_namespace_for_each_path \- Traverse paths
+.SH SYNOPSIS
+.B "nvme_namespace_for_each_path
+.BI "(n " ","
+.BI "p " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "p" 12
+\fInvme_path_t\fP object
diff --git a/doc/man/nvme_namespace_for_each_path_safe.2 b/doc/man/nvme_namespace_for_each_path_safe.2
new file mode 100644
index 0000000..2186042
--- /dev/null
+++ b/doc/man/nvme_namespace_for_each_path_safe.2
@@ -0,0 +1,15 @@
+.TH "nvme_namespace_for_each_path_safe" 9 "nvme_namespace_for_each_path_safe" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_namespace_for_each_path_safe \- Traverse paths
+.SH SYNOPSIS
+.B "nvme_namespace_for_each_path_safe
+.BI "(n " ","
+.BI "p " ","
+.BI "_p " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "p" 12
+\fInvme_path_t\fP object
+.IP "_p" 12
+A \fInvme_path_t_node\fP to use as temporary storage
diff --git a/doc/man/nvme_namespace_next_path.2 b/doc/man/nvme_namespace_next_path.2
new file mode 100644
index 0000000..56a9ad6
--- /dev/null
+++ b/doc/man/nvme_namespace_next_path.2
@@ -0,0 +1,14 @@
+.TH "nvme_namespace_next_path" 9 "nvme_namespace_next_path" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_namespace_next_path \- Next path iterator
+.SH SYNOPSIS
+.B "nvme_path_t" nvme_namespace_next_path
+.BI "(nvme_ns_t ns " ","
+.BI "nvme_path_t p " ");"
+.SH ARGUMENTS
+.IP "ns" 12
+Namespace instance
+.IP "p" 12
+Previous \fInvme_path_t\fP object of an \fIns\fP iterator
+.SH "RETURN"
+Next \fInvme_path_t\fP object of an \fIns\fP iterator
diff --git a/doc/man/nvme_nbft_free.2 b/doc/man/nvme_nbft_free.2
new file mode 100644
index 0000000..b4a3c0e
--- /dev/null
+++ b/doc/man/nvme_nbft_free.2
@@ -0,0 +1,9 @@
+.TH "nvme_nbft_free" 9 "nvme_nbft_free" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_nbft_free \- Free the struct nbft_info and its contents
+.SH SYNOPSIS
+.B "void" nvme_nbft_free
+.BI "(struct nbft_info *nbft " ");"
+.SH ARGUMENTS
+.IP "nbft" 12
+Parsed NBFT table data.
diff --git a/doc/man/nvme_nbft_read.2 b/doc/man/nvme_nbft_read.2
new file mode 100644
index 0000000..ac97752
--- /dev/null
+++ b/doc/man/nvme_nbft_read.2
@@ -0,0 +1,17 @@
+.TH "nvme_nbft_read" 9 "nvme_nbft_read" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_nbft_read \- Read and parse contents of an ACPI NBFT table
+.SH SYNOPSIS
+.B "int" nvme_nbft_read
+.BI "(struct nbft_info **nbft " ","
+.BI "const char *filename " ");"
+.SH ARGUMENTS
+.IP "nbft" 12
+Parsed NBFT table data.
+.IP "filename" 12
+Filename of the raw NBFT table to read.
+.SH "DESCRIPTION"
+Read and parse the specified NBFT file into a struct nbft_info.
+Free with \fBnvme_nbft_free\fP.
+.SH "RETURN"
+0 on success, errno otherwise.
diff --git a/doc/man/nvme_nd_ns_fpi.2 b/doc/man/nvme_nd_ns_fpi.2
new file mode 100644
index 0000000..38ba32f
--- /dev/null
+++ b/doc/man/nvme_nd_ns_fpi.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "enum nvme_nd_ns_fpi" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_nd_ns_fpi \- If a format operation is in progress, this field indicates the percentage of the namespace that remains to be formatted.
+.SH SYNOPSIS
+enum nvme_nd_ns_fpi {
+.br
+.BI " NVME_NS_FPI_REMAINING"
+,
+.br
+.br
+.BI " NVME_NS_FPI_SUPPORTED"
+
+};
+.SH Constants
+.IP "NVME_NS_FPI_REMAINING" 12
+Mask to get the format percent remaining value
+.IP "NVME_NS_FPI_SUPPORTED" 12
+If set, indicates that the namespace supports the
+Format Progress Indicator defined for the field.
diff --git a/doc/man/nvme_next_host.2 b/doc/man/nvme_next_host.2
new file mode 100644
index 0000000..fe6457b
--- /dev/null
+++ b/doc/man/nvme_next_host.2
@@ -0,0 +1,14 @@
+.TH "nvme_next_host" 9 "nvme_next_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_next_host \- Next host iterator
+.SH SYNOPSIS
+.B "nvme_host_t" nvme_next_host
+.BI "(nvme_root_t r " ","
+.BI "nvme_host_t h " ");"
+.SH ARGUMENTS
+.IP "r" 12
+\fInvme_root_t\fP object
+.IP "h" 12
+Previous \fInvme_host_t\fP iterator
+.SH "RETURN"
+Next \fInvme_host_t\fP object in an iterator
diff --git a/doc/man/nvme_next_subsystem.2 b/doc/man/nvme_next_subsystem.2
new file mode 100644
index 0000000..1529bdb
--- /dev/null
+++ b/doc/man/nvme_next_subsystem.2
@@ -0,0 +1,14 @@
+.TH "nvme_next_subsystem" 9 "nvme_next_subsystem" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_next_subsystem \- Next subsystem iterator
+.SH SYNOPSIS
+.B "nvme_subsystem_t" nvme_next_subsystem
+.BI "(nvme_host_t h " ","
+.BI "nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "h" 12
+\fInvme_host_t\fP object
+.IP "s" 12
+Previous \fInvme_subsystem_t\fP iterator
+.SH "RETURN"
+next \fInvme_subsystem_t\fP object in an iterator
diff --git a/doc/man/nvme_ns_attach.2 b/doc/man/nvme_ns_attach.2
new file mode 100644
index 0000000..cea24a4
--- /dev/null
+++ b/doc/man/nvme_ns_attach.2
@@ -0,0 +1,12 @@
+.TH "nvme_ns_attach" 9 "nvme_ns_attach" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_attach \- Attach or detach namespace to controller(s)
+.SH SYNOPSIS
+.B "int" nvme_ns_attach
+.BI "(struct nvme_ns_attach_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_ns_attach_args\fP Argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ns_attach_ctrls.2 b/doc/man/nvme_ns_attach_ctrls.2
new file mode 100644
index 0000000..e3abef5
--- /dev/null
+++ b/doc/man/nvme_ns_attach_ctrls.2
@@ -0,0 +1,18 @@
+.TH "nvme_ns_attach_ctrls" 9 "nvme_ns_attach_ctrls" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_attach_ctrls \- Attach namespace to controllers
+.SH SYNOPSIS
+.B "int" nvme_ns_attach_ctrls
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ctrl_list *ctrlist " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID to attach
+.IP "ctrlist" 12
+Controller list to modify attachment state of nsid
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ns_attach_sel.2 b/doc/man/nvme_ns_attach_sel.2
new file mode 100644
index 0000000..d1f3e67
--- /dev/null
+++ b/doc/man/nvme_ns_attach_sel.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_ns_attach_sel" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ns_attach_sel \- Namespace Attachment - Select
+.SH SYNOPSIS
+enum nvme_ns_attach_sel {
+.br
+.BI " NVME_NS_ATTACH_SEL_CTRL_ATTACH"
+,
+.br
+.br
+.BI " NVME_NS_ATTACH_SEL_CTRL_DEATTACH"
+
+};
+.SH Constants
+.IP "NVME_NS_ATTACH_SEL_CTRL_ATTACH" 12
+Namespace attach selection
+.IP "NVME_NS_ATTACH_SEL_CTRL_DEATTACH" 12
+Namespace detach selection
diff --git a/doc/man/nvme_ns_compare.2 b/doc/man/nvme_ns_compare.2
new file mode 100644
index 0000000..37700cb
--- /dev/null
+++ b/doc/man/nvme_ns_compare.2
@@ -0,0 +1,20 @@
+.TH "nvme_ns_compare" 9 "nvme_ns_compare" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_compare \- Compare data on a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_compare
+.BI "(nvme_ns_t n " ","
+.BI "void *buf " ","
+.BI "off_t offset " ","
+.BI "size_t count " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "buf" 12
+Buffer with data to be compared
+.IP "offset" 12
+LBA offset of \fIn\fP
+.IP "count" 12
+Number of sectors in \fIbuf\fP
+.SH "RETURN"
+Number of sectors compared
diff --git a/doc/man/nvme_ns_detach_ctrls.2 b/doc/man/nvme_ns_detach_ctrls.2
new file mode 100644
index 0000000..062e89c
--- /dev/null
+++ b/doc/man/nvme_ns_detach_ctrls.2
@@ -0,0 +1,18 @@
+.TH "nvme_ns_detach_ctrls" 9 "nvme_ns_detach_ctrls" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_detach_ctrls \- Detach namespace from controllers
+.SH SYNOPSIS
+.B "int" nvme_ns_detach_ctrls
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_ctrl_list *ctrlist " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID to detach
+.IP "ctrlist" 12
+Controller list to modify attachment state of nsid
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ns_flush.2 b/doc/man/nvme_ns_flush.2
new file mode 100644
index 0000000..dd9a0b8
--- /dev/null
+++ b/doc/man/nvme_ns_flush.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_flush" 9 "nvme_ns_flush" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_flush \- Flush data to a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_flush
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+0 on success, -1 on error.
diff --git a/doc/man/nvme_ns_get_csi.2 b/doc/man/nvme_ns_get_csi.2
new file mode 100644
index 0000000..61ab0c9
--- /dev/null
+++ b/doc/man/nvme_ns_get_csi.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_csi" 9 "nvme_ns_get_csi" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_csi \- Command set identifier of a namespace
+.SH SYNOPSIS
+.B "enum nvme_csi" nvme_ns_get_csi
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+The namespace's command set identifier in use
diff --git a/doc/man/nvme_ns_get_ctrl.2 b/doc/man/nvme_ns_get_ctrl.2
new file mode 100644
index 0000000..2c3be94
--- /dev/null
+++ b/doc/man/nvme_ns_get_ctrl.2
@@ -0,0 +1,13 @@
+.TH "nvme_ns_get_ctrl" 9 "nvme_ns_get_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_ctrl \- &nvme_ctrl_t of a namespace
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvme_ns_get_ctrl
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "DESCRIPTION"
+nvme_ctrl_t object may be NULL for a multipathed namespace
+.SH "RETURN"
+nvme_ctrl_t object of \fIn\fP if present
diff --git a/doc/man/nvme_ns_get_eui64.2 b/doc/man/nvme_ns_get_eui64.2
new file mode 100644
index 0000000..2f8a8b9
--- /dev/null
+++ b/doc/man/nvme_ns_get_eui64.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_eui64" 9 "nvme_ns_get_eui64" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_eui64 \- 64-bit eui of a namespace
+.SH SYNOPSIS
+.B "const uint8_t *" nvme_ns_get_eui64
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+A pointer to the 64-bit eui
diff --git a/doc/man/nvme_ns_get_fd.2 b/doc/man/nvme_ns_get_fd.2
new file mode 100644
index 0000000..fee2816
--- /dev/null
+++ b/doc/man/nvme_ns_get_fd.2
@@ -0,0 +1,18 @@
+.TH "nvme_ns_get_fd" 9 "nvme_ns_get_fd" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_fd \- Get associated file descriptor
+.SH SYNOPSIS
+.B "int" nvme_ns_get_fd
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "DESCRIPTION"
+libnvme will \fBopen\fP the file (if not already opened) and keep
+an internal copy of the file descriptor. Following calls to
+this API retrieve the internal cached copy of the file
+descriptor. The file will remain opened and the fd will
+remain cached until the ns object is deleted or
+\fBnvme_ns_release_fd\fP is called.
+.SH "RETURN"
+File descriptor associated with \fIn\fP or -1
diff --git a/doc/man/nvme_ns_get_firmware.2 b/doc/man/nvme_ns_get_firmware.2
new file mode 100644
index 0000000..2e314cc
--- /dev/null
+++ b/doc/man/nvme_ns_get_firmware.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_firmware" 9 "nvme_ns_get_firmware" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_firmware \- Firmware string of a namespace
+.SH SYNOPSIS
+.B "const char *" nvme_ns_get_firmware
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+Firmware string of \fIn\fP
diff --git a/doc/man/nvme_ns_get_generic_name.2 b/doc/man/nvme_ns_get_generic_name.2
new file mode 100644
index 0000000..f076891
--- /dev/null
+++ b/doc/man/nvme_ns_get_generic_name.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_generic_name" 9 "nvme_ns_get_generic_name" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_generic_name \- Returns name of generic namespace chardev.
+.SH SYNOPSIS
+.B "const char *" nvme_ns_get_generic_name
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+Name of generic namespace chardev
diff --git a/doc/man/nvme_ns_get_lba_count.2 b/doc/man/nvme_ns_get_lba_count.2
new file mode 100644
index 0000000..dcc03c4
--- /dev/null
+++ b/doc/man/nvme_ns_get_lba_count.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_lba_count" 9 "nvme_ns_get_lba_count" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_lba_count \- LBA count of a namespace
+.SH SYNOPSIS
+.B "uint64_t" nvme_ns_get_lba_count
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+LBA count of \fIn\fP
diff --git a/doc/man/nvme_ns_get_lba_size.2 b/doc/man/nvme_ns_get_lba_size.2
new file mode 100644
index 0000000..d6e6fcb
--- /dev/null
+++ b/doc/man/nvme_ns_get_lba_size.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_lba_size" 9 "nvme_ns_get_lba_size" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_lba_size \- LBA size of a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_get_lba_size
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+LBA size of \fIn\fP
diff --git a/doc/man/nvme_ns_get_lba_util.2 b/doc/man/nvme_ns_get_lba_util.2
new file mode 100644
index 0000000..c34b4c6
--- /dev/null
+++ b/doc/man/nvme_ns_get_lba_util.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_lba_util" 9 "nvme_ns_get_lba_util" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_lba_util \- LBA utilization of a namespace
+.SH SYNOPSIS
+.B "uint64_t" nvme_ns_get_lba_util
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+LBA utilization of \fIn\fP
diff --git a/doc/man/nvme_ns_get_meta_size.2 b/doc/man/nvme_ns_get_meta_size.2
new file mode 100644
index 0000000..0d15ef2
--- /dev/null
+++ b/doc/man/nvme_ns_get_meta_size.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_meta_size" 9 "nvme_ns_get_meta_size" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_meta_size \- Metadata size of a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_get_meta_size
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+Metadata size of \fIn\fP
diff --git a/doc/man/nvme_ns_get_model.2 b/doc/man/nvme_ns_get_model.2
new file mode 100644
index 0000000..2cc4f89
--- /dev/null
+++ b/doc/man/nvme_ns_get_model.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_model" 9 "nvme_ns_get_model" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_model \- Model of a namespace
+.SH SYNOPSIS
+.B "const char *" nvme_ns_get_model
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+Model string of \fIn\fP
diff --git a/doc/man/nvme_ns_get_name.2 b/doc/man/nvme_ns_get_name.2
new file mode 100644
index 0000000..809aa5a
--- /dev/null
+++ b/doc/man/nvme_ns_get_name.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_name" 9 "nvme_ns_get_name" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_name \- sysfs name of a namespace
+.SH SYNOPSIS
+.B "const char *" nvme_ns_get_name
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+sysfs name of \fIn\fP
diff --git a/doc/man/nvme_ns_get_nguid.2 b/doc/man/nvme_ns_get_nguid.2
new file mode 100644
index 0000000..4e8d956
--- /dev/null
+++ b/doc/man/nvme_ns_get_nguid.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_nguid" 9 "nvme_ns_get_nguid" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_nguid \- 128-bit nguid of a namespace
+.SH SYNOPSIS
+.B "const uint8_t *" nvme_ns_get_nguid
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+A pointer to the 128-bit nguid
diff --git a/doc/man/nvme_ns_get_nsid.2 b/doc/man/nvme_ns_get_nsid.2
new file mode 100644
index 0000000..8ed328c
--- /dev/null
+++ b/doc/man/nvme_ns_get_nsid.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_nsid" 9 "nvme_ns_get_nsid" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_nsid \- NSID of a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_get_nsid
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+NSID of \fIn\fP
diff --git a/doc/man/nvme_ns_get_serial.2 b/doc/man/nvme_ns_get_serial.2
new file mode 100644
index 0000000..21b802b
--- /dev/null
+++ b/doc/man/nvme_ns_get_serial.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_serial" 9 "nvme_ns_get_serial" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_serial \- Serial number of a namespace
+.SH SYNOPSIS
+.B "const char *" nvme_ns_get_serial
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+Serial number string of \fIn\fP
diff --git a/doc/man/nvme_ns_get_subsystem.2 b/doc/man/nvme_ns_get_subsystem.2
new file mode 100644
index 0000000..6b86f15
--- /dev/null
+++ b/doc/man/nvme_ns_get_subsystem.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_subsystem" 9 "nvme_ns_get_subsystem" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_subsystem \- &nvme_subsystem_t of a namespace
+.SH SYNOPSIS
+.B "nvme_subsystem_t" nvme_ns_get_subsystem
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+nvme_subsystem_t object of \fIn\fP
diff --git a/doc/man/nvme_ns_get_sysfs_dir.2 b/doc/man/nvme_ns_get_sysfs_dir.2
new file mode 100644
index 0000000..870b8d7
--- /dev/null
+++ b/doc/man/nvme_ns_get_sysfs_dir.2
@@ -0,0 +1,11 @@
+.TH "nvme_ns_get_sysfs_dir" 9 "nvme_ns_get_sysfs_dir" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_sysfs_dir \- sysfs directory of a namespace
+.SH SYNOPSIS
+.B "const char *" nvme_ns_get_sysfs_dir
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.SH "RETURN"
+sysfs directory name of \fIn\fP
diff --git a/doc/man/nvme_ns_get_uuid.2 b/doc/man/nvme_ns_get_uuid.2
new file mode 100644
index 0000000..265d5f1
--- /dev/null
+++ b/doc/man/nvme_ns_get_uuid.2
@@ -0,0 +1,14 @@
+.TH "nvme_ns_get_uuid" 9 "nvme_ns_get_uuid" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_get_uuid \- UUID of a namespace
+.SH SYNOPSIS
+.B "void" nvme_ns_get_uuid
+.BI "(nvme_ns_t n " ","
+.BI "unsigned char out[NVME_UUID_LEN] " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "out" 12
+buffer for the UUID
+.SH "DESCRIPTION"
+Copies the namespace's uuid into \fIout\fP
diff --git a/doc/man/nvme_ns_id_desc.2 b/doc/man/nvme_ns_id_desc.2
new file mode 100644
index 0000000..685765e
--- /dev/null
+++ b/doc/man/nvme_ns_id_desc.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "struct nvme_ns_id_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_ns_id_desc \- Namespace identifier type descriptor
+.SH SYNOPSIS
+struct nvme_ns_id_desc {
+.br
+.BI " __u8 nidt;"
+.br
+.BI " __u8 nidl;"
+.br
+.BI " __le16 rsvd;"
+.br
+.BI " __u8 nid[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nidt" 12
+Namespace Identifier Type, see \fIenum nvme_ns_id_desc_nidt\fP
+.IP "nidl" 12
+Namespace Identifier Length contains the length in bytes of the
+\fIstruct nvme_id_ns\fP.nid.
+.IP "rsvd" 12
+Reserved
+.IP "nid" 12
+Namespace Identifier contains a value that is globally unique and
+assigned to the namespace when the namespace is created. The length
+is defined in \fIstruct nvme_id_ns\fP.nidl.
diff --git a/doc/man/nvme_ns_id_desc_nidt.2 b/doc/man/nvme_ns_id_desc_nidt.2
new file mode 100644
index 0000000..82d116a
--- /dev/null
+++ b/doc/man/nvme_ns_id_desc_nidt.2
@@ -0,0 +1,33 @@
+.TH "libnvme" 9 "enum nvme_ns_id_desc_nidt" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ns_id_desc_nidt \- Known namespace identifier types
+.SH SYNOPSIS
+enum nvme_ns_id_desc_nidt {
+.br
+.BI " NVME_NIDT_EUI64"
+,
+.br
+.br
+.BI " NVME_NIDT_NGUID"
+,
+.br
+.br
+.BI " NVME_NIDT_UUID"
+,
+.br
+.br
+.BI " NVME_NIDT_CSI"
+
+};
+.SH Constants
+.IP "NVME_NIDT_EUI64" 12
+IEEE Extended Unique Identifier, the NID field contains a
+copy of the EUI64 field in the struct nvme_id_ns.eui64.
+.IP "NVME_NIDT_NGUID" 12
+Namespace Globally Unique Identifier, the NID field
+contains a copy of the NGUID field in struct nvme_id_ns.nguid.
+.IP "NVME_NIDT_UUID" 12
+The NID field contains a 128-bit Universally Unique
+Identifier (UUID) as specified in RFC 4122.
+.IP "NVME_NIDT_CSI" 12
+The NID field contains the command set identifier.
diff --git a/doc/man/nvme_ns_identify.2 b/doc/man/nvme_ns_identify.2
new file mode 100644
index 0000000..d3fce45
--- /dev/null
+++ b/doc/man/nvme_ns_identify.2
@@ -0,0 +1,17 @@
+.TH "nvme_ns_identify" 9 "nvme_ns_identify" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_identify \- Issue an 'identify namespace' command
+.SH SYNOPSIS
+.B "int" nvme_ns_identify
+.BI "(nvme_ns_t n " ","
+.BI "struct nvme_id_ns *ns " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "ns" 12
+\fInvme_id_ns\fP buffer
+.SH "DESCRIPTION"
+Writes the data returned by the 'identify namespace' command
+into \fIns\fP.
+.SH "RETURN"
+0 on success, -1 on error.
diff --git a/doc/man/nvme_ns_identify_descs.2 b/doc/man/nvme_ns_identify_descs.2
new file mode 100644
index 0000000..fb9ad37
--- /dev/null
+++ b/doc/man/nvme_ns_identify_descs.2
@@ -0,0 +1,17 @@
+.TH "nvme_ns_identify_descs" 9 "nvme_ns_identify_descs" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_identify_descs \- Issue an 'identify descriptors' command
+.SH SYNOPSIS
+.B "int" nvme_ns_identify_descs
+.BI "(nvme_ns_t n " ","
+.BI "struct nvme_ns_id_desc *descs " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "descs" 12
+List of identify descriptors
+.SH "DESCRIPTION"
+Writes the data returned by the 'identify descriptors' command
+into \fIdescs\fP.
+.SH "RETURN"
+0 on success, -1 on error.
diff --git a/doc/man/nvme_ns_list.2 b/doc/man/nvme_ns_list.2
new file mode 100644
index 0000000..7c71ee3
--- /dev/null
+++ b/doc/man/nvme_ns_list.2
@@ -0,0 +1,15 @@
+.TH "libnvme" 9 "struct nvme_ns_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_ns_list \- Namespace List
+.SH SYNOPSIS
+struct nvme_ns_list {
+.br
+.BI " __le32 ns[NVME_ID_NS_LIST_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "ns" 12
+Namespace Identifier
diff --git a/doc/man/nvme_ns_metadata_type.2 b/doc/man/nvme_ns_metadata_type.2
new file mode 100644
index 0000000..e50a6da
--- /dev/null
+++ b/doc/man/nvme_ns_metadata_type.2
@@ -0,0 +1,34 @@
+.TH "libnvme" 9 "enum nvme_ns_metadata_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ns_metadata_type \- Namespace Metadata Element Types
+.SH SYNOPSIS
+enum nvme_ns_metadata_type {
+.br
+.BI " NVME_NS_METADATA_OS_NS_NAME"
+,
+.br
+.br
+.BI " NVME_NS_METADATA_PRE_BOOT_NS_NAME"
+,
+.br
+.br
+.BI " NVME_NS_METADATA_OS_NS_QUAL_1"
+,
+.br
+.br
+.BI " NVME_NS_METADATA_OS_NS_QUAL_2"
+
+};
+.SH Constants
+.IP "NVME_NS_METADATA_OS_NS_NAME" 12
+Name of the namespace in the
+operating system
+.IP "NVME_NS_METADATA_PRE_BOOT_NS_NAME" 12
+Name of the namespace in the pre-boot
+environment.
+.IP "NVME_NS_METADATA_OS_NS_QUAL_1" 12
+First qualifier of the Operating System
+Namespace Name.
+.IP "NVME_NS_METADATA_OS_NS_QUAL_2" 12
+Second qualifier of the Operating System
+Namespace Name.
diff --git a/doc/man/nvme_ns_mgmt.2 b/doc/man/nvme_ns_mgmt.2
new file mode 100644
index 0000000..7b40ec3
--- /dev/null
+++ b/doc/man/nvme_ns_mgmt.2
@@ -0,0 +1,12 @@
+.TH "nvme_ns_mgmt" 9 "nvme_ns_mgmt" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_mgmt \- Issue a Namespace management command
+.SH SYNOPSIS
+.B "int" nvme_ns_mgmt
+.BI "(struct nvme_ns_mgmt_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_ns_mgmt_args\fP Argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ns_mgmt_create.2 b/doc/man/nvme_ns_mgmt_create.2
new file mode 100644
index 0000000..11a842d
--- /dev/null
+++ b/doc/man/nvme_ns_mgmt_create.2
@@ -0,0 +1,32 @@
+.TH "nvme_ns_mgmt_create" 9 "nvme_ns_mgmt_create" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_mgmt_create \- Create a non attached namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_mgmt_create
+.BI "(int fd " ","
+.BI "struct nvme_id_ns *ns " ","
+.BI "__u32 *nsid " ","
+.BI "__u32 timeout " ","
+.BI "__u8 csi " ","
+.BI "struct nvme_ns_mgmt_host_sw_specified *data " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "ns" 12
+Namespace identification that defines ns creation parameters
+.IP "nsid" 12
+On success, set to the namespace id that was created
+.IP "timeout" 12
+Override the default timeout to this value in milliseconds;
+set to 0 to use the system default.
+.IP "csi" 12
+Command Set Identifier
+.IP "data" 12
+Host Software Specified Fields that defines ns creation parameters
+.SH "DESCRIPTION"
+On successful creation, the namespace exists in the subsystem, but is not
+attached to any controller. Use the \fBnvme_ns_attach_ctrls\fP to assign the
+namespace to one or more controllers.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ns_mgmt_delete.2 b/doc/man/nvme_ns_mgmt_delete.2
new file mode 100644
index 0000000..97985ab
--- /dev/null
+++ b/doc/man/nvme_ns_mgmt_delete.2
@@ -0,0 +1,19 @@
+.TH "nvme_ns_mgmt_delete" 9 "nvme_ns_mgmt_delete" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_mgmt_delete \- Delete a non attached namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_mgmt_delete
+.BI "(int fd " ","
+.BI "__u32 nsid " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace identifier to delete
+.SH "DESCRIPTION"
+It is recommended that a namespace being deleted is not attached to any
+controller. Use the \fBnvme_ns_detach_ctrls\fP first if the namespace is still
+attached.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ns_mgmt_host_sw_specified.2 b/doc/man/nvme_ns_mgmt_host_sw_specified.2
new file mode 100644
index 0000000..86f5164
--- /dev/null
+++ b/doc/man/nvme_ns_mgmt_host_sw_specified.2
@@ -0,0 +1,122 @@
+.TH "libnvme" 9 "struct nvme_ns_mgmt_host_sw_specified" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_ns_mgmt_host_sw_specified \- Namespace management Host Software Specified Fields.
+.SH SYNOPSIS
+struct nvme_ns_mgmt_host_sw_specified {
+.br
+.BI " __le64 nsze;"
+.br
+.BI " __le64 ncap;"
+.br
+.BI " __u8 rsvd16[10];"
+.br
+.BI " __u8 flbas;"
+.br
+.BI " __u8 rsvd27[2];"
+.br
+.BI " __u8 dps;"
+.br
+.BI " __u8 nmic;"
+.br
+.BI " __u8 rsvd31[61];"
+.br
+.BI " __le32 anagrpid;"
+.br
+.BI " __u8 rsvd96[4];"
+.br
+.BI " __le16 nvmsetid;"
+.br
+.BI " __le16 endgid;"
+.br
+.BI " __u8 rsvd104[280];"
+.br
+.BI " __le64 lbstm;"
+.br
+.BI " __le16 nphndls;"
+.br
+.BI " __u8 rsvd394[105];"
+.br
+.BI " union {"
+.br
+.BI " __u8 rsvd499[13];"
+.br
+.BI " struct {"
+.br
+.BI " __u8 znsco;"
+.br
+.BI " __le32 rar;"
+.br
+.BI " __le32 ror;"
+.br
+.BI " __le32 rnumzrwa;"
+.br
+.BI " } zns;"
+.br
+.BI " };"
+.br
+.BI " __le16 phndl[128];"
+.br
+.BI " __u8 rsvd768[3328];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nsze" 12
+Namespace Size indicates the total size of the namespace in
+logical blocks. The number of logical blocks is based on the
+formatted LBA size.
+.IP "ncap" 12
+Namespace Capacity indicates the maximum number of logical blocks
+that may be allocated in the namespace at any point in time. The
+number of logical blocks is based on the formatted LBA size.
+.IP "rsvd16" 12
+Reserved
+.IP "flbas" 12
+Formatted LBA Size, see \fIenum nvme_id_ns_flbas\fP.
+.IP "rsvd27" 12
+Reserved
+.IP "dps" 12
+End-to-end Data Protection Type Settings, see
+\fIenum nvme_id_ns_dps\fP.
+.IP "nmic" 12
+Namespace Multi-path I/O and Namespace Sharing Capabilities, see
+\fIenum nvme_id_ns_nmic\fP.
+.IP "rsvd31" 12
+Reserved
+.IP "anagrpid" 12
+ANA Group Identifier indicates the ANA Group Identifier of the
+ANA group of which the namespace is a member.
+.IP "rsvd96" 12
+Reserved
+.IP "nvmsetid" 12
+NVM Set Identifier indicates the NVM Set with which this
+namespace is associated.
+.IP "endgid" 12
+Endurance Group Identifier indicates the Endurance Group with
+which this namespace is associated.
+.IP "rsvd104" 12
+Reserved
+.IP "lbstm" 12
+Logical Block Storage Tag Mask Identifies the mask for the
+Storage Tag field for the protection information
+.IP "nphndls" 12
+Number of Placement Handles specifies the number of Placement
+Handles included in the Placement Handle List
+.IP "rsvd394" 12
+Reserved
+.IP "{unnamed_union}" 12
+anonymous
+.IP "rsvd499" 12
+Reserved for I/O Command Sets that extend this specification.
+.IP "zns" 12
+rsvd499( Zoned Namespace Command Set specific field )
+.IP "phndl" 12
+Placement Handle Associated RUH : This field specifies the Reclaim
+Unit Handle Identifier to be associated with the Placement Handle
+value. If the Flexible Data Placement capability is not supported or
+not enabled in specified Endurance Group, then the controller shall
+ignore this field.
+.IP "rsvd768" 12
+Reserved
diff --git a/doc/man/nvme_ns_mgmt_sel.2 b/doc/man/nvme_ns_mgmt_sel.2
new file mode 100644
index 0000000..1fd519c
--- /dev/null
+++ b/doc/man/nvme_ns_mgmt_sel.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_ns_mgmt_sel" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ns_mgmt_sel \- Namespace Management - Select
+.SH SYNOPSIS
+enum nvme_ns_mgmt_sel {
+.br
+.BI " NVME_NS_MGMT_SEL_CREATE"
+,
+.br
+.br
+.BI " NVME_NS_MGMT_SEL_DELETE"
+
+};
+.SH Constants
+.IP "NVME_NS_MGMT_SEL_CREATE" 12
+Namespace Create selection
+.IP "NVME_NS_MGMT_SEL_DELETE" 12
+Namespace Delete selection
diff --git a/doc/man/nvme_ns_read.2 b/doc/man/nvme_ns_read.2
new file mode 100644
index 0000000..7f2164c
--- /dev/null
+++ b/doc/man/nvme_ns_read.2
@@ -0,0 +1,20 @@
+.TH "nvme_ns_read" 9 "nvme_ns_read" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_read \- Read from a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_read
+.BI "(nvme_ns_t n " ","
+.BI "void *buf " ","
+.BI "off_t offset " ","
+.BI "size_t count " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "buf" 12
+Buffer into which the data will be transferred
+.IP "offset" 12
+LBA offset of \fIn\fP
+.IP "count" 12
+Number of sectors in \fIbuf\fP
+.SH "RETURN"
+Number of sectors read or -1 on error.
diff --git a/doc/man/nvme_ns_release_fd.2 b/doc/man/nvme_ns_release_fd.2
new file mode 100644
index 0000000..9cd406d
--- /dev/null
+++ b/doc/man/nvme_ns_release_fd.2
@@ -0,0 +1,9 @@
+.TH "nvme_ns_release_fd" 9 "nvme_ns_release_fd" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_release_fd \- Close fd and clear fd from ns object
+.SH SYNOPSIS
+.B "void" nvme_ns_release_fd
+.BI "(nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
diff --git a/doc/man/nvme_ns_rescan.2 b/doc/man/nvme_ns_rescan.2
new file mode 100644
index 0000000..bd05c54
--- /dev/null
+++ b/doc/man/nvme_ns_rescan.2
@@ -0,0 +1,13 @@
+.TH "nvme_ns_rescan" 9 "nvme_ns_rescan" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_rescan \- Initiate a controller rescan
+.SH SYNOPSIS
+.B "int" nvme_ns_rescan
+.BI "(int fd " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.SH "DESCRIPTION"
+This should only be sent to controller handles, not to namespaces.
+.SH "RETURN"
+0 if a rescan was initiated or -1 with errno set otherwise.
diff --git a/doc/man/nvme_ns_verify.2 b/doc/man/nvme_ns_verify.2
new file mode 100644
index 0000000..665b1c3
--- /dev/null
+++ b/doc/man/nvme_ns_verify.2
@@ -0,0 +1,17 @@
+.TH "nvme_ns_verify" 9 "nvme_ns_verify" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_verify \- Verify data on a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_verify
+.BI "(nvme_ns_t n " ","
+.BI "off_t offset " ","
+.BI "size_t count " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "offset" 12
+LBA offset of \fIn\fP
+.IP "count" 12
+Number of sectors to be verified
+.SH "RETURN"
+Number of sectors verified
diff --git a/doc/man/nvme_ns_write.2 b/doc/man/nvme_ns_write.2
new file mode 100644
index 0000000..02275ed
--- /dev/null
+++ b/doc/man/nvme_ns_write.2
@@ -0,0 +1,20 @@
+.TH "nvme_ns_write" 9 "nvme_ns_write" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_write \- Write to a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_write
+.BI "(nvme_ns_t n " ","
+.BI "void *buf " ","
+.BI "off_t offset " ","
+.BI "size_t count " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "buf" 12
+Buffer with data to be written
+.IP "offset" 12
+LBA offset of \fIn\fP
+.IP "count" 12
+Number of sectors in \fIbuf\fP
+.SH "RETURN"
+Number of sectors written or -1 on error
diff --git a/doc/man/nvme_ns_write_protect_cfg.2 b/doc/man/nvme_ns_write_protect_cfg.2
new file mode 100644
index 0000000..074a38e
--- /dev/null
+++ b/doc/man/nvme_ns_write_protect_cfg.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_ns_write_protect_cfg" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_ns_write_protect_cfg \- Write Protection - Write Protection State
+.SH SYNOPSIS
+enum nvme_ns_write_protect_cfg {
+.br
+.BI " NVME_NS_WP_CFG_NONE"
+,
+.br
+.br
+.BI " NVME_NS_WP_CFG_PROTECT"
+,
+.br
+.br
+.BI " NVME_NS_WP_CFG_PROTECT_POWER_CYCLE"
+,
+.br
+.br
+.BI " NVME_NS_WP_CFG_PROTECT_PERMANENT"
+
+};
+.SH Constants
+.IP "NVME_NS_WP_CFG_NONE" 12
+No Write Protect
+.IP "NVME_NS_WP_CFG_PROTECT" 12
+Write Protect
+.IP "NVME_NS_WP_CFG_PROTECT_POWER_CYCLE" 12
+Write Protect Until Power Cycle
+.IP "NVME_NS_WP_CFG_PROTECT_PERMANENT" 12
+Permanent Write Protect
diff --git a/doc/man/nvme_ns_write_uncorrectable.2 b/doc/man/nvme_ns_write_uncorrectable.2
new file mode 100644
index 0000000..5c16ed1
--- /dev/null
+++ b/doc/man/nvme_ns_write_uncorrectable.2
@@ -0,0 +1,17 @@
+.TH "nvme_ns_write_uncorrectable" 9 "nvme_ns_write_uncorrectable" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_write_uncorrectable \- Issus a 'write uncorrectable' command
+.SH SYNOPSIS
+.B "int" nvme_ns_write_uncorrectable
+.BI "(nvme_ns_t n " ","
+.BI "off_t offset " ","
+.BI "size_t count " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "offset" 12
+LBA offset in \fIn\fP
+.IP "count" 12
+Number of sectors to be written
+.SH "RETURN"
+Number of sectors written
diff --git a/doc/man/nvme_ns_write_zeros.2 b/doc/man/nvme_ns_write_zeros.2
new file mode 100644
index 0000000..21fa7ec
--- /dev/null
+++ b/doc/man/nvme_ns_write_zeros.2
@@ -0,0 +1,17 @@
+.TH "nvme_ns_write_zeros" 9 "nvme_ns_write_zeros" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_ns_write_zeros \- Write zeros to a namespace
+.SH SYNOPSIS
+.B "int" nvme_ns_write_zeros
+.BI "(nvme_ns_t n " ","
+.BI "off_t offset " ","
+.BI "size_t count " ");"
+.SH ARGUMENTS
+.IP "n" 12
+Namespace instance
+.IP "offset" 12
+LBA offset in \fIn\fP
+.IP "count" 12
+Number of sectors to be written
+.SH "RETURN"
+Number of sectors written
diff --git a/doc/man/nvme_nss_hw_err_event.2 b/doc/man/nvme_nss_hw_err_event.2
new file mode 100644
index 0000000..3d387ca
--- /dev/null
+++ b/doc/man/nvme_nss_hw_err_event.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_nss_hw_err_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_nss_hw_err_event \- NVM Subsystem Hardware Error Event
+.SH SYNOPSIS
+struct nvme_nss_hw_err_event {
+.br
+.BI " __le16 nss_hw_err_event_code;"
+.br
+.BI " __u8 rsvd2[2];"
+.br
+.BI " __u8 *add_hw_err_info;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nss_hw_err_event_code" 12
+NVM Subsystem Hardware Error Event Code
+.IP "rsvd2" 12
+Reserved
+.IP "add_hw_err_info" 12
+Additional Hardware Error Information
diff --git a/doc/man/nvme_nvm_id_ns.2 b/doc/man/nvme_nvm_id_ns.2
new file mode 100644
index 0000000..c970512
--- /dev/null
+++ b/doc/man/nvme_nvm_id_ns.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_nvm_id_ns" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_nvm_id_ns \- NVME Command Set I/O Command Set Specific Identify Namespace Data Structure
+.SH SYNOPSIS
+struct nvme_nvm_id_ns {
+.br
+.BI " __le64 lbstm;"
+.br
+.BI " __u8 pic;"
+.br
+.BI " __u8 rsvd9[3];"
+.br
+.BI " __le32 elbaf[64];"
+.br
+.BI " __u8 rsvd268[3828];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "lbstm" 12
+Logical Block Storage Tag Mask
+.IP "pic" 12
+Protection Information Capabilities
+.IP "rsvd9" 12
+Reserved
+.IP "elbaf" 12
+List of Extended LBA Format Support
+.IP "rsvd268" 12
+Reserved
diff --git a/doc/man/nvme_nvm_id_ns_elbaf.2 b/doc/man/nvme_nvm_id_ns_elbaf.2
new file mode 100644
index 0000000..a43a996
--- /dev/null
+++ b/doc/man/nvme_nvm_id_ns_elbaf.2
@@ -0,0 +1,20 @@
+.TH "libnvme" 9 "enum nvme_nvm_id_ns_elbaf" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_nvm_id_ns_elbaf \- This field indicates the extended LBA format
+.SH SYNOPSIS
+enum nvme_nvm_id_ns_elbaf {
+.br
+.BI " NVME_NVM_ELBAF_STS_MASK"
+,
+.br
+.br
+.BI " NVME_NVM_ELBAF_PIF_MASK"
+
+};
+.SH Constants
+.IP "NVME_NVM_ELBAF_STS_MASK" 12
+Mask to get the storage tag size used to determine
+the variable-sized storage tag/reference tag fields
+.IP "NVME_NVM_ELBAF_PIF_MASK" 12
+Mask to get the protection information format for
+the extended LBA format.
diff --git a/doc/man/nvme_nvm_identify_ctrl.2 b/doc/man/nvme_nvm_identify_ctrl.2
new file mode 100644
index 0000000..dfca1be
--- /dev/null
+++ b/doc/man/nvme_nvm_identify_ctrl.2
@@ -0,0 +1,18 @@
+.TH "nvme_nvm_identify_ctrl" 9 "nvme_nvm_identify_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_nvm_identify_ctrl \- Identify controller data
+.SH SYNOPSIS
+.B "int" nvme_nvm_identify_ctrl
+.BI "(int fd " ","
+.BI "struct nvme_id_ctrl_nvm *id " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "id" 12
+User space destination address to transfer the data
+.SH "DESCRIPTION"
+Return an identify controller data structure to the host of
+processing controller.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_nvmeset_pl_status.2 b/doc/man/nvme_nvmeset_pl_status.2
new file mode 100644
index 0000000..c6d99bf
--- /dev/null
+++ b/doc/man/nvme_nvmeset_pl_status.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_nvmeset_pl_status" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_nvmeset_pl_status \- Predictable Latency Per NVM Set Log - Status
+.SH SYNOPSIS
+enum nvme_nvmeset_pl_status {
+.br
+.BI " NVME_NVMSET_PL_STATUS_DISABLED"
+,
+.br
+.br
+.BI " NVME_NVMSET_PL_STATUS_DTWIN"
+,
+.br
+.br
+.BI " NVME_NVMSET_PL_STATUS_NDWIN"
+
+};
+.SH Constants
+.IP "NVME_NVMSET_PL_STATUS_DISABLED" 12
+Not used (Predictable Latency Mode not enabled)
+.IP "NVME_NVMSET_PL_STATUS_DTWIN" 12
+Deterministic Window (DTWIN)
+.IP "NVME_NVMSET_PL_STATUS_NDWIN" 12
+Non-Deterministic Window (NDWIN)
diff --git a/doc/man/nvme_nvmset_attr.2 b/doc/man/nvme_nvmset_attr.2
new file mode 100644
index 0000000..f0af656
--- /dev/null
+++ b/doc/man/nvme_nvmset_attr.2
@@ -0,0 +1,46 @@
+.TH "libnvme" 9 "struct nvme_nvmset_attr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_nvmset_attr \- NVM Set Attributes Entry
+.SH SYNOPSIS
+struct nvme_nvmset_attr {
+.br
+.BI " __le16 nvmsetid;"
+.br
+.BI " __le16 endgid;"
+.br
+.BI " __u8 rsvd4[4];"
+.br
+.BI " __le32 rr4kt;"
+.br
+.BI " __le32 ows;"
+.br
+.BI " __u8 tnvmsetcap[16];"
+.br
+.BI " __u8 unvmsetcap[16];"
+.br
+.BI " __u8 rsvd48[80];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nvmsetid" 12
+NVM Set Identifier
+.IP "endgid" 12
+Endurance Group Identifier
+.IP "rsvd4" 12
+Reserved
+.IP "rr4kt" 12
+Random 4 KiB Read Typical indicates the typical
+time to complete a 4 KiB random read in 100 nanosecond units
+when the NVM Set is in a Predictable Latency Mode Deterministic
+Window and there is 1 outstanding command per NVM Set.
+.IP "ows" 12
+Optimal Write Size
+.IP "tnvmsetcap" 12
+Total NVM Set Capacity
+.IP "unvmsetcap" 12
+Unallocated NVM Set Capacity
+.IP "rsvd48" 12
+Reserved
diff --git a/doc/man/nvme_nvmset_pl_events.2 b/doc/man/nvme_nvmset_pl_events.2
new file mode 100644
index 0000000..feb1f1f
--- /dev/null
+++ b/doc/man/nvme_nvmset_pl_events.2
@@ -0,0 +1,40 @@
+.TH "libnvme" 9 "enum nvme_nvmset_pl_events" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_nvmset_pl_events \- Predictable Latency Per NVM Set Log - Event Type
+.SH SYNOPSIS
+enum nvme_nvmset_pl_events {
+.br
+.BI " NVME_NVMSET_PL_EVENT_DTWIN_READ_WARN"
+,
+.br
+.br
+.BI " NVME_NVMSET_PL_EVENT_DTWIN_WRITE_WARN"
+,
+.br
+.br
+.BI " NVME_NVMSET_PL_EVENT_DTWIN_TIME_WARN"
+,
+.br
+.br
+.BI " NVME_NVMSET_PL_EVENT_DTWIN_EXCEEDED"
+,
+.br
+.br
+.BI " NVME_NVMSET_PL_EVENT_DTWIN_EXCURSION"
+
+};
+.SH Constants
+.IP "NVME_NVMSET_PL_EVENT_DTWIN_READ_WARN" 12
+DTWIN Reads Warning
+.IP "NVME_NVMSET_PL_EVENT_DTWIN_WRITE_WARN" 12
+DTWIN Writes Warning
+.IP "NVME_NVMSET_PL_EVENT_DTWIN_TIME_WARN" 12
+DTWIN Time Warning
+.IP "NVME_NVMSET_PL_EVENT_DTWIN_EXCEEDED" 12
+Autonomous transition from DTWIN
+to NDWIN due to typical or
+maximum value exceeded
+.IP "NVME_NVMSET_PL_EVENT_DTWIN_EXCURSION" 12
+Autonomous transition from DTWIN
+to NDWIN due to Deterministic
+Excursion
diff --git a/doc/man/nvme_nvmset_predictable_lat_log.2 b/doc/man/nvme_nvmset_predictable_lat_log.2
new file mode 100644
index 0000000..fc05409
--- /dev/null
+++ b/doc/man/nvme_nvmset_predictable_lat_log.2
@@ -0,0 +1,67 @@
+.TH "libnvme" 9 "struct nvme_nvmset_predictable_lat_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_nvmset_predictable_lat_log \- Predictable Latency Mode - Deterministic Threshold Configuration Data
+.SH SYNOPSIS
+struct nvme_nvmset_predictable_lat_log {
+.br
+.BI " __u8 status;"
+.br
+.BI " __u8 rsvd1;"
+.br
+.BI " __le16 event_type;"
+.br
+.BI " __u8 rsvd4[28];"
+.br
+.BI " __le64 dtwin_rt;"
+.br
+.BI " __le64 dtwin_wt;"
+.br
+.BI " __le64 dtwin_tmax;"
+.br
+.BI " __le64 ndwin_tmin_hi;"
+.br
+.BI " __le64 ndwin_tmin_lo;"
+.br
+.BI " __u8 rsvd72[56];"
+.br
+.BI " __le64 dtwin_re;"
+.br
+.BI " __le64 dtwin_we;"
+.br
+.BI " __le64 dtwin_te;"
+.br
+.BI " __u8 rsvd152[360];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "status" 12
+Status
+.IP "rsvd1" 12
+Reserved
+.IP "event_type" 12
+Event Type
+.IP "rsvd4" 12
+Reserved
+.IP "dtwin_rt" 12
+DTWIN Reads Typical
+.IP "dtwin_wt" 12
+DTWIN Writes Typical
+.IP "dtwin_tmax" 12
+DTWIN Time Maximum
+.IP "ndwin_tmin_hi" 12
+NDWIN Time Minimum High
+.IP "ndwin_tmin_lo" 12
+NDWIN Time Minimum Low
+.IP "rsvd72" 12
+Reserved
+.IP "dtwin_re" 12
+DTWIN Reads Estimate
+.IP "dtwin_we" 12
+DTWIN Writes Estimate
+.IP "dtwin_te" 12
+DTWIN Time Estimate
+.IP "rsvd152" 12
+Reserved
diff --git a/doc/man/nvme_open.2 b/doc/man/nvme_open.2
new file mode 100644
index 0000000..56f266f
--- /dev/null
+++ b/doc/man/nvme_open.2
@@ -0,0 +1,15 @@
+.TH "nvme_open" 9 "nvme_open" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_open \- Open an nvme controller or namespace device
+.SH SYNOPSIS
+.B "int" nvme_open
+.BI "(const char *name " ");"
+.SH ARGUMENTS
+.IP "name" 12
+The basename of the device to open
+.SH "DESCRIPTION"
+This will look for the handle in /dev/ and validate the name and filetype
+match linux conventions.
+.SH "RETURN"
+A file descriptor for the device on a successful open, or -1 with
+errno set otherwise.
diff --git a/doc/man/nvme_passthru_cmd.2 b/doc/man/nvme_passthru_cmd.2
new file mode 100644
index 0000000..565bd5f
--- /dev/null
+++ b/doc/man/nvme_passthru_cmd.2
@@ -0,0 +1,83 @@
+.TH "libnvme" 9 "struct nvme_passthru_cmd" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_passthru_cmd \- nvme passthrough command structure
+.SH SYNOPSIS
+struct nvme_passthru_cmd {
+.br
+.BI " __u8 opcode;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __u16 rsvd1;"
+.br
+.BI " __u32 nsid;"
+.br
+.BI " __u32 cdw2;"
+.br
+.BI " __u32 cdw3;"
+.br
+.BI " __u64 metadata;"
+.br
+.BI " __u64 addr;"
+.br
+.BI " __u32 metadata_len;"
+.br
+.BI " __u32 data_len;"
+.br
+.BI " __u32 cdw10;"
+.br
+.BI " __u32 cdw11;"
+.br
+.BI " __u32 cdw12;"
+.br
+.BI " __u32 cdw13;"
+.br
+.BI " __u32 cdw14;"
+.br
+.BI " __u32 cdw15;"
+.br
+.BI " __u32 timeout_ms;"
+.br
+.BI " __u32 result;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "opcode" 12
+Operation code, see \fIenum nvme_io_opcodes\fP and \fIenum nvme_admin_opcodes\fP
+.IP "flags" 12
+Not supported: intended for command flags (eg: SGL, FUSE)
+.IP "rsvd1" 12
+Reserved for future use
+.IP "nsid" 12
+Namespace Identifier, or Fabrics type
+.IP "cdw2" 12
+Command Dword 2 (no spec defined use)
+.IP "cdw3" 12
+Command Dword 3 (no spec defined use)
+.IP "metadata" 12
+User space address to metadata buffer (NULL if not used)
+.IP "addr" 12
+User space address to data buffer (NULL if not used)
+.IP "metadata_len" 12
+Metadata buffer transfer length
+.IP "data_len" 12
+Data buffer transfer length
+.IP "cdw10" 12
+Command Dword 10 (command specific)
+.IP "cdw11" 12
+Command Dword 11 (command specific)
+.IP "cdw12" 12
+Command Dword 12 (command specific)
+.IP "cdw13" 12
+Command Dword 13 (command specific)
+.IP "cdw14" 12
+Command Dword 14 (command specific)
+.IP "cdw15" 12
+Command Dword 15 (command specific)
+.IP "timeout_ms" 12
+If non-zero, overrides system default timeout in milliseconds
+.IP "result" 12
+Set on completion to the command's CQE DWORD 0 controller response
diff --git a/doc/man/nvme_passthru_cmd64.2 b/doc/man/nvme_passthru_cmd64.2
new file mode 100644
index 0000000..8d8ba84
--- /dev/null
+++ b/doc/man/nvme_passthru_cmd64.2
@@ -0,0 +1,87 @@
+.TH "libnvme" 9 "struct nvme_passthru_cmd64" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_passthru_cmd64 \- 64-bit nvme passthrough command structure
+.SH SYNOPSIS
+struct nvme_passthru_cmd64 {
+.br
+.BI " __u8 opcode;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __u16 rsvd1;"
+.br
+.BI " __u32 nsid;"
+.br
+.BI " __u32 cdw2;"
+.br
+.BI " __u32 cdw3;"
+.br
+.BI " __u64 metadata;"
+.br
+.BI " __u64 addr;"
+.br
+.BI " __u32 metadata_len;"
+.br
+.BI " __u32 data_len;"
+.br
+.BI " __u32 cdw10;"
+.br
+.BI " __u32 cdw11;"
+.br
+.BI " __u32 cdw12;"
+.br
+.BI " __u32 cdw13;"
+.br
+.BI " __u32 cdw14;"
+.br
+.BI " __u32 cdw15;"
+.br
+.BI " __u32 timeout_ms;"
+.br
+.BI " __u32 rsvd2;"
+.br
+.BI " __u64 result;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "opcode" 12
+Operation code, see \fIenum nvme_io_opcodes\fP and \fIenum nvme_admin_opcodes\fP
+.IP "flags" 12
+Not supported: intended for command flags (eg: SGL, FUSE)
+.IP "rsvd1" 12
+Reserved for future use
+.IP "nsid" 12
+Namespace Identifier, or Fabrics type
+.IP "cdw2" 12
+Command Dword 2 (no spec defined use)
+.IP "cdw3" 12
+Command Dword 3 (no spec defined use)
+.IP "metadata" 12
+User space address to metadata buffer (NULL if not used)
+.IP "addr" 12
+User space address to data buffer (NULL if not used)
+.IP "metadata_len" 12
+Metadata buffer transfer length
+.IP "data_len" 12
+Data buffer transfer length
+.IP "cdw10" 12
+Command Dword 10 (command specific)
+.IP "cdw11" 12
+Command Dword 11 (command specific)
+.IP "cdw12" 12
+Command Dword 12 (command specific)
+.IP "cdw13" 12
+Command Dword 13 (command specific)
+.IP "cdw14" 12
+Command Dword 14 (command specific)
+.IP "cdw15" 12
+Command Dword 15 (command specific)
+.IP "timeout_ms" 12
+If non-zero, overrides system default timeout in milliseconds
+.IP "rsvd2" 12
+Reserved for future use (and fills an implicit struct pad
+.IP "result" 12
+Set on completion to the command's CQE DWORD 0-1 controller response
diff --git a/doc/man/nvme_path_get_ana_state.2 b/doc/man/nvme_path_get_ana_state.2
new file mode 100644
index 0000000..1ff2cc2
--- /dev/null
+++ b/doc/man/nvme_path_get_ana_state.2
@@ -0,0 +1,11 @@
+.TH "nvme_path_get_ana_state" 9 "nvme_path_get_ana_state" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_path_get_ana_state \- ANA state of an nvme_path_t object
+.SH SYNOPSIS
+.B "const char *" nvme_path_get_ana_state
+.BI "(nvme_path_t p " ");"
+.SH ARGUMENTS
+.IP "p" 12
+\fInvme_path_t\fP object
+.SH "RETURN"
+ANA (Asynchronous Namespace Access) state of \fIp\fP
diff --git a/doc/man/nvme_path_get_ctrl.2 b/doc/man/nvme_path_get_ctrl.2
new file mode 100644
index 0000000..4b4bb78
--- /dev/null
+++ b/doc/man/nvme_path_get_ctrl.2
@@ -0,0 +1,11 @@
+.TH "nvme_path_get_ctrl" 9 "nvme_path_get_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_path_get_ctrl \- Parent controller of an nvme_path_t object
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvme_path_get_ctrl
+.BI "(nvme_path_t p " ");"
+.SH ARGUMENTS
+.IP "p" 12
+\fInvme_path_t\fP object
+.SH "RETURN"
+Parent controller if present
diff --git a/doc/man/nvme_path_get_name.2 b/doc/man/nvme_path_get_name.2
new file mode 100644
index 0000000..e335a98
--- /dev/null
+++ b/doc/man/nvme_path_get_name.2
@@ -0,0 +1,11 @@
+.TH "nvme_path_get_name" 9 "nvme_path_get_name" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_path_get_name \- sysfs name of an &nvme_path_t object
+.SH SYNOPSIS
+.B "const char *" nvme_path_get_name
+.BI "(nvme_path_t p " ");"
+.SH ARGUMENTS
+.IP "p" 12
+\fInvme_path_t\fP object
+.SH "RETURN"
+sysfs name of \fIp\fP
diff --git a/doc/man/nvme_path_get_ns.2 b/doc/man/nvme_path_get_ns.2
new file mode 100644
index 0000000..318162b
--- /dev/null
+++ b/doc/man/nvme_path_get_ns.2
@@ -0,0 +1,11 @@
+.TH "nvme_path_get_ns" 9 "nvme_path_get_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_path_get_ns \- Parent namespace of an nvme_path_t object
+.SH SYNOPSIS
+.B "nvme_ns_t" nvme_path_get_ns
+.BI "(nvme_path_t p " ");"
+.SH ARGUMENTS
+.IP "p" 12
+\fInvme_path_t\fP object
+.SH "RETURN"
+Parent namespace if present
diff --git a/doc/man/nvme_path_get_sysfs_dir.2 b/doc/man/nvme_path_get_sysfs_dir.2
new file mode 100644
index 0000000..b7a3f07
--- /dev/null
+++ b/doc/man/nvme_path_get_sysfs_dir.2
@@ -0,0 +1,11 @@
+.TH "nvme_path_get_sysfs_dir" 9 "nvme_path_get_sysfs_dir" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_path_get_sysfs_dir \- sysfs directory of an nvme_path_t object
+.SH SYNOPSIS
+.B "const char *" nvme_path_get_sysfs_dir
+.BI "(nvme_path_t p " ");"
+.SH ARGUMENTS
+.IP "p" 12
+\fInvme_path_t\fP object
+.SH "RETURN"
+sysfs directory of \fIp\fP
diff --git a/doc/man/nvme_paths_filter.2 b/doc/man/nvme_paths_filter.2
new file mode 100644
index 0000000..2b862da
--- /dev/null
+++ b/doc/man/nvme_paths_filter.2
@@ -0,0 +1,11 @@
+.TH "nvme_paths_filter" 9 "nvme_paths_filter" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_paths_filter \- Filter for paths
+.SH SYNOPSIS
+.B "int" nvme_paths_filter
+.BI "(const struct dirent *d " ");"
+.SH ARGUMENTS
+.IP "d" 12
+dirent to check
+.SH "RETURN"
+1 if \fId\fP matches, 0 otherwise
diff --git a/doc/man/nvme_persistent_event_entry.2 b/doc/man/nvme_persistent_event_entry.2
new file mode 100644
index 0000000..8a47c92
--- /dev/null
+++ b/doc/man/nvme_persistent_event_entry.2
@@ -0,0 +1,51 @@
+.TH "libnvme" 9 "struct nvme_persistent_event_entry" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_persistent_event_entry \- Persistent Event
+.SH SYNOPSIS
+struct nvme_persistent_event_entry {
+.br
+.BI " __u8 etype;"
+.br
+.BI " __u8 etype_rev;"
+.br
+.BI " __u8 ehl;"
+.br
+.BI " __u8 ehai;"
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " __le64 ets;"
+.br
+.BI " __le16 pelpid;"
+.br
+.BI " __u8 rsvd16[4];"
+.br
+.BI " __le16 vsil;"
+.br
+.BI " __le16 el;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "etype" 12
+Event Type
+.IP "etype_rev" 12
+Event Type Revision
+.IP "ehl" 12
+Event Header Length
+.IP "ehai" 12
+Event Header Additional Info
+.IP "cntlid" 12
+Controller Identifier
+.IP "ets" 12
+Event Timestamp
+.IP "pelpid" 12
+Port Identifier
+.IP "rsvd16" 12
+Reserved
+.IP "vsil" 12
+Vendor Specific Information Length
+.IP "el" 12
+Event Length
diff --git a/doc/man/nvme_persistent_event_log.2 b/doc/man/nvme_persistent_event_log.2
new file mode 100644
index 0000000..a8dc951
--- /dev/null
+++ b/doc/man/nvme_persistent_event_log.2
@@ -0,0 +1,87 @@
+.TH "libnvme" 9 "struct nvme_persistent_event_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_persistent_event_log \- Persistent Event Log
+.SH SYNOPSIS
+struct nvme_persistent_event_log {
+.br
+.BI " __u8 lid;"
+.br
+.BI " __u8 rsvd1[3];"
+.br
+.BI " __le32 tnev;"
+.br
+.BI " __le64 tll;"
+.br
+.BI " __u8 rv;"
+.br
+.BI " __u8 rsvd17;"
+.br
+.BI " __le16 lhl;"
+.br
+.BI " __le64 ts;"
+.br
+.BI " __u8 poh[16];"
+.br
+.BI " __le64 pcc;"
+.br
+.BI " __le16 vid;"
+.br
+.BI " __le16 ssvid;"
+.br
+.BI " char sn[20];"
+.br
+.BI " char mn[40];"
+.br
+.BI " char subnqn[NVME_NQN_LENGTH];"
+.br
+.BI " __le16 gen_number;"
+.br
+.BI " __le32 rci;"
+.br
+.BI " __u8 rsvd378[102];"
+.br
+.BI " __u8 seb[32];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "lid" 12
+Log Identifier
+.IP "rsvd1" 12
+Reserved
+.IP "tnev" 12
+Total Number of Events
+.IP "tll" 12
+Total Log Length
+.IP "rv" 12
+Log Revision
+.IP "rsvd17" 12
+Reserved
+.IP "lhl" 12
+Log Header Length
+.IP "ts" 12
+Timestamp
+.IP "poh" 12
+Power on Hours
+.IP "pcc" 12
+Power Cycle Count
+.IP "vid" 12
+PCI Vendor ID
+.IP "ssvid" 12
+PCI Subsystem Vendor ID
+.IP "sn" 12
+Serial Number
+.IP "mn" 12
+Model Number
+.IP "subnqn" 12
+NVM Subsystem NVMe Qualified Name
+.IP "gen_number" 12
+Generation Number
+.IP "rci" 12
+Reporting Context Information
+.IP "rsvd378" 12
+Reserved
+.IP "seb" 12
+Supported Events Bitmap
diff --git a/doc/man/nvme_persistent_event_types.2 b/doc/man/nvme_persistent_event_types.2
new file mode 100644
index 0000000..ea6b6d2
--- /dev/null
+++ b/doc/man/nvme_persistent_event_types.2
@@ -0,0 +1,84 @@
+.TH "libnvme" 9 "enum nvme_persistent_event_types" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_persistent_event_types \- Persistent event log events
+.SH SYNOPSIS
+enum nvme_persistent_event_types {
+.br
+.BI " NVME_PEL_SMART_HEALTH_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_FW_COMMIT_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_TIMESTAMP_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_POWER_ON_RESET_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_NSS_HW_ERROR_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_CHANGE_NS_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_FORMAT_START_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_FORMAT_COMPLETION_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_SANITIZE_START_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_SANITIZE_COMPLETION_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_SET_FEATURE_EVENT"
+,
+.br
+.br
+.BI " NVME_PEL_TELEMETRY_CRT"
+,
+.br
+.br
+.BI " NVME_PEL_THERMAL_EXCURSION_EVENT"
+
+};
+.SH Constants
+.IP "NVME_PEL_SMART_HEALTH_EVENT" 12
+SMART / Health Log Snapshot Event
+.IP "NVME_PEL_FW_COMMIT_EVENT" 12
+Firmware Commit Event
+.IP "NVME_PEL_TIMESTAMP_EVENT" 12
+Timestamp Change Event
+.IP "NVME_PEL_POWER_ON_RESET_EVENT" 12
+Power-on or Reset Event
+.IP "NVME_PEL_NSS_HW_ERROR_EVENT" 12
+NVM Subsystem Hardware Error Event
+.IP "NVME_PEL_CHANGE_NS_EVENT" 12
+Change Namespace Event
+.IP "NVME_PEL_FORMAT_START_EVENT" 12
+Format NVM Start Event
+.IP "NVME_PEL_FORMAT_COMPLETION_EVENT" 12
+Format NVM Completion Event
+.IP "NVME_PEL_SANITIZE_START_EVENT" 12
+Sanitize Start Event
+.IP "NVME_PEL_SANITIZE_COMPLETION_EVENT" 12
+Sanitize Completion Event
+.IP "NVME_PEL_SET_FEATURE_EVENT" 12
+Set Feature Event
+.IP "NVME_PEL_TELEMETRY_CRT" 12
+Telemetry Log Create Event
+.IP "NVME_PEL_THERMAL_EXCURSION_EVENT" 12
+Thermal Excursion Event
diff --git a/doc/man/nvme_pevent_log_action.2 b/doc/man/nvme_pevent_log_action.2
new file mode 100644
index 0000000..2e09053
--- /dev/null
+++ b/doc/man/nvme_pevent_log_action.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_pevent_log_action" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_pevent_log_action \- Persistent Event Log - Action
+.SH SYNOPSIS
+enum nvme_pevent_log_action {
+.br
+.BI " NVME_PEVENT_LOG_READ"
+,
+.br
+.br
+.BI " NVME_PEVENT_LOG_EST_CTX_AND_READ"
+,
+.br
+.br
+.BI " NVME_PEVENT_LOG_RELEASE_CTX"
+
+};
+.SH Constants
+.IP "NVME_PEVENT_LOG_READ" 12
+Read Log Data
+.IP "NVME_PEVENT_LOG_EST_CTX_AND_READ" 12
+Establish Context and Read Log Data
+.IP "NVME_PEVENT_LOG_RELEASE_CTX" 12
+Release Context
diff --git a/doc/man/nvme_phy_rx_eom_log.2 b/doc/man/nvme_phy_rx_eom_log.2
new file mode 100644
index 0000000..a90bac2
--- /dev/null
+++ b/doc/man/nvme_phy_rx_eom_log.2
@@ -0,0 +1,99 @@
+.TH "libnvme" 9 "struct nvme_phy_rx_eom_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_phy_rx_eom_log \- Physical Interface Receiver Eye Opening Measurement Log
+.SH SYNOPSIS
+struct nvme_phy_rx_eom_log {
+.br
+.BI " __u8 lid;"
+.br
+.BI " __u8 eomip;"
+.br
+.BI " __le16 hsize;"
+.br
+.BI " __le32 rsize;"
+.br
+.BI " __u8 eomdgn;"
+.br
+.BI " __u8 lr;"
+.br
+.BI " __u8 odp;"
+.br
+.BI " __u8 lanes;"
+.br
+.BI " __u8 epl;"
+.br
+.BI " __u8 lspfc;"
+.br
+.BI " __u8 li;"
+.br
+.BI " __u8 rsvd15[3];"
+.br
+.BI " __le16 lsic;"
+.br
+.BI " __le32 dsize;"
+.br
+.BI " __le16 nd;"
+.br
+.BI " __le16 maxtb;"
+.br
+.BI " __le16 maxlr;"
+.br
+.BI " __le16 etgood;"
+.br
+.BI " __le16 etbetter;"
+.br
+.BI " __le16 etbest;"
+.br
+.BI " __u8 rsvd36[28];"
+.br
+.BI " struct nvme_eom_lane_desc descs[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "lid" 12
+Log Identifier
+.IP "eomip" 12
+EOM In Progress
+.IP "hsize" 12
+Header Size
+.IP "rsize" 12
+Result Size
+.IP "eomdgn" 12
+EOM Data Generation Number
+.IP "lr" 12
+Log Revision
+.IP "odp" 12
+Optional Data Present
+.IP "lanes" 12
+Number of lanes configured for this port
+.IP "epl" 12
+Eyes Per Lane
+.IP "lspfc" 12
+Log Specific Parameter Field Copy
+.IP "li" 12
+Link Information
+.IP "rsvd15" 12
+Reserved
+.IP "lsic" 12
+Log Specific Identifier Copy
+.IP "dsize" 12
+Descriptor Size
+.IP "nd" 12
+Number of Descriptors
+.IP "maxtb" 12
+Maximum Top Bottom
+.IP "maxlr" 12
+Maximum Left Right
+.IP "etgood" 12
+Estimated Time for Good Quality
+.IP "etbetter" 12
+Estimated Time for Better Quality
+.IP "etbest" 12
+Estimated Time for Best Quality
+.IP "rsvd36" 12
+Reserved
+.IP "descs" 12
+EOM Lane Descriptors
diff --git a/doc/man/nvme_phy_rx_eom_progress.2 b/doc/man/nvme_phy_rx_eom_progress.2
new file mode 100644
index 0000000..6fcc680
--- /dev/null
+++ b/doc/man/nvme_phy_rx_eom_progress.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_phy_rx_eom_progress" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_phy_rx_eom_progress \- EOM In Progress Values
+.SH SYNOPSIS
+enum nvme_phy_rx_eom_progress {
+.br
+.BI " NVME_PHY_RX_EOM_NOT_STARTED"
+,
+.br
+.br
+.BI " NVME_PHY_RX_EOM_IN_PROGRESS"
+,
+.br
+.br
+.BI " NVME_PHY_RX_EOM_COMPLETED"
+
+};
+.SH Constants
+.IP "NVME_PHY_RX_EOM_NOT_STARTED" 12
+EOM Not Started
+.IP "NVME_PHY_RX_EOM_IN_PROGRESS" 12
+EOM In Progress
+.IP "NVME_PHY_RX_EOM_COMPLETED" 12
+EOM Completed
diff --git a/doc/man/nvme_plm_config.2 b/doc/man/nvme_plm_config.2
new file mode 100644
index 0000000..ec3f1a8
--- /dev/null
+++ b/doc/man/nvme_plm_config.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_plm_config" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_plm_config \- Predictable Latency Mode - Deterministic Threshold Configuration Data Structure
+.SH SYNOPSIS
+struct nvme_plm_config {
+.br
+.BI " __le16 ee;"
+.br
+.BI " __u8 rsvd2[30];"
+.br
+.BI " __le64 dtwinrt;"
+.br
+.BI " __le64 dtwinwt;"
+.br
+.BI " __le64 dtwintt;"
+.br
+.BI " __u8 rsvd56[456];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "ee" 12
+Enable Event
+.IP "rsvd2" 12
+Reserved
+.IP "dtwinrt" 12
+DTWIN Reads Threshold
+.IP "dtwinwt" 12
+DTWIN Writes Threshold
+.IP "dtwintt" 12
+DTWIN Time Threshold
+.IP "rsvd56" 12
+Reserved
diff --git a/doc/man/nvme_pmr_size.2 b/doc/man/nvme_pmr_size.2
new file mode 100644
index 0000000..8ca8b55
--- /dev/null
+++ b/doc/man/nvme_pmr_size.2
@@ -0,0 +1,11 @@
+.TH "nvme_pmr_size" 9 "nvme_pmr_size" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_pmr_size \- Calculate size of persistent memory region elasticity buffer
+.SH SYNOPSIS
+.B "__u64" nvme_pmr_size
+.BI "(__u32 pmrebs " ");"
+.SH ARGUMENTS
+.IP "pmrebs" 12
+Value from controller register NVME_REG_PMREBS
+.SH "RETURN"
+size of controller persistent memory buffer in bytes
diff --git a/doc/man/nvme_pmr_throughput.2 b/doc/man/nvme_pmr_throughput.2
new file mode 100644
index 0000000..115c54c
--- /dev/null
+++ b/doc/man/nvme_pmr_throughput.2
@@ -0,0 +1,11 @@
+.TH "nvme_pmr_throughput" 9 "nvme_pmr_throughput" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_pmr_throughput \- Calculate throughput of persistent memory buffer
+.SH SYNOPSIS
+.B "__u64" nvme_pmr_throughput
+.BI "(__u32 pmrswtp " ");"
+.SH ARGUMENTS
+.IP "pmrswtp" 12
+Value from controller register NVME_REG_PMRSWTP
+.SH "RETURN"
+throughput of controller persistent memory buffer in bytes/second
diff --git a/doc/man/nvme_power_on_reset_info_list.2 b/doc/man/nvme_power_on_reset_info_list.2
new file mode 100644
index 0000000..89797e7
--- /dev/null
+++ b/doc/man/nvme_power_on_reset_info_list.2
@@ -0,0 +1,39 @@
+.TH "libnvme" 9 "struct nvme_power_on_reset_info_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_power_on_reset_info_list \- Controller Reset Information
+.SH SYNOPSIS
+struct nvme_power_on_reset_info_list {
+.br
+.BI " __le16 cid;"
+.br
+.BI " __u8 fw_act;"
+.br
+.BI " __u8 op_in_prog;"
+.br
+.BI " __u8 rsvd4[12];"
+.br
+.BI " __le32 ctrl_power_cycle;"
+.br
+.BI " __le64 power_on_ml_seconds;"
+.br
+.BI " __le64 ctrl_time_stamp;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "cid" 12
+Controller ID
+.IP "fw_act" 12
+Firmware Activation
+.IP "op_in_prog" 12
+Operation in Progress
+.IP "rsvd4" 12
+Reserved
+.IP "ctrl_power_cycle" 12
+Controller Power Cycle
+.IP "power_on_ml_seconds" 12
+Power on milliseconds
+.IP "ctrl_time_stamp" 12
+Controller Timestamp
diff --git a/doc/man/nvme_primary_ctrl_cap.2 b/doc/man/nvme_primary_ctrl_cap.2
new file mode 100644
index 0000000..b1bd3eb
--- /dev/null
+++ b/doc/man/nvme_primary_ctrl_cap.2
@@ -0,0 +1,83 @@
+.TH "libnvme" 9 "struct nvme_primary_ctrl_cap" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_primary_ctrl_cap \- Identify - Controller Capabilities Structure
+.SH SYNOPSIS
+struct nvme_primary_ctrl_cap {
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " __le16 portid;"
+.br
+.BI " __u8 crt;"
+.br
+.BI " __u8 rsvd5[27];"
+.br
+.BI " __le32 vqfrt;"
+.br
+.BI " __le32 vqrfa;"
+.br
+.BI " __le16 vqrfap;"
+.br
+.BI " __le16 vqprt;"
+.br
+.BI " __le16 vqfrsm;"
+.br
+.BI " __le16 vqgran;"
+.br
+.BI " __u8 rsvd48[16];"
+.br
+.BI " __le32 vifrt;"
+.br
+.BI " __le32 virfa;"
+.br
+.BI " __le16 virfap;"
+.br
+.BI " __le16 viprt;"
+.br
+.BI " __le16 vifrsm;"
+.br
+.BI " __le16 vigran;"
+.br
+.BI " __u8 rsvd80[4016];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "cntlid" 12
+Controller Identifier
+.IP "portid" 12
+Port Identifier
+.IP "crt" 12
+Controller Resource Types
+.IP "rsvd5" 12
+reserved
+.IP "vqfrt" 12
+VQ Resources Flexible Total
+.IP "vqrfa" 12
+VQ Resources Flexible Assigned
+.IP "vqrfap" 12
+VQ Resources Flexible Allocated to Primary
+.IP "vqprt" 12
+VQ Resources Private Total
+.IP "vqfrsm" 12
+VQ Resources Flexible Secondary Maximum
+.IP "vqgran" 12
+VQ Flexible Resource Preferred Granularity
+.IP "rsvd48" 12
+reserved
+.IP "vifrt" 12
+VI Resources Flexible Total
+.IP "virfa" 12
+VI Resources Flexible Assigned
+.IP "virfap" 12
+VI Resources Flexible Allocated to Primary
+.IP "viprt" 12
+VI Resources Private Total
+.IP "vifrsm" 12
+VI Resources Flexible Secondary Maximum
+.IP "vigran" 12
+VI Flexible Resource Preferred Granularity
+.IP "rsvd80" 12
+reserved
diff --git a/doc/man/nvme_psd_flags.2 b/doc/man/nvme_psd_flags.2
new file mode 100644
index 0000000..6f2fec9
--- /dev/null
+++ b/doc/man/nvme_psd_flags.2
@@ -0,0 +1,26 @@
+.TH "libnvme" 9 "enum nvme_psd_flags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_psd_flags \- Possible flag values in nvme power state descriptor
+.SH SYNOPSIS
+enum nvme_psd_flags {
+.br
+.BI " NVME_PSD_FLAGS_MXPS"
+,
+.br
+.br
+.BI " NVME_PSD_FLAGS_NOPS"
+
+};
+.SH Constants
+.IP "NVME_PSD_FLAGS_MXPS" 12
+Indicates the scale for the Maximum Power
+field. If this bit is cleared, then the scale of the
+Maximum Power field is in 0.01 Watts. If this bit is
+set, then the scale of the Maximum Power field is in
+0.0001 Watts.
+.IP "NVME_PSD_FLAGS_NOPS" 12
+Indicates whether the controller processes I/O
+commands in this power state. If this bit is cleared,
+then the controller processes I/O commands in this
+power state. If this bit is set, then the controller
+does not process I/O commands in this power state.
diff --git a/doc/man/nvme_psd_power_scale.2 b/doc/man/nvme_psd_power_scale.2
new file mode 100644
index 0000000..216728f
--- /dev/null
+++ b/doc/man/nvme_psd_power_scale.2
@@ -0,0 +1,11 @@
+.TH "nvme_psd_power_scale" 9 "nvme_psd_power_scale" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_psd_power_scale \- power scale occupies the upper 3 bits
+.SH SYNOPSIS
+.B "unsigned int" nvme_psd_power_scale
+.BI "(__u8 ps " ");"
+.SH ARGUMENTS
+.IP "ps" 12
+power scale value
+.SH "RETURN"
+power scale value
diff --git a/doc/man/nvme_psd_ps.2 b/doc/man/nvme_psd_ps.2
new file mode 100644
index 0000000..1e80665
--- /dev/null
+++ b/doc/man/nvme_psd_ps.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_psd_ps" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_psd_ps \- Known values for &struct nvme_psd %ips and %aps. Use with nvme_psd_power_scale() to extract the power scale field to match this enum.
+.SH SYNOPSIS
+enum nvme_psd_ps {
+.br
+.BI " NVME_PSD_PS_NOT_REPORTED"
+,
+.br
+.br
+.BI " NVME_PSD_PS_100_MICRO_WATT"
+,
+.br
+.br
+.BI " NVME_PSD_PS_10_MILLI_WATT"
+
+};
+.SH Constants
+.IP "NVME_PSD_PS_NOT_REPORTED" 12
+Not reported
+.IP "NVME_PSD_PS_100_MICRO_WATT" 12
+0.0001 watt scale
+.IP "NVME_PSD_PS_10_MILLI_WATT" 12
+0.01 watt scale
diff --git a/doc/man/nvme_psd_workload.2 b/doc/man/nvme_psd_workload.2
new file mode 100644
index 0000000..214667e
--- /dev/null
+++ b/doc/man/nvme_psd_workload.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "enum nvme_psd_workload" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_psd_workload \- Specifies a workload hint in the Power Management Feature (see &struct nvme_psd.apw) to inform the NVM subsystem or indicate the conditions for the active power level.
+.SH SYNOPSIS
+enum nvme_psd_workload {
+.br
+.BI " NVME_PSD_WORKLOAD_NP"
+,
+.br
+.br
+.BI " NVME_PSD_WORKLOAD_1"
+,
+.br
+.br
+.BI " NVME_PSD_WORKLOAD_2"
+
+};
+.SH Constants
+.IP "NVME_PSD_WORKLOAD_NP" 12
+The workload is unknown or not provided.
+.IP "NVME_PSD_WORKLOAD_1" 12
+Extended Idle Period with a Burst of Random Write
+consists of five minutes of idle followed by
+thirty-two random write commands of size 1 MiB
+submitted to a single controller while all other
+controllers in the NVM subsystem are idle, and then
+thirty (30) seconds of idle.
+.IP "NVME_PSD_WORKLOAD_2" 12
+Heavy Sequential Writes consists of 80,000
+sequential write commands of size 128 KiB submitted to
+a single controller while all other controllers in the
+NVM subsystem are idle. The submission queue(s)
+should be sufficiently large allowing the host to
+ensure there are multiple commands pending at all
+times during the workload.
diff --git a/doc/man/nvme_read.2 b/doc/man/nvme_read.2
new file mode 100644
index 0000000..28415c7
--- /dev/null
+++ b/doc/man/nvme_read.2
@@ -0,0 +1,12 @@
+.TH "nvme_read" 9 "nvme_read" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_read \- Submit an nvme user read command
+.SH SYNOPSIS
+.B "int" nvme_read
+.BI "(struct nvme_io_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_read_config.2 b/doc/man/nvme_read_config.2
new file mode 100644
index 0000000..2647839
--- /dev/null
+++ b/doc/man/nvme_read_config.2
@@ -0,0 +1,17 @@
+.TH "nvme_read_config" 9 "nvme_read_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_read_config \- Read NVMe JSON configuration file
+.SH SYNOPSIS
+.B "int" nvme_read_config
+.BI "(nvme_root_t r " ","
+.BI "const char *config_file " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t object
+.IP "config_file" 12
+JSON configuration file
+.SH "DESCRIPTION"
+Read in the contents of \fIconfig_file\fP and merge them with
+the elements in \fIr\fP.
+.SH "RETURN"
+0 on success, -1 on failure with errno set.
diff --git a/doc/man/nvme_refresh_topology.2 b/doc/man/nvme_refresh_topology.2
new file mode 100644
index 0000000..465a824
--- /dev/null
+++ b/doc/man/nvme_refresh_topology.2
@@ -0,0 +1,11 @@
+.TH "nvme_refresh_topology" 9 "nvme_refresh_topology" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_refresh_topology \- Refresh nvme_root_t object contents
+.SH SYNOPSIS
+.B "void" nvme_refresh_topology
+.BI "(nvme_root_t r " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t object
+.SH "DESCRIPTION"
+Removes all elements in \fIr\fP and rescans the existing topology.
diff --git a/doc/man/nvme_register_offsets.2 b/doc/man/nvme_register_offsets.2
new file mode 100644
index 0000000..692443b
--- /dev/null
+++ b/doc/man/nvme_register_offsets.2
@@ -0,0 +1,174 @@
+.TH "libnvme" 9 "enum nvme_register_offsets" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_register_offsets \- controller registers for all transports. This is the layout of BAR0/1 for PCIe, and properties for fabrics.
+.SH SYNOPSIS
+enum nvme_register_offsets {
+.br
+.BI " NVME_REG_CAP"
+,
+.br
+.br
+.BI " NVME_REG_VS"
+,
+.br
+.br
+.BI " NVME_REG_INTMS"
+,
+.br
+.br
+.BI " NVME_REG_INTMC"
+,
+.br
+.br
+.BI " NVME_REG_CC"
+,
+.br
+.br
+.BI " NVME_REG_CSTS"
+,
+.br
+.br
+.BI " NVME_REG_NSSR"
+,
+.br
+.br
+.BI " NVME_REG_AQA"
+,
+.br
+.br
+.BI " NVME_REG_ASQ"
+,
+.br
+.br
+.BI " NVME_REG_ACQ"
+,
+.br
+.br
+.BI " NVME_REG_CMBLOC"
+,
+.br
+.br
+.BI " NVME_REG_CMBSZ"
+,
+.br
+.br
+.BI " NVME_REG_BPINFO"
+,
+.br
+.br
+.BI " NVME_REG_BPRSEL"
+,
+.br
+.br
+.BI " NVME_REG_BPMBL"
+,
+.br
+.br
+.BI " NVME_REG_CMBMSC"
+,
+.br
+.br
+.BI " NVME_REG_CMBSTS"
+,
+.br
+.br
+.BI " NVME_REG_CMBEBS"
+,
+.br
+.br
+.BI " NVME_REG_CMBSWTP"
+,
+.br
+.br
+.BI " NVME_REG_NSSD"
+,
+.br
+.br
+.BI " NVME_REG_CRTO"
+,
+.br
+.br
+.BI " NVME_REG_PMRCAP"
+,
+.br
+.br
+.BI " NVME_REG_PMRCTL"
+,
+.br
+.br
+.BI " NVME_REG_PMRSTS"
+,
+.br
+.br
+.BI " NVME_REG_PMREBS"
+,
+.br
+.br
+.BI " NVME_REG_PMRSWTP"
+,
+.br
+.br
+.BI " NVME_REG_PMRMSCL"
+,
+.br
+.br
+.BI " NVME_REG_PMRMSCU"
+
+};
+.SH Constants
+.IP "NVME_REG_CAP" 12
+Controller Capabilities
+.IP "NVME_REG_VS" 12
+Version
+.IP "NVME_REG_INTMS" 12
+Interrupt Mask Set
+.IP "NVME_REG_INTMC" 12
+Interrupt Mask Clear
+.IP "NVME_REG_CC" 12
+Controller Configuration
+.IP "NVME_REG_CSTS" 12
+Controller Status
+.IP "NVME_REG_NSSR" 12
+NVM Subsystem Reset
+.IP "NVME_REG_AQA" 12
+Admin Queue Attributes
+.IP "NVME_REG_ASQ" 12
+Admin SQ Base Address
+.IP "NVME_REG_ACQ" 12
+Admin CQ Base Address
+.IP "NVME_REG_CMBLOC" 12
+Controller Memory Buffer Location
+.IP "NVME_REG_CMBSZ" 12
+Controller Memory Buffer Size
+.IP "NVME_REG_BPINFO" 12
+Boot Partition Information
+.IP "NVME_REG_BPRSEL" 12
+Boot Partition Read Select
+.IP "NVME_REG_BPMBL" 12
+Boot Partition Memory Buffer Location
+.IP "NVME_REG_CMBMSC" 12
+Controller Memory Buffer Memory Space Control
+.IP "NVME_REG_CMBSTS" 12
+Controller Memory Buffer Status
+.IP "NVME_REG_CMBEBS" 12
+Controller Memory Buffer Elasticity Buffer Size
+.IP "NVME_REG_CMBSWTP" 12
+Controller Memory Buffer Sustained Write Throughput
+.IP "NVME_REG_NSSD" 12
+NVM Subsystem Shutdown
+.IP "NVME_REG_CRTO" 12
+Controller Ready Timeouts
+.IP "NVME_REG_PMRCAP" 12
+Persistent Memory Capabilities
+.IP "NVME_REG_PMRCTL" 12
+Persistent Memory Region Control
+.IP "NVME_REG_PMRSTS" 12
+Persistent Memory Region Status
+.IP "NVME_REG_PMREBS" 12
+Persistent Memory Region Elasticity Buffer Size
+.IP "NVME_REG_PMRSWTP" 12
+Memory Region Sustained Write Throughput
+.IP "NVME_REG_PMRMSCL" 12
+Persistent Memory Region Controller Memory Space Control Lower
+.IP "NVME_REG_PMRMSCU" 12
+Persistent Memory Region Controller Memory Space Control Upper
diff --git a/doc/man/nvme_registered_ctrl.2 b/doc/man/nvme_registered_ctrl.2
new file mode 100644
index 0000000..ea8a9fd
--- /dev/null
+++ b/doc/man/nvme_registered_ctrl.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "struct nvme_registered_ctrl" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_registered_ctrl \- Registered Controller Data Structure
+.SH SYNOPSIS
+struct nvme_registered_ctrl {
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " __u8 rcsts;"
+.br
+.BI " __u8 rsvd3[5];"
+.br
+.BI " __le64 hostid;"
+.br
+.BI " __le64 rkey;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "cntlid" 12
+Controller ID
+.IP "rcsts" 12
+Reservation Status
+.IP "rsvd3" 12
+Reserved
+.IP "hostid" 12
+Host Identifier
+.IP "rkey" 12
+Reservation Key
diff --git a/doc/man/nvme_registered_ctrl_ext.2 b/doc/man/nvme_registered_ctrl_ext.2
new file mode 100644
index 0000000..e48dc7b
--- /dev/null
+++ b/doc/man/nvme_registered_ctrl_ext.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_registered_ctrl_ext" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_registered_ctrl_ext \- Registered Controller Extended Data Structure
+.SH SYNOPSIS
+struct nvme_registered_ctrl_ext {
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " __u8 rcsts;"
+.br
+.BI " __u8 rsvd3[5];"
+.br
+.BI " __le64 rkey;"
+.br
+.BI " __u8 hostid[16];"
+.br
+.BI " __u8 rsvd32[32];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "cntlid" 12
+Controller ID
+.IP "rcsts" 12
+Reservation Status
+.IP "rsvd3" 12
+Reserved
+.IP "rkey" 12
+Reservation Key
+.IP "hostid" 12
+Host Identifier
+.IP "rsvd32" 12
+Reserved
diff --git a/doc/man/nvme_rescan_ctrl.2 b/doc/man/nvme_rescan_ctrl.2
new file mode 100644
index 0000000..0466205
--- /dev/null
+++ b/doc/man/nvme_rescan_ctrl.2
@@ -0,0 +1,9 @@
+.TH "nvme_rescan_ctrl" 9 "nvme_rescan_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_rescan_ctrl \- Rescan an existing controller
+.SH SYNOPSIS
+.B "void" nvme_rescan_ctrl
+.BI "(nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
diff --git a/doc/man/nvme_resv_acquire.2 b/doc/man/nvme_resv_acquire.2
new file mode 100644
index 0000000..310b645
--- /dev/null
+++ b/doc/man/nvme_resv_acquire.2
@@ -0,0 +1,16 @@
+.TH "nvme_resv_acquire" 9 "nvme_resv_acquire" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_resv_acquire \- Send an nvme reservation acquire
+.SH SYNOPSIS
+.B "int" nvme_resv_acquire
+.BI "(struct nvme_resv_acquire_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_resv_acquire\fP argument structure
+.SH "DESCRIPTION"
+The Reservation Acquire command acquires a reservation on a namespace,
+preempt a reservation held on a namespace, and abort a reservation held on a
+namespace.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_resv_cptpl.2 b/doc/man/nvme_resv_cptpl.2
new file mode 100644
index 0000000..074e5f7
--- /dev/null
+++ b/doc/man/nvme_resv_cptpl.2
@@ -0,0 +1,26 @@
+.TH "libnvme" 9 "enum nvme_resv_cptpl" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_resv_cptpl \- Reservation Register - Change Persist Through Power Loss State
+.SH SYNOPSIS
+enum nvme_resv_cptpl {
+.br
+.BI " NVME_RESERVATION_CPTPL_NO_CHANGE"
+,
+.br
+.br
+.BI " NVME_RESERVATION_CPTPL_CLEAR"
+,
+.br
+.br
+.BI " NVME_RESERVATION_CPTPL_PERSIST"
+
+};
+.SH Constants
+.IP "NVME_RESERVATION_CPTPL_NO_CHANGE" 12
+No change to PTPL state
+.IP "NVME_RESERVATION_CPTPL_CLEAR" 12
+Reservations are released and
+registrants are cleared on a power on
+.IP "NVME_RESERVATION_CPTPL_PERSIST" 12
+Reservations and registrants persist
+across a power loss
diff --git a/doc/man/nvme_resv_notification_log.2 b/doc/man/nvme_resv_notification_log.2
new file mode 100644
index 0000000..5da53e1
--- /dev/null
+++ b/doc/man/nvme_resv_notification_log.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvme_resv_notification_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_resv_notification_log \- Reservation Notification Log
+.SH SYNOPSIS
+struct nvme_resv_notification_log {
+.br
+.BI " __le64 lpc;"
+.br
+.BI " __u8 rnlpt;"
+.br
+.BI " __u8 nalp;"
+.br
+.BI " __u8 rsvd9[2];"
+.br
+.BI " __le32 nsid;"
+.br
+.BI " __u8 rsvd16[48];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "lpc" 12
+Log Page Count
+.IP "rnlpt" 12
+See \fIenum nvme_resv_notify_rnlpt\fP.
+.IP "nalp" 12
+Number of Available Log Pages
+.IP "rsvd9" 12
+Reserved
+.IP "nsid" 12
+Namespace ID
+.IP "rsvd16" 12
+Reserved
diff --git a/doc/man/nvme_resv_notify_rnlpt.2 b/doc/man/nvme_resv_notify_rnlpt.2
new file mode 100644
index 0000000..817578e
--- /dev/null
+++ b/doc/man/nvme_resv_notify_rnlpt.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_resv_notify_rnlpt" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_resv_notify_rnlpt \- Reservation Notification Log - Reservation Notification Log Page Type
+.SH SYNOPSIS
+enum nvme_resv_notify_rnlpt {
+.br
+.BI " NVME_RESV_NOTIFY_RNLPT_EMPTY"
+,
+.br
+.br
+.BI " NVME_RESV_NOTIFY_RNLPT_REGISTRATION_PREEMPTED"
+,
+.br
+.br
+.BI " NVME_RESV_NOTIFY_RNLPT_RESERVATION_RELEASED"
+,
+.br
+.br
+.BI " NVME_RESV_NOTIFY_RNLPT_RESERVATION_PREEMPTED"
+
+};
+.SH Constants
+.IP "NVME_RESV_NOTIFY_RNLPT_EMPTY" 12
+Empty Log Page
+.IP "NVME_RESV_NOTIFY_RNLPT_REGISTRATION_PREEMPTED" 12
+Registration Preempted
+.IP "NVME_RESV_NOTIFY_RNLPT_RESERVATION_RELEASED" 12
+Reservation Released
+.IP "NVME_RESV_NOTIFY_RNLPT_RESERVATION_PREEMPTED" 12
+Reservation Preempted
diff --git a/doc/man/nvme_resv_racqa.2 b/doc/man/nvme_resv_racqa.2
new file mode 100644
index 0000000..f813c56
--- /dev/null
+++ b/doc/man/nvme_resv_racqa.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_resv_racqa" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_resv_racqa \- Reservation Acquire - Reservation Acquire Action
+.SH SYNOPSIS
+enum nvme_resv_racqa {
+.br
+.BI " NVME_RESERVATION_RACQA_ACQUIRE"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RACQA_PREEMPT"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT"
+
+};
+.SH Constants
+.IP "NVME_RESERVATION_RACQA_ACQUIRE" 12
+Acquire
+.IP "NVME_RESERVATION_RACQA_PREEMPT" 12
+Preempt
+.IP "NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT" 12
+Preempt and Abort
diff --git a/doc/man/nvme_resv_register.2 b/doc/man/nvme_resv_register.2
new file mode 100644
index 0000000..8c7a397
--- /dev/null
+++ b/doc/man/nvme_resv_register.2
@@ -0,0 +1,15 @@
+.TH "nvme_resv_register" 9 "nvme_resv_register" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_resv_register \- Send an nvme reservation register
+.SH SYNOPSIS
+.B "int" nvme_resv_register
+.BI "(struct nvme_resv_register_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_resv_register_args\fP argument structure
+.SH "DESCRIPTION"
+The Reservation Register command registers, unregisters, or replaces a
+reservation key.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_resv_release.2 b/doc/man/nvme_resv_release.2
new file mode 100644
index 0000000..ba48b42
--- /dev/null
+++ b/doc/man/nvme_resv_release.2
@@ -0,0 +1,12 @@
+.TH "nvme_resv_release" 9 "nvme_resv_release" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_resv_release \- Send an nvme reservation release
+.SH SYNOPSIS
+.B "int" nvme_resv_release
+.BI "(struct nvme_resv_release_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_resv_release_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_resv_report.2 b/doc/man/nvme_resv_report.2
new file mode 100644
index 0000000..8a0de97
--- /dev/null
+++ b/doc/man/nvme_resv_report.2
@@ -0,0 +1,16 @@
+.TH "nvme_resv_report" 9 "nvme_resv_report" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_resv_report \- Send an nvme reservation report
+.SH SYNOPSIS
+.B "int" nvme_resv_report
+.BI "(struct nvme_resv_report_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+struct nvme_resv_report_args argument structure
+.SH "DESCRIPTION"
+Returns a Reservation Status data structure to memory that describes the
+registration and reservation status of a namespace. See the definition for
+the returned structure, \fIstruct nvme_reservation_status\fP, for more details.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_resv_rrega.2 b/doc/man/nvme_resv_rrega.2
new file mode 100644
index 0000000..aae8270
--- /dev/null
+++ b/doc/man/nvme_resv_rrega.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvme_resv_rrega" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_resv_rrega \- Reservation Register - Reservation Register Action
+.SH SYNOPSIS
+enum nvme_resv_rrega {
+.br
+.BI " NVME_RESERVATION_RREGA_REGISTER_KEY"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RREGA_UNREGISTER_KEY"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RREGA_REPLACE_KEY"
+
+};
+.SH Constants
+.IP "NVME_RESERVATION_RREGA_REGISTER_KEY" 12
+Register Reservation Key
+.IP "NVME_RESERVATION_RREGA_UNREGISTER_KEY" 12
+Unregister Reservation Key
+.IP "NVME_RESERVATION_RREGA_REPLACE_KEY" 12
+Replace Reservation Key
diff --git a/doc/man/nvme_resv_rrela.2 b/doc/man/nvme_resv_rrela.2
new file mode 100644
index 0000000..3a0dbb3
--- /dev/null
+++ b/doc/man/nvme_resv_rrela.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_resv_rrela" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_resv_rrela \- Reservation Release - Reservation Release Action
+.SH SYNOPSIS
+enum nvme_resv_rrela {
+.br
+.BI " NVME_RESERVATION_RRELA_RELEASE"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RRELA_CLEAR"
+
+};
+.SH Constants
+.IP "NVME_RESERVATION_RRELA_RELEASE" 12
+Release
+.IP "NVME_RESERVATION_RRELA_CLEAR" 12
+Clear
diff --git a/doc/man/nvme_resv_rtype.2 b/doc/man/nvme_resv_rtype.2
new file mode 100644
index 0000000..1407583
--- /dev/null
+++ b/doc/man/nvme_resv_rtype.2
@@ -0,0 +1,42 @@
+.TH "libnvme" 9 "enum nvme_resv_rtype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_resv_rtype \- Reservation Type Encoding
+.SH SYNOPSIS
+enum nvme_resv_rtype {
+.br
+.BI " NVME_RESERVATION_RTYPE_WE"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RTYPE_EA"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RTYPE_WERO"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RTYPE_EARO"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RTYPE_WEAR"
+,
+.br
+.br
+.BI " NVME_RESERVATION_RTYPE_EAAR"
+
+};
+.SH Constants
+.IP "NVME_RESERVATION_RTYPE_WE" 12
+Write Exclusive Reservation
+.IP "NVME_RESERVATION_RTYPE_EA" 12
+Exclusive Access Reservation
+.IP "NVME_RESERVATION_RTYPE_WERO" 12
+Write Exclusive - Registrants Only Reservation
+.IP "NVME_RESERVATION_RTYPE_EARO" 12
+Exclusive Access - Registrants Only Reservation
+.IP "NVME_RESERVATION_RTYPE_WEAR" 12
+Write Exclusive - All Registrants Reservation
+.IP "NVME_RESERVATION_RTYPE_EAAR" 12
+Exclusive Access - All Registrants Reservation
diff --git a/doc/man/nvme_resv_status.2 b/doc/man/nvme_resv_status.2
new file mode 100644
index 0000000..1d02777
--- /dev/null
+++ b/doc/man/nvme_resv_status.2
@@ -0,0 +1,59 @@
+.TH "libnvme" 9 "struct nvme_resv_status" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_resv_status \- Reservation Status Data Structure
+.SH SYNOPSIS
+struct nvme_resv_status {
+.br
+.BI " __le32 gen;"
+.br
+.BI " __u8 rtype;"
+.br
+.BI " __u8 regctl[2];"
+.br
+.BI " __u8 rsvd7[2];"
+.br
+.BI " __u8 ptpls;"
+.br
+.BI " __u8 rsvd10[14];"
+.br
+.BI " union {"
+.br
+.BI " struct {"
+.br
+.BI " __u8 rsvd24[40];"
+.br
+.BI " struct nvme_registered_ctrl_ext regctl_eds[0];"
+.br
+.BI " };"
+.br
+.BI " struct nvme_registered_ctrl regctl_ds[0];"
+.br
+.BI " };"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "gen" 12
+Generation
+.IP "rtype" 12
+Reservation Type
+.IP "regctl" 12
+Number of Registered Controllers
+.IP "rsvd7" 12
+Reserved
+.IP "ptpls" 12
+Persist Through Power Loss State
+.IP "rsvd10" 12
+Reserved
+.IP "{unnamed_union}" 12
+anonymous
+.IP "{unnamed_struct}" 12
+anonymous
+.IP "rsvd24" 12
+Reserved
+.IP "regctl_eds" 12
+Registered Controller Extended Data Structure
+.IP "regctl_ds" 12
+Registered Controller Data Structure
diff --git a/doc/man/nvme_sanitize_compln_event.2 b/doc/man/nvme_sanitize_compln_event.2
new file mode 100644
index 0000000..b192923
--- /dev/null
+++ b/doc/man/nvme_sanitize_compln_event.2
@@ -0,0 +1,27 @@
+.TH "libnvme" 9 "struct nvme_sanitize_compln_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_sanitize_compln_event \- Sanitize Completion Event Data
+.SH SYNOPSIS
+struct nvme_sanitize_compln_event {
+.br
+.BI " __le16 sani_prog;"
+.br
+.BI " __le16 sani_status;"
+.br
+.BI " __le16 cmpln_info;"
+.br
+.BI " __u8 rsvd6[2];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "sani_prog" 12
+Sanitize Progress
+.IP "sani_status" 12
+Sanitize Status
+.IP "cmpln_info" 12
+Completion Information
+.IP "rsvd6" 12
+Reserved
diff --git a/doc/man/nvme_sanitize_log_page.2 b/doc/man/nvme_sanitize_log_page.2
new file mode 100644
index 0000000..ee300b2
--- /dev/null
+++ b/doc/man/nvme_sanitize_log_page.2
@@ -0,0 +1,106 @@
+.TH "libnvme" 9 "struct nvme_sanitize_log_page" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_sanitize_log_page \- Sanitize Status (Log Identifier 81h)
+.SH SYNOPSIS
+struct nvme_sanitize_log_page {
+.br
+.BI " __le16 sprog;"
+.br
+.BI " __le16 sstat;"
+.br
+.BI " __le32 scdw10;"
+.br
+.BI " __le32 eto;"
+.br
+.BI " __le32 etbe;"
+.br
+.BI " __le32 etce;"
+.br
+.BI " __le32 etond;"
+.br
+.BI " __le32 etbend;"
+.br
+.BI " __le32 etcend;"
+.br
+.BI " __u8 rsvd32[480];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "sprog" 12
+Sanitize Progress (SPROG): indicates the fraction complete of the
+sanitize operation. The value is a numerator of the fraction
+complete that has 65,536 (10000h) as its denominator. This value
+shall be set to FFFFh if the \fIsstat\fP field is not set to
+NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS.
+.IP "sstat" 12
+Sanitize Status (SSTAT): indicates the status associated with
+the most recent sanitize operation. See \fIenum nvme_sanitize_sstat\fP.
+.IP "scdw10" 12
+Sanitize Command Dword 10 Information (SCDW10): contains the value
+of the Command Dword 10 field of the Sanitize command that started
+the sanitize operation.
+.IP "eto" 12
+Estimated Time For Overwrite: indicates the number of seconds required
+to complete an Overwrite sanitize operation with 16 passes in
+the background when the No-Deallocate Modifies Media After Sanitize
+field is not set to 10b. A value of 0h indicates that the sanitize
+operation is expected to be completed in the background when the
+Sanitize command that started that operation is completed. A value
+of FFFFFFFFh indicates that no time period is reported.
+.IP "etbe" 12
+Estimated Time For Block Erase: indicates the number of seconds
+required to complete a Block Erase sanitize operation in the
+background when the No-Deallocate Modifies Media After Sanitize
+field is not set to 10b. A value of 0h indicates that the sanitize
+operation is expected to be completed in the background when the
+Sanitize command that started that operation is completed.
+A value of FFFFFFFFh indicates that no time period is reported.
+.IP "etce" 12
+Estimated Time For Crypto Erase: indicates the number of seconds
+required to complete a Crypto Erase sanitize operation in the
+background when the No-Deallocate Modifies Media After Sanitize
+field is not set to 10b. A value of 0h indicates that the sanitize
+operation is expected to be completed in the background when the
+Sanitize command that started that operation is completed.
+A value of FFFFFFFFh indicates that no time period is reported.
+.IP "etond" 12
+Estimated Time For Overwrite With No-Deallocate Media Modification:
+indicates the number of seconds required to complete an Overwrite
+sanitize operation and the associated additional media modification
+after the Overwrite sanitize operation in the background when
+the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+command that requested the Overwrite sanitize operation; and
+the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+A value of 0h indicates that the sanitize operation is expected
+to be completed in the background when the Sanitize command that
+started that operation is completed. A value of FFFFFFFFh indicates
+that no time period is reported.
+.IP "etbend" 12
+Estimated Time For Block Erase With No-Deallocate Media Modification:
+indicates the number of seconds required to complete a Block Erase
+sanitize operation and the associated additional media modification
+after the Block Erase sanitize operation in the background when
+the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+command that requested the Overwrite sanitize operation; and
+the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+A value of 0h indicates that the sanitize operation is expected
+to be completed in the background when the Sanitize command that
+started that operation is completed. A value of FFFFFFFFh indicates
+that no time period is reported.
+.IP "etcend" 12
+Estimated Time For Crypto Erase With No-Deallocate Media Modification:
+indicates the number of seconds required to complete a Crypto Erase
+sanitize operation and the associated additional media modification
+after the Crypto Erase sanitize operation in the background when
+the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+command that requested the Overwrite sanitize operation; and
+the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+A value of 0h indicates that the sanitize operation is expected
+to be completed in the background when the Sanitize command that
+started that operation is completed. A value of FFFFFFFFh indicates
+that no time period is reported.
+.IP "rsvd32" 12
+Reserved
diff --git a/doc/man/nvme_sanitize_nvm.2 b/doc/man/nvme_sanitize_nvm.2
new file mode 100644
index 0000000..05f59a1
--- /dev/null
+++ b/doc/man/nvme_sanitize_nvm.2
@@ -0,0 +1,22 @@
+.TH "nvme_sanitize_nvm" 9 "nvme_sanitize_nvm" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_sanitize_nvm \- Start a sanitize operation
+.SH SYNOPSIS
+.B "int" nvme_sanitize_nvm
+.BI "(struct nvme_sanitize_nvm_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_sanitize_nvm_args\fP argument structure
+.SH "DESCRIPTION"
+A sanitize operation alters all user data in the NVM subsystem such that
+recovery of any previous user data from any cache, the non-volatile media,
+or any Controller Memory Buffer is not possible.
+
+The Sanitize command starts a sanitize operation or to recover from a
+previously failed sanitize operation. The sanitize operation types that may
+be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize
+operations are processed in the background, i.e., completion of the sanitize
+command does not indicate completion of the sanitize operation.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_sanitize_sanact.2 b/doc/man/nvme_sanitize_sanact.2
new file mode 100644
index 0000000..dc9b4c0
--- /dev/null
+++ b/doc/man/nvme_sanitize_sanact.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_sanitize_sanact" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_sanitize_sanact \- Sanitize Action
+.SH SYNOPSIS
+enum nvme_sanitize_sanact {
+.br
+.BI " NVME_SANITIZE_SANACT_EXIT_FAILURE"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SANACT_START_BLOCK_ERASE"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SANACT_START_OVERWRITE"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SANACT_START_CRYPTO_ERASE"
+
+};
+.SH Constants
+.IP "NVME_SANITIZE_SANACT_EXIT_FAILURE" 12
+Exit Failure Mode.
+.IP "NVME_SANITIZE_SANACT_START_BLOCK_ERASE" 12
+Start a Block Erase sanitize operation.
+.IP "NVME_SANITIZE_SANACT_START_OVERWRITE" 12
+Start an Overwrite sanitize operation.
+.IP "NVME_SANITIZE_SANACT_START_CRYPTO_ERASE" 12
+Start a Crypto Erase sanitize operation.
diff --git a/doc/man/nvme_sanitize_sstat.2 b/doc/man/nvme_sanitize_sstat.2
new file mode 100644
index 0000000..f337b63
--- /dev/null
+++ b/doc/man/nvme_sanitize_sstat.2
@@ -0,0 +1,105 @@
+.TH "libnvme" 9 "enum nvme_sanitize_sstat" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_sanitize_sstat \- Sanitize Status (SSTAT)
+.SH SYNOPSIS
+enum nvme_sanitize_sstat {
+.br
+.BI " NVME_SANITIZE_SSTAT_STATUS_SHIFT"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_STATUS_MASK"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_STATUS_NEVER_SANITIZED"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_STATUS_COMPLETE_SUCCESS"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_STATUS_COMPLETED_FAILED"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_STATUS_ND_COMPLETE_SUCCESS"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_COMPLETED_PASSES_SHIFT"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_COMPLETED_PASSES_MASK"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK"
+,
+.br
+.br
+.BI " NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED"
+
+};
+.SH Constants
+.IP "NVME_SANITIZE_SSTAT_STATUS_SHIFT" 12
+Shift amount to get the status value of
+the most recent sanitize operation from
+the \fIstruct nvme_sanitize_log_page\fP.sstat
+field.
+.IP "NVME_SANITIZE_SSTAT_STATUS_MASK" 12
+Mask to get the status value of the most
+recent sanitize operation.
+.IP "NVME_SANITIZE_SSTAT_STATUS_NEVER_SANITIZED" 12
+The NVM subsystem has never been
+sanitized.
+.IP "NVME_SANITIZE_SSTAT_STATUS_COMPLETE_SUCCESS" 12
+The most recent sanitize operation
+completed successfully including any
+additional media modification.
+.IP "NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS" 12
+A sanitize operation is currently in progress.
+.IP "NVME_SANITIZE_SSTAT_STATUS_COMPLETED_FAILED" 12
+The most recent sanitize operation
+failed.
+.IP "NVME_SANITIZE_SSTAT_STATUS_ND_COMPLETE_SUCCESS" 12
+The most recent sanitize operation
+for which No-Deallocate After Sanitize was
+requested has completed successfully with
+deallocation of all user data.
+.IP "NVME_SANITIZE_SSTAT_COMPLETED_PASSES_SHIFT" 12
+Shift amount to get the number
+of completed passes if the most recent
+sanitize operation was an Overwrite. This
+value shall be cleared to 0h if the most
+recent sanitize operation was not
+an Overwrite.
+.IP "NVME_SANITIZE_SSTAT_COMPLETED_PASSES_MASK" 12
+Mask to get the number of completed
+passes.
+.IP "NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT" 12
+Shift amount to get the Global
+Data Erased value from the
+\fIstruct nvme_sanitize_log_page\fP.sstat field.
+.IP "NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK" 12
+Mask to get the Global Data Erased
+value.
+.IP "NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED" 12
+Global Data Erased: if set, then no
+namespace user data in the NVM subsystem
+has been written to and no Persistent
+Memory Region in the NVM subsystem has
+been enabled since being manufactured and
+the NVM subsystem has never been sanitized;
+or since the most recent successful sanitize
+operation.
diff --git a/doc/man/nvme_sanitize_start_event.2 b/doc/man/nvme_sanitize_start_event.2
new file mode 100644
index 0000000..f58da81
--- /dev/null
+++ b/doc/man/nvme_sanitize_start_event.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_sanitize_start_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_sanitize_start_event \- Sanitize Start Event Data
+.SH SYNOPSIS
+struct nvme_sanitize_start_event {
+.br
+.BI " __le32 sani_cap;"
+.br
+.BI " __le32 sani_cdw10;"
+.br
+.BI " __le32 sani_cdw11;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "sani_cap" 12
+SANICAP
+.IP "sani_cdw10" 12
+Sanitize CDW10
+.IP "sani_cdw11" 12
+Sanitize CDW11
diff --git a/doc/man/nvme_scan.2 b/doc/man/nvme_scan.2
new file mode 100644
index 0000000..702aa24
--- /dev/null
+++ b/doc/man/nvme_scan.2
@@ -0,0 +1,11 @@
+.TH "nvme_scan" 9 "nvme_scan" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan \- Scan NVMe topology
+.SH SYNOPSIS
+.B "nvme_root_t" nvme_scan
+.BI "(const char *config_file " ");"
+.SH ARGUMENTS
+.IP "config_file" 12
+Configuration file
+.SH "RETURN"
+nvme_root_t object of found elements
diff --git a/doc/man/nvme_scan_ctrl.2 b/doc/man/nvme_scan_ctrl.2
new file mode 100644
index 0000000..4b58e11
--- /dev/null
+++ b/doc/man/nvme_scan_ctrl.2
@@ -0,0 +1,16 @@
+.TH "nvme_scan_ctrl" 9 "nvme_scan_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan_ctrl \- Scan on a controller
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvme_scan_ctrl
+.BI "(nvme_root_t r " ","
+.BI "const char *name " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t object
+.IP "name" 12
+Name of the controller
+.SH "DESCRIPTION"
+Scans a controller with sysfs name \fIname\fP and add it to \fIr\fP.
+.SH "RETURN"
+nvme_ctrl_t object
diff --git a/doc/man/nvme_scan_ctrl_namespace_paths.2 b/doc/man/nvme_scan_ctrl_namespace_paths.2
new file mode 100644
index 0000000..be53a33
--- /dev/null
+++ b/doc/man/nvme_scan_ctrl_namespace_paths.2
@@ -0,0 +1,14 @@
+.TH "nvme_scan_ctrl_namespace_paths" 9 "nvme_scan_ctrl_namespace_paths" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan_ctrl_namespace_paths \- Scan for namespace paths in a controller
+.SH SYNOPSIS
+.B "int" nvme_scan_ctrl_namespace_paths
+.BI "(nvme_ctrl_t c " ","
+.BI "struct dirent ***paths " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller to scan
+.IP "paths" 12
+Pointer to array of dirents
+.SH "RETURN"
+number of entries in \fIpaths\fP
diff --git a/doc/man/nvme_scan_ctrl_namespaces.2 b/doc/man/nvme_scan_ctrl_namespaces.2
new file mode 100644
index 0000000..51bf044
--- /dev/null
+++ b/doc/man/nvme_scan_ctrl_namespaces.2
@@ -0,0 +1,14 @@
+.TH "nvme_scan_ctrl_namespaces" 9 "nvme_scan_ctrl_namespaces" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan_ctrl_namespaces \- Scan for namespaces in a controller
+.SH SYNOPSIS
+.B "int" nvme_scan_ctrl_namespaces
+.BI "(nvme_ctrl_t c " ","
+.BI "struct dirent ***ns " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller to scan
+.IP "ns" 12
+Pointer to array of dirents
+.SH "RETURN"
+number of entries in \fIns\fP
diff --git a/doc/man/nvme_scan_ctrls.2 b/doc/man/nvme_scan_ctrls.2
new file mode 100644
index 0000000..a4bc643
--- /dev/null
+++ b/doc/man/nvme_scan_ctrls.2
@@ -0,0 +1,11 @@
+.TH "nvme_scan_ctrls" 9 "nvme_scan_ctrls" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan_ctrls \- Scan for controllers
+.SH SYNOPSIS
+.B "int" nvme_scan_ctrls
+.BI "(struct dirent ***ctrls " ");"
+.SH ARGUMENTS
+.IP "ctrls" 12
+Pointer to array of dirents
+.SH "RETURN"
+number of entries in \fIctrls\fP
diff --git a/doc/man/nvme_scan_namespace.2 b/doc/man/nvme_scan_namespace.2
new file mode 100644
index 0000000..0f38493
--- /dev/null
+++ b/doc/man/nvme_scan_namespace.2
@@ -0,0 +1,11 @@
+.TH "nvme_scan_namespace" 9 "nvme_scan_namespace" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan_namespace \- scan namespace based on sysfs name
+.SH SYNOPSIS
+.B "nvme_ns_t" nvme_scan_namespace
+.BI "(const char *name " ");"
+.SH ARGUMENTS
+.IP "name" 12
+sysfs name of the namespace to scan
+.SH "RETURN"
+nvme_ns_t object or NULL if not found.
diff --git a/doc/man/nvme_scan_subsystem_namespaces.2 b/doc/man/nvme_scan_subsystem_namespaces.2
new file mode 100644
index 0000000..26d0432
--- /dev/null
+++ b/doc/man/nvme_scan_subsystem_namespaces.2
@@ -0,0 +1,14 @@
+.TH "nvme_scan_subsystem_namespaces" 9 "nvme_scan_subsystem_namespaces" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan_subsystem_namespaces \- Scan for namespaces in a subsystem
+.SH SYNOPSIS
+.B "int" nvme_scan_subsystem_namespaces
+.BI "(nvme_subsystem_t s " ","
+.BI "struct dirent ***ns " ");"
+.SH ARGUMENTS
+.IP "s" 12
+Subsystem to scan
+.IP "ns" 12
+Pointer to array of dirents
+.SH "RETURN"
+number of entries in \fIns\fP
diff --git a/doc/man/nvme_scan_subsystems.2 b/doc/man/nvme_scan_subsystems.2
new file mode 100644
index 0000000..f35a05c
--- /dev/null
+++ b/doc/man/nvme_scan_subsystems.2
@@ -0,0 +1,11 @@
+.TH "nvme_scan_subsystems" 9 "nvme_scan_subsystems" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan_subsystems \- Scan for subsystems
+.SH SYNOPSIS
+.B "int" nvme_scan_subsystems
+.BI "(struct dirent ***subsys " ");"
+.SH ARGUMENTS
+.IP "subsys" 12
+Pointer to array of dirents
+.SH "RETURN"
+number of entries in \fIsubsys\fP
diff --git a/doc/man/nvme_scan_topology.2 b/doc/man/nvme_scan_topology.2
new file mode 100644
index 0000000..d9b9d8d
--- /dev/null
+++ b/doc/man/nvme_scan_topology.2
@@ -0,0 +1,20 @@
+.TH "nvme_scan_topology" 9 "nvme_scan_topology" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_scan_topology \- Scan NVMe topology and apply filter
+.SH SYNOPSIS
+.B "int" nvme_scan_topology
+.BI "(nvme_root_t r " ","
+.BI "nvme_scan_filter_t f " ","
+.BI "void *f_args " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t object
+.IP "f" 12
+filter to apply
+.IP "f_args" 12
+user-specified argument to \fIf\fP
+.SH "DESCRIPTION"
+Scans the NVMe topology and filters out the resulting elements
+by applying \fIf\fP.
+.SH "RETURN"
+Number of elements scanned
diff --git a/doc/man/nvme_secondary_ctrl.2 b/doc/man/nvme_secondary_ctrl.2
new file mode 100644
index 0000000..7bdd9da
--- /dev/null
+++ b/doc/man/nvme_secondary_ctrl.2
@@ -0,0 +1,43 @@
+.TH "libnvme" 9 "struct nvme_secondary_ctrl" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_secondary_ctrl \- Secondary Controller Entry
+.SH SYNOPSIS
+struct nvme_secondary_ctrl {
+.br
+.BI " __le16 scid;"
+.br
+.BI " __le16 pcid;"
+.br
+.BI " __u8 scs;"
+.br
+.BI " __u8 rsvd5[3];"
+.br
+.BI " __le16 vfn;"
+.br
+.BI " __le16 nvq;"
+.br
+.BI " __le16 nvi;"
+.br
+.BI " __u8 rsvd14[18];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "scid" 12
+Secondary Controller Identifier
+.IP "pcid" 12
+Primary Controller Identifier
+.IP "scs" 12
+Secondary Controller State
+.IP "rsvd5" 12
+Reserved
+.IP "vfn" 12
+Virtual Function Number
+.IP "nvq" 12
+Number of VQ Flexible Resources Assigned
+.IP "nvi" 12
+Number of VI Flexible Resources Assigned
+.IP "rsvd14" 12
+Reserved
diff --git a/doc/man/nvme_secondary_ctrl_list.2 b/doc/man/nvme_secondary_ctrl_list.2
new file mode 100644
index 0000000..41a5c18
--- /dev/null
+++ b/doc/man/nvme_secondary_ctrl_list.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_secondary_ctrl_list" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_secondary_ctrl_list \- Secondary Controller List
+.SH SYNOPSIS
+struct nvme_secondary_ctrl_list {
+.br
+.BI " __u8 num;"
+.br
+.BI " __u8 rsvd[31];"
+.br
+.BI " struct nvme_secondary_ctrl sc_entry[NVME_ID_SECONDARY_CTRL_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "num" 12
+Number of Identifiers
+.IP "rsvd" 12
+Reserved
+.IP "sc_entry" 12
+Secondary Controller Entry
diff --git a/doc/man/nvme_security_receive.2 b/doc/man/nvme_security_receive.2
new file mode 100644
index 0000000..0aa7439
--- /dev/null
+++ b/doc/man/nvme_security_receive.2
@@ -0,0 +1,12 @@
+.TH "nvme_security_receive" 9 "nvme_security_receive" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_security_receive \- Security Receive command
+.SH SYNOPSIS
+.B "int" nvme_security_receive
+.BI "(struct nvme_security_receive_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_security_receive\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_security_send.2 b/doc/man/nvme_security_send.2
new file mode 100644
index 0000000..80586d9
--- /dev/null
+++ b/doc/man/nvme_security_send.2
@@ -0,0 +1,21 @@
+.TH "nvme_security_send" 9 "nvme_security_send" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_security_send \- Security Send command
+.SH SYNOPSIS
+.B "int" nvme_security_send
+.BI "(struct nvme_security_send_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_security_send\fP argument structure
+.SH "DESCRIPTION"
+The Security Send command transfers security protocol data to the
+controller. The data structure transferred to the controller as part of this
+command contains security protocol specific commands to be performed by the
+controller. The data structure transferred may also contain data or
+parameters associated with the security protocol commands.
+
+The security data is protocol specific and is not defined by the NVMe
+specification.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_self_test_log.2 b/doc/man/nvme_self_test_log.2
new file mode 100644
index 0000000..91a3987
--- /dev/null
+++ b/doc/man/nvme_self_test_log.2
@@ -0,0 +1,43 @@
+.TH "libnvme" 9 "struct nvme_self_test_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_self_test_log \- Device Self-test (Log Identifier 06h)
+.SH SYNOPSIS
+struct nvme_self_test_log {
+.br
+.BI " __u8 current_operation;"
+.br
+.BI " __u8 completion;"
+.br
+.BI " __u8 rsvd[2];"
+.br
+.BI " struct nvme_st_result result[NVME_LOG_ST_MAX_RESULTS];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "current_operation" 12
+Current Device Self-Test Operation: indicates the status
+of the current device self-test operation. If a device
+self-test operation is in process (i.e., this field is set
+to #NVME_ST_CURR_OP_SHORT or #NVME_ST_CURR_OP_EXTENDED),
+then the controller shall not set this field to
+#NVME_ST_CURR_OP_NOT_RUNNING until a new Self-test Result
+Data Structure is created (i.e., if a device self-test
+operation completes or is aborted, then the controller
+shall create a Self-test Result Data Structure prior to
+setting this field to #NVME_ST_CURR_OP_NOT_RUNNING).
+See \fIenum nvme_st_curr_op\fP.
+.IP "completion" 12
+Current Device Self-Test Completion: indicates the percentage
+of the device self-test operation that is complete (e.g.,
+a value of 25 indicates that 25% of the device self-test
+operation is complete and 75% remains to be tested).
+If the \fIcurrent_operation\fP field is cleared to
+#NVME_ST_CURR_OP_NOT_RUNNING (indicating there is no device
+self-test operation in progress), then this field is ignored.
+.IP "rsvd" 12
+Reserved
+.IP "result" 12
+Self-test Result Data Structures, see \fIstruct nvme_st_result\fP.
diff --git a/doc/man/nvme_set_feature_event.2 b/doc/man/nvme_set_feature_event.2
new file mode 100644
index 0000000..a9d4879
--- /dev/null
+++ b/doc/man/nvme_set_feature_event.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_set_feature_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_set_feature_event \- Set Feature Event Data
+.SH SYNOPSIS
+struct nvme_set_feature_event {
+.br
+.BI " __le32 layout;"
+.br
+.BI " __le32 cdw_mem[0];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "layout" 12
+Set Feature Event Layout
+.IP "cdw_mem" 12
+Command Dwords Memory buffer
diff --git a/doc/man/nvme_set_features.2 b/doc/man/nvme_set_features.2
new file mode 100644
index 0000000..82fa8bd
--- /dev/null
+++ b/doc/man/nvme_set_features.2
@@ -0,0 +1,12 @@
+.TH "nvme_set_features" 9 "nvme_set_features" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features \- Set a feature attribute
+.SH SYNOPSIS
+.B "int" nvme_set_features
+.BI "(struct nvme_set_features_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_set_features_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_arbitration.2 b/doc/man/nvme_set_features_arbitration.2
new file mode 100644
index 0000000..7e0fb94
--- /dev/null
+++ b/doc/man/nvme_set_features_arbitration.2
@@ -0,0 +1,30 @@
+.TH "nvme_set_features_arbitration" 9 "nvme_set_features_arbitration" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_arbitration \- Set arbitration features
+.SH SYNOPSIS
+.B "int" nvme_set_features_arbitration
+.BI "(int fd " ","
+.BI "__u8 ab " ","
+.BI "__u8 lpw " ","
+.BI "__u8 mpw " ","
+.BI "__u8 hpw " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "ab" 12
+Arbitration Burst
+.IP "lpw" 12
+Low Priority Weight
+.IP "mpw" 12
+Medium Priority Weight
+.IP "hpw" 12
+High Priority Weight
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_async_event.2 b/doc/man/nvme_set_features_async_event.2
new file mode 100644
index 0000000..7067093
--- /dev/null
+++ b/doc/man/nvme_set_features_async_event.2
@@ -0,0 +1,21 @@
+.TH "nvme_set_features_async_event" 9 "nvme_set_features_async_event" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_async_event \- Set asynchronous event feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_async_event
+.BI "(int fd " ","
+.BI "__u32 events " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "events" 12
+Events to enable
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_auto_pst.2 b/doc/man/nvme_set_features_auto_pst.2
new file mode 100644
index 0000000..4cdd746
--- /dev/null
+++ b/doc/man/nvme_set_features_auto_pst.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_auto_pst" 9 "nvme_set_features_auto_pst" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_auto_pst \- Set autonomous power state feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_auto_pst
+.BI "(int fd " ","
+.BI "bool apste " ","
+.BI "bool save " ","
+.BI "struct nvme_feat_auto_pst *apst " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "apste" 12
+Autonomous Power State Transition Enable
+.IP "save" 12
+Save value across power states
+.IP "apst" 12
+Autonomous Power State Transition
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_data.2 b/doc/man/nvme_set_features_data.2
new file mode 100644
index 0000000..a2182b9
--- /dev/null
+++ b/doc/man/nvme_set_features_data.2
@@ -0,0 +1,33 @@
+.TH "nvme_set_features_data" 9 "nvme_set_features_data" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_data \- Helper function for @nvme_set_features()
+.SH SYNOPSIS
+.B "int" nvme_set_features_data
+.BI "(int fd " ","
+.BI "__u8 fid " ","
+.BI "__u32 nsid " ","
+.BI "__u32 cdw11 " ","
+.BI "bool save " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "fid" 12
+Feature identifier
+.IP "nsid" 12
+Namespace ID, if applicable
+.IP "cdw11" 12
+Value to set the feature to
+.IP "save" 12
+Save value across power states
+.IP "data_len" 12
+Length of feature data, if applicable, in bytes
+.IP "data" 12
+User address of feature data, if applicable
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_endurance_evt_cfg.2 b/doc/man/nvme_set_features_endurance_evt_cfg.2
new file mode 100644
index 0000000..615acca
--- /dev/null
+++ b/doc/man/nvme_set_features_endurance_evt_cfg.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_endurance_evt_cfg" 9 "nvme_set_features_endurance_evt_cfg" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_endurance_evt_cfg \- Set endurance event config feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_endurance_evt_cfg
+.BI "(int fd " ","
+.BI "__u16 endgid " ","
+.BI "__u8 egwarn " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "endgid" 12
+Endurance Group Identifier
+.IP "egwarn" 12
+Flags to enable warning, see \fIenum nvme_eg_critical_warning_flags\fP
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_err_recovery.2 b/doc/man/nvme_set_features_err_recovery.2
new file mode 100644
index 0000000..11128d7
--- /dev/null
+++ b/doc/man/nvme_set_features_err_recovery.2
@@ -0,0 +1,27 @@
+.TH "nvme_set_features_err_recovery" 9 "nvme_set_features_err_recovery" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_err_recovery \- Set error recovery feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_err_recovery
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u16 tler " ","
+.BI "bool dulbe " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "tler" 12
+Time-limited error recovery value
+.IP "dulbe" 12
+Deallocated or Unwritten Logical Block Error Enable
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_hctm.2 b/doc/man/nvme_set_features_hctm.2
new file mode 100644
index 0000000..024b379
--- /dev/null
+++ b/doc/man/nvme_set_features_hctm.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_hctm" 9 "nvme_set_features_hctm" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_hctm \- Set thermal management feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_hctm
+.BI "(int fd " ","
+.BI "__u16 tmt2 " ","
+.BI "__u16 tmt1 " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "tmt2" 12
+Thermal Management Temperature 2
+.IP "tmt1" 12
+Thermal Management Temperature 1
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_host_behavior.2 b/doc/man/nvme_set_features_host_behavior.2
new file mode 100644
index 0000000..d8e40d6
--- /dev/null
+++ b/doc/man/nvme_set_features_host_behavior.2
@@ -0,0 +1,18 @@
+.TH "nvme_set_features_host_behavior" 9 "nvme_set_features_host_behavior" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_host_behavior \- Set host behavior feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_host_behavior
+.BI "(int fd " ","
+.BI "bool save " ","
+.BI "struct nvme_feat_host_behavior *data " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "save" 12
+Save value across power states
+.IP "data" 12
+Pointer to structure nvme_feat_host_behavior
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_host_id.2 b/doc/man/nvme_set_features_host_id.2
new file mode 100644
index 0000000..12cb403
--- /dev/null
+++ b/doc/man/nvme_set_features_host_id.2
@@ -0,0 +1,21 @@
+.TH "nvme_set_features_host_id" 9 "nvme_set_features_host_id" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_host_id \- Set enable extended host identifiers feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_host_id
+.BI "(int fd " ","
+.BI "bool exhid " ","
+.BI "bool save " ","
+.BI "__u8 *hostid " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "exhid" 12
+Enable Extended Host Identifier
+.IP "save" 12
+Save value across power states
+.IP "hostid" 12
+Host ID to set
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_iocs_profile.2 b/doc/man/nvme_set_features_iocs_profile.2
new file mode 100644
index 0000000..5e193e6
--- /dev/null
+++ b/doc/man/nvme_set_features_iocs_profile.2
@@ -0,0 +1,18 @@
+.TH "nvme_set_features_iocs_profile" 9 "nvme_set_features_iocs_profile" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_iocs_profile \- Set I/O command set profile feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_iocs_profile
+.BI "(int fd " ","
+.BI "__u16 iocsi " ","
+.BI "bool save " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "iocsi" 12
+I/O Command Set Combination Index
+.IP "save" 12
+Save value across power states
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_irq_coalesce.2 b/doc/man/nvme_set_features_irq_coalesce.2
new file mode 100644
index 0000000..2f07011
--- /dev/null
+++ b/doc/man/nvme_set_features_irq_coalesce.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_irq_coalesce" 9 "nvme_set_features_irq_coalesce" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_irq_coalesce \- Set IRQ coalesce feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_irq_coalesce
+.BI "(int fd " ","
+.BI "__u8 thr " ","
+.BI "__u8 time " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "thr" 12
+Aggregation Threshold
+.IP "time" 12
+Aggregation Time
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_irq_config.2 b/doc/man/nvme_set_features_irq_config.2
new file mode 100644
index 0000000..1e3ebeb
--- /dev/null
+++ b/doc/man/nvme_set_features_irq_config.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_irq_config" 9 "nvme_set_features_irq_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_irq_config \- Set IRQ config feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_irq_config
+.BI "(int fd " ","
+.BI "__u16 iv " ","
+.BI "bool cd " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "iv" 12
+Interrupt Vector
+.IP "cd" 12
+Coalescing Disable
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_lba_range.2 b/doc/man/nvme_set_features_lba_range.2
new file mode 100644
index 0000000..e55d81f
--- /dev/null
+++ b/doc/man/nvme_set_features_lba_range.2
@@ -0,0 +1,27 @@
+.TH "nvme_set_features_lba_range" 9 "nvme_set_features_lba_range" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_lba_range \- Set LBA range feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_lba_range
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u8 nr_ranges " ","
+.BI "bool save " ","
+.BI "struct nvme_lba_range_type *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "nr_ranges" 12
+Number of ranges in \fIdata\fP
+.IP "save" 12
+Save value across power states
+.IP "data" 12
+User address of feature data
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_lba_sts_interval.2 b/doc/man/nvme_set_features_lba_sts_interval.2
new file mode 100644
index 0000000..6ffdc8b
--- /dev/null
+++ b/doc/man/nvme_set_features_lba_sts_interval.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_lba_sts_interval" 9 "nvme_set_features_lba_sts_interval" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_lba_sts_interval \- Set LBA status information feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_lba_sts_interval
+.BI "(int fd " ","
+.BI "__u16 lsiri " ","
+.BI "__u16 lsipi " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "lsiri" 12
+LBA Status Information Report Interval
+.IP "lsipi" 12
+LBA Status Information Poll Interval
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_nopsc.2 b/doc/man/nvme_set_features_nopsc.2
new file mode 100644
index 0000000..867cfba
--- /dev/null
+++ b/doc/man/nvme_set_features_nopsc.2
@@ -0,0 +1,21 @@
+.TH "nvme_set_features_nopsc" 9 "nvme_set_features_nopsc" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_nopsc \- Set non-operational power state feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_nopsc
+.BI "(int fd " ","
+.BI "bool noppme " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "noppme" 12
+Non-Operational Power State Permissive Mode Enable
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_plm_config.2 b/doc/man/nvme_set_features_plm_config.2
new file mode 100644
index 0000000..ef92ca5
--- /dev/null
+++ b/doc/man/nvme_set_features_plm_config.2
@@ -0,0 +1,27 @@
+.TH "nvme_set_features_plm_config" 9 "nvme_set_features_plm_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_plm_config \- Set predictable latency feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_plm_config
+.BI "(int fd " ","
+.BI "bool enable " ","
+.BI "__u16 nvmsetid " ","
+.BI "bool save " ","
+.BI "struct nvme_plm_config *data " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "enable" 12
+Predictable Latency Enable
+.IP "nvmsetid" 12
+NVM Set Identifier
+.IP "save" 12
+Save value across power states
+.IP "data" 12
+Pointer to structure nvme_plm_config
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_plm_window.2 b/doc/man/nvme_set_features_plm_window.2
new file mode 100644
index 0000000..40678a9
--- /dev/null
+++ b/doc/man/nvme_set_features_plm_window.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_plm_window" 9 "nvme_set_features_plm_window" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_plm_window \- Set window select feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_plm_window
+.BI "(int fd " ","
+.BI "enum nvme_feat_plm_window_select sel " ","
+.BI "__u16 nvmsetid " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "sel" 12
+Window Select
+.IP "nvmsetid" 12
+NVM Set Identifier
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_power_mgmt.2 b/doc/man/nvme_set_features_power_mgmt.2
new file mode 100644
index 0000000..531c41c
--- /dev/null
+++ b/doc/man/nvme_set_features_power_mgmt.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_power_mgmt" 9 "nvme_set_features_power_mgmt" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_power_mgmt \- Set power management feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_power_mgmt
+.BI "(int fd " ","
+.BI "__u8 ps " ","
+.BI "__u8 wh " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "ps" 12
+Power State
+.IP "wh" 12
+Workload Hint
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_resv_mask.2 b/doc/man/nvme_set_features_resv_mask.2
new file mode 100644
index 0000000..cf129fe
--- /dev/null
+++ b/doc/man/nvme_set_features_resv_mask.2
@@ -0,0 +1,25 @@
+.TH "nvme_set_features_resv_mask" 9 "nvme_set_features_resv_mask" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_resv_mask \- Set reservation notification mask feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_resv_mask
+.BI "(int fd " ","
+.BI "__u32 mask " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "mask" 12
+Reservation Notification Mask Field
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+
+Deprecated: doesn't support specifying a NSID.
+Use \fBnvme_set_features_resv_mask2\fP instead.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_resv_mask2.2 b/doc/man/nvme_set_features_resv_mask2.2
new file mode 100644
index 0000000..454b8c8
--- /dev/null
+++ b/doc/man/nvme_set_features_resv_mask2.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_resv_mask2" 9 "nvme_set_features_resv_mask2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_resv_mask2 \- Set reservation notification mask feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_resv_mask2
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u32 mask " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "mask" 12
+Reservation Notification Mask Field
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_resv_persist.2 b/doc/man/nvme_set_features_resv_persist.2
new file mode 100644
index 0000000..534b664
--- /dev/null
+++ b/doc/man/nvme_set_features_resv_persist.2
@@ -0,0 +1,25 @@
+.TH "nvme_set_features_resv_persist" 9 "nvme_set_features_resv_persist" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_resv_persist \- Set persist through power loss feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_resv_persist
+.BI "(int fd " ","
+.BI "bool ptpl " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "ptpl" 12
+Persist Through Power Loss
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+
+Deprecated: doesn't support specifying a NSID.
+Use \fBnvme_set_features_resv_persist2\fP instead.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_resv_persist2.2 b/doc/man/nvme_set_features_resv_persist2.2
new file mode 100644
index 0000000..70eed52
--- /dev/null
+++ b/doc/man/nvme_set_features_resv_persist2.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_resv_persist2" 9 "nvme_set_features_resv_persist2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_resv_persist2 \- Set persist through power loss feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_resv_persist2
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "bool ptpl " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "ptpl" 12
+Persist Through Power Loss
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_rrl.2 b/doc/man/nvme_set_features_rrl.2
new file mode 100644
index 0000000..c1b13d1
--- /dev/null
+++ b/doc/man/nvme_set_features_rrl.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_rrl" 9 "nvme_set_features_rrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_rrl \- Set read recovery level feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_rrl
+.BI "(int fd " ","
+.BI "__u8 rrl " ","
+.BI "__u16 nvmsetid " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "rrl" 12
+Read recovery level setting
+.IP "nvmsetid" 12
+NVM set id
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_sanitize.2 b/doc/man/nvme_set_features_sanitize.2
new file mode 100644
index 0000000..3cb9493
--- /dev/null
+++ b/doc/man/nvme_set_features_sanitize.2
@@ -0,0 +1,21 @@
+.TH "nvme_set_features_sanitize" 9 "nvme_set_features_sanitize" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_sanitize \- Set sanitize feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_sanitize
+.BI "(int fd " ","
+.BI "bool nodrm " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nodrm" 12
+No-Deallocate Response Mode
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_simple.2 b/doc/man/nvme_set_features_simple.2
new file mode 100644
index 0000000..dfc2a4e
--- /dev/null
+++ b/doc/man/nvme_set_features_simple.2
@@ -0,0 +1,27 @@
+.TH "nvme_set_features_simple" 9 "nvme_set_features_simple" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_simple \- Helper function for @nvme_set_features()
+.SH SYNOPSIS
+.B "int" nvme_set_features_simple
+.BI "(int fd " ","
+.BI "__u8 fid " ","
+.BI "__u32 nsid " ","
+.BI "__u32 cdw11 " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "fid" 12
+Feature identifier
+.IP "nsid" 12
+Namespace ID, if applicable
+.IP "cdw11" 12
+Value to set the feature to
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_sw_progress.2 b/doc/man/nvme_set_features_sw_progress.2
new file mode 100644
index 0000000..0d8a02d
--- /dev/null
+++ b/doc/man/nvme_set_features_sw_progress.2
@@ -0,0 +1,21 @@
+.TH "nvme_set_features_sw_progress" 9 "nvme_set_features_sw_progress" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_sw_progress \- Set pre-boot software load count feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_sw_progress
+.BI "(int fd " ","
+.BI "__u8 pbslc " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "pbslc" 12
+Pre-boot Software Load Count
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_temp_thresh.2 b/doc/man/nvme_set_features_temp_thresh.2
new file mode 100644
index 0000000..cf435e3
--- /dev/null
+++ b/doc/man/nvme_set_features_temp_thresh.2
@@ -0,0 +1,27 @@
+.TH "nvme_set_features_temp_thresh" 9 "nvme_set_features_temp_thresh" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_temp_thresh \- Set temperature threshold feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_temp_thresh
+.BI "(int fd " ","
+.BI "__u16 tmpth " ","
+.BI "__u8 tmpsel " ","
+.BI "enum nvme_feat_tmpthresh_thsel thsel " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "tmpth" 12
+Temperature Threshold
+.IP "tmpsel" 12
+Threshold Temperature Select
+.IP "thsel" 12
+Threshold Type Select
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_timestamp.2 b/doc/man/nvme_set_features_timestamp.2
new file mode 100644
index 0000000..9268ba8
--- /dev/null
+++ b/doc/man/nvme_set_features_timestamp.2
@@ -0,0 +1,18 @@
+.TH "nvme_set_features_timestamp" 9 "nvme_set_features_timestamp" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_timestamp \- Set timestamp feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_timestamp
+.BI "(int fd " ","
+.BI "bool save " ","
+.BI "__u64 timestamp " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "save" 12
+Save value across power states
+.IP "timestamp" 12
+The current timestamp value to assign to this feature
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_volatile_wc.2 b/doc/man/nvme_set_features_volatile_wc.2
new file mode 100644
index 0000000..777a1a1
--- /dev/null
+++ b/doc/man/nvme_set_features_volatile_wc.2
@@ -0,0 +1,21 @@
+.TH "nvme_set_features_volatile_wc" 9 "nvme_set_features_volatile_wc" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_volatile_wc \- Set volatile write cache feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_volatile_wc
+.BI "(int fd " ","
+.BI "bool wce " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "wce" 12
+Write cache enable
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_write_atomic.2 b/doc/man/nvme_set_features_write_atomic.2
new file mode 100644
index 0000000..fe0848f
--- /dev/null
+++ b/doc/man/nvme_set_features_write_atomic.2
@@ -0,0 +1,21 @@
+.TH "nvme_set_features_write_atomic" 9 "nvme_set_features_write_atomic" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_write_atomic \- Set write atomic feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_write_atomic
+.BI "(int fd " ","
+.BI "bool dn " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "dn" 12
+Disable Normal
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_write_protect.2 b/doc/man/nvme_set_features_write_protect.2
new file mode 100644
index 0000000..d8e7d61
--- /dev/null
+++ b/doc/man/nvme_set_features_write_protect.2
@@ -0,0 +1,25 @@
+.TH "nvme_set_features_write_protect" 9 "nvme_set_features_write_protect" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_write_protect \- Set write protect feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_write_protect
+.BI "(int fd " ","
+.BI "enum nvme_feat_nswpcfg_state state " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "state" 12
+Write Protection State
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "DESCRIPTION"
+
+Deprecated: doesn't support specifying a NSID.
+Use \fBnvme_set_features_write_protect2\fP instead.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_features_write_protect2.2 b/doc/man/nvme_set_features_write_protect2.2
new file mode 100644
index 0000000..5591cd3
--- /dev/null
+++ b/doc/man/nvme_set_features_write_protect2.2
@@ -0,0 +1,24 @@
+.TH "nvme_set_features_write_protect2" 9 "nvme_set_features_write_protect2" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_features_write_protect2 \- Set write protect feature
+.SH SYNOPSIS
+.B "int" nvme_set_features_write_protect2
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "enum nvme_feat_nswpcfg_state state " ","
+.BI "bool save " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "state" 12
+Write Protection State
+.IP "save" 12
+Save value across power states
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_keyring.2 b/doc/man/nvme_set_keyring.2
new file mode 100644
index 0000000..81a08ad
--- /dev/null
+++ b/doc/man/nvme_set_keyring.2
@@ -0,0 +1,15 @@
+.TH "nvme_set_keyring" 9 "nvme_set_keyring" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_keyring \- Link keyring for lookup
+.SH SYNOPSIS
+.B "int" nvme_set_keyring
+.BI "(long keyring_id " ");"
+.SH ARGUMENTS
+.IP "keyring_id" 12
+Keyring id
+.SH "DESCRIPTION"
+Links \fIkeyring_id\fP into the session keyring such that
+its keys are available for further key lookups.
+.SH "RETURN"
+0 on success, a negative number on error
+with errno set.
diff --git a/doc/man/nvme_set_property.2 b/doc/man/nvme_set_property.2
new file mode 100644
index 0000000..1ef7c5a
--- /dev/null
+++ b/doc/man/nvme_set_property.2
@@ -0,0 +1,15 @@
+.TH "nvme_set_property" 9 "nvme_set_property" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_property \- Set controller property
+.SH SYNOPSIS
+.B "int" nvme_set_property
+.BI "(struct nvme_set_property_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_set_property_args\fP argument structure
+.SH "DESCRIPTION"
+This is an NVMe-over-Fabrics specific command, not applicable to PCIe. These
+properties align to the PCI MMIO controller registers.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_set_root.2 b/doc/man/nvme_set_root.2
new file mode 100644
index 0000000..8f90e50
--- /dev/null
+++ b/doc/man/nvme_set_root.2
@@ -0,0 +1,16 @@
+.TH "nvme_set_root" 9 "nvme_set_root" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_set_root \- Set nvme_root_t context
+.SH SYNOPSIS
+.B "void" nvme_set_root
+.BI "(nvme_root_t r " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t context
+.SH "DESCRIPTION"
+In order to be able to log from code paths where no root object is passed in
+via the arguments use the the default one which can be set via this call.
+When creating a new root object with \fInvme_create_root\fP the global root object
+will be set as well. This means the global root object is always pointing to
+the latest created root object. Note the first \fInvme_free_tree\fP call will reset
+the global root object.
diff --git a/doc/man/nvme_smart_crit.2 b/doc/man/nvme_smart_crit.2
new file mode 100644
index 0000000..828124f
--- /dev/null
+++ b/doc/man/nvme_smart_crit.2
@@ -0,0 +1,54 @@
+.TH "libnvme" 9 "enum nvme_smart_crit" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_smart_crit \- Critical Warning
+.SH SYNOPSIS
+enum nvme_smart_crit {
+.br
+.BI " NVME_SMART_CRIT_SPARE"
+,
+.br
+.br
+.BI " NVME_SMART_CRIT_TEMPERATURE"
+,
+.br
+.br
+.BI " NVME_SMART_CRIT_DEGRADED"
+,
+.br
+.br
+.BI " NVME_SMART_CRIT_MEDIA"
+,
+.br
+.br
+.BI " NVME_SMART_CRIT_VOLATILE_MEMORY"
+,
+.br
+.br
+.BI " NVME_SMART_CRIT_PMR_RO"
+
+};
+.SH Constants
+.IP "NVME_SMART_CRIT_SPARE" 12
+If set, then the available spare capacity has fallen
+below the threshold.
+.IP "NVME_SMART_CRIT_TEMPERATURE" 12
+If set, then a temperature is either greater
+than or equal to an over temperature threshold; or
+less than or equal to an under temperature threshold.
+.IP "NVME_SMART_CRIT_DEGRADED" 12
+If set, then the NVM subsystem reliability has
+been degraded due to significant media related errors
+or any internal error that degrades NVM subsystem
+reliability.
+.IP "NVME_SMART_CRIT_MEDIA" 12
+If set, then all of the media has been placed in read
+only mode. The controller shall not set this bit if
+the read-only condition on the media is a result of
+a change in the write protection state of a namespace.
+.IP "NVME_SMART_CRIT_VOLATILE_MEMORY" 12
+If set, then the volatile memory backup
+device has failed. This field is only valid if the
+controller has a volatile memory backup solution.
+.IP "NVME_SMART_CRIT_PMR_RO" 12
+If set, then the Persistent Memory Region has become
+read-only or unreliable.
diff --git a/doc/man/nvme_smart_egcw.2 b/doc/man/nvme_smart_egcw.2
new file mode 100644
index 0000000..28d2205
--- /dev/null
+++ b/doc/man/nvme_smart_egcw.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "enum nvme_smart_egcw" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_smart_egcw \- Endurance Group Critical Warning Summary
+.SH SYNOPSIS
+enum nvme_smart_egcw {
+.br
+.BI " NVME_SMART_EGCW_SPARE"
+,
+.br
+.br
+.BI " NVME_SMART_EGCW_DEGRADED"
+,
+.br
+.br
+.BI " NVME_SMART_EGCW_RO"
+
+};
+.SH Constants
+.IP "NVME_SMART_EGCW_SPARE" 12
+If set, then the available spare capacity of one or
+more Endurance Groups has fallen below the threshold.
+.IP "NVME_SMART_EGCW_DEGRADED" 12
+If set, then the reliability of one or more
+Endurance Groups has been degraded due to significant
+media related errors or any internal error that
+degrades NVM subsystem reliability.
+.IP "NVME_SMART_EGCW_RO" 12
+If set, then the namespaces in one or more Endurance
+Groups have been placed in read only mode not as
+a result of a change in the write protection state
+of a namespace.
diff --git a/doc/man/nvme_smart_log.2 b/doc/man/nvme_smart_log.2
new file mode 100644
index 0000000..ffeb798
--- /dev/null
+++ b/doc/man/nvme_smart_log.2
@@ -0,0 +1,235 @@
+.TH "libnvme" 9 "struct nvme_smart_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_smart_log \- SMART / Health Information Log (Log Identifier 02h)
+.SH SYNOPSIS
+struct nvme_smart_log {
+.br
+.BI " __u8 critical_warning;"
+.br
+.BI " __u8 temperature[2];"
+.br
+.BI " __u8 avail_spare;"
+.br
+.BI " __u8 spare_thresh;"
+.br
+.BI " __u8 percent_used;"
+.br
+.BI " __u8 endu_grp_crit_warn_sumry;"
+.br
+.BI " __u8 rsvd7[25];"
+.br
+.BI " __u8 data_units_read[16];"
+.br
+.BI " __u8 data_units_written[16];"
+.br
+.BI " __u8 host_reads[16];"
+.br
+.BI " __u8 host_writes[16];"
+.br
+.BI " __u8 ctrl_busy_time[16];"
+.br
+.BI " __u8 power_cycles[16];"
+.br
+.BI " __u8 power_on_hours[16];"
+.br
+.BI " __u8 unsafe_shutdowns[16];"
+.br
+.BI " __u8 media_errors[16];"
+.br
+.BI " __u8 num_err_log_entries[16];"
+.br
+.BI " __le32 warning_temp_time;"
+.br
+.BI " __le32 critical_comp_time;"
+.br
+.BI " __le16 temp_sensor[8];"
+.br
+.BI " __le32 thm_temp1_trans_count;"
+.br
+.BI " __le32 thm_temp2_trans_count;"
+.br
+.BI " __le32 thm_temp1_total_time;"
+.br
+.BI " __le32 thm_temp2_total_time;"
+.br
+.BI " __u8 rsvd232[280];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "critical_warning" 12
+This field indicates critical warnings for the state
+of the controller. Critical warnings may result in an
+asynchronous event notification to the host. Bits in
+this field represent the current associated state and
+are not persistent (see \fIenum nvme_smart_crit\fP).
+.IP "temperature" 12
+Composite Temperature: Contains a value corresponding
+to a temperature in Kelvins that represents the current
+composite temperature of the controller and namespace(s)
+associated with that controller. The manner in which
+this value is computed is implementation specific and
+may not represent the actual temperature of any physical
+point in the NVM subsystem. Warning and critical
+overheating composite temperature threshold values are
+reported by the WCTEMP and CCTEMP fields in the Identify
+Controller data structure.
+.IP "avail_spare" 12
+Available Spare: Contains a normalized percentage (0%
+to 100%) of the remaining spare capacity available.
+.IP "spare_thresh" 12
+Available Spare Threshold: When the Available Spare
+falls below the threshold indicated in this field, an
+asynchronous event completion may occur. The value is
+indicated as a normalized percentage (0% to 100%).
+The values 101 to 255 are reserved.
+.IP "percent_used" 12
+Percentage Used: Contains a vendor specific estimate
+of the percentage of NVM subsystem life used based on
+the actual usage and the manufacturer's prediction of
+NVM life. A value of 100 indicates that the estimated
+endurance of the NVM in the NVM subsystem has been
+consumed, but may not indicate an NVM subsystem failure.
+The value is allowed to exceed 100. Percentages greater
+than 254 shall be represented as 255. This value shall
+be updated once per power-on hour (when the controller
+is not in a sleep state).
+.IP "endu_grp_crit_warn_sumry" 12
+Endurance Group Critical Warning Summary: This field
+indicates critical warnings for the state of Endurance
+Groups. Bits in this field represent the current associated
+state and are not persistent (see \fIenum nvme_smart_egcw\fP).
+.IP "rsvd7" 12
+Reserved
+.IP "data_units_read" 12
+Data Units Read: Contains the number of 512 byte data
+units the host has read from the controller; this value
+does not include metadata. This value is reported in
+thousands (i.e., a value of 1 corresponds to 1000
+units of 512 bytes read) and is rounded up (e.g., one
+indicates the that number of 512 byte data units read
+is from 1 to 1000, three indicates that the number of
+512 byte data units read is from 2001 to 3000). When
+the LBA size is a value other than 512 bytes, the
+controller shall convert the amount of data read to
+512 byte units. For the NVM command set, logical blocks
+read as part of Compare, Read, and Verify operations
+shall be included in this value. A value of 0h in
+this field indicates that the number of Data Units Read
+is not reported.
+.IP "data_units_written" 12
+Data Units Written: Contains the number of 512 byte
+data units the host has written to the controller;
+this value does not include metadata. This value is
+reported in thousands (i.e., a value of 1 corresponds
+to 1000 units of 512 bytes written) and is rounded up
+(e.g., one indicates that the number of 512 byte data
+units written is from 1 to 1,000, three indicates that
+the number of 512 byte data units written is from 2001
+to 3000). When the LBA size is a value other than 512
+bytes, the controller shall convert the amount of data
+written to 512 byte units. For the NVM command set,
+logical blocks written as part of Write operations shall
+be included in this value. Write Uncorrectable commands
+and Write Zeroes commands shall not impact this value.
+A value of 0h in this field indicates that the number
+of Data Units Written is not reported.
+.IP "host_reads" 12
+Host Read Commands: Contains the number of read commands
+completed by the controller. For the NVM command set,
+this value is the sum of the number of Compare commands
+and the number of Read commands.
+.IP "host_writes" 12
+Host Write Commands: Contains the number of write
+commands completed by the controller. For the NVM
+command set, this is the number of Write commands.
+.IP "ctrl_busy_time" 12
+Controller Busy Time: Contains the amount of time the
+controller is busy with I/O commands. The controller
+is busy when there is a command outstanding to an I/O
+Queue (specifically, a command was issued via an I/O
+Submission Queue Tail doorbell write and the corresponding
+completion queue entry has not been posted yet to the
+associated I/O Completion Queue). This value is
+reported in minutes.
+.IP "power_cycles" 12
+Power Cycles: Contains the number of power cycles.
+.IP "power_on_hours" 12
+Power On Hours: Contains the number of power-on hours.
+This may not include time that the controller was
+powered and in a non-operational power state.
+.IP "unsafe_shutdowns" 12
+Unsafe Shutdowns: Contains the number of unsafe
+shutdowns. This count is incremented when a Shutdown
+Notification (CC.SHN) is not received prior to loss of power.
+.IP "media_errors" 12
+Media and Data Integrity Errors: Contains the number
+of occurrences where the controller detected an
+unrecovered data integrity error. Errors such as
+uncorrectable ECC, CRC checksum failure, or LBA tag
+mismatch are included in this field. Errors introduced
+as a result of a Write Uncorrectable command may or
+may not be included in this field.
+.IP "num_err_log_entries" 12
+Number of Error Information Log Entries: Contains the
+number of Error Information log entries over the life
+of the controller.
+.IP "warning_temp_time" 12
+Warning Composite Temperature Time: Contains the amount
+of time in minutes that the controller is operational
+and the Composite Temperature is greater than or equal
+to the Warning Composite Temperature Threshold (WCTEMP)
+field and less than the Critical Composite Temperature
+Threshold (CCTEMP) field in the Identify Controller
+data structure. If the value of the WCTEMP or CCTEMP
+field is 0h, then this field is always cleared to 0h
+regardless of the Composite Temperature value.
+.IP "critical_comp_time" 12
+Critical Composite Temperature Time: Contains the amount
+of time in minutes that the controller is operational
+and the Composite Temperature is greater than or equal
+to the Critical Composite Temperature Threshold (CCTEMP)
+field in the Identify Controller data structure. If
+the value of the CCTEMP field is 0h, then this field
+is always cleared to 0h regardless of the Composite
+Temperature value.
+.IP "temp_sensor" 12
+Temperature Sensor 1-8: Contains the current temperature
+in degrees Kelvin reported by temperature sensors 1-8.
+The physical point in the NVM subsystem whose temperature
+is reported by the temperature sensor and the temperature
+accuracy is implementation specific. An implementation
+that does not implement the temperature sensor reports
+a value of 0h.
+.IP "thm_temp1_trans_count" 12
+Thermal Management Temperature 1 Transition Count:
+Contains the number of times the controller transitioned
+to lower power active power states or performed vendor
+specific thermal management actions while minimizing
+the impact on performance in order to attempt to reduce
+the Composite Temperature because of the host controlled
+thermal management feature (i.e., the Composite
+Temperature rose above the Thermal Management
+Temperature 1). This counter shall not wrap once the
+value FFFFFFFFh is reached. A value of 0h, indicates
+that this transition has never occurred or this field
+is not implemented.
+.IP "thm_temp2_trans_count" 12
+Thermal Management Temperature 2 Transition Count
+.IP "thm_temp1_total_time" 12
+Total Time For Thermal Management Temperature 1:
+Contains the number of seconds that the controller
+had transitioned to lower power active power states or
+performed vendor specific thermal management actions
+while minimizing the impact on performance in order to
+attempt to reduce the Composite Temperature because of
+the host controlled thermal management feature. This
+counter shall not wrap once the value FFFFFFFFh is
+reached. A value of 0h, indicates that this transition
+has never occurred or this field is not implemented.
+.IP "thm_temp2_total_time" 12
+Total Time For Thermal Management Temperature 2
+.IP "rsvd232" 12
+Reserved
diff --git a/doc/man/nvme_st_code.2 b/doc/man/nvme_st_code.2
new file mode 100644
index 0000000..913a09a
--- /dev/null
+++ b/doc/man/nvme_st_code.2
@@ -0,0 +1,43 @@
+.TH "libnvme" 9 "enum nvme_st_code" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_st_code \- Self-test Code value
+.SH SYNOPSIS
+enum nvme_st_code {
+.br
+.BI " NVME_ST_CODE_RESERVED"
+,
+.br
+.br
+.BI " NVME_ST_CODE_SHORT"
+,
+.br
+.br
+.BI " NVME_ST_CODE_EXTENDED"
+,
+.br
+.br
+.BI " NVME_ST_CODE_VS"
+,
+.br
+.br
+.BI " NVME_ST_CODE_ABORT"
+,
+.br
+.br
+.BI " NVME_ST_CODE_SHIFT"
+
+};
+.SH Constants
+.IP "NVME_ST_CODE_RESERVED" 12
+Reserved.
+.IP "NVME_ST_CODE_SHORT" 12
+Short device self-test operation.
+.IP "NVME_ST_CODE_EXTENDED" 12
+Extended device self-test operation.
+.IP "NVME_ST_CODE_VS" 12
+Vendor specific.
+.IP "NVME_ST_CODE_ABORT" 12
+Abort device self-test operation.
+.IP "NVME_ST_CODE_SHIFT" 12
+Shift amount to get the code value from the
+\fIstruct nvme_st_result\fP.dsts field.
diff --git a/doc/man/nvme_st_curr_op.2 b/doc/man/nvme_st_curr_op.2
new file mode 100644
index 0000000..d09ef83
--- /dev/null
+++ b/doc/man/nvme_st_curr_op.2
@@ -0,0 +1,50 @@
+.TH "libnvme" 9 "enum nvme_st_curr_op" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_st_curr_op \- Current Device Self-Test Operation
+.SH SYNOPSIS
+enum nvme_st_curr_op {
+.br
+.BI " NVME_ST_CURR_OP_NOT_RUNNING"
+,
+.br
+.br
+.BI " NVME_ST_CURR_OP_SHORT"
+,
+.br
+.br
+.BI " NVME_ST_CURR_OP_EXTENDED"
+,
+.br
+.br
+.BI " NVME_ST_CURR_OP_VS"
+,
+.br
+.br
+.BI " NVME_ST_CURR_OP_RESERVED"
+,
+.br
+.br
+.BI " NVME_ST_CURR_OP_MASK"
+,
+.br
+.br
+.BI " NVME_ST_CURR_OP_CMPL_MASK"
+
+};
+.SH Constants
+.IP "NVME_ST_CURR_OP_NOT_RUNNING" 12
+No device self-test operation in progress.
+.IP "NVME_ST_CURR_OP_SHORT" 12
+Short device self-test operation in progress.
+.IP "NVME_ST_CURR_OP_EXTENDED" 12
+Extended device self-test operation in progress.
+.IP "NVME_ST_CURR_OP_VS" 12
+Vendor specific.
+.IP "NVME_ST_CURR_OP_RESERVED" 12
+Reserved.
+.IP "NVME_ST_CURR_OP_MASK" 12
+Mask to get the current operation value from the
+\fIstruct nvme_self_test_log\fP.current_operation field.
+.IP "NVME_ST_CURR_OP_CMPL_MASK" 12
+Mask to get the current operation completion value
+from the \fIstruct nvme_self_test_log\fP.completion field.
diff --git a/doc/man/nvme_st_result.2 b/doc/man/nvme_st_result.2
new file mode 100644
index 0000000..f773b7e
--- /dev/null
+++ b/doc/man/nvme_st_result.2
@@ -0,0 +1,77 @@
+.TH "libnvme" 9 "struct nvme_st_result" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_st_result \- Self-test Result
+.SH SYNOPSIS
+struct nvme_st_result {
+.br
+.BI " __u8 dsts;"
+.br
+.BI " __u8 seg;"
+.br
+.BI " __u8 vdi;"
+.br
+.BI " __u8 rsvd;"
+.br
+.BI " __le64 poh;"
+.br
+.BI " __le32 nsid;"
+.br
+.BI " __le64 flba;"
+.br
+.BI " __u8 sct;"
+.br
+.BI " __u8 sc;"
+.br
+.BI " __u8 vs[2];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "dsts" 12
+Device Self-test Status: Indicates the device self-test code and the
+status of the operation (see \fIenum nvme_status_result\fP and \fIenum nvme_st_code\fP).
+.IP "seg" 12
+Segment Number: Iindicates the segment number where the first self-test
+failure occurred. If Device Self-test Status (\fIdsts\fP) is not set to
+#NVME_ST_RESULT_KNOWN_SEG_FAIL, then this field should be ignored.
+.IP "vdi" 12
+Valid Diagnostic Information: Indicates the diagnostic failure
+information that is reported. See \fIenum nvme_st_valid_diag_info\fP.
+.IP "rsvd" 12
+Reserved
+.IP "poh" 12
+Power On Hours (POH): Indicates the number of power-on hours at the
+time the device self-test operation was completed or aborted. This
+does not include time that the controller was powered and in a low
+power state condition.
+.IP "nsid" 12
+Namespace Identifier (NSID): Indicates the namespace that the Failing
+LBA occurred on. Valid only when the NSID Valid bit
+(#NVME_ST_VALID_DIAG_INFO_NSID) is set in the Valid Diagnostic
+Information (\fIvdi\fP) field.
+.IP "flba" 12
+Failing LBA: indicates the LBA of the logical block that caused the
+test to fail. If the device encountered more than one failed logical
+block during the test, then this field only indicates one of those
+failed logical blocks. Valid only when the NSID Valid bit
+(#NVME_ST_VALID_DIAG_INFO_FLBA) is set in the Valid Diagnostic
+Information (\fIvdi\fP) field.
+.IP "sct" 12
+Status Code Type: This field may contain additional information related
+to errors or conditions. Bits 2:0 may contain additional information
+relating to errors or conditions that occurred during the device
+self-test operation represented in the same format used in the Status
+Code Type field of the completion queue entry (refer to \fIenum nvme_status_field\fP).
+Valid only when the NSID Valid bit (#NVME_ST_VALID_DIAG_INFO_SCT) is
+set in the Valid Diagnostic Information (\fIvdi\fP) field.
+.IP "sc" 12
+Status Code: This field may contain additional information relating
+to errors or conditions that occurred during the device self-test
+operation represented in the same format used in the Status Code field
+of the completion queue entry. Valid only when the SCT Valid bit
+(#NVME_ST_VALID_DIAG_INFO_SC) is set in the Valid Diagnostic
+Information (\fIvdi\fP) field.
+.IP "vs" 12
+Vendor Specific.
diff --git a/doc/man/nvme_st_valid_diag_info.2 b/doc/man/nvme_st_valid_diag_info.2
new file mode 100644
index 0000000..9d963a8
--- /dev/null
+++ b/doc/man/nvme_st_valid_diag_info.2
@@ -0,0 +1,34 @@
+.TH "libnvme" 9 "enum nvme_st_valid_diag_info" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_st_valid_diag_info \- Valid Diagnostic Information
+.SH SYNOPSIS
+enum nvme_st_valid_diag_info {
+.br
+.BI " NVME_ST_VALID_DIAG_INFO_NSID"
+,
+.br
+.br
+.BI " NVME_ST_VALID_DIAG_INFO_FLBA"
+,
+.br
+.br
+.BI " NVME_ST_VALID_DIAG_INFO_SCT"
+,
+.br
+.br
+.BI " NVME_ST_VALID_DIAG_INFO_SC"
+
+};
+.SH Constants
+.IP "NVME_ST_VALID_DIAG_INFO_NSID" 12
+NSID Valid: if set, then the contents of
+the Namespace Identifier field are valid.
+.IP "NVME_ST_VALID_DIAG_INFO_FLBA" 12
+FLBA Valid: if set, then the contents of
+the Failing LBA field are valid.
+.IP "NVME_ST_VALID_DIAG_INFO_SCT" 12
+SCT Valid: if set, then the contents of
+the Status Code Type field are valid.
+.IP "NVME_ST_VALID_DIAG_INFO_SC" 12
+SC Valid: if set, then the contents of
+the Status Code field are valid.
diff --git a/doc/man/nvme_status_code.2 b/doc/man/nvme_status_code.2
new file mode 100644
index 0000000..502bda8
--- /dev/null
+++ b/doc/man/nvme_status_code.2
@@ -0,0 +1,12 @@
+.TH "nvme_status_code" 9 "nvme_status_code" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_status_code \- Returns the NVMe Status Code
+.SH SYNOPSIS
+.B "__u16" nvme_status_code
+.BI "(__u16 status_field " ");"
+.SH ARGUMENTS
+.IP "status_field" 12
+The NVMe Completion Queue Entry's Status Field
+See \fIenum nvme_status_field\fP
+.SH "RETURN"
+status code
diff --git a/doc/man/nvme_status_code_type.2 b/doc/man/nvme_status_code_type.2
new file mode 100644
index 0000000..e4e0e34
--- /dev/null
+++ b/doc/man/nvme_status_code_type.2
@@ -0,0 +1,12 @@
+.TH "nvme_status_code_type" 9 "nvme_status_code_type" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_status_code_type \- Returns the NVMe Status Code Type
+.SH SYNOPSIS
+.B "__u16" nvme_status_code_type
+.BI "(__u16 status_field " ");"
+.SH ARGUMENTS
+.IP "status_field" 12
+The NVMe Completion Queue Entry's Status Field
+See \fIenum nvme_status_field\fP
+.SH "RETURN"
+status code type
diff --git a/doc/man/nvme_status_equals.2 b/doc/man/nvme_status_equals.2
new file mode 100644
index 0000000..88eb928
--- /dev/null
+++ b/doc/man/nvme_status_equals.2
@@ -0,0 +1,17 @@
+.TH "nvme_status_equals" 9 "nvme_status_equals" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_status_equals \- helper to check a status against a type and value
+.SH SYNOPSIS
+.B "__u32" nvme_status_equals
+.BI "(int status " ","
+.BI "enum nvme_status_type type " ","
+.BI "unsigned int value " ");"
+.SH ARGUMENTS
+.IP "status" 12
+the (non-negative) return value from the NVMe API
+.IP "type" 12
+the status type
+.IP "value" 12
+the status value
+.SH "RETURN"
+true if \fIstatus\fP is of the specified type and value
diff --git a/doc/man/nvme_status_field.2 b/doc/man/nvme_status_field.2
new file mode 100644
index 0000000..2ef64fe
--- /dev/null
+++ b/doc/man/nvme_status_field.2
@@ -0,0 +1,1210 @@
+.TH "libnvme" 9 "enum nvme_status_field" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_status_field \- Defines all parts of the nvme status field: status code, status code type, and additional flags.
+.SH SYNOPSIS
+enum nvme_status_field {
+.br
+.BI " NVME_SCT_GENERIC"
+,
+.br
+.br
+.BI " NVME_SCT_CMD_SPECIFIC"
+,
+.br
+.br
+.BI " NVME_SCT_MEDIA"
+,
+.br
+.br
+.BI " NVME_SCT_PATH"
+,
+.br
+.br
+.BI " NVME_SCT_VS"
+,
+.br
+.br
+.BI " NVME_SCT_MASK"
+,
+.br
+.br
+.BI " NVME_SCT_SHIFT"
+,
+.br
+.br
+.BI " NVME_SC_MASK"
+,
+.br
+.br
+.BI " NVME_SC_SHIFT"
+,
+.br
+.br
+.BI " NVME_SC_SUCCESS"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_OPCODE"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_FIELD"
+,
+.br
+.br
+.BI " NVME_SC_CMDID_CONFLICT"
+,
+.br
+.br
+.BI " NVME_SC_DATA_XFER_ERROR"
+,
+.br
+.br
+.BI " NVME_SC_POWER_LOSS"
+,
+.br
+.br
+.BI " NVME_SC_INTERNAL"
+,
+.br
+.br
+.BI " NVME_SC_ABORT_REQ"
+,
+.br
+.br
+.BI " NVME_SC_ABORT_QUEUE"
+,
+.br
+.br
+.BI " NVME_SC_FUSED_FAIL"
+,
+.br
+.br
+.BI " NVME_SC_FUSED_MISSING"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_NS"
+,
+.br
+.br
+.BI " NVME_SC_CMD_SEQ_ERROR"
+,
+.br
+.br
+.BI " NVME_SC_SGL_INVALID_LAST"
+,
+.br
+.br
+.BI " NVME_SC_SGL_INVALID_COUNT"
+,
+.br
+.br
+.BI " NVME_SC_SGL_INVALID_DATA"
+,
+.br
+.br
+.BI " NVME_SC_SGL_INVALID_METADATA"
+,
+.br
+.br
+.BI " NVME_SC_SGL_INVALID_TYPE"
+,
+.br
+.br
+.BI " NVME_SC_CMB_INVALID_USE"
+,
+.br
+.br
+.BI " NVME_SC_PRP_INVALID_OFFSET"
+,
+.br
+.br
+.BI " NVME_SC_AWU_EXCEEDED"
+,
+.br
+.br
+.BI " NVME_SC_OP_DENIED"
+,
+.br
+.br
+.BI " NVME_SC_SGL_INVALID_OFFSET"
+,
+.br
+.br
+.BI " NVME_SC_HOSTID_FORMAT"
+,
+.br
+.br
+.BI " NVME_SC_KAT_EXPIRED"
+,
+.br
+.br
+.BI " NVME_SC_KAT_INVALID"
+,
+.br
+.br
+.BI " NVME_SC_CMD_ABORTED_PREMEPT"
+,
+.br
+.br
+.BI " NVME_SC_SANITIZE_FAILED"
+,
+.br
+.br
+.BI " NVME_SC_SANITIZE_IN_PROGRESS"
+,
+.br
+.br
+.BI " NVME_SC_SGL_INVALID_GRANULARITY"
+,
+.br
+.br
+.BI " NVME_SC_CMD_IN_CMBQ_NOT_SUPP"
+,
+.br
+.br
+.BI " NVME_SC_NS_WRITE_PROTECTED"
+,
+.br
+.br
+.BI " NVME_SC_CMD_INTERRUPTED"
+,
+.br
+.br
+.BI " NVME_SC_TRAN_TPORT_ERROR"
+,
+.br
+.br
+.BI " NVME_SC_PROHIBITED_BY_CMD_AND_FEAT"
+,
+.br
+.br
+.BI " NVME_SC_ADMIN_CMD_MEDIA_NOT_READY"
+,
+.br
+.br
+.BI " NVME_SC_FDP_DISABLED"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_PLACEMENT_HANDLE_LIST"
+,
+.br
+.br
+.BI " NVME_SC_LBA_RANGE"
+,
+.br
+.br
+.BI " NVME_SC_CAP_EXCEEDED"
+,
+.br
+.br
+.BI " NVME_SC_NS_NOT_READY"
+,
+.br
+.br
+.BI " NVME_SC_RESERVATION_CONFLICT"
+,
+.br
+.br
+.BI " NVME_SC_FORMAT_IN_PROGRESS"
+,
+.br
+.br
+.BI " NVME_SC_CQ_INVALID"
+,
+.br
+.br
+.BI " NVME_SC_QID_INVALID"
+,
+.br
+.br
+.BI " NVME_SC_QUEUE_SIZE"
+,
+.br
+.br
+.BI " NVME_SC_ABORT_LIMIT"
+,
+.br
+.br
+.BI " NVME_SC_ABORT_MISSING"
+,
+.br
+.br
+.BI " NVME_SC_ASYNC_LIMIT"
+,
+.br
+.br
+.BI " NVME_SC_FIRMWARE_SLOT"
+,
+.br
+.br
+.BI " NVME_SC_FIRMWARE_IMAGE"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_VECTOR"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_LOG_PAGE"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_FORMAT"
+,
+.br
+.br
+.BI " NVME_SC_FW_NEEDS_CONV_RESET"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_QUEUE"
+,
+.br
+.br
+.BI " NVME_SC_FEATURE_NOT_SAVEABLE"
+,
+.br
+.br
+.BI " NVME_SC_FEATURE_NOT_CHANGEABLE"
+,
+.br
+.br
+.BI " NVME_SC_FEATURE_NOT_PER_NS"
+,
+.br
+.br
+.BI " NVME_SC_FW_NEEDS_SUBSYS_RESET"
+,
+.br
+.br
+.BI " NVME_SC_FW_NEEDS_RESET"
+,
+.br
+.br
+.BI " NVME_SC_FW_NEEDS_MAX_TIME"
+,
+.br
+.br
+.BI " NVME_SC_FW_ACTIVATE_PROHIBITED"
+,
+.br
+.br
+.BI " NVME_SC_OVERLAPPING_RANGE"
+,
+.br
+.br
+.BI " NVME_SC_NS_INSUFFICIENT_CAP"
+,
+.br
+.br
+.BI " NVME_SC_NS_ID_UNAVAILABLE"
+,
+.br
+.br
+.BI " NVME_SC_NS_ALREADY_ATTACHED"
+,
+.br
+.br
+.BI " NVME_SC_NS_IS_PRIVATE"
+,
+.br
+.br
+.BI " NVME_SC_NS_NOT_ATTACHED"
+,
+.br
+.br
+.BI " NVME_SC_THIN_PROV_NOT_SUPP"
+,
+.br
+.br
+.BI " NVME_SC_CTRL_LIST_INVALID"
+,
+.br
+.br
+.BI " NVME_SC_SELF_TEST_IN_PROGRESS"
+,
+.br
+.br
+.BI " NVME_SC_BP_WRITE_PROHIBITED"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_CTRL_ID"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_SEC_CTRL_STATE"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_CTRL_RESOURCES"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_RESOURCE_ID"
+,
+.br
+.br
+.BI " NVME_SC_PMR_SAN_PROHIBITED"
+,
+.br
+.br
+.BI " NVME_SC_ANA_GROUP_ID_INVALID"
+,
+.br
+.br
+.BI " NVME_SC_ANA_ATTACH_FAILED"
+,
+.br
+.br
+.BI " NVME_SC_INSUFFICIENT_CAP"
+,
+.br
+.br
+.BI " NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED"
+,
+.br
+.br
+.BI " NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED"
+,
+.br
+.br
+.BI " NVME_SC_IOCS_NOT_SUPPORTED"
+,
+.br
+.br
+.BI " NVME_SC_IOCS_NOT_ENABLED"
+,
+.br
+.br
+.BI " NVME_SC_IOCS_COMBINATION_REJECTED"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_IOCS"
+,
+.br
+.br
+.BI " NVME_SC_ID_UNAVAILABLE"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_DISCOVERY_INFO"
+,
+.br
+.br
+.BI " NVME_SC_ZONING_DATA_STRUCT_LOCKED"
+,
+.br
+.br
+.BI " NVME_SC_ZONING_DATA_STRUCT_NOTFND"
+,
+.br
+.br
+.BI " NVME_SC_INSUFFICIENT_DISC_RES"
+,
+.br
+.br
+.BI " NVME_SC_REQSTD_FUNCTION_DISABLED"
+,
+.br
+.br
+.BI " NVME_SC_ZONEGRP_ORIGINATOR_INVLD"
+,
+.br
+.br
+.BI " NVME_SC_BAD_ATTRIBUTES"
+,
+.br
+.br
+.BI " NVME_SC_INVALID_PI"
+,
+.br
+.br
+.BI " NVME_SC_READ_ONLY"
+,
+.br
+.br
+.BI " NVME_SC_CMD_SIZE_LIMIT_EXCEEDED"
+,
+.br
+.br
+.BI " NVME_SC_INCOMPATIBLE_NS"
+,
+.br
+.br
+.BI " NVME_SC_FAST_COPY_NOT_POSSIBLE"
+,
+.br
+.br
+.BI " NVME_SC_OVERLAPPING_IO_RANGE"
+,
+.br
+.br
+.BI " NVME_SC_INSUFFICIENT_RESOURCES"
+,
+.br
+.br
+.BI " NVME_SC_CONNECT_FORMAT"
+,
+.br
+.br
+.BI " NVME_SC_CONNECT_CTRL_BUSY"
+,
+.br
+.br
+.BI " NVME_SC_CONNECT_INVALID_PARAM"
+,
+.br
+.br
+.BI " NVME_SC_CONNECT_RESTART_DISC"
+,
+.br
+.br
+.BI " NVME_SC_CONNECT_INVALID_HOST"
+,
+.br
+.br
+.BI " NVME_SC_DISCONNECT_INVALID_QTYPE"
+,
+.br
+.br
+.BI " NVME_SC_DISCOVERY_RESTART"
+,
+.br
+.br
+.BI " NVME_SC_AUTH_REQUIRED"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_INVALID_OP_REQUEST"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_BOUNDARY_ERROR"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_FULL"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_READ_ONLY"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_OFFLINE"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_INVALID_WRITE"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_TOO_MANY_ACTIVE"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_TOO_MANY_OPENS"
+,
+.br
+.br
+.BI " NVME_SC_ZNS_INVAL_TRANSITION"
+,
+.br
+.br
+.BI " NVME_SC_WRITE_FAULT"
+,
+.br
+.br
+.BI " NVME_SC_READ_ERROR"
+,
+.br
+.br
+.BI " NVME_SC_GUARD_CHECK"
+,
+.br
+.br
+.BI " NVME_SC_APPTAG_CHECK"
+,
+.br
+.br
+.BI " NVME_SC_REFTAG_CHECK"
+,
+.br
+.br
+.BI " NVME_SC_COMPARE_FAILED"
+,
+.br
+.br
+.BI " NVME_SC_ACCESS_DENIED"
+,
+.br
+.br
+.BI " NVME_SC_UNWRITTEN_BLOCK"
+,
+.br
+.br
+.BI " NVME_SC_STORAGE_TAG_CHECK"
+,
+.br
+.br
+.BI " NVME_SC_ANA_INTERNAL_PATH_ERROR"
+,
+.br
+.br
+.BI " NVME_SC_ANA_PERSISTENT_LOSS"
+,
+.br
+.br
+.BI " NVME_SC_ANA_INACCESSIBLE"
+,
+.br
+.br
+.BI " NVME_SC_ANA_TRANSITION"
+,
+.br
+.br
+.BI " NVME_SC_CTRL_PATH_ERROR"
+,
+.br
+.br
+.BI " NVME_SC_HOST_PATH_ERROR"
+,
+.br
+.br
+.BI " NVME_SC_CMD_ABORTED_BY_HOST"
+,
+.br
+.br
+.BI " NVME_SC_CRD"
+,
+.br
+.br
+.BI " NVME_SC_MORE"
+,
+.br
+.br
+.BI " NVME_SC_DNR"
+
+};
+.SH Constants
+.IP "NVME_SCT_GENERIC" 12
+Generic errors applicable to multiple opcodes
+.IP "NVME_SCT_CMD_SPECIFIC" 12
+Errors associated to a specific opcode
+.IP "NVME_SCT_MEDIA" 12
+Errors associated with media and data integrity
+.IP "NVME_SCT_PATH" 12
+Errors associated with the paths connection
+.IP "NVME_SCT_VS" 12
+Vendor specific errors
+.IP "NVME_SCT_MASK" 12
+Mask to get the value of the Status Code Type
+.IP "NVME_SCT_SHIFT" 12
+Shift value to get the value of the Status
+Code Type
+.IP "NVME_SC_MASK" 12
+Mask to get the value of the status code.
+.IP "NVME_SC_SHIFT" 12
+Shift value to get the value of the status
+code.
+.IP "NVME_SC_SUCCESS" 12
+Successful Completion: The command
+completed without error.
+.IP "NVME_SC_INVALID_OPCODE" 12
+Invalid Command Opcode: A reserved coded
+value or an unsupported value in the
+command opcode field.
+.IP "NVME_SC_INVALID_FIELD" 12
+Invalid Field in Command: A reserved
+coded value or an unsupported value in a
+defined field.
+.IP "NVME_SC_CMDID_CONFLICT" 12
+Command ID Conflict: The command
+identifier is already in use.
+.IP "NVME_SC_DATA_XFER_ERROR" 12
+Data Transfer Error: Transferring the
+data or metadata associated with a
+command experienced an error.
+.IP "NVME_SC_POWER_LOSS" 12
+Commands Aborted due to Power Loss
+Notification: Indicates that the command
+was aborted due to a power loss
+notification.
+.IP "NVME_SC_INTERNAL" 12
+Internal Error: The command was not
+completed successfully due to an internal error.
+.IP "NVME_SC_ABORT_REQ" 12
+Command Abort Requested: The command was
+aborted due to an Abort command being
+received that specified the Submission
+Queue Identifier and Command Identifier
+of this command.
+.IP "NVME_SC_ABORT_QUEUE" 12
+Command Aborted due to SQ Deletion: The
+command was aborted due to a Delete I/O
+Submission Queue request received for the
+Submission Queue to which the command was
+submitted.
+.IP "NVME_SC_FUSED_FAIL" 12
+Command Aborted due to Failed Fused Command:
+The command was aborted due to the other
+command in a fused operation failing.
+.IP "NVME_SC_FUSED_MISSING" 12
+Aborted due to Missing Fused Command: The
+fused command was aborted due to the
+adjacent submission queue entry not
+containing a fused command that is the
+other command.
+.IP "NVME_SC_INVALID_NS" 12
+Invalid Namespace or Format: The
+namespace or the format of that namespace
+is invalid.
+.IP "NVME_SC_CMD_SEQ_ERROR" 12
+Command Sequence Error: The command was
+aborted due to a protocol violation in a
+multi-command sequence.
+.IP "NVME_SC_SGL_INVALID_LAST" 12
+Invalid SGL Segment Descriptor: The
+command includes an invalid SGL Last
+Segment or SGL Segment descriptor.
+.IP "NVME_SC_SGL_INVALID_COUNT" 12
+Invalid Number of SGL Descriptors: There
+is an SGL Last Segment descriptor or an
+SGL Segment descriptor in a location
+other than the last descriptor of a
+segment based on the length indicated.
+.IP "NVME_SC_SGL_INVALID_DATA" 12
+Data SGL Length Invalid: This may occur
+if the length of a Data SGL is too short.
+This may occur if the length of a Data
+SGL is too long and the controller does
+not support SGL transfers longer than the
+amount of data to be transferred as
+indicated in the SGL Support field of the
+Identify Controller data structure.
+.IP "NVME_SC_SGL_INVALID_METADATA" 12
+Metadata SGL Length Invalid: This may
+occur if the length of a Metadata SGL is
+too short. This may occur if the length
+of a Metadata SGL is too long and the
+controller does not support SGL transfers
+longer than the amount of data to be
+transferred as indicated in the SGL
+Support field of the Identify Controller
+data structure.
+.IP "NVME_SC_SGL_INVALID_TYPE" 12
+SGL Descriptor Type Invalid: The type of
+an SGL Descriptor is a type that is not
+supported by the controller.
+.IP "NVME_SC_CMB_INVALID_USE" 12
+Invalid Use of Controller Memory Buffer:
+The attempted use of the Controller
+Memory Buffer is not supported by the
+controller.
+.IP "NVME_SC_PRP_INVALID_OFFSET" 12
+PRP Offset Invalid: The Offset field for
+a PRP entry is invalid.
+.IP "NVME_SC_AWU_EXCEEDED" 12
+Atomic Write Unit Exceeded: The length
+specified exceeds the atomic write unit size.
+.IP "NVME_SC_OP_DENIED" 12
+Operation Denied: The command was denied
+due to lack of access rights. Refer to
+the appropriate security specification.
+.IP "NVME_SC_SGL_INVALID_OFFSET" 12
+SGL Offset Invalid: The offset specified
+in a descriptor is invalid. This may
+occur when using capsules for data
+transfers in NVMe over Fabrics
+implementations and an invalid offset in
+the capsule is specified.
+.IP "NVME_SC_HOSTID_FORMAT" 12
+Host Identifier Inconsistent Format: The
+NVM subsystem detected the simultaneous
+use of 64- bit and 128-bit Host
+Identifier values on different
+controllers.
+.IP "NVME_SC_KAT_EXPIRED" 12
+Keep Alive Timer Expired: The Keep Alive
+Timer expired.
+.IP "NVME_SC_KAT_INVALID" 12
+Keep Alive Timeout Invalid: The Keep
+Alive Timeout value specified is invalid.
+.IP "NVME_SC_CMD_ABORTED_PREMEPT" 12
+Command Aborted due to Preempt and Abort:
+The command was aborted due to a
+Reservation Acquire command.
+.IP "NVME_SC_SANITIZE_FAILED" 12
+Sanitize Failed: The most recent sanitize
+operation failed and no recovery action
+has been successfully completed.
+.IP "NVME_SC_SANITIZE_IN_PROGRESS" 12
+Sanitize In Progress: The requested
+function (e.g., command) is prohibited
+while a sanitize operation is in
+progress.
+.IP "NVME_SC_SGL_INVALID_GRANULARITY" 12
+SGL Data Block Granularity Invalid: The
+Address alignment or Length granularity
+for an SGL Data Block descriptor is
+invalid.
+.IP "NVME_SC_CMD_IN_CMBQ_NOT_SUPP" 12
+Command Not Supported for Queue in CMB:
+The implementation does not support
+submission of the command to a Submission
+Queue in the Controller Memory Buffer or
+command completion to a Completion Queue
+in the Controller Memory Buffer.
+.IP "NVME_SC_NS_WRITE_PROTECTED" 12
+Namespace is Write Protected: The command
+is prohibited while the namespace is
+write protected as a result of a change
+in the namespace write protection state
+as defined by the Namespace Write
+Protection State Machine.
+.IP "NVME_SC_CMD_INTERRUPTED" 12
+Command Interrupted: Command processing
+was interrupted and the controller is
+unable to successfully complete the
+command. The host should retry the
+command.
+.IP "NVME_SC_TRAN_TPORT_ERROR" 12
+Transient Transport Error: A transient
+transport error was detected. If the
+command is retried on the same
+controller, the command is likely to
+succeed. A command that fails with a
+transient transport error four or more
+times should be treated as a persistent
+transport error that is not likely to
+succeed if retried on the same
+controller.
+.IP "NVME_SC_PROHIBITED_BY_CMD_AND_FEAT" 12
+Command Prohibited by Command and Feature
+Lockdown: The command was aborted due to
+command execution being prohibited by
+the Command and Feature Lockdown.
+.IP "NVME_SC_ADMIN_CMD_MEDIA_NOT_READY" 12
+Admin Command Media Not Ready: The Admin
+command requires access to media and
+the media is not ready.
+.IP "NVME_SC_FDP_DISABLED" 12
+Command is not allowed when
+Flexible Data Placement is disabled.
+.IP "NVME_SC_INVALID_PLACEMENT_HANDLE_LIST" 12
+The Placement Handle List is invalid
+due to invalid Reclaim Unit Handle Identifier or
+valid Reclaim Unit Handle Identifier but restricted or
+the Placement Handle List number of entries exceeded the
+maximum number allowed.
+.IP "NVME_SC_LBA_RANGE" 12
+LBA Out of Range: The command references
+an LBA that exceeds the size of the namespace.
+.IP "NVME_SC_CAP_EXCEEDED" 12
+Capacity Exceeded: Execution of the
+command has caused the capacity of the
+namespace to be exceeded.
+.IP "NVME_SC_NS_NOT_READY" 12
+Namespace Not Ready: The namespace is not
+ready to be accessed as a result of a
+condition other than a condition that is
+reported as an Asymmetric Namespace
+Access condition.
+.IP "NVME_SC_RESERVATION_CONFLICT" 12
+Reservation Conflict: The command was
+aborted due to a conflict with a
+reservation held on the accessed
+namespace.
+.IP "NVME_SC_FORMAT_IN_PROGRESS" 12
+Format In Progress: A Format NVM command
+is in progress on the namespace.
+.IP "NVME_SC_CQ_INVALID" 12
+Completion Queue Invalid: The Completion
+Queue identifier specified in the command
+does not exist.
+.IP "NVME_SC_QID_INVALID" 12
+Invalid Queue Identifier: The creation of
+the I/O Completion Queue failed due to an
+invalid queue identifier specified as
+part of the command. An invalid queue
+identifier is one that is currently in
+use or one that is outside the range
+supported by the controller.
+.IP "NVME_SC_QUEUE_SIZE" 12
+Invalid Queue Size: The host attempted to
+create an I/O Completion Queue with an
+invalid number of entries.
+.IP "NVME_SC_ABORT_LIMIT" 12
+Abort Command Limit Exceeded: The number
+of concurrently outstanding Abort commands
+has exceeded the limit indicated in the
+Identify Controller data structure.
+.IP "NVME_SC_ABORT_MISSING" 12
+Abort Command is missing: The abort
+command is missing.
+.IP "NVME_SC_ASYNC_LIMIT" 12
+Asynchronous Event Request Limit
+Exceeded: The number of concurrently
+outstanding Asynchronous Event Request
+commands has been exceeded.
+.IP "NVME_SC_FIRMWARE_SLOT" 12
+Invalid Firmware Slot: The firmware slot
+indicated is invalid or read only. This
+error is indicated if the firmware slot
+exceeds the number supported.
+.IP "NVME_SC_FIRMWARE_IMAGE" 12
+Invalid Firmware Image: The firmware
+image specified for activation is invalid
+and not loaded by the controller.
+.IP "NVME_SC_INVALID_VECTOR" 12
+Invalid Interrupt Vector: The creation of
+the I/O Completion Queue failed due to an
+invalid interrupt vector specified as
+part of the command.
+.IP "NVME_SC_INVALID_LOG_PAGE" 12
+Invalid Log Page: The log page indicated
+is invalid. This error condition is also
+returned if a reserved log page is
+requested.
+.IP "NVME_SC_INVALID_FORMAT" 12
+Invalid Format: The LBA Format specified
+is not supported.
+.IP "NVME_SC_FW_NEEDS_CONV_RESET" 12
+Firmware Activation Requires Conventional Reset:
+The firmware commit was successful,
+however, activation of the firmware image
+requires a conventional reset.
+.IP "NVME_SC_INVALID_QUEUE" 12
+Invalid Queue Deletion: Invalid I/O
+Completion Queue specified to delete.
+.IP "NVME_SC_FEATURE_NOT_SAVEABLE" 12
+Feature Identifier Not Saveable: The
+Feature Identifier specified does not
+support a saveable value.
+.IP "NVME_SC_FEATURE_NOT_CHANGEABLE" 12
+Feature Not Changeable: The Feature
+Identifier is not able to be changed.
+.IP "NVME_SC_FEATURE_NOT_PER_NS" 12
+Feature Not Namespace Specific: The
+Feature Identifier specified is not
+namespace specific. The Feature
+Identifier settings apply across all
+namespaces.
+.IP "NVME_SC_FW_NEEDS_SUBSYS_RESET" 12
+Firmware Activation Requires NVM
+Subsystem Reset: The firmware commit was
+successful, however, activation of the
+firmware image requires an NVM Subsystem.
+.IP "NVME_SC_FW_NEEDS_RESET" 12
+Firmware Activation Requires Controller
+Level Reset: The firmware commit was
+successful; however, the image specified
+does not support being activated without
+a reset.
+.IP "NVME_SC_FW_NEEDS_MAX_TIME" 12
+Firmware Activation Requires Maximum Time
+Violation: The image specified if
+activated immediately would exceed the
+Maximum Time for Firmware Activation
+(MTFA) value reported in Identify
+Controller.
+.IP "NVME_SC_FW_ACTIVATE_PROHIBITED" 12
+Firmware Activation Prohibited: The image
+specified is being prohibited from
+activation by the controller for vendor
+specific reasons.
+.IP "NVME_SC_OVERLAPPING_RANGE" 12
+Overlapping Range: The downloaded
+firmware image has overlapping ranges.
+.IP "NVME_SC_NS_INSUFFICIENT_CAP" 12
+Namespace Insufficient Capacity: Creating
+the namespace requires more free space
+than is currently available.
+.IP "NVME_SC_NS_ID_UNAVAILABLE" 12
+Namespace Identifier Unavailable: The
+number of namespaces supported has been
+exceeded.
+.IP "NVME_SC_NS_ALREADY_ATTACHED" 12
+Namespace Already Attached: The
+controller is already attached to the
+namespace specified.
+.IP "NVME_SC_NS_IS_PRIVATE" 12
+Namespace Is Private: The namespace is
+private and is already attached to one
+controller.
+.IP "NVME_SC_NS_NOT_ATTACHED" 12
+Namespace Not Attached: The request to
+detach the controller could not be
+completed because the controller is not
+attached to the namespace.
+.IP "NVME_SC_THIN_PROV_NOT_SUPP" 12
+Thin Provisioning Not Supported: Thin
+provisioning is not supported by the
+controller.
+.IP "NVME_SC_CTRL_LIST_INVALID" 12
+Controller List Invalid: The controller
+list provided contains invalid controller
+ids.
+.IP "NVME_SC_SELF_TEST_IN_PROGRESS" 12
+Device Self-test In Progress: The controller
+or NVM subsystem already has a device
+self-test operation in process.
+.IP "NVME_SC_BP_WRITE_PROHIBITED" 12
+Boot Partition Write Prohibited: The
+command is trying to modify a locked Boot
+Partition.
+.IP "NVME_SC_INVALID_CTRL_ID" 12
+Invalid Controller Identifier:
+.IP "NVME_SC_INVALID_SEC_CTRL_STATE" 12
+Invalid Secondary Controller State
+.IP "NVME_SC_INVALID_CTRL_RESOURCES" 12
+Invalid Number of Controller Resources
+.IP "NVME_SC_INVALID_RESOURCE_ID" 12
+Invalid Resource Identifier
+.IP "NVME_SC_PMR_SAN_PROHIBITED" 12
+Sanitize Prohibited While Persistent
+Memory Region is Enabled
+.IP "NVME_SC_ANA_GROUP_ID_INVALID" 12
+ANA Group Identifier Invalid: The specified
+ANA Group Identifier (ANAGRPID) is not
+supported in the submitted command.
+.IP "NVME_SC_ANA_ATTACH_FAILED" 12
+ANA Attach Failed: The controller is not
+attached to the namespace as a result
+of an ANA condition.
+.IP "NVME_SC_INSUFFICIENT_CAP" 12
+Insufficient Capacity: Requested operation
+requires more free space than is currently
+available.
+.IP "NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED" 12
+Namespace Attachment Limit Exceeded:
+Attaching the ns to a controller causes
+max number of ns attachments allowed
+to be exceeded.
+.IP "NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED" 12
+Prohibition of Command Execution
+Not Supported
+.IP "NVME_SC_IOCS_NOT_SUPPORTED" 12
+I/O Command Set Not Supported
+.IP "NVME_SC_IOCS_NOT_ENABLED" 12
+I/O Command Set Not Enabled
+.IP "NVME_SC_IOCS_COMBINATION_REJECTED" 12
+I/O Command Set Combination Rejected
+.IP "NVME_SC_INVALID_IOCS" 12
+Invalid I/O Command Set
+.IP "NVME_SC_ID_UNAVAILABLE" 12
+Identifier Unavailable
+.IP "NVME_SC_INVALID_DISCOVERY_INFO" 12
+The discovery information provided in
+one or more extended discovery
+information entries is not applicable
+for the type of entity selected in
+the Entity Type (ETYPE) field of the
+Discovery Information Management
+command data portion’s header.
+.IP "NVME_SC_ZONING_DATA_STRUCT_LOCKED" 12
+The requested Zoning data structure
+is locked on the CDC.
+.IP "NVME_SC_ZONING_DATA_STRUCT_NOTFND" 12
+The requested Zoning data structure
+does not exist on the CDC.
+.IP "NVME_SC_INSUFFICIENT_DISC_RES" 12
+The number of discover information
+entries provided in the data portion
+of the Discovery Information
+Management command for a registration
+task (i.e., TAS field cleared to 0h)
+exceeds the available capacity for
+new discovery information entries on
+the CDC or DDC. This may be a
+transient condition.
+.IP "NVME_SC_REQSTD_FUNCTION_DISABLED" 12
+Fabric Zoning is not enabled on the
+CDC
+.IP "NVME_SC_ZONEGRP_ORIGINATOR_INVLD" 12
+The NQN contained in the ZoneGroup
+Originator field does not match the
+Host NQN used by the DDC to connect
+to the CDC.
+.IP "NVME_SC_BAD_ATTRIBUTES" 12
+Conflicting Dataset Management Attributes
+.IP "NVME_SC_INVALID_PI" 12
+Invalid Protection Information
+.IP "NVME_SC_READ_ONLY" 12
+Attempted Write to Read Only Range
+.IP "NVME_SC_CMD_SIZE_LIMIT_EXCEEDED" 12
+Command Size Limit Exceeded
+.IP "NVME_SC_INCOMPATIBLE_NS" 12
+Incompatible Namespace or Format: At
+least one source namespace and the
+destination namespace have incompatible
+formats.
+.IP "NVME_SC_FAST_COPY_NOT_POSSIBLE" 12
+Fast Copy Not Possible: The Fast Copy
+Only (FCO) bit was set to ‘1’ in a Source
+Range entry and the controller was not
+able to use fast copy operations to copy
+the specified data.
+.IP "NVME_SC_OVERLAPPING_IO_RANGE" 12
+Overlapping I/O Range: A source logical
+block range overlaps the destination
+logical block range.
+.IP "NVME_SC_INSUFFICIENT_RESOURCES" 12
+Insufficient Resources: A resource
+shortage prevented the controller from
+performing the requested copy.
+.IP "NVME_SC_CONNECT_FORMAT" 12
+Incompatible Format: The NVM subsystem
+does not support the record format
+specified by the host.
+.IP "NVME_SC_CONNECT_CTRL_BUSY" 12
+Controller Busy: The controller is
+already associated with a host.
+.IP "NVME_SC_CONNECT_INVALID_PARAM" 12
+Connect Invalid Parameters: One or more
+of the command parameters.
+.IP "NVME_SC_CONNECT_RESTART_DISC" 12
+Connect Restart Discovery: The NVM
+subsystem requested is not available.
+.IP "NVME_SC_CONNECT_INVALID_HOST" 12
+Connect Invalid Host: The host is either
+not allowed to establish an association
+to any controller in the NVM subsystem or
+the host is not allowed to establish an
+association to the specified controller
+.IP "NVME_SC_DISCONNECT_INVALID_QTYPE" 12
+Invalid Queue Type: The command was sent
+on the wrong queue type.
+.IP "NVME_SC_DISCOVERY_RESTART" 12
+Discover Restart: The snapshot of the
+records is now invalid or out of date.
+.IP "NVME_SC_AUTH_REQUIRED" 12
+Authentication Required: NVMe in-band
+authentication is required and the queue
+has not yet been authenticated.
+.IP "NVME_SC_ZNS_INVALID_OP_REQUEST" 12
+Invalid Zone Operation Request:
+The operation requested is invalid. This may be due to
+various conditions, including: attempting to allocate a
+ZRWA when a zone is not in the ZSE:Empty state; or
+invalid Flush Explicit ZRWA Range Send Zone Action
+operation.
+.IP "NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE" 12
+ZRWA Resources Unavailable:
+No ZRWAs are available.
+.IP "NVME_SC_ZNS_BOUNDARY_ERROR" 12
+Zone Boundary Error: The command specifies
+logical blocks in more than one zone.
+.IP "NVME_SC_ZNS_FULL" 12
+Zone Is Full: The accessed zone is in the
+ZSF:Full state.
+.IP "NVME_SC_ZNS_READ_ONLY" 12
+Zone Is Read Only: The accessed zone is
+in the ZSRO:Read Only state.
+.IP "NVME_SC_ZNS_OFFLINE" 12
+Zone Is Offline: The accessed zone is
+in the ZSO:Offline state.
+.IP "NVME_SC_ZNS_INVALID_WRITE" 12
+Zone Invalid Write: The write to a zone
+was not at the write pointer.
+.IP "NVME_SC_ZNS_TOO_MANY_ACTIVE" 12
+Too Many Active Zones: The controller
+does not allow additional active zones.
+.IP "NVME_SC_ZNS_TOO_MANY_OPENS" 12
+Too Many Open Zones: The controller does
+not allow additional open zones.
+.IP "NVME_SC_ZNS_INVAL_TRANSITION" 12
+Invalid Zone State Transition: The request
+is not a valid zone state transition.
+.IP "NVME_SC_WRITE_FAULT" 12
+Write Fault: The write data could not be
+committed to the media.
+.IP "NVME_SC_READ_ERROR" 12
+Unrecovered Read Error: The read data
+could not be recovered from the media.
+.IP "NVME_SC_GUARD_CHECK" 12
+End-to-end Guard Check Error: The command
+was aborted due to an end-to-end guard
+check failure.
+.IP "NVME_SC_APPTAG_CHECK" 12
+End-to-end Application Tag Check Error:
+The command was aborted due to an
+end-to-end application tag check failure.
+.IP "NVME_SC_REFTAG_CHECK" 12
+End-to-end Reference Tag Check Error: The
+command was aborted due to an end-to-end
+reference tag check failure.
+.IP "NVME_SC_COMPARE_FAILED" 12
+Compare Failure: The command failed due
+to a miscompare during a Compare command.
+.IP "NVME_SC_ACCESS_DENIED" 12
+Access Denied: Access to the namespace
+and/or LBA range is denied due to lack of
+access rights.
+.IP "NVME_SC_UNWRITTEN_BLOCK" 12
+Deallocated or Unwritten Logical Block:
+The command failed due to an attempt to
+read from or verify an LBA range
+containing a deallocated or unwritten
+logical block.
+.IP "NVME_SC_STORAGE_TAG_CHECK" 12
+End-to-End Storage Tag Check Error: The
+command was aborted due to an end-to-end
+storage tag check failure.
+.IP "NVME_SC_ANA_INTERNAL_PATH_ERROR" 12
+Internal Path Error: The command was not
+completed as the result of a controller
+internal error that is specific to the
+controller processing the command.
+.IP "NVME_SC_ANA_PERSISTENT_LOSS" 12
+Asymmetric Access Persistent Loss: The
+requested function (e.g., command) is not
+able to be performed as a result of the
+relationship between the controller and
+the namespace being in the ANA Persistent
+Loss state.
+.IP "NVME_SC_ANA_INACCESSIBLE" 12
+Asymmetric Access Inaccessible: The
+requested function (e.g., command) is not
+able to be performed as a result of the
+relationship between the controller and
+the namespace being in the ANA
+Inaccessible state.
+.IP "NVME_SC_ANA_TRANSITION" 12
+Asymmetric Access Transition: The
+requested function (e.g., command) is not
+able to be performed as a result of the
+relationship between the controller and
+the namespace transitioning between
+Asymmetric Namespace Access states.
+.IP "NVME_SC_CTRL_PATH_ERROR" 12
+Controller Pathing Error: A pathing error
+was detected by the controller.
+.IP "NVME_SC_HOST_PATH_ERROR" 12
+Host Pathing Error: A pathing error was
+detected by the host.
+.IP "NVME_SC_CMD_ABORTED_BY_HOST" 12
+Command Aborted By Host: The command was
+aborted as a result of host action.
+.IP "NVME_SC_CRD" 12
+Mask to get value of Command Retry Delay
+index
+.IP "NVME_SC_MORE" 12
+More bit. If set, more status information
+for this command as part of the Error
+Information log that may be retrieved with
+the Get Log Page command.
+.IP "NVME_SC_DNR" 12
+Do Not Retry bit. If set, if the same
+command is re-submitted to any controller
+in the NVM subsystem, then that
+re-submitted command is expected to fail.
diff --git a/doc/man/nvme_status_get_type.2 b/doc/man/nvme_status_get_type.2
new file mode 100644
index 0000000..d5fe855
--- /dev/null
+++ b/doc/man/nvme_status_get_type.2
@@ -0,0 +1,11 @@
+.TH "nvme_status_get_type" 9 "nvme_status_get_type" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_status_get_type \- extract the type from a nvme_* return value
+.SH SYNOPSIS
+.B "__u32" nvme_status_get_type
+.BI "(int status " ");"
+.SH ARGUMENTS
+.IP "status" 12
+the (non-negative) return value from the NVMe API
+.SH "RETURN"
+the type component of the status.
diff --git a/doc/man/nvme_status_get_value.2 b/doc/man/nvme_status_get_value.2
new file mode 100644
index 0000000..a44340a
--- /dev/null
+++ b/doc/man/nvme_status_get_value.2
@@ -0,0 +1,12 @@
+.TH "nvme_status_get_value" 9 "nvme_status_get_value" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_status_get_value \- extract the status value from a nvme_* return value
+.SH SYNOPSIS
+.B "__u32" nvme_status_get_value
+.BI "(int status " ");"
+.SH ARGUMENTS
+.IP "status" 12
+the (non-negative) return value from the NVMe API
+.SH "RETURN"
+the value component of the status; the set of values will depend
+on the status type.
diff --git a/doc/man/nvme_status_result.2 b/doc/man/nvme_status_result.2
new file mode 100644
index 0000000..6358dee
--- /dev/null
+++ b/doc/man/nvme_status_result.2
@@ -0,0 +1,87 @@
+.TH "libnvme" 9 "enum nvme_status_result" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_status_result \- Result of the device self-test operation
+.SH SYNOPSIS
+enum nvme_status_result {
+.br
+.BI " NVME_ST_RESULT_NO_ERR"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_ABORTED"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_CLR"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_NS_REMOVED"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_ABORTED_FORMAT"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_FATAL_ERR"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_UNKNOWN_SEG_FAIL"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_KNOWN_SEG_FAIL"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_ABORTED_UNKNOWN"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_ABORTED_SANITIZE"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_NOT_USED"
+,
+.br
+.br
+.BI " NVME_ST_RESULT_MASK"
+
+};
+.SH Constants
+.IP "NVME_ST_RESULT_NO_ERR" 12
+Operation completed without error.
+.IP "NVME_ST_RESULT_ABORTED" 12
+Operation was aborted by a Device Self-test command.
+.IP "NVME_ST_RESULT_CLR" 12
+Operation was aborted by a Controller Level Reset.
+.IP "NVME_ST_RESULT_NS_REMOVED" 12
+Operation was aborted due to a removal of
+a namespace from the namespace inventory.
+.IP "NVME_ST_RESULT_ABORTED_FORMAT" 12
+Operation was aborted due to the processing
+of a Format NVM command.
+.IP "NVME_ST_RESULT_FATAL_ERR" 12
+A fatal error or unknown test error occurred
+while the controller was executing the device
+self-test operation and the operation did
+not complete.
+.IP "NVME_ST_RESULT_UNKNOWN_SEG_FAIL" 12
+Operation completed with a segment that failed
+and the segment that failed is not known.
+.IP "NVME_ST_RESULT_KNOWN_SEG_FAIL" 12
+Operation completed with one or more failed
+segments and the first segment that failed
+is indicated in the Segment Number field.
+.IP "NVME_ST_RESULT_ABORTED_UNKNOWN" 12
+Operation was aborted for unknown reason.
+.IP "NVME_ST_RESULT_ABORTED_SANITIZE" 12
+Operation was aborted due to a sanitize operation.
+.IP "NVME_ST_RESULT_NOT_USED" 12
+Entry not used (does not contain a test result).
+.IP "NVME_ST_RESULT_MASK" 12
+Mask to get the status result value from
+the \fIstruct nvme_st_result\fP.dsts field.
diff --git a/doc/man/nvme_status_to_errno.2 b/doc/man/nvme_status_to_errno.2
new file mode 100644
index 0000000..aea4aec
--- /dev/null
+++ b/doc/man/nvme_status_to_errno.2
@@ -0,0 +1,15 @@
+.TH "nvme_status_to_errno" 9 "nvme_status_to_errno" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_status_to_errno \- Converts nvme return status to errno
+.SH SYNOPSIS
+.B "__u8" nvme_status_to_errno
+.BI "(int status " ","
+.BI "bool fabrics " ");"
+.SH ARGUMENTS
+.IP "status" 12
+Return status from an nvme passthrough command
+.IP "fabrics" 12
+Set to true if \fIstatus\fP is to a fabrics target.
+.SH "RETURN"
+An errno representing the nvme status if it is an nvme status field,
+or unchanged status is < 0 since errno is already set.
diff --git a/doc/man/nvme_status_to_string.2 b/doc/man/nvme_status_to_string.2
new file mode 100644
index 0000000..da37833
--- /dev/null
+++ b/doc/man/nvme_status_to_string.2
@@ -0,0 +1,15 @@
+.TH "nvme_status_to_string" 9 "nvme_status_to_string" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_status_to_string \- Returns string describing nvme return status.
+.SH SYNOPSIS
+.B "const char *" nvme_status_to_string
+.BI "(int status " ","
+.BI "bool fabrics " ");"
+.SH ARGUMENTS
+.IP "status" 12
+Return status from an nvme passthrough command
+.IP "fabrics" 12
+Set to true if \fIstatus\fP is to a fabrics target.
+.SH "RETURN"
+String representation of the nvme status if it is an nvme status field,
+or a standard errno string if status is < 0.
diff --git a/doc/man/nvme_status_type.2 b/doc/man/nvme_status_type.2
new file mode 100644
index 0000000..238cf6e
--- /dev/null
+++ b/doc/man/nvme_status_type.2
@@ -0,0 +1,40 @@
+.TH "libnvme" 9 "enum nvme_status_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_status_type \- type encoding for NVMe return values, when represented as an int.
+.SH SYNOPSIS
+enum nvme_status_type {
+.br
+.BI " NVME_STATUS_TYPE_SHIFT"
+,
+.br
+.br
+.BI " NVME_STATUS_TYPE_MASK"
+,
+.br
+.br
+.BI " NVME_STATUS_TYPE_NVME"
+,
+.br
+.br
+.BI " NVME_STATUS_TYPE_MI"
+
+};
+.SH Constants
+.IP "NVME_STATUS_TYPE_SHIFT" 12
+shift value for status bits
+.IP "NVME_STATUS_TYPE_MASK" 12
+mask value for status bits
+.IP "NVME_STATUS_TYPE_NVME" 12
+NVMe command status value, typically from CDW3
+.IP "NVME_STATUS_TYPE_MI" 12
+NVMe-MI header status
+.SH "Description"
+
+The nvme_* api returns an int, with negative values indicating an internal
+or syscall error, zero signifying success, positive values representing
+the NVMe status.
+
+That latter case (the NVMe status) may represent status values from
+different parts of the transport/controller/etc, and are at most 16 bits of
+data. So, we use the most-significant 3 bits of the signed int to indicate
+which type of status this is.
diff --git a/doc/man/nvme_streams_directive_params.2 b/doc/man/nvme_streams_directive_params.2
new file mode 100644
index 0000000..1df2958
--- /dev/null
+++ b/doc/man/nvme_streams_directive_params.2
@@ -0,0 +1,51 @@
+.TH "libnvme" 9 "struct nvme_streams_directive_params" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_streams_directive_params \- Streams Directive - Return Parameters Data Structure
+.SH SYNOPSIS
+struct nvme_streams_directive_params {
+.br
+.BI " __le16 msl;"
+.br
+.BI " __le16 nssa;"
+.br
+.BI " __le16 nsso;"
+.br
+.BI " __u8 nssc;"
+.br
+.BI " __u8 rsvd[9];"
+.br
+.BI " __le32 sws;"
+.br
+.BI " __le16 sgs;"
+.br
+.BI " __le16 nsa;"
+.br
+.BI " __le16 nso;"
+.br
+.BI " __u8 rsvd2[6];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "msl" 12
+Max Streams Limit
+.IP "nssa" 12
+NVM Subsystem Streams Available
+.IP "nsso" 12
+NVM Subsystem Streams Open
+.IP "nssc" 12
+NVM Subsystem Stream Capability
+.IP "rsvd" 12
+Reserved
+.IP "sws" 12
+Stream Write Size
+.IP "sgs" 12
+Stream Granularity Size
+.IP "nsa" 12
+Namespace Streams Allocated
+.IP "nso" 12
+Namespace Streams Open
+.IP "rsvd2" 12
+Reserved
diff --git a/doc/man/nvme_streams_directive_status.2 b/doc/man/nvme_streams_directive_status.2
new file mode 100644
index 0000000..2955ac3
--- /dev/null
+++ b/doc/man/nvme_streams_directive_status.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_streams_directive_status" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_streams_directive_status \- Streams Directive - Get Status Data Structure
+.SH SYNOPSIS
+struct nvme_streams_directive_status {
+.br
+.BI " __le16 osc;"
+.br
+.BI " __le16 sid[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "osc" 12
+Open Stream Count
+.IP "sid" 12
+Stream Identifier
diff --git a/doc/man/nvme_submit_admin_passthru.2 b/doc/man/nvme_submit_admin_passthru.2
new file mode 100644
index 0000000..3171798
--- /dev/null
+++ b/doc/man/nvme_submit_admin_passthru.2
@@ -0,0 +1,20 @@
+.TH "nvme_submit_admin_passthru" 9 "nvme_submit_admin_passthru" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_submit_admin_passthru \- Submit an nvme passthrough admin command
+.SH SYNOPSIS
+.B "int" nvme_submit_admin_passthru
+.BI "(int fd " ","
+.BI "struct nvme_passthru_cmd *cmd " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "cmd" 12
+The nvme admin command to send
+.IP "result" 12
+Optional field to return the result from the CQE DW0
+.SH "DESCRIPTION"
+Uses NVME_IOCTL_ADMIN_CMD for the ioctl request.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_submit_admin_passthru64.2 b/doc/man/nvme_submit_admin_passthru64.2
new file mode 100644
index 0000000..9815df5
--- /dev/null
+++ b/doc/man/nvme_submit_admin_passthru64.2
@@ -0,0 +1,20 @@
+.TH "nvme_submit_admin_passthru64" 9 "nvme_submit_admin_passthru64" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_submit_admin_passthru64 \- Submit a 64-bit nvme passthrough admin command
+.SH SYNOPSIS
+.B "int" nvme_submit_admin_passthru64
+.BI "(int fd " ","
+.BI "struct nvme_passthru_cmd64 *cmd " ","
+.BI "__u64 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "cmd" 12
+The nvme admin command to send
+.IP "result" 12
+Optional field to return the result from the CQE DW0-1
+.SH "DESCRIPTION"
+Uses NVME_IOCTL_ADMIN64_CMD for the ioctl request.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_submit_io_passthru.2 b/doc/man/nvme_submit_io_passthru.2
new file mode 100644
index 0000000..ccae5e4
--- /dev/null
+++ b/doc/man/nvme_submit_io_passthru.2
@@ -0,0 +1,20 @@
+.TH "nvme_submit_io_passthru" 9 "nvme_submit_io_passthru" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_submit_io_passthru \- Submit an nvme passthrough command
+.SH SYNOPSIS
+.B "int" nvme_submit_io_passthru
+.BI "(int fd " ","
+.BI "struct nvme_passthru_cmd *cmd " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "cmd" 12
+The nvme io command to send
+.IP "result" 12
+Optional field to return the result from the CQE DW0
+.SH "DESCRIPTION"
+Uses NVME_IOCTL_IO_CMD for the ioctl request.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_submit_io_passthru64.2 b/doc/man/nvme_submit_io_passthru64.2
new file mode 100644
index 0000000..914764a
--- /dev/null
+++ b/doc/man/nvme_submit_io_passthru64.2
@@ -0,0 +1,20 @@
+.TH "nvme_submit_io_passthru64" 9 "nvme_submit_io_passthru64" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_submit_io_passthru64 \- Submit a 64-bit nvme passthrough command
+.SH SYNOPSIS
+.B "int" nvme_submit_io_passthru64
+.BI "(int fd " ","
+.BI "struct nvme_passthru_cmd64 *cmd " ","
+.BI "__u64 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "cmd" 12
+The nvme io command to send
+.IP "result" 12
+Optional field to return the result from the CQE DW0-1
+.SH "DESCRIPTION"
+Uses NVME_IOCTL_IO64_CMD for the ioctl request.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_subsys_filter.2 b/doc/man/nvme_subsys_filter.2
new file mode 100644
index 0000000..5447271
--- /dev/null
+++ b/doc/man/nvme_subsys_filter.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsys_filter" 9 "nvme_subsys_filter" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsys_filter \- Filter for subsystems
+.SH SYNOPSIS
+.B "int" nvme_subsys_filter
+.BI "(const struct dirent *d " ");"
+.SH ARGUMENTS
+.IP "d" 12
+dirent to check
+.SH "RETURN"
+1 if \fId\fP matches, 0 otherwise
diff --git a/doc/man/nvme_subsys_type.2 b/doc/man/nvme_subsys_type.2
new file mode 100644
index 0000000..4217159
--- /dev/null
+++ b/doc/man/nvme_subsys_type.2
@@ -0,0 +1,37 @@
+.TH "libnvme" 9 "enum nvme_subsys_type" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_subsys_type \- Type of the NVM subsystem.
+.SH SYNOPSIS
+enum nvme_subsys_type {
+.br
+.BI " NVME_NQN_DISC"
+,
+.br
+.br
+.BI " NVME_NQN_NVME"
+,
+.br
+.br
+.BI " NVME_NQN_CURR"
+
+};
+.SH Constants
+.IP "NVME_NQN_DISC" 12
+Discovery type target subsystem. Describes a referral to another
+Discovery Service composed of Discovery controllers that provide
+additional discovery records. Multiple Referral entries may
+be reported for each Discovery Service (if that Discovery Service
+has multiple NVM subsystem ports or supports multiple protocols).
+.IP "NVME_NQN_NVME" 12
+NVME type target subsystem. Describes an NVM subsystem whose
+controllers may have attached namespaces (an NVM subsystem
+that is not composed of Discovery controllers). Multiple NVM
+Subsystem entries may be reported for each NVM subsystem if
+that NVM subsystem has multiple NVM subsystem ports.
+.IP "NVME_NQN_CURR" 12
+Current Discovery type target subsystem. Describes this Discovery
+subsystem (the Discovery Service that contains the controller
+processing the Get Log Page command). Multiple Current Discovery
+Subsystem entries may be reported for this Discovery subsystem
+if the current Discovery subsystem has multiple NVM subsystem
+ports.
diff --git a/doc/man/nvme_subsystem_first_ctrl.2 b/doc/man/nvme_subsystem_first_ctrl.2
new file mode 100644
index 0000000..9cb1518
--- /dev/null
+++ b/doc/man/nvme_subsystem_first_ctrl.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsystem_first_ctrl" 9 "nvme_subsystem_first_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_first_ctrl \- First ctrl iterator
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvme_subsystem_first_ctrl
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.SH "RETURN"
+First controller of an \fIs\fP iterator
diff --git a/doc/man/nvme_subsystem_first_ns.2 b/doc/man/nvme_subsystem_first_ns.2
new file mode 100644
index 0000000..bdf2473
--- /dev/null
+++ b/doc/man/nvme_subsystem_first_ns.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsystem_first_ns" 9 "nvme_subsystem_first_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_first_ns \- Start namespace iterator
+.SH SYNOPSIS
+.B "nvme_ns_t" nvme_subsystem_first_ns
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.SH "RETURN"
+First \fInvme_ns_t\fP object of an \fIs\fP iterator
diff --git a/doc/man/nvme_subsystem_for_each_ctrl.2 b/doc/man/nvme_subsystem_for_each_ctrl.2
new file mode 100644
index 0000000..e0d29b9
--- /dev/null
+++ b/doc/man/nvme_subsystem_for_each_ctrl.2
@@ -0,0 +1,12 @@
+.TH "nvme_subsystem_for_each_ctrl" 9 "nvme_subsystem_for_each_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_for_each_ctrl \- Traverse controllers
+.SH SYNOPSIS
+.B "nvme_subsystem_for_each_ctrl
+.BI "(s " ","
+.BI "c " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "c" 12
+Controller instance
diff --git a/doc/man/nvme_subsystem_for_each_ctrl_safe.2 b/doc/man/nvme_subsystem_for_each_ctrl_safe.2
new file mode 100644
index 0000000..06bbf6a
--- /dev/null
+++ b/doc/man/nvme_subsystem_for_each_ctrl_safe.2
@@ -0,0 +1,15 @@
+.TH "nvme_subsystem_for_each_ctrl_safe" 9 "nvme_subsystem_for_each_ctrl_safe" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_for_each_ctrl_safe \- Traverse controllers
+.SH SYNOPSIS
+.B "nvme_subsystem_for_each_ctrl_safe
+.BI "(s " ","
+.BI "c " ","
+.BI "_c " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "c" 12
+Controller instance
+.IP "_c" 12
+A \fInvme_ctrl_t_node\fP to use as temporary storage
diff --git a/doc/man/nvme_subsystem_for_each_ns.2 b/doc/man/nvme_subsystem_for_each_ns.2
new file mode 100644
index 0000000..06d9517
--- /dev/null
+++ b/doc/man/nvme_subsystem_for_each_ns.2
@@ -0,0 +1,12 @@
+.TH "nvme_subsystem_for_each_ns" 9 "nvme_subsystem_for_each_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_for_each_ns \- Traverse namespaces
+.SH SYNOPSIS
+.B "nvme_subsystem_for_each_ns
+.BI "(s " ","
+.BI "n " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "n" 12
+\fInvme_ns_t\fP object
diff --git a/doc/man/nvme_subsystem_for_each_ns_safe.2 b/doc/man/nvme_subsystem_for_each_ns_safe.2
new file mode 100644
index 0000000..d8abb7f
--- /dev/null
+++ b/doc/man/nvme_subsystem_for_each_ns_safe.2
@@ -0,0 +1,15 @@
+.TH "nvme_subsystem_for_each_ns_safe" 9 "nvme_subsystem_for_each_ns_safe" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_for_each_ns_safe \- Traverse namespaces
+.SH SYNOPSIS
+.B "nvme_subsystem_for_each_ns_safe
+.BI "(s " ","
+.BI "n " ","
+.BI "_n " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "n" 12
+\fInvme_ns_t\fP object
+.IP "_n" 12
+A \fInvme_ns_t_node\fP to use as temporary storage
diff --git a/doc/man/nvme_subsystem_get_application.2 b/doc/man/nvme_subsystem_get_application.2
new file mode 100644
index 0000000..c5d2ce5
--- /dev/null
+++ b/doc/man/nvme_subsystem_get_application.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsystem_get_application" 9 "nvme_subsystem_get_application" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_get_application \- Return the application string
+.SH SYNOPSIS
+.B "const char *" nvme_subsystem_get_application
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.SH "RETURN"
+Managing application string or NULL if not set.
diff --git a/doc/man/nvme_subsystem_get_host.2 b/doc/man/nvme_subsystem_get_host.2
new file mode 100644
index 0000000..8c91f9e
--- /dev/null
+++ b/doc/man/nvme_subsystem_get_host.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsystem_get_host" 9 "nvme_subsystem_get_host" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_get_host \- Returns nvme_host_t object
+.SH SYNOPSIS
+.B "nvme_host_t" nvme_subsystem_get_host
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+subsystem
+.SH "RETURN"
+\fInvme_host_t\fP object from \fIs\fP
diff --git a/doc/man/nvme_subsystem_get_iopolicy.2 b/doc/man/nvme_subsystem_get_iopolicy.2
new file mode 100644
index 0000000..83a38a5
--- /dev/null
+++ b/doc/man/nvme_subsystem_get_iopolicy.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsystem_get_iopolicy" 9 "nvme_subsystem_get_iopolicy" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_get_iopolicy \- Return the IO policy of subsytem
+.SH SYNOPSIS
+.B "const char *" nvme_subsystem_get_iopolicy
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.SH "RETURN"
+IO policy used by current subsystem
diff --git a/doc/man/nvme_subsystem_get_name.2 b/doc/man/nvme_subsystem_get_name.2
new file mode 100644
index 0000000..77cd881
--- /dev/null
+++ b/doc/man/nvme_subsystem_get_name.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsystem_get_name" 9 "nvme_subsystem_get_name" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_get_name \- sysfs name of an nvme_subsystem_t object
+.SH SYNOPSIS
+.B "const char *" nvme_subsystem_get_name
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.SH "RETURN"
+sysfs name of \fIs\fP
diff --git a/doc/man/nvme_subsystem_get_nqn.2 b/doc/man/nvme_subsystem_get_nqn.2
new file mode 100644
index 0000000..2c8aad9
--- /dev/null
+++ b/doc/man/nvme_subsystem_get_nqn.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsystem_get_nqn" 9 "nvme_subsystem_get_nqn" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_get_nqn \- Retrieve NQN from subsystem
+.SH SYNOPSIS
+.B "const char *" nvme_subsystem_get_nqn
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.SH "RETURN"
+NQN of subsystem
diff --git a/doc/man/nvme_subsystem_get_sysfs_dir.2 b/doc/man/nvme_subsystem_get_sysfs_dir.2
new file mode 100644
index 0000000..85451f8
--- /dev/null
+++ b/doc/man/nvme_subsystem_get_sysfs_dir.2
@@ -0,0 +1,11 @@
+.TH "nvme_subsystem_get_sysfs_dir" 9 "nvme_subsystem_get_sysfs_dir" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_get_sysfs_dir \- sysfs directory of an nvme_subsystem_t object
+.SH SYNOPSIS
+.B "const char *" nvme_subsystem_get_sysfs_dir
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.SH "RETURN"
+sysfs directory name of \fIs\fP
diff --git a/doc/man/nvme_subsystem_get_type.2 b/doc/man/nvme_subsystem_get_type.2
new file mode 100644
index 0000000..06458c3
--- /dev/null
+++ b/doc/man/nvme_subsystem_get_type.2
@@ -0,0 +1,13 @@
+.TH "nvme_subsystem_get_type" 9 "nvme_subsystem_get_type" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_get_type \- Returns the type of a subsystem
+.SH SYNOPSIS
+.B "const char *" nvme_subsystem_get_type
+.BI "(nvme_subsystem_t s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.SH "DESCRIPTION"
+Returns the subsystem type of \fIs\fP.
+.SH "RETURN"
+'nvm' or 'discovery'
diff --git a/doc/man/nvme_subsystem_lookup_namespace.2 b/doc/man/nvme_subsystem_lookup_namespace.2
new file mode 100644
index 0000000..5506aa8
--- /dev/null
+++ b/doc/man/nvme_subsystem_lookup_namespace.2
@@ -0,0 +1,14 @@
+.TH "nvme_subsystem_lookup_namespace" 9 "nvme_subsystem_lookup_namespace" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_lookup_namespace \- lookup namespace by NSID
+.SH SYNOPSIS
+.B "nvme_ns_t" nvme_subsystem_lookup_namespace
+.BI "(struct nvme_subsystem *s " ","
+.BI "__u32 nsid " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.IP "nsid" 12
+Namespace id
+.SH "RETURN"
+nvme_ns_t of the namespace with id \fInsid\fP in subsystem \fIs\fP
diff --git a/doc/man/nvme_subsystem_next_ctrl.2 b/doc/man/nvme_subsystem_next_ctrl.2
new file mode 100644
index 0000000..0bc5fcd
--- /dev/null
+++ b/doc/man/nvme_subsystem_next_ctrl.2
@@ -0,0 +1,14 @@
+.TH "nvme_subsystem_next_ctrl" 9 "nvme_subsystem_next_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_next_ctrl \- Next ctrl iterator
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvme_subsystem_next_ctrl
+.BI "(nvme_subsystem_t s " ","
+.BI "nvme_ctrl_t c " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "c" 12
+Previous controller instance of an \fIs\fP iterator
+.SH "RETURN"
+Next controller of an \fIs\fP iterator
diff --git a/doc/man/nvme_subsystem_next_ns.2 b/doc/man/nvme_subsystem_next_ns.2
new file mode 100644
index 0000000..0e13407
--- /dev/null
+++ b/doc/man/nvme_subsystem_next_ns.2
@@ -0,0 +1,14 @@
+.TH "nvme_subsystem_next_ns" 9 "nvme_subsystem_next_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_next_ns \- Next namespace iterator
+.SH SYNOPSIS
+.B "nvme_ns_t" nvme_subsystem_next_ns
+.BI "(nvme_subsystem_t s " ","
+.BI "nvme_ns_t n " ");"
+.SH ARGUMENTS
+.IP "s" 12
+\fInvme_subsystem_t\fP object
+.IP "n" 12
+Previous \fInvme_ns_t\fP iterator
+.SH "RETURN"
+Next \fInvme_ns_t\fP object of an \fIs\fP iterator
diff --git a/doc/man/nvme_subsystem_release_fds.2 b/doc/man/nvme_subsystem_release_fds.2
new file mode 100644
index 0000000..1dd034e
--- /dev/null
+++ b/doc/man/nvme_subsystem_release_fds.2
@@ -0,0 +1,13 @@
+.TH "nvme_subsystem_release_fds" 9 "nvme_subsystem_release_fds" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_release_fds \- Close all opened fds under subsystem
+.SH SYNOPSIS
+.B "void" nvme_subsystem_release_fds
+.BI "(struct nvme_subsystem *s " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.SH "DESCRIPTION"
+Controller and Namespace objects cache the file descriptors
+of opened nvme devices. This API can be used to close and
+clear all cached fds under this subsystem.
diff --git a/doc/man/nvme_subsystem_reset.2 b/doc/man/nvme_subsystem_reset.2
new file mode 100644
index 0000000..6ed7dec
--- /dev/null
+++ b/doc/man/nvme_subsystem_reset.2
@@ -0,0 +1,14 @@
+.TH "nvme_subsystem_reset" 9 "nvme_subsystem_reset" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_reset \- Initiate a subsystem reset
+.SH SYNOPSIS
+.B "int" nvme_subsystem_reset
+.BI "(int fd " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.SH "DESCRIPTION"
+This should only be sent to controller handles, not to namespaces.
+.SH "RETURN"
+Zero if a subsystem reset was initiated or -1 with errno set
+otherwise.
diff --git a/doc/man/nvme_subsystem_set_application.2 b/doc/man/nvme_subsystem_set_application.2
new file mode 100644
index 0000000..49d4273
--- /dev/null
+++ b/doc/man/nvme_subsystem_set_application.2
@@ -0,0 +1,14 @@
+.TH "nvme_subsystem_set_application" 9 "nvme_subsystem_set_application" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_subsystem_set_application \- Set the application string
+.SH SYNOPSIS
+.B "void" nvme_subsystem_set_application
+.BI "(nvme_subsystem_t s " ","
+.BI "const char *a " ");"
+.SH ARGUMENTS
+.IP "s" 12
+nvme_subsystem_t object
+.IP "a" 12
+application string
+.SH "DESCRIPTION"
+Sets the managing application string for \fIs\fP.
diff --git a/doc/man/nvme_supported_cap_config_list_log.2 b/doc/man/nvme_supported_cap_config_list_log.2
new file mode 100644
index 0000000..b5abeaf
--- /dev/null
+++ b/doc/man/nvme_supported_cap_config_list_log.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "struct nvme_supported_cap_config_list_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_supported_cap_config_list_log \- Supported Capacity Configuration list log page
+.SH SYNOPSIS
+struct nvme_supported_cap_config_list_log {
+.br
+.BI " __u8 sccn;"
+.br
+.BI " __u8 rsvd1[15];"
+.br
+.BI " struct nvme_capacity_config_desc cap_config_desc[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "sccn" 12
+Number of capacity configuration
+.IP "rsvd1" 12
+Reserved
+.IP "cap_config_desc" 12
+Capacity configuration descriptor.
+See \fIstruct\fP nvme_capacity_config_desc
diff --git a/doc/man/nvme_supported_log_pages.2 b/doc/man/nvme_supported_log_pages.2
new file mode 100644
index 0000000..4252e4d
--- /dev/null
+++ b/doc/man/nvme_supported_log_pages.2
@@ -0,0 +1,17 @@
+.TH "libnvme" 9 "struct nvme_supported_log_pages" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_supported_log_pages \- Supported Log Pages - Log
+.SH SYNOPSIS
+struct nvme_supported_log_pages {
+.br
+.BI " __le32 lid_support[NVME_LOG_SUPPORTED_LOG_PAGES_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "lid_support" 12
+Log Page Identifier Supported
+.SH "Description"
+Supported Log Pages (Log Identifier 00h)
diff --git a/doc/man/nvme_telemetry_da.2 b/doc/man/nvme_telemetry_da.2
new file mode 100644
index 0000000..c5b4af9
--- /dev/null
+++ b/doc/man/nvme_telemetry_da.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvme_telemetry_da" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_telemetry_da \- Telemetry Log Data Area
+.SH SYNOPSIS
+enum nvme_telemetry_da {
+.br
+.BI " NVME_TELEMETRY_DA_1"
+,
+.br
+.br
+.BI " NVME_TELEMETRY_DA_2"
+,
+.br
+.br
+.BI " NVME_TELEMETRY_DA_3"
+,
+.br
+.br
+.BI " NVME_TELEMETRY_DA_4"
+
+};
+.SH Constants
+.IP "NVME_TELEMETRY_DA_1" 12
+Data Area 1
+.IP "NVME_TELEMETRY_DA_2" 12
+Data Area 2
+.IP "NVME_TELEMETRY_DA_3" 12
+Data Area 3
+.IP "NVME_TELEMETRY_DA_4" 12
+Data Area 4
diff --git a/doc/man/nvme_telemetry_log.2 b/doc/man/nvme_telemetry_log.2
new file mode 100644
index 0000000..d859465
--- /dev/null
+++ b/doc/man/nvme_telemetry_log.2
@@ -0,0 +1,88 @@
+.TH "libnvme" 9 "struct nvme_telemetry_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_telemetry_log \- Retrieve internal data specific to the manufacturer.
+.SH SYNOPSIS
+struct nvme_telemetry_log {
+.br
+.BI " __u8 lpi;"
+.br
+.BI " __u8 rsvd1[4];"
+.br
+.BI " __u8 ieee[3];"
+.br
+.BI " __le16 dalb1;"
+.br
+.BI " __le16 dalb2;"
+.br
+.BI " __le16 dalb3;"
+.br
+.BI " __u8 rsvd14[2];"
+.br
+.BI " __le32 dalb4;"
+.br
+.BI " __u8 rsvd20[361];"
+.br
+.BI " __u8 hostdgn;"
+.br
+.BI " __u8 ctrlavail;"
+.br
+.BI " __u8 ctrldgn;"
+.br
+.BI " __u8 rsnident[128];"
+.br
+.BI " __u8 data_area[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "lpi" 12
+Log Identifier, either NVME_LOG_LID_TELEMETRY_HOST or
+NVME_LOG_LID_TELEMETRY_CTRL
+.IP "rsvd1" 12
+Reserved
+.IP "ieee" 12
+IEEE OUI Identifier is the Organization Unique Identifier (OUI)
+for the controller vendor that is able to interpret the data.
+.IP "dalb1" 12
+Telemetry Controller-Initiated Data Area 1 Last Block is
+the value of the last block in this area.
+.IP "dalb2" 12
+Telemetry Controller-Initiated Data Area 1 Last Block is
+the value of the last block in this area.
+.IP "dalb3" 12
+Telemetry Controller-Initiated Data Area 1 Last Block is
+the value of the last block in this area.
+.IP "rsvd14" 12
+Reserved
+.IP "dalb4" 12
+Telemetry Controller-Initiated Data Area 4 Last Block is
+the value of the last block in this area.
+.IP "rsvd20" 12
+Reserved
+.IP "hostdgn" 12
+Telemetry Host-Initiated Data Generation Number is a
+value that is incremented each time the host initiates a
+capture of its internal controller state in the controller .
+.IP "ctrlavail" 12
+Telemetry Controller-Initiated Data Available, if cleared,
+then the controller telemetry log does not contain saved
+internal controller state. If this field is set to 1h, the
+controller log contains saved internal controller state. If
+this field is set to 1h, the data will be latched until the
+host releases it by reading the log with RAE cleared.
+.IP "ctrldgn" 12
+Telemetry Controller-Initiated Data Generation Number is
+a value that is incremented each time the controller initiates a
+capture of its internal controller state in the controller .
+.IP "rsnident" 12
+Reason Identifiers a vendor specific identifier that describes
+the operating conditions of the controller at the time of
+capture.
+.IP "data_area" 12
+Telemetry data blocks, vendor specific information data.
+.SH "Description"
+This log consists of a header describing the log and zero or more Telemetry
+Data Blocks. All Telemetry Data Blocks are NVME_LOG_TELEM_BLOCK_SIZE, 512
+bytes, in size. This log captures the controller’s internal state.
diff --git a/doc/man/nvme_thermal_exc_event.2 b/doc/man/nvme_thermal_exc_event.2
new file mode 100644
index 0000000..34a28ce
--- /dev/null
+++ b/doc/man/nvme_thermal_exc_event.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_thermal_exc_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_thermal_exc_event \- Thermal Excursion Event Data
+.SH SYNOPSIS
+struct nvme_thermal_exc_event {
+.br
+.BI " __u8 over_temp;"
+.br
+.BI " __u8 threshold;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "over_temp" 12
+Over Temperature
+.IP "threshold" 12
+temperature threshold
diff --git a/doc/man/nvme_time_stamp_change_event.2 b/doc/man/nvme_time_stamp_change_event.2
new file mode 100644
index 0000000..ddc48c8
--- /dev/null
+++ b/doc/man/nvme_time_stamp_change_event.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_time_stamp_change_event" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_time_stamp_change_event \- Timestamp Change Event
+.SH SYNOPSIS
+struct nvme_time_stamp_change_event {
+.br
+.BI " __le64 previous_timestamp;"
+.br
+.BI " __le64 ml_secs_since_reset;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "previous_timestamp" 12
+Previous Timestamp
+.IP "ml_secs_since_reset" 12
+Milliseconds Since Reset
diff --git a/doc/man/nvme_timestamp.2 b/doc/man/nvme_timestamp.2
new file mode 100644
index 0000000..9012a8c
--- /dev/null
+++ b/doc/man/nvme_timestamp.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_timestamp" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_timestamp \- Timestamp - Data Structure for Get Features
+.SH SYNOPSIS
+struct nvme_timestamp {
+.br
+.BI " __u8 timestamp[6];"
+.br
+.BI " __u8 attr;"
+.br
+.BI " __u8 rsvd;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "timestamp" 12
+Timestamp value based on origin and synch field
+.IP "attr" 12
+Attribute
+.IP "rsvd" 12
+Reserved
diff --git a/doc/man/nvme_unlink_ctrl.2 b/doc/man/nvme_unlink_ctrl.2
new file mode 100644
index 0000000..c0eb8a1
--- /dev/null
+++ b/doc/man/nvme_unlink_ctrl.2
@@ -0,0 +1,9 @@
+.TH "nvme_unlink_ctrl" 9 "nvme_unlink_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_unlink_ctrl \- Unlink controller
+.SH SYNOPSIS
+.B "void" nvme_unlink_ctrl
+.BI "(struct nvme_ctrl *c " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
diff --git a/doc/man/nvme_update_config.2 b/doc/man/nvme_update_config.2
new file mode 100644
index 0000000..c73b018
--- /dev/null
+++ b/doc/man/nvme_update_config.2
@@ -0,0 +1,13 @@
+.TH "nvme_update_config" 9 "nvme_update_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_update_config \- Update JSON configuration
+.SH SYNOPSIS
+.B "int" nvme_update_config
+.BI "(nvme_root_t r " ");"
+.SH ARGUMENTS
+.IP "r" 12
+nvme_root_t object
+.SH "DESCRIPTION"
+Updates the JSON configuration file with the contents of \fIr\fP.
+.SH "RETURN"
+0 on success, -1 on failure.
diff --git a/doc/man/nvme_uring_cmd.2 b/doc/man/nvme_uring_cmd.2
new file mode 100644
index 0000000..df8eb6a
--- /dev/null
+++ b/doc/man/nvme_uring_cmd.2
@@ -0,0 +1,83 @@
+.TH "libnvme" 9 "struct nvme_uring_cmd" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_uring_cmd \- nvme uring command structure
+.SH SYNOPSIS
+struct nvme_uring_cmd {
+.br
+.BI " __u8 opcode;"
+.br
+.BI " __u8 flags;"
+.br
+.BI " __u16 rsvd1;"
+.br
+.BI " __u32 nsid;"
+.br
+.BI " __u32 cdw2;"
+.br
+.BI " __u32 cdw3;"
+.br
+.BI " __u64 metadata;"
+.br
+.BI " __u64 addr;"
+.br
+.BI " __u32 metadata_len;"
+.br
+.BI " __u32 data_len;"
+.br
+.BI " __u32 cdw10;"
+.br
+.BI " __u32 cdw11;"
+.br
+.BI " __u32 cdw12;"
+.br
+.BI " __u32 cdw13;"
+.br
+.BI " __u32 cdw14;"
+.br
+.BI " __u32 cdw15;"
+.br
+.BI " __u32 timeout_ms;"
+.br
+.BI " __u32 rsvd2;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "opcode" 12
+Operation code, see \fIenum nvme_io_opcodes\fP and \fIenum nvme_admin_opcodes\fP
+.IP "flags" 12
+Not supported: intended for command flags (eg: SGL, FUSE)
+.IP "rsvd1" 12
+Reserved for future use
+.IP "nsid" 12
+Namespace Identifier, or Fabrics type
+.IP "cdw2" 12
+Command Dword 2 (no spec defined use)
+.IP "cdw3" 12
+Command Dword 3 (no spec defined use)
+.IP "metadata" 12
+User space address to metadata buffer (NULL if not used)
+.IP "addr" 12
+User space address to data buffer (NULL if not used)
+.IP "metadata_len" 12
+Metadata buffer transfer length
+.IP "data_len" 12
+Data buffer transfer length
+.IP "cdw10" 12
+Command Dword 10 (command specific)
+.IP "cdw11" 12
+Command Dword 11 (command specific)
+.IP "cdw12" 12
+Command Dword 12 (command specific)
+.IP "cdw13" 12
+Command Dword 13 (command specific)
+.IP "cdw14" 12
+Command Dword 14 (command specific)
+.IP "cdw15" 12
+Command Dword 15 (command specific)
+.IP "timeout_ms" 12
+If non-zero, overrides system default timeout in milliseconds
+.IP "rsvd2" 12
+Reserved for future use (and fills an implicit struct pad
diff --git a/doc/man/nvme_verify.2 b/doc/man/nvme_verify.2
new file mode 100644
index 0000000..d71380e
--- /dev/null
+++ b/doc/man/nvme_verify.2
@@ -0,0 +1,16 @@
+.TH "nvme_verify" 9 "nvme_verify" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_verify \- Send an nvme verify command
+.SH SYNOPSIS
+.B "int" nvme_verify
+.BI "(struct nvme_io_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_args\fP argument structure
+.SH "DESCRIPTION"
+The Verify command verifies integrity of stored information by reading data
+and metadata, if applicable, for the LBAs indicated without transferring any
+data or metadata to the host.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_version.2 b/doc/man/nvme_version.2
new file mode 100644
index 0000000..d30d233
--- /dev/null
+++ b/doc/man/nvme_version.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_version" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_version \- Selector for version to be returned by @nvme_get_version
+.SH SYNOPSIS
+enum nvme_version {
+.br
+.BI " NVME_VERSION_PROJECT"
+,
+.br
+.br
+.BI " NVME_VERSION_GIT"
+
+};
+.SH Constants
+.IP "NVME_VERSION_PROJECT" 12
+Project release version
+.IP "NVME_VERSION_GIT" 12
+Git reference
diff --git a/doc/man/nvme_virt_mgmt_act.2 b/doc/man/nvme_virt_mgmt_act.2
new file mode 100644
index 0000000..02794df
--- /dev/null
+++ b/doc/man/nvme_virt_mgmt_act.2
@@ -0,0 +1,31 @@
+.TH "libnvme" 9 "enum nvme_virt_mgmt_act" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_virt_mgmt_act \- Virtualization Management - Action
+.SH SYNOPSIS
+enum nvme_virt_mgmt_act {
+.br
+.BI " NVME_VIRT_MGMT_ACT_PRIM_CTRL_FLEX_ALLOC"
+,
+.br
+.br
+.BI " NVME_VIRT_MGMT_ACT_OFFLINE_SEC_CTRL"
+,
+.br
+.br
+.BI " NVME_VIRT_MGMT_ACT_ASSIGN_SEC_CTRL"
+,
+.br
+.br
+.BI " NVME_VIRT_MGMT_ACT_ONLINE_SEC_CTRL"
+
+};
+.SH Constants
+.IP "NVME_VIRT_MGMT_ACT_PRIM_CTRL_FLEX_ALLOC" 12
+Primary Controller Flexible
+Allocation
+.IP "NVME_VIRT_MGMT_ACT_OFFLINE_SEC_CTRL" 12
+Secondary Controller Offline
+.IP "NVME_VIRT_MGMT_ACT_ASSIGN_SEC_CTRL" 12
+Secondary Controller Assign
+.IP "NVME_VIRT_MGMT_ACT_ONLINE_SEC_CTRL" 12
+Secondary Controller Online
diff --git a/doc/man/nvme_virt_mgmt_rt.2 b/doc/man/nvme_virt_mgmt_rt.2
new file mode 100644
index 0000000..e106c13
--- /dev/null
+++ b/doc/man/nvme_virt_mgmt_rt.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_virt_mgmt_rt" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_virt_mgmt_rt \- Virtualization Management - Resource Type
+.SH SYNOPSIS
+enum nvme_virt_mgmt_rt {
+.br
+.BI " NVME_VIRT_MGMT_RT_VQ_RESOURCE"
+,
+.br
+.br
+.BI " NVME_VIRT_MGMT_RT_VI_RESOURCE"
+
+};
+.SH Constants
+.IP "NVME_VIRT_MGMT_RT_VQ_RESOURCE" 12
+VQ Resources
+.IP "NVME_VIRT_MGMT_RT_VI_RESOURCE" 12
+VI Resources
diff --git a/doc/man/nvme_virtual_mgmt.2 b/doc/man/nvme_virtual_mgmt.2
new file mode 100644
index 0000000..1a3ae31
--- /dev/null
+++ b/doc/man/nvme_virtual_mgmt.2
@@ -0,0 +1,20 @@
+.TH "nvme_virtual_mgmt" 9 "nvme_virtual_mgmt" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_virtual_mgmt \- Virtualization resource management
+.SH SYNOPSIS
+.B "int" nvme_virtual_mgmt
+.BI "(struct nvme_virtual_mgmt_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_virtual_mgmt_args\fP argument structure
+.SH "DESCRIPTION"
+The Virtualization Management command is supported by primary controllers
+that support the Virtualization Enhancements capability. This command is
+used for several functions:
+
+- Modifying Flexible Resource allocation for the primary controller
+- Assigning Flexible Resources for secondary controllers
+- Setting the Online and Offline state for secondary controllers
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_write.2 b/doc/man/nvme_write.2
new file mode 100644
index 0000000..b670227
--- /dev/null
+++ b/doc/man/nvme_write.2
@@ -0,0 +1,12 @@
+.TH "nvme_write" 9 "nvme_write" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_write \- Submit an nvme user write command
+.SH SYNOPSIS
+.B "int" nvme_write
+.BI "(struct nvme_io_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_write_uncorrectable.2 b/doc/man/nvme_write_uncorrectable.2
new file mode 100644
index 0000000..6e365e6
--- /dev/null
+++ b/doc/man/nvme_write_uncorrectable.2
@@ -0,0 +1,17 @@
+.TH "nvme_write_uncorrectable" 9 "nvme_write_uncorrectable" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_write_uncorrectable \- Submit an nvme write uncorrectable command
+.SH SYNOPSIS
+.B "int" nvme_write_uncorrectable
+.BI "(struct nvme_io_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_args\fP argument structure
+.SH "DESCRIPTION"
+The Write Uncorrectable command marks a range of logical blocks as invalid.
+When the specified logical block(s) are read after this operation, a failure
+is returned with Unrecovered Read Error status. To clear the invalid logical
+block status, a write operation on those logical blocks is required.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_write_zeros.2 b/doc/man/nvme_write_zeros.2
new file mode 100644
index 0000000..ce4353b
--- /dev/null
+++ b/doc/man/nvme_write_zeros.2
@@ -0,0 +1,17 @@
+.TH "nvme_write_zeros" 9 "nvme_write_zeros" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_write_zeros \- Submit an nvme write zeroes command
+.SH SYNOPSIS
+.B "int" nvme_write_zeros
+.BI "(struct nvme_io_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_io_args\fP argument structure
+.SH "DESCRIPTION"
+The Write Zeroes command sets a range of logical blocks to zero. After
+successful completion of this command, the value returned by subsequent
+reads of logical blocks in this range shall be all bytes cleared to 0h until
+a write occurs to this LBA range.
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_zns_append.2 b/doc/man/nvme_zns_append.2
new file mode 100644
index 0000000..22562f9
--- /dev/null
+++ b/doc/man/nvme_zns_append.2
@@ -0,0 +1,12 @@
+.TH "nvme_zns_append" 9 "nvme_zns_append" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_zns_append \- Append data to a zone
+.SH SYNOPSIS
+.B "int" nvme_zns_append
+.BI "(struct nvme_zns_append_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_zns_append_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_zns_changed_zone_log.2 b/doc/man/nvme_zns_changed_zone_log.2
new file mode 100644
index 0000000..b350a71
--- /dev/null
+++ b/doc/man/nvme_zns_changed_zone_log.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_zns_changed_zone_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_zns_changed_zone_log \- ZNS Changed Zone List log
+.SH SYNOPSIS
+struct nvme_zns_changed_zone_log {
+.br
+.BI " __le16 nrzid;"
+.br
+.BI " __u8 rsvd2[6];"
+.br
+.BI " __le64 zid[NVME_ZNS_CHANGED_ZONES_MAX];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nrzid" 12
+Number of Zone Identifiers
+.IP "rsvd2" 12
+Reserved
+.IP "zid" 12
+Zone Identifier
diff --git a/doc/man/nvme_zns_desc.2 b/doc/man/nvme_zns_desc.2
new file mode 100644
index 0000000..d298a06
--- /dev/null
+++ b/doc/man/nvme_zns_desc.2
@@ -0,0 +1,47 @@
+.TH "libnvme" 9 "struct nvme_zns_desc" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_zns_desc \- Zone Descriptor Data Structure
+.SH SYNOPSIS
+struct nvme_zns_desc {
+.br
+.BI " __u8 zt;"
+.br
+.BI " __u8 zs;"
+.br
+.BI " __u8 za;"
+.br
+.BI " __u8 zai;"
+.br
+.BI " __u8 rsvd4[4];"
+.br
+.BI " __le64 zcap;"
+.br
+.BI " __le64 zslba;"
+.br
+.BI " __le64 wp;"
+.br
+.BI " __u8 rsvd32[32];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "zt" 12
+Zone Type
+.IP "zs" 12
+Zone State
+.IP "za" 12
+Zone Attributes
+.IP "zai" 12
+Zone Attributes Information
+.IP "rsvd4" 12
+Reserved
+.IP "zcap" 12
+Zone Capacity
+.IP "zslba" 12
+Zone Start Logical Block Address
+.IP "wp" 12
+Write Pointer
+.IP "rsvd32" 12
+Reserved
diff --git a/doc/man/nvme_zns_id_ctrl.2 b/doc/man/nvme_zns_id_ctrl.2
new file mode 100644
index 0000000..62b0951
--- /dev/null
+++ b/doc/man/nvme_zns_id_ctrl.2
@@ -0,0 +1,19 @@
+.TH "libnvme" 9 "struct nvme_zns_id_ctrl" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_zns_id_ctrl \- I/O Command Set Specific Identify Controller Data Structure for the Zoned Namespace Command Set
+.SH SYNOPSIS
+struct nvme_zns_id_ctrl {
+.br
+.BI " __u8 zasl;"
+.br
+.BI " __u8 rsvd1[4095];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "zasl" 12
+Zone Append Size Limit
+.IP "rsvd1" 12
+Reserved
diff --git a/doc/man/nvme_zns_id_ns.2 b/doc/man/nvme_zns_id_ns.2
new file mode 100644
index 0000000..a482232
--- /dev/null
+++ b/doc/man/nvme_zns_id_ns.2
@@ -0,0 +1,87 @@
+.TH "libnvme" 9 "struct nvme_zns_id_ns" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_zns_id_ns \- Zoned Namespace Command Set Specific Identify Namespace Data Structure
+.SH SYNOPSIS
+struct nvme_zns_id_ns {
+.br
+.BI " __le16 zoc;"
+.br
+.BI " __le16 ozcs;"
+.br
+.BI " __le32 mar;"
+.br
+.BI " __le32 mor;"
+.br
+.BI " __le32 rrl;"
+.br
+.BI " __le32 frl;"
+.br
+.BI " __le32 rrl1;"
+.br
+.BI " __le32 rrl2;"
+.br
+.BI " __le32 rrl3;"
+.br
+.BI " __le32 frl1;"
+.br
+.BI " __le32 frl2;"
+.br
+.BI " __le32 frl3;"
+.br
+.BI " __le32 numzrwa;"
+.br
+.BI " __le16 zrwafg;"
+.br
+.BI " __le16 zrwasz;"
+.br
+.BI " __u8 zrwacap;"
+.br
+.BI " __u8 rsvd53[2763];"
+.br
+.BI " struct nvme_zns_lbafe lbafe[64];"
+.br
+.BI " __u8 vs[256];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "zoc" 12
+Zone Operation Characteristics
+.IP "ozcs" 12
+Optional Zoned Command Support
+.IP "mar" 12
+Maximum Active Resources
+.IP "mor" 12
+Maximum Open Resources
+.IP "rrl" 12
+Reset Recommended Limit
+.IP "frl" 12
+Finish Recommended Limit
+.IP "rrl1" 12
+Reset Recommended Limit 1
+.IP "rrl2" 12
+Reset Recommended Limit 2
+.IP "rrl3" 12
+Reset Recommended Limit 3
+.IP "frl1" 12
+Finish Recommended Limit 1
+.IP "frl2" 12
+Finish Recommended Limit 2
+.IP "frl3" 12
+Finish Recommended Limit 3
+.IP "numzrwa" 12
+Number of ZRWA Resources
+.IP "zrwafg" 12
+ZRWA Flush Granularity
+.IP "zrwasz" 12
+ZRWA Size
+.IP "zrwacap" 12
+ZRWA Capability
+.IP "rsvd53" 12
+Reserved
+.IP "lbafe" 12
+LBA Format Extension
+.IP "vs" 12
+Vendor Specific
diff --git a/doc/man/nvme_zns_identify_ctrl.2 b/doc/man/nvme_zns_identify_ctrl.2
new file mode 100644
index 0000000..0658853
--- /dev/null
+++ b/doc/man/nvme_zns_identify_ctrl.2
@@ -0,0 +1,15 @@
+.TH "nvme_zns_identify_ctrl" 9 "nvme_zns_identify_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_zns_identify_ctrl \- ZNS identify controller data
+.SH SYNOPSIS
+.B "int" nvme_zns_identify_ctrl
+.BI "(int fd " ","
+.BI "struct nvme_zns_id_ctrl *id " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "id" 12
+User space destination address to transfer the data
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_zns_identify_ns.2 b/doc/man/nvme_zns_identify_ns.2
new file mode 100644
index 0000000..22fe900
--- /dev/null
+++ b/doc/man/nvme_zns_identify_ns.2
@@ -0,0 +1,18 @@
+.TH "nvme_zns_identify_ns" 9 "nvme_zns_identify_ns" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_zns_identify_ns \- ZNS identify namespace data
+.SH SYNOPSIS
+.B "int" nvme_zns_identify_ns
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "struct nvme_zns_id_ns *data " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace to identify
+.IP "data" 12
+User space destination address to transfer the data
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_zns_lbafe.2 b/doc/man/nvme_zns_lbafe.2
new file mode 100644
index 0000000..46585d5
--- /dev/null
+++ b/doc/man/nvme_zns_lbafe.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_zns_lbafe" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_zns_lbafe \- LBA Format Extension Data Structure
+.SH SYNOPSIS
+struct nvme_zns_lbafe {
+.br
+.BI " __le64 zsze;"
+.br
+.BI " __u8 zdes;"
+.br
+.BI " __u8 rsvd9[7];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "zsze" 12
+Zone Size
+.IP "zdes" 12
+Zone Descriptor Extension Size
+.IP "rsvd9" 12
+reserved
diff --git a/doc/man/nvme_zns_mgmt_recv.2 b/doc/man/nvme_zns_mgmt_recv.2
new file mode 100644
index 0000000..5d3ed0e
--- /dev/null
+++ b/doc/man/nvme_zns_mgmt_recv.2
@@ -0,0 +1,12 @@
+.TH "nvme_zns_mgmt_recv" 9 "nvme_zns_mgmt_recv" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_zns_mgmt_recv \- ZNS management receive command
+.SH SYNOPSIS
+.B "int" nvme_zns_mgmt_recv
+.BI "(struct nvme_zns_mgmt_recv_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_zns_mgmt_recv_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_zns_mgmt_send.2 b/doc/man/nvme_zns_mgmt_send.2
new file mode 100644
index 0000000..8e4dafa
--- /dev/null
+++ b/doc/man/nvme_zns_mgmt_send.2
@@ -0,0 +1,12 @@
+.TH "nvme_zns_mgmt_send" 9 "nvme_zns_mgmt_send" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_zns_mgmt_send \- ZNS management send command
+.SH SYNOPSIS
+.B "int" nvme_zns_mgmt_send
+.BI "(struct nvme_zns_mgmt_send_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+\fIstruct nvme_zns_mgmt_send_args\fP argument structure
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_zns_recv_action.2 b/doc/man/nvme_zns_recv_action.2
new file mode 100644
index 0000000..fffa027
--- /dev/null
+++ b/doc/man/nvme_zns_recv_action.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvme_zns_recv_action" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_zns_recv_action \- Zone Management Receive - Zone Receive Action Specific Features
+.SH SYNOPSIS
+enum nvme_zns_recv_action {
+.br
+.BI " NVME_ZNS_ZRA_REPORT_ZONES"
+,
+.br
+.br
+.BI " NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES"
+
+};
+.SH Constants
+.IP "NVME_ZNS_ZRA_REPORT_ZONES" 12
+Report Zones
+.IP "NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES" 12
+Extended Report Zones
diff --git a/doc/man/nvme_zns_report_options.2 b/doc/man/nvme_zns_report_options.2
new file mode 100644
index 0000000..f5ba222
--- /dev/null
+++ b/doc/man/nvme_zns_report_options.2
@@ -0,0 +1,54 @@
+.TH "libnvme" 9 "enum nvme_zns_report_options" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_zns_report_options \- Zone Management Receive - Zone Receive Action Specific Field
+.SH SYNOPSIS
+enum nvme_zns_report_options {
+.br
+.BI " NVME_ZNS_ZRAS_REPORT_ALL"
+,
+.br
+.br
+.BI " NVME_ZNS_ZRAS_REPORT_EMPTY"
+,
+.br
+.br
+.BI " NVME_ZNS_ZRAS_REPORT_IMPL_OPENED"
+,
+.br
+.br
+.BI " NVME_ZNS_ZRAS_REPORT_EXPL_OPENED"
+,
+.br
+.br
+.BI " NVME_ZNS_ZRAS_REPORT_CLOSED"
+,
+.br
+.br
+.BI " NVME_ZNS_ZRAS_REPORT_FULL"
+,
+.br
+.br
+.BI " NVME_ZNS_ZRAS_REPORT_READ_ONLY"
+,
+.br
+.br
+.BI " NVME_ZNS_ZRAS_REPORT_OFFLINE"
+
+};
+.SH Constants
+.IP "NVME_ZNS_ZRAS_REPORT_ALL" 12
+List all zones
+.IP "NVME_ZNS_ZRAS_REPORT_EMPTY" 12
+List the zones in the ZSE:Empty state
+.IP "NVME_ZNS_ZRAS_REPORT_IMPL_OPENED" 12
+List the zones in the ZSIO:Implicitly Opened state
+.IP "NVME_ZNS_ZRAS_REPORT_EXPL_OPENED" 12
+List the zones in the ZSEO:Explicitly Opened state
+.IP "NVME_ZNS_ZRAS_REPORT_CLOSED" 12
+List the zones in the ZSC:Closed state
+.IP "NVME_ZNS_ZRAS_REPORT_FULL" 12
+List the zones in the ZSF:Full state
+.IP "NVME_ZNS_ZRAS_REPORT_READ_ONLY" 12
+List the zones in the ZSRO:Read Only state
+.IP "NVME_ZNS_ZRAS_REPORT_OFFLINE" 12
+List the zones in the ZSO:Offline state
diff --git a/doc/man/nvme_zns_report_zones.2 b/doc/man/nvme_zns_report_zones.2
new file mode 100644
index 0000000..767839f
--- /dev/null
+++ b/doc/man/nvme_zns_report_zones.2
@@ -0,0 +1,39 @@
+.TH "nvme_zns_report_zones" 9 "nvme_zns_report_zones" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvme_zns_report_zones \- Return the list of zones
+.SH SYNOPSIS
+.B "int" nvme_zns_report_zones
+.BI "(int fd " ","
+.BI "__u32 nsid " ","
+.BI "__u64 slba " ","
+.BI "enum nvme_zns_report_options opts " ","
+.BI "bool extended " ","
+.BI "bool partial " ","
+.BI "__u32 data_len " ","
+.BI "void *data " ","
+.BI "__u32 timeout " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "fd" 12
+File descriptor of nvme device
+.IP "nsid" 12
+Namespace ID
+.IP "slba" 12
+Starting LBA
+.IP "opts" 12
+Reporting options
+.IP "extended" 12
+Extended report
+.IP "partial" 12
+Partial report requested
+.IP "data_len" 12
+Length of the data buffer
+.IP "data" 12
+Userspace address of the report zones data
+.IP "timeout" 12
+timeout in ms
+.IP "result" 12
+The command completion result from CQE dword0
+.SH "RETURN"
+The nvme command status if a response was received (see
+\fIenum nvme_status_field\fP) or -1 with errno set otherwise.
diff --git a/doc/man/nvme_zns_send_action.2 b/doc/man/nvme_zns_send_action.2
new file mode 100644
index 0000000..c349307
--- /dev/null
+++ b/doc/man/nvme_zns_send_action.2
@@ -0,0 +1,48 @@
+.TH "libnvme" 9 "enum nvme_zns_send_action" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_zns_send_action \- Zone Management Send - Zone Send Action
+.SH SYNOPSIS
+enum nvme_zns_send_action {
+.br
+.BI " NVME_ZNS_ZSA_CLOSE"
+,
+.br
+.br
+.BI " NVME_ZNS_ZSA_FINISH"
+,
+.br
+.br
+.BI " NVME_ZNS_ZSA_OPEN"
+,
+.br
+.br
+.BI " NVME_ZNS_ZSA_RESET"
+,
+.br
+.br
+.BI " NVME_ZNS_ZSA_OFFLINE"
+,
+.br
+.br
+.BI " NVME_ZNS_ZSA_SET_DESC_EXT"
+,
+.br
+.br
+.BI " NVME_ZNS_ZSA_ZRWA_FLUSH"
+
+};
+.SH Constants
+.IP "NVME_ZNS_ZSA_CLOSE" 12
+Close Zone
+.IP "NVME_ZNS_ZSA_FINISH" 12
+Finish Zone
+.IP "NVME_ZNS_ZSA_OPEN" 12
+Open Zone
+.IP "NVME_ZNS_ZSA_RESET" 12
+Reset Zone
+.IP "NVME_ZNS_ZSA_OFFLINE" 12
+Offline Zone
+.IP "NVME_ZNS_ZSA_SET_DESC_EXT" 12
+Set Zone Descriptor Extension
+.IP "NVME_ZNS_ZSA_ZRWA_FLUSH" 12
+Flush
diff --git a/doc/man/nvme_zns_za.2 b/doc/man/nvme_zns_za.2
new file mode 100644
index 0000000..5f5e25a
--- /dev/null
+++ b/doc/man/nvme_zns_za.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "enum nvme_zns_za" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_zns_za \- Zone Descriptor Data Structure
+.SH SYNOPSIS
+enum nvme_zns_za {
+.br
+.BI " NVME_ZNS_ZA_ZFC"
+,
+.br
+.br
+.BI " NVME_ZNS_ZA_FZR"
+,
+.br
+.br
+.BI " NVME_ZNS_ZA_RZR"
+,
+.br
+.br
+.BI " NVME_ZNS_ZA_ZRWAV"
+,
+.br
+.br
+.BI " NVME_ZNS_ZA_ZDEV"
+
+};
+.SH Constants
+.IP "NVME_ZNS_ZA_ZFC" 12
+Zone Finished by Controller
+.IP "NVME_ZNS_ZA_FZR" 12
+Finish Zone Recommended
+.IP "NVME_ZNS_ZA_RZR" 12
+Reset Zone Recommended
+.IP "NVME_ZNS_ZA_ZRWAV" 12
+.IP "NVME_ZNS_ZA_ZDEV" 12
+Zone Descriptor Extension Valid
diff --git a/doc/man/nvme_zns_zs.2 b/doc/man/nvme_zns_zs.2
new file mode 100644
index 0000000..3945750
--- /dev/null
+++ b/doc/man/nvme_zns_zs.2
@@ -0,0 +1,48 @@
+.TH "libnvme" 9 "enum nvme_zns_zs" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_zns_zs \- Zone Descriptor Data Structure - Zone State
+.SH SYNOPSIS
+enum nvme_zns_zs {
+.br
+.BI " NVME_ZNS_ZS_EMPTY"
+,
+.br
+.br
+.BI " NVME_ZNS_ZS_IMPL_OPEN"
+,
+.br
+.br
+.BI " NVME_ZNS_ZS_EXPL_OPEN"
+,
+.br
+.br
+.BI " NVME_ZNS_ZS_CLOSED"
+,
+.br
+.br
+.BI " NVME_ZNS_ZS_READ_ONLY"
+,
+.br
+.br
+.BI " NVME_ZNS_ZS_FULL"
+,
+.br
+.br
+.BI " NVME_ZNS_ZS_OFFLINE"
+
+};
+.SH Constants
+.IP "NVME_ZNS_ZS_EMPTY" 12
+Empty state
+.IP "NVME_ZNS_ZS_IMPL_OPEN" 12
+Implicitly open state
+.IP "NVME_ZNS_ZS_EXPL_OPEN" 12
+Explicitly open state
+.IP "NVME_ZNS_ZS_CLOSED" 12
+Closed state
+.IP "NVME_ZNS_ZS_READ_ONLY" 12
+Read only state
+.IP "NVME_ZNS_ZS_FULL" 12
+Full state
+.IP "NVME_ZNS_ZS_OFFLINE" 12
+Offline state
diff --git a/doc/man/nvme_zns_zt.2 b/doc/man/nvme_zns_zt.2
new file mode 100644
index 0000000..3c78b70
--- /dev/null
+++ b/doc/man/nvme_zns_zt.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nvme_zns_zt" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvme_zns_zt \- Zone Descriptor Data Structure - Zone Type
+.SH SYNOPSIS
+enum nvme_zns_zt {
+.br
+.BI " NVME_ZONE_TYPE_SEQWRITE_REQ"
+
+};
+.SH Constants
+.IP "NVME_ZONE_TYPE_SEQWRITE_REQ" 12
+Sequential Write Required
diff --git a/doc/man/nvme_zone_report.2 b/doc/man/nvme_zone_report.2
new file mode 100644
index 0000000..5f8bf18
--- /dev/null
+++ b/doc/man/nvme_zone_report.2
@@ -0,0 +1,23 @@
+.TH "libnvme" 9 "struct nvme_zone_report" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvme_zone_report \- Report Zones Data Structure
+.SH SYNOPSIS
+struct nvme_zone_report {
+.br
+.BI " __le64 nr_zones;"
+.br
+.BI " __u8 rsvd8[56];"
+.br
+.BI " struct nvme_zns_desc entries[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "nr_zones" 12
+Number of descriptors in \fIentries\fP
+.IP "rsvd8" 12
+Reserved
+.IP "entries" 12
+Zoned namespace descriptors
diff --git a/doc/man/nvmf_add_ctrl.2 b/doc/man/nvmf_add_ctrl.2
new file mode 100644
index 0000000..6f9d672
--- /dev/null
+++ b/doc/man/nvmf_add_ctrl.2
@@ -0,0 +1,21 @@
+.TH "nvmf_add_ctrl" 9 "nvmf_add_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_add_ctrl \- Connect a controller and update topology
+.SH SYNOPSIS
+.B "int" nvmf_add_ctrl
+.BI "(nvme_host_t h " ","
+.BI "nvme_ctrl_t c " ","
+.BI "const struct nvme_fabrics_config *cfg " ");"
+.SH ARGUMENTS
+.IP "h" 12
+Host to which the controller should be attached
+.IP "c" 12
+Controller to be connected
+.IP "cfg" 12
+Default configuration for the controller
+.SH "DESCRIPTION"
+Issues a 'connect' command to the NVMe-oF controller and inserts \fIc\fP
+into the topology using \fIh\fP as parent.
+\fIc\fP must be initialized and not connected to the topology.
+.SH "RETURN"
+0 on success; on failure errno is set and -1 is returned.
diff --git a/doc/man/nvmf_addr_family.2 b/doc/man/nvmf_addr_family.2
new file mode 100644
index 0000000..0531b0c
--- /dev/null
+++ b/doc/man/nvmf_addr_family.2
@@ -0,0 +1,43 @@
+.TH "libnvme" 9 "enum nvmf_addr_family" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_addr_family \- Address Family codes for Discovery Log Page entry ADRFAM field
+.SH SYNOPSIS
+enum nvmf_addr_family {
+.br
+.BI " NVMF_ADDR_FAMILY_PCI"
+,
+.br
+.br
+.BI " NVMF_ADDR_FAMILY_IP4"
+,
+.br
+.br
+.BI " NVMF_ADDR_FAMILY_IP6"
+,
+.br
+.br
+.BI " NVMF_ADDR_FAMILY_IB"
+,
+.br
+.br
+.BI " NVMF_ADDR_FAMILY_FC"
+,
+.br
+.br
+.BI " NVMF_ADDR_FAMILY_LOOP"
+
+};
+.SH Constants
+.IP "NVMF_ADDR_FAMILY_PCI" 12
+PCIe
+.IP "NVMF_ADDR_FAMILY_IP4" 12
+AF_INET: IPv4 address family.
+.IP "NVMF_ADDR_FAMILY_IP6" 12
+AF_INET6: IPv6 address family.
+.IP "NVMF_ADDR_FAMILY_IB" 12
+AF_IB: InfiniBand address family.
+.IP "NVMF_ADDR_FAMILY_FC" 12
+Fibre Channel address family.
+.IP "NVMF_ADDR_FAMILY_LOOP" 12
+Intra-host Transport (i.e., loopback), reserved
+for host usage.
diff --git a/doc/man/nvmf_adrfam_str.2 b/doc/man/nvmf_adrfam_str.2
new file mode 100644
index 0000000..c0535e4
--- /dev/null
+++ b/doc/man/nvmf_adrfam_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_adrfam_str" 9 "nvmf_adrfam_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_adrfam_str \- Decode ADRFAM field
+.SH SYNOPSIS
+.B "const char *" nvmf_adrfam_str
+.BI "(__u8 adrfam " ");"
+.SH ARGUMENTS
+.IP "adrfam" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the address family field in the discovery
+log page entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_cms_str.2 b/doc/man/nvmf_cms_str.2
new file mode 100644
index 0000000..fbae107
--- /dev/null
+++ b/doc/man/nvmf_cms_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_cms_str" 9 "nvmf_cms_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_cms_str \- Decode RDMA connection management service field
+.SH SYNOPSIS
+.B "const char *" nvmf_cms_str
+.BI "(__u8 cms " ");"
+.SH ARGUMENTS
+.IP "cms" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the RDMA connection management service field in the discovery
+log page entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_connect_data.2 b/doc/man/nvmf_connect_data.2
new file mode 100644
index 0000000..8fb600a
--- /dev/null
+++ b/doc/man/nvmf_connect_data.2
@@ -0,0 +1,35 @@
+.TH "libnvme" 9 "struct nvmf_connect_data" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvmf_connect_data \- Data payload for the 'connect' command
+.SH SYNOPSIS
+struct nvmf_connect_data {
+.br
+.BI " __u8 hostid[16];"
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " char rsvd4[238];"
+.br
+.BI " char subsysnqn[NVME_NQN_LENGTH];"
+.br
+.BI " char hostnqn[NVME_NQN_LENGTH];"
+.br
+.BI " char rsvd5[256];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "hostid" 12
+Host ID of the connecting host
+.IP "cntlid" 12
+Requested controller ID
+.IP "rsvd4" 12
+Reserved
+.IP "subsysnqn" 12
+Subsystem NQN to connect to
+.IP "hostnqn" 12
+Host NQN of the connecting host
+.IP "rsvd5" 12
+Reserved
diff --git a/doc/man/nvmf_connect_disc_entry.2 b/doc/man/nvmf_connect_disc_entry.2
new file mode 100644
index 0000000..0d767cc
--- /dev/null
+++ b/doc/man/nvmf_connect_disc_entry.2
@@ -0,0 +1,20 @@
+.TH "nvmf_connect_disc_entry" 9 "nvmf_connect_disc_entry" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_connect_disc_entry \- Connect controller based on the discovery log page entry
+.SH SYNOPSIS
+.B "nvme_ctrl_t" nvmf_connect_disc_entry
+.BI "(nvme_host_t h " ","
+.BI "struct nvmf_disc_log_entry *e " ","
+.BI "const struct nvme_fabrics_config *defcfg " ","
+.BI "bool *discover " ");"
+.SH ARGUMENTS
+.IP "h" 12
+Host to which the controller should be connected
+.IP "e" 12
+Discovery log page entry
+.IP "defcfg" 12
+Default configuration to be used for the new controller
+.IP "discover" 12
+Set to 'true' if the new controller is a discovery controller
+.SH "RETURN"
+Pointer to the new controller
diff --git a/doc/man/nvmf_default_config.2 b/doc/man/nvmf_default_config.2
new file mode 100644
index 0000000..7523ad2
--- /dev/null
+++ b/doc/man/nvmf_default_config.2
@@ -0,0 +1,11 @@
+.TH "nvmf_default_config" 9 "nvmf_default_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_default_config \- Default values for fabrics configuration
+.SH SYNOPSIS
+.B "void" nvmf_default_config
+.BI "(struct nvme_fabrics_config *cfg " ");"
+.SH ARGUMENTS
+.IP "cfg" 12
+config values to set
+.SH "DESCRIPTION"
+Initializes \fIcfg\fP with default values.
diff --git a/doc/man/nvmf_dim_data.2 b/doc/man/nvmf_dim_data.2
new file mode 100644
index 0000000..1fc72a3
--- /dev/null
+++ b/doc/man/nvmf_dim_data.2
@@ -0,0 +1,63 @@
+.TH "libnvme" 9 "struct nvmf_dim_data" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvmf_dim_data \- Discovery Information Management (DIM) - Data
+.SH SYNOPSIS
+struct nvmf_dim_data {
+.br
+.BI " __le32 tdl;"
+.br
+.BI " __u8 rsvd4[4];"
+.br
+.BI " __le64 nument;"
+.br
+.BI " __le16 entfmt;"
+.br
+.BI " __le16 etype;"
+.br
+.BI " __u8 portlcl;"
+.br
+.BI " __u8 rsvd21;"
+.br
+.BI " __le16 ektype;"
+.br
+.BI " char eid[NVME_NQN_LENGTH];"
+.br
+.BI " char ename[NVMF_ENAME_LEN];"
+.br
+.BI " char ever[NVMF_EVER_LEN];"
+.br
+.BI " __u8 rsvd600[424];"
+.br
+.BI " union nvmf_die die[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "tdl" 12
+Total Data Length
+.IP "rsvd4" 12
+Reserved
+.IP "nument" 12
+Number of entries
+.IP "entfmt" 12
+Entry Format (\fIenum nvmf_dim_entfmt\fP)
+.IP "etype" 12
+Entity Type (\fIenum nvmf_dim_etype\fP)
+.IP "portlcl" 12
+Port Local
+.IP "rsvd21" 12
+Reserved
+.IP "ektype" 12
+Entry Key Type
+.IP "eid" 12
+Entity Identifier (e.g. Host NQN)
+.IP "ename" 12
+Entity Name (e.g. hostname)
+.IP "ever" 12
+Entity Version (e.g. OS Name/Version)
+.IP "rsvd600" 12
+Reserved
+.IP "die" 12
+Discovery Information Entry (see \fInument\fP above)
diff --git a/doc/man/nvmf_dim_entfmt.2 b/doc/man/nvmf_dim_entfmt.2
new file mode 100644
index 0000000..08f2c2b
--- /dev/null
+++ b/doc/man/nvmf_dim_entfmt.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvmf_dim_entfmt" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_dim_entfmt \- Discovery Information Management Entry Format
+.SH SYNOPSIS
+enum nvmf_dim_entfmt {
+.br
+.BI " NVMF_DIM_ENTFMT_BASIC"
+,
+.br
+.br
+.BI " NVMF_DIM_ENTFMT_EXTENDED"
+
+};
+.SH Constants
+.IP "NVMF_DIM_ENTFMT_BASIC" 12
+Basic discovery information entry
+.IP "NVMF_DIM_ENTFMT_EXTENDED" 12
+Extended discovery information entry
diff --git a/doc/man/nvmf_dim_etype.2 b/doc/man/nvmf_dim_etype.2
new file mode 100644
index 0000000..6b246e8
--- /dev/null
+++ b/doc/man/nvmf_dim_etype.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvmf_dim_etype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_dim_etype \- Discovery Information Management Entity Type
+.SH SYNOPSIS
+enum nvmf_dim_etype {
+.br
+.BI " NVMF_DIM_ETYPE_HOST"
+,
+.br
+.br
+.BI " NVMF_DIM_ETYPE_DDC"
+,
+.br
+.br
+.BI " NVMF_DIM_ETYPE_CDC"
+
+};
+.SH Constants
+.IP "NVMF_DIM_ETYPE_HOST" 12
+Host
+.IP "NVMF_DIM_ETYPE_DDC" 12
+Direct Discovery controller
+.IP "NVMF_DIM_ETYPE_CDC" 12
+Centralized Discovery controller
diff --git a/doc/man/nvmf_dim_tas.2 b/doc/man/nvmf_dim_tas.2
new file mode 100644
index 0000000..e77e97d
--- /dev/null
+++ b/doc/man/nvmf_dim_tas.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "enum nvmf_dim_tas" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_dim_tas \- Discovery Information Management Task
+.SH SYNOPSIS
+enum nvmf_dim_tas {
+.br
+.BI " NVMF_DIM_TAS_REGISTER"
+,
+.br
+.br
+.BI " NVMF_DIM_TAS_DEREGISTER"
+,
+.br
+.br
+.BI " NVMF_DIM_TAS_UPDATE"
+
+};
+.SH Constants
+.IP "NVMF_DIM_TAS_REGISTER" 12
+Register
+.IP "NVMF_DIM_TAS_DEREGISTER" 12
+Deregister
+.IP "NVMF_DIM_TAS_UPDATE" 12
+Update
diff --git a/doc/man/nvmf_disc_eflags.2 b/doc/man/nvmf_disc_eflags.2
new file mode 100644
index 0000000..1c7608d
--- /dev/null
+++ b/doc/man/nvmf_disc_eflags.2
@@ -0,0 +1,46 @@
+.TH "libnvme" 9 "enum nvmf_disc_eflags" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_disc_eflags \- Discovery Log Page entry flags.
+.SH SYNOPSIS
+enum nvmf_disc_eflags {
+.br
+.BI " NVMF_DISC_EFLAGS_NONE"
+,
+.br
+.br
+.BI " NVMF_DISC_EFLAGS_DUPRETINFO"
+,
+.br
+.br
+.BI " NVMF_DISC_EFLAGS_EPCSD"
+,
+.br
+.br
+.BI " NVMF_DISC_EFLAGS_NCC"
+
+};
+.SH Constants
+.IP "NVMF_DISC_EFLAGS_NONE" 12
+Indicates that none of the DUPRETINFO or EPCSD
+features are supported.
+.IP "NVMF_DISC_EFLAGS_DUPRETINFO" 12
+Duplicate Returned Information (DUPRETINFO):
+Indicates that using the content of this entry
+to access this Discovery Service returns the same
+information that is returned by using the content
+of other entries in this log page that also have
+this flag set.
+.IP "NVMF_DISC_EFLAGS_EPCSD" 12
+Explicit Persistent Connection Support for Discovery (EPCSD):
+Indicates that Explicit Persistent Connections are
+supported for the Discovery controller.
+.IP "NVMF_DISC_EFLAGS_NCC" 12
+No CDC Connectivity (NCC): If set to
+'1', then no DDC that describes this entry
+is currently connected to the CDC. If
+cleared to '0', then at least one DDC that
+describes this entry is currently
+connected to the CDC. If the Discovery
+controller returning this log page is not
+a CDC, then this bit shall be cleared to
+'0' and should be ignored by the host.
diff --git a/doc/man/nvmf_disc_log_entry.2 b/doc/man/nvmf_disc_log_entry.2
new file mode 100644
index 0000000..f61c2f5
--- /dev/null
+++ b/doc/man/nvmf_disc_log_entry.2
@@ -0,0 +1,96 @@
+.TH "libnvme" 9 "struct nvmf_disc_log_entry" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvmf_disc_log_entry \- Discovery Log Page entry
+.SH SYNOPSIS
+struct nvmf_disc_log_entry {
+.br
+.BI " __u8 trtype;"
+.br
+.BI " __u8 adrfam;"
+.br
+.BI " __u8 subtype;"
+.br
+.BI " __u8 treq;"
+.br
+.BI " __le16 portid;"
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " __le16 asqsz;"
+.br
+.BI " __le16 eflags;"
+.br
+.BI " __u8 rsvd12[20];"
+.br
+.BI " char trsvcid[NVMF_TRSVCID_SIZE];"
+.br
+.BI " __u8 rsvd64[192];"
+.br
+.BI " char subnqn[NVME_NQN_LENGTH];"
+.br
+.BI " char traddr[NVMF_TRADDR_SIZE];"
+.br
+.BI " union nvmf_tsas tsas;"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "trtype" 12
+Transport Type (TRTYPE): Specifies the NVMe Transport type.
+See \fIenum nvmf_trtype\fP.
+.IP "adrfam" 12
+Address Family (ADRFAM): Specifies the address family.
+See \fIenum nvmf_addr_family\fP.
+.IP "subtype" 12
+Subsystem Type (SUBTYPE): Specifies the type of the NVM subsystem
+that is indicated in this entry. See \fIenum nvme_subsys_type\fP.
+.IP "treq" 12
+Transport Requirements (TREQ): Indicates requirements for the NVMe
+Transport. See \fIenum nvmf_treq\fP.
+.IP "portid" 12
+Port ID (PORTID): Specifies a particular NVM subsystem port.
+Different NVMe Transports or address families may utilize the same
+Port ID value (e.g. a Port ID may support both iWARP and RoCE).
+.IP "cntlid" 12
+Controller ID (CNTLID): Specifies the controller ID. If the NVM
+subsystem uses a dynamic controller model, then this field shall
+be set to FFFFh. If the NVM subsystem uses a static controller model,
+then this field may be set to a specific controller ID (values 0h
+to FFEFh are valid). If the NVM subsystem uses a static controller
+model and the value indicated is FFFEh, then the host should remember
+the Controller ID returned as part of the Fabrics Connect command
+in order to re-establish an association in the future with the same
+controller.
+.IP "asqsz" 12
+Admin Max SQ Size (ASQSZ): Specifies the maximum size of an Admin
+Submission Queue. This applies to all controllers in the NVM
+subsystem. The value shall be a minimum of 32 entries.
+.IP "eflags" 12
+Entry Flags (EFLAGS): Indicates additional information related to
+the current entry. See \fIenum nvmf_disc_eflags\fP.
+.IP "rsvd12" 12
+Reserved
+.IP "trsvcid" 12
+Transport Service Identifier (TRSVCID): Specifies the NVMe Transport
+service identifier as an ASCII string. The NVMe Transport service
+identifier is specified by the associated NVMe Transport binding
+specification.
+.IP "rsvd64" 12
+Reserved
+.IP "subnqn" 12
+NVM Subsystem Qualified Name (SUBNQN): NVMe Qualified Name (NQN)
+that uniquely identifies the NVM subsystem. For a subsystem, if that
+Discovery subsystem has a unique NQN (i.e., the NVM Subsystem NVMe
+Qualified Name (SUBNQN) field in that Discovery subsystem's Identify
+Controller data structure contains a unique NQN value), then the
+value returned shall be that unique NQN. If the Discovery subsystem
+does not have a unique NQN, then the value returned shall be the
+well-known Discovery Service NQN (nqn.2014-08.org.nvmexpress.discovery).
+.IP "traddr" 12
+Transport Address (TRADDR): Specifies the address of the NVM subsystem
+that may be used for a Connect command as an ASCII string. The
+Address Family field describes the reference for parsing this field.
+.IP "tsas" 12
+Transport specific attribute settings
diff --git a/doc/man/nvmf_discovery_log.2 b/doc/man/nvmf_discovery_log.2
new file mode 100644
index 0000000..4ad3412
--- /dev/null
+++ b/doc/man/nvmf_discovery_log.2
@@ -0,0 +1,38 @@
+.TH "libnvme" 9 "struct nvmf_discovery_log" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvmf_discovery_log \- Discovery Log Page (Log Identifier 70h)
+.SH SYNOPSIS
+struct nvmf_discovery_log {
+.br
+.BI " __le64 genctr;"
+.br
+.BI " __le64 numrec;"
+.br
+.BI " __le16 recfmt;"
+.br
+.BI " __u8 rsvd14[1006];"
+.br
+.BI " struct nvmf_disc_log_entry entries[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "genctr" 12
+Generation Counter (GENCTR): Indicates the version of the discovery
+information, starting at a value of 0h. For each change in the
+Discovery Log Page, this counter is incremented by one. If the value
+of this field is FFFFFFFF_FFFFFFFFh, then the field shall be cleared
+to 0h when incremented (i.e., rolls over to 0h).
+.IP "numrec" 12
+Number of Records (NUMREC): Indicates the number of records
+contained in the log.
+.IP "recfmt" 12
+Record Format (RECFMT): Specifies the format of the Discovery Log
+Page. If a new format is defined, this value is incremented by one.
+The format of the record specified in this definition shall be 0h.
+.IP "rsvd14" 12
+Reserved
+.IP "entries" 12
+Discovery Log Page Entries - see \fIstruct nvmf_disc_log_entry\fP.
diff --git a/doc/man/nvmf_eflags_str.2 b/doc/man/nvmf_eflags_str.2
new file mode 100644
index 0000000..b98e848
--- /dev/null
+++ b/doc/man/nvmf_eflags_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_eflags_str" 9 "nvmf_eflags_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_eflags_str \- Decode EFLAGS field
+.SH SYNOPSIS
+.B "const char *" nvmf_eflags_str
+.BI "(__u16 eflags " ");"
+.SH ARGUMENTS
+.IP "eflags" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the EFLAGS field in the discovery log page
+entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_exat_len.2 b/doc/man/nvmf_exat_len.2
new file mode 100644
index 0000000..0fe3f5a
--- /dev/null
+++ b/doc/man/nvmf_exat_len.2
@@ -0,0 +1,15 @@
+.TH "nvmf_exat_len" 9 "nvmf_exat_len" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_exat_len \- Return length rounded up by 4
+.SH SYNOPSIS
+.B "__u16" nvmf_exat_len
+.BI "(size_t val_len " ");"
+.SH ARGUMENTS
+.IP "val_len" 12
+Value length
+.SH "DESCRIPTION"
+Return the size in bytes, rounded to a multiple of 4 (e.g., size of
+__u32), of the buffer needed to hold the exat value of size
+\fIval_len\fP.
+.SH "RETURN"
+Length rounded up by 4
diff --git a/doc/man/nvmf_exattype.2 b/doc/man/nvmf_exattype.2
new file mode 100644
index 0000000..93e2e8e
--- /dev/null
+++ b/doc/man/nvmf_exattype.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvmf_exattype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_exattype \- Extended Attribute Type
+.SH SYNOPSIS
+enum nvmf_exattype {
+.br
+.BI " NVMF_EXATTYPE_HOSTID"
+,
+.br
+.br
+.BI " NVMF_EXATTYPE_SYMNAME"
+
+};
+.SH Constants
+.IP "NVMF_EXATTYPE_HOSTID" 12
+Host Identifier
+.IP "NVMF_EXATTYPE_SYMNAME" 12
+Symblic Name
diff --git a/doc/man/nvmf_ext_attr.2 b/doc/man/nvmf_ext_attr.2
new file mode 100644
index 0000000..539f0f5
--- /dev/null
+++ b/doc/man/nvmf_ext_attr.2
@@ -0,0 +1,24 @@
+.TH "libnvme" 9 "struct nvmf_ext_attr" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvmf_ext_attr \- Extended Attribute (EXAT)
+.SH SYNOPSIS
+struct nvmf_ext_attr {
+.br
+.BI " __le16 exattype;"
+.br
+.BI " __le16 exatlen;"
+.br
+.BI " __u8 exatval[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "exattype" 12
+Extended Attribute Type (EXATTYPE) - see \fIenum\fP nvmf_exattype
+.IP "exatlen" 12
+Extended Attribute Length (EXATLEN)
+.IP "exatval" 12
+Extended Attribute Value (EXATVAL) - size allocated for array
+must be a multiple of 4 bytes
diff --git a/doc/man/nvmf_ext_die.2 b/doc/man/nvmf_ext_die.2
new file mode 100644
index 0000000..e66a1e2
--- /dev/null
+++ b/doc/man/nvmf_ext_die.2
@@ -0,0 +1,79 @@
+.TH "libnvme" 9 "struct nvmf_ext_die" "February 2024" "API Manual" LINUX
+.SH NAME
+struct nvmf_ext_die \- Extended Discovery Information Entry (DIE)
+.SH SYNOPSIS
+struct nvmf_ext_die {
+.br
+.BI " __u8 trtype;"
+.br
+.BI " __u8 adrfam;"
+.br
+.BI " __u8 subtype;"
+.br
+.BI " __u8 treq;"
+.br
+.BI " __le16 portid;"
+.br
+.BI " __le16 cntlid;"
+.br
+.BI " __le16 asqsz;"
+.br
+.BI " __u8 rsvd10[22];"
+.br
+.BI " char trsvcid[NVMF_TRSVCID_SIZE];"
+.br
+.BI " __u8 resv64[192];"
+.br
+.BI " char nqn[NVME_NQN_LENGTH];"
+.br
+.BI " char traddr[NVMF_TRADDR_SIZE];"
+.br
+.BI " union nvmf_tsas tsas;"
+.br
+.BI " __le32 tel;"
+.br
+.BI " __le16 numexat;"
+.br
+.BI " __u8 resv1030[2];"
+.br
+.BI " struct nvmf_ext_attr exat[];"
+.br
+.BI "
+};
+.br
+
+.SH Members
+.IP "trtype" 12
+Transport Type (\fIenum nvmf_trtype\fP)
+.IP "adrfam" 12
+Address Family (\fIenum nvmf_addr_family\fP)
+.IP "subtype" 12
+Subsystem Type (\fIenum nvme_subsys_type\fP)
+.IP "treq" 12
+Transport Requirements (\fIenum nvmf_treq\fP)
+.IP "portid" 12
+Port ID
+.IP "cntlid" 12
+Controller ID
+.IP "asqsz" 12
+Admin Max SQ Size
+.IP "rsvd10" 12
+Reserved
+.IP "trsvcid" 12
+Transport Service Identifier
+.IP "resv64" 12
+Reserved
+.IP "nqn" 12
+NVM Qualified Name
+.IP "traddr" 12
+Transport Address
+.IP "tsas" 12
+Transport Specific Address Subtype (\fIunion nvmf_tsas\fP)
+.IP "tel" 12
+Total Entry Length
+.IP "numexat" 12
+Number of Extended Attributes
+.IP "resv1030" 12
+Reserved
+.IP "exat" 12
+Extended Attributes 0 (\fIstruct nvmf_ext_attr\fP)
diff --git a/doc/man/nvmf_get_discovery_log.2 b/doc/man/nvmf_get_discovery_log.2
new file mode 100644
index 0000000..1b6df15
--- /dev/null
+++ b/doc/man/nvmf_get_discovery_log.2
@@ -0,0 +1,22 @@
+.TH "nvmf_get_discovery_log" 9 "nvmf_get_discovery_log" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_get_discovery_log \- Return the discovery log page
+.SH SYNOPSIS
+.B "int" nvmf_get_discovery_log
+.BI "(nvme_ctrl_t c " ","
+.BI "struct nvmf_discovery_log **logp " ","
+.BI "int max_retries " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Discovery controller to use
+.IP "logp" 12
+Pointer to the log page to be returned
+.IP "max_retries" 12
+Number of retries in case of failure
+.SH "DESCRIPTION"
+The memory allocated for the log page and returned in \fIlogp\fP
+must be freed by the caller using \fBfree\fP.
+.SH "NOTE"
+Consider using \fBnvmf_get_discovery_wargs\fP instead.
+.SH "RETURN"
+0 on success; on failure -1 is returned and errno is set
diff --git a/doc/man/nvmf_get_discovery_wargs.2 b/doc/man/nvmf_get_discovery_wargs.2
new file mode 100644
index 0000000..8a3e232
--- /dev/null
+++ b/doc/man/nvmf_get_discovery_wargs.2
@@ -0,0 +1,20 @@
+.TH "nvmf_get_discovery_wargs" 9 "nvmf_get_discovery_wargs" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_get_discovery_wargs \- Get the discovery log page with args
+.SH SYNOPSIS
+.B "struct nvmf_discovery_log *" nvmf_get_discovery_wargs
+.BI "(struct nvme_get_discovery_args *args " ");"
+.SH ARGUMENTS
+.IP "args" 12
+Argument structure
+.SH "DESCRIPTION"
+This function is similar to \fBnvmf_get_discovery_log\fP, but
+takes an extensible \fIargs\fP parameter. \fIargs\fP provides more
+options than \fBnvmf_get_discovery_log\fP.
+
+This function performs a get discovery log page (DLP) command
+and returns the DLP. The memory allocated for the returned
+DLP must be freed by the caller using \fBfree\fP.
+.SH "RETURN"
+Pointer to the discovery log page (to be freed). NULL
+on failure and errno is set.
diff --git a/doc/man/nvmf_hostid_from_file.2 b/doc/man/nvmf_hostid_from_file.2
new file mode 100644
index 0000000..d05cec0
--- /dev/null
+++ b/doc/man/nvmf_hostid_from_file.2
@@ -0,0 +1,13 @@
+.TH "nvmf_hostid_from_file" 9 "nvmf_hostid_from_file" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_hostid_from_file \- Reads the host identifier from the config default location
+.SH SYNOPSIS
+.B "char *" nvmf_hostid_from_file
+.SH ARGUMENTS
+.SH "DESCRIPTION"
+
+Retrieve the host idenditifer from the config file located in $SYSCONFDIR/nvme/.
+$SYSCONFDIR is usually /etc.
+.SH "RETURN"
+The host identifier, or NULL if unsuccessful. If found, the caller
+is responsible to free the string.
diff --git a/doc/man/nvmf_hostnqn_from_file.2 b/doc/man/nvmf_hostnqn_from_file.2
new file mode 100644
index 0000000..5423f7c
--- /dev/null
+++ b/doc/man/nvmf_hostnqn_from_file.2
@@ -0,0 +1,13 @@
+.TH "nvmf_hostnqn_from_file" 9 "nvmf_hostnqn_from_file" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_hostnqn_from_file \- Reads the host nvm qualified name from the config default location
+.SH SYNOPSIS
+.B "char *" nvmf_hostnqn_from_file
+.SH ARGUMENTS
+.SH "DESCRIPTION"
+
+Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme.
+$SYSCONFDIR is usually /etc.
+.SH "RETURN"
+The host nqn, or NULL if unsuccessful. If found, the caller
+is responsible to free the string.
diff --git a/doc/man/nvmf_hostnqn_generate.2 b/doc/man/nvmf_hostnqn_generate.2
new file mode 100644
index 0000000..a1ee41a
--- /dev/null
+++ b/doc/man/nvmf_hostnqn_generate.2
@@ -0,0 +1,9 @@
+.TH "nvmf_hostnqn_generate" 9 "nvmf_hostnqn_generate" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_hostnqn_generate \- Generate a machine specific host nqn
+.SH SYNOPSIS
+.B "char *" nvmf_hostnqn_generate
+.SH ARGUMENTS
+.SH "RETURN"
+An nvm namespace qualified name string based on the machine
+identifier, or NULL if not successful.
diff --git a/doc/man/nvmf_log_discovery_lid_support.2 b/doc/man/nvmf_log_discovery_lid_support.2
new file mode 100644
index 0000000..1d3beca
--- /dev/null
+++ b/doc/man/nvmf_log_discovery_lid_support.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvmf_log_discovery_lid_support" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_log_discovery_lid_support \- Discovery log specific support
+.SH SYNOPSIS
+enum nvmf_log_discovery_lid_support {
+.br
+.BI " NVMF_LOG_DISC_LID_NONE"
+,
+.br
+.br
+.BI " NVMF_LOG_DISC_LID_EXTDLPES"
+,
+.br
+.br
+.BI " NVMF_LOG_DISC_LID_PLEOS"
+,
+.br
+.br
+.BI " NVMF_LOG_DISC_LID_ALLSUBES"
+
+};
+.SH Constants
+.IP "NVMF_LOG_DISC_LID_NONE" 12
+None
+.IP "NVMF_LOG_DISC_LID_EXTDLPES" 12
+Extended Discovery Log Page Entries Supported
+.IP "NVMF_LOG_DISC_LID_PLEOS" 12
+Port Local Entries Only Supported
+.IP "NVMF_LOG_DISC_LID_ALLSUBES" 12
+All NVM Subsystem Entries Supported
diff --git a/doc/man/nvmf_log_discovery_lsp.2 b/doc/man/nvmf_log_discovery_lsp.2
new file mode 100644
index 0000000..a50eff3
--- /dev/null
+++ b/doc/man/nvmf_log_discovery_lsp.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvmf_log_discovery_lsp" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_log_discovery_lsp \- Discovery log specific field
+.SH SYNOPSIS
+enum nvmf_log_discovery_lsp {
+.br
+.BI " NVMF_LOG_DISC_LSP_NONE"
+,
+.br
+.br
+.BI " NVMF_LOG_DISC_LSP_EXTDLPE"
+,
+.br
+.br
+.BI " NVMF_LOG_DISC_LSP_PLEO"
+,
+.br
+.br
+.BI " NVMF_LOG_DISC_LSP_ALLSUBE"
+
+};
+.SH Constants
+.IP "NVMF_LOG_DISC_LSP_NONE" 12
+None
+.IP "NVMF_LOG_DISC_LSP_EXTDLPE" 12
+Extended Discovery Log Page Entries
+.IP "NVMF_LOG_DISC_LSP_PLEO" 12
+Port Local Entries Only
+.IP "NVMF_LOG_DISC_LSP_ALLSUBE" 12
+All NVM Subsystem Entries
diff --git a/doc/man/nvmf_prtype_str.2 b/doc/man/nvmf_prtype_str.2
new file mode 100644
index 0000000..4a356de
--- /dev/null
+++ b/doc/man/nvmf_prtype_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_prtype_str" 9 "nvmf_prtype_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_prtype_str \- Decode RDMA Provider type field
+.SH SYNOPSIS
+.B "const char *" nvmf_prtype_str
+.BI "(__u8 prtype " ");"
+.SH ARGUMENTS
+.IP "prtype" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the RDMA Provider type field in the discovery
+log page entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_qptype_str.2 b/doc/man/nvmf_qptype_str.2
new file mode 100644
index 0000000..8409b40
--- /dev/null
+++ b/doc/man/nvmf_qptype_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_qptype_str" 9 "nvmf_qptype_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_qptype_str \- Decode RDMA QP Service type field
+.SH SYNOPSIS
+.B "const char *" nvmf_qptype_str
+.BI "(__u8 qptype " ");"
+.SH ARGUMENTS
+.IP "qptype" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the RDMA QP Service type field in the discovery log page
+entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_rdma_cms.2 b/doc/man/nvmf_rdma_cms.2
new file mode 100644
index 0000000..18e9aaf
--- /dev/null
+++ b/doc/man/nvmf_rdma_cms.2
@@ -0,0 +1,12 @@
+.TH "libnvme" 9 "enum nvmf_rdma_cms" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_rdma_cms \- RDMA Connection Management Service Type codes for Discovery Log Page entry TSAS RDMA_CMS field
+.SH SYNOPSIS
+enum nvmf_rdma_cms {
+.br
+.BI " NVMF_RDMA_CMS_RDMA_CM"
+
+};
+.SH Constants
+.IP "NVMF_RDMA_CMS_RDMA_CM" 12
+Sockets based endpoint addressing
diff --git a/doc/man/nvmf_rdma_prtype.2 b/doc/man/nvmf_rdma_prtype.2
new file mode 100644
index 0000000..d09edad
--- /dev/null
+++ b/doc/man/nvmf_rdma_prtype.2
@@ -0,0 +1,36 @@
+.TH "libnvme" 9 "enum nvmf_rdma_prtype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_rdma_prtype \- RDMA Provider Type codes for Discovery Log Page entry TSAS RDMA_PRTYPE field
+.SH SYNOPSIS
+enum nvmf_rdma_prtype {
+.br
+.BI " NVMF_RDMA_PRTYPE_NOT_SPECIFIED"
+,
+.br
+.br
+.BI " NVMF_RDMA_PRTYPE_IB"
+,
+.br
+.br
+.BI " NVMF_RDMA_PRTYPE_ROCE"
+,
+.br
+.br
+.BI " NVMF_RDMA_PRTYPE_ROCEV2"
+,
+.br
+.br
+.BI " NVMF_RDMA_PRTYPE_IWARP"
+
+};
+.SH Constants
+.IP "NVMF_RDMA_PRTYPE_NOT_SPECIFIED" 12
+No Provider Specified
+.IP "NVMF_RDMA_PRTYPE_IB" 12
+InfiniBand
+.IP "NVMF_RDMA_PRTYPE_ROCE" 12
+InfiniBand RoCE
+.IP "NVMF_RDMA_PRTYPE_ROCEV2" 12
+InfiniBand RoCEV2
+.IP "NVMF_RDMA_PRTYPE_IWARP" 12
+iWARP
diff --git a/doc/man/nvmf_rdma_qptype.2 b/doc/man/nvmf_rdma_qptype.2
new file mode 100644
index 0000000..d6d6fc4
--- /dev/null
+++ b/doc/man/nvmf_rdma_qptype.2
@@ -0,0 +1,18 @@
+.TH "libnvme" 9 "enum nvmf_rdma_qptype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_rdma_qptype \- RDMA QP Service Type codes for Discovery Log Page entry TSAS RDMA_QPTYPE field
+.SH SYNOPSIS
+enum nvmf_rdma_qptype {
+.br
+.BI " NVMF_RDMA_QPTYPE_CONNECTED"
+,
+.br
+.br
+.BI " NVMF_RDMA_QPTYPE_DATAGRAM"
+
+};
+.SH Constants
+.IP "NVMF_RDMA_QPTYPE_CONNECTED" 12
+Reliable Connected
+.IP "NVMF_RDMA_QPTYPE_DATAGRAM" 12
+Reliable Datagram
diff --git a/doc/man/nvmf_register_ctrl.2 b/doc/man/nvmf_register_ctrl.2
new file mode 100644
index 0000000..7ef04b1
--- /dev/null
+++ b/doc/man/nvmf_register_ctrl.2
@@ -0,0 +1,22 @@
+.TH "nvmf_register_ctrl" 9 "nvmf_register_ctrl" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_register_ctrl \- Perform registration task with a DC
+.SH SYNOPSIS
+.B "int" nvmf_register_ctrl
+.BI "(nvme_ctrl_t c " ","
+.BI "enum nvmf_dim_tas tas " ","
+.BI "__u32 *result " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller instance
+.IP "tas" 12
+Task field of the Command Dword 10 (cdw10). Indicates whether to
+perform a Registration, Deregistration, or Registration-update.
+.IP "result" 12
+The command-specific result returned by the DC upon command
+completion.
+.SH "DESCRIPTION"
+Perform registration task with a Discovery Controller (DC). Three
+tasks are supported: register, deregister, and registration update.
+.SH "RETURN"
+0 on success; on failure -1 is returned and errno is set
diff --git a/doc/man/nvmf_sectype_str.2 b/doc/man/nvmf_sectype_str.2
new file mode 100644
index 0000000..5d2bca1
--- /dev/null
+++ b/doc/man/nvmf_sectype_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_sectype_str" 9 "nvmf_sectype_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_sectype_str \- Decode SECTYPE field
+.SH SYNOPSIS
+.B "const char *" nvmf_sectype_str
+.BI "(__u8 sectype " ");"
+.SH ARGUMENTS
+.IP "sectype" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the SECTYPE field in the discovery log page
+entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_subtype_str.2 b/doc/man/nvmf_subtype_str.2
new file mode 100644
index 0000000..c5f8271
--- /dev/null
+++ b/doc/man/nvmf_subtype_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_subtype_str" 9 "nvmf_subtype_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_subtype_str \- Decode SUBTYPE field
+.SH SYNOPSIS
+.B "const char *" nvmf_subtype_str
+.BI "(__u8 subtype " ");"
+.SH ARGUMENTS
+.IP "subtype" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the subsystem type field in the discovery
+log page entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_tcp_sectype.2 b/doc/man/nvmf_tcp_sectype.2
new file mode 100644
index 0000000..d25de9d
--- /dev/null
+++ b/doc/man/nvmf_tcp_sectype.2
@@ -0,0 +1,26 @@
+.TH "libnvme" 9 "enum nvmf_tcp_sectype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_tcp_sectype \- Transport Specific Address Subtype Definition for NVMe/TCP Transport
+.SH SYNOPSIS
+enum nvmf_tcp_sectype {
+.br
+.BI " NVMF_TCP_SECTYPE_NONE"
+,
+.br
+.br
+.BI " NVMF_TCP_SECTYPE_TLS"
+,
+.br
+.br
+.BI " NVMF_TCP_SECTYPE_TLS13"
+
+};
+.SH Constants
+.IP "NVMF_TCP_SECTYPE_NONE" 12
+No Security
+.IP "NVMF_TCP_SECTYPE_TLS" 12
+Transport Layer Security version 1.2
+.IP "NVMF_TCP_SECTYPE_TLS13" 12
+Transport Layer Security version 1.3 or a subsequent
+version. The TLS protocol negotiates the version and
+cipher suite for each TCP connection.
diff --git a/doc/man/nvmf_treq.2 b/doc/man/nvmf_treq.2
new file mode 100644
index 0000000..12af12d
--- /dev/null
+++ b/doc/man/nvmf_treq.2
@@ -0,0 +1,30 @@
+.TH "libnvme" 9 "enum nvmf_treq" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_treq \- Transport Requirements codes for Discovery Log Page entry TREQ field
+.SH SYNOPSIS
+enum nvmf_treq {
+.br
+.BI " NVMF_TREQ_NOT_SPECIFIED"
+,
+.br
+.br
+.BI " NVMF_TREQ_REQUIRED"
+,
+.br
+.br
+.BI " NVMF_TREQ_NOT_REQUIRED"
+,
+.br
+.br
+.BI " NVMF_TREQ_DISABLE_SQFLOW"
+
+};
+.SH Constants
+.IP "NVMF_TREQ_NOT_SPECIFIED" 12
+Not specified
+.IP "NVMF_TREQ_REQUIRED" 12
+Required
+.IP "NVMF_TREQ_NOT_REQUIRED" 12
+Not Required
+.IP "NVMF_TREQ_DISABLE_SQFLOW" 12
+SQ flow control disable supported
diff --git a/doc/man/nvmf_treq_str.2 b/doc/man/nvmf_treq_str.2
new file mode 100644
index 0000000..2c65a53
--- /dev/null
+++ b/doc/man/nvmf_treq_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_treq_str" 9 "nvmf_treq_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_treq_str \- Decode TREQ field
+.SH SYNOPSIS
+.B "const char *" nvmf_treq_str
+.BI "(__u8 treq " ");"
+.SH ARGUMENTS
+.IP "treq" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the transport requirements field in the
+discovery log page entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_trtype.2 b/doc/man/nvmf_trtype.2
new file mode 100644
index 0000000..98b6ac5
--- /dev/null
+++ b/doc/man/nvmf_trtype.2
@@ -0,0 +1,43 @@
+.TH "libnvme" 9 "enum nvmf_trtype" "February 2024" "API Manual" LINUX
+.SH NAME
+enum nvmf_trtype \- Transport Type codes for Discovery Log Page entry TRTYPE field
+.SH SYNOPSIS
+enum nvmf_trtype {
+.br
+.BI " NVMF_TRTYPE_UNSPECIFIED"
+,
+.br
+.br
+.BI " NVMF_TRTYPE_RDMA"
+,
+.br
+.br
+.BI " NVMF_TRTYPE_FC"
+,
+.br
+.br
+.BI " NVMF_TRTYPE_TCP"
+,
+.br
+.br
+.BI " NVMF_TRTYPE_LOOP"
+,
+.br
+.br
+.BI " NVMF_TRTYPE_MAX"
+
+};
+.SH Constants
+.IP "NVMF_TRTYPE_UNSPECIFIED" 12
+Not indicated
+.IP "NVMF_TRTYPE_RDMA" 12
+RDMA
+.IP "NVMF_TRTYPE_FC" 12
+Fibre Channel
+.IP "NVMF_TRTYPE_TCP" 12
+TCP
+.IP "NVMF_TRTYPE_LOOP" 12
+Intra-host Transport (i.e., loopback), reserved
+for host usage.
+.IP "NVMF_TRTYPE_MAX" 12
+Maximum value for \fIenum nvmf_trtype\fP
diff --git a/doc/man/nvmf_trtype_str.2 b/doc/man/nvmf_trtype_str.2
new file mode 100644
index 0000000..98fd0a0
--- /dev/null
+++ b/doc/man/nvmf_trtype_str.2
@@ -0,0 +1,14 @@
+.TH "nvmf_trtype_str" 9 "nvmf_trtype_str" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_trtype_str \- Decode TRTYPE field
+.SH SYNOPSIS
+.B "const char *" nvmf_trtype_str
+.BI "(__u8 trtype " ");"
+.SH ARGUMENTS
+.IP "trtype" 12
+value to be decoded
+.SH "DESCRIPTION"
+Decode the transport type field in the discovery
+log page entry.
+.SH "RETURN"
+decoded string
diff --git a/doc/man/nvmf_update_config.2 b/doc/man/nvmf_update_config.2
new file mode 100644
index 0000000..243b40e
--- /dev/null
+++ b/doc/man/nvmf_update_config.2
@@ -0,0 +1,15 @@
+.TH "nvmf_update_config" 9 "nvmf_update_config" "February 2024" "libnvme API manual" LINUX
+.SH NAME
+nvmf_update_config \- Update fabrics configuration values
+.SH SYNOPSIS
+.B "void" nvmf_update_config
+.BI "(nvme_ctrl_t c " ","
+.BI "const struct nvme_fabrics_config *cfg " ");"
+.SH ARGUMENTS
+.IP "c" 12
+Controller to be modified
+.IP "cfg" 12
+Updated configuration values
+.SH "DESCRIPTION"
+Updates the values from \fIc\fP with the configuration values from \fIcfg\fP;
+all non-default values from \fIcfg\fP will overwrite the values in \fIc\fP.
diff --git a/doc/meson.build b/doc/meson.build
new file mode 100644
index 0000000..f12f3b9
--- /dev/null
+++ b/doc/meson.build
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2022 Dell Inc.
+# Copyright (c) 2022 SUSE LLC
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+# Authors: Daniel Wagner <dwagner@suse.de>
+#
+
+api_files = [
+ 'fabrics.h',
+ 'filters.h',
+ 'ioctl.h',
+ 'linux.h',
+ 'log.h',
+ 'mi.h',
+ 'nbft.h',
+ 'tree.h',
+ 'types.h',
+ 'util.h'
+]
+
+api_paths = []
+foreach f: api_files
+ api_paths += files('../src/nvme/' + f)
+endforeach
+
+sphinx_sources = [
+ 'conf.py',
+ 'api.rst',
+ 'index.rst',
+ 'quickstart.rst',
+ 'installation.rst',
+ 'mi.rst',
+ 'config-schema.json'
+]
+
+static_sources = []
+foreach file : sphinx_sources
+ static_sources += configure_file(input: file + '.in',
+ output: file,
+ configuration: substs)
+endforeach
+
+subdir('rst')
+
+top_source_dir = meson.current_source_dir() + '/../'
+
+want_docs = get_option('docs')
+want_docs_build = get_option('docs-build')
+kernel_doc = find_program(top_source_dir + 'scripts/kernel-doc')
+kernel_doc_check = find_program(top_source_dir +'scripts/kernel-doc-check')
+
+test('kdoc', kernel_doc_check, args: api_paths)
+
+if want_docs != 'false'
+
+ if want_docs == 'all' or want_docs == 'man'
+ mandir = join_paths(get_option('mandir'), 'man2')
+ list_man_pages = find_program(top_source_dir + 'scripts/list-man-pages.sh')
+ if want_docs_build
+ foreach apif : api_paths
+ c = run_command(list_man_pages, apif, check: true)
+ man_pages = c.stdout().split()
+ foreach page : man_pages
+ custom_target(
+ page.underscorify() + '_man',
+ input: apif,
+ output: page + '.2',
+ capture: true,
+ command: [kernel_doc,
+ '-module', 'libnvme',
+ '-man',
+ '-function',
+ page,
+ apif],
+ install: true,
+ install_dir: mandir)
+ endforeach
+ endforeach
+ else
+ if want_docs == 'all' or want_docs == 'man'
+ list_pre_compiled = find_program(top_source_dir + 'scripts/list-pre-compiled.sh')
+ m = run_command(list_pre_compiled, check: true)
+ man_pages = m.stdout().strip().split('\n')
+ install_data(man_pages, install_dir: mandir)
+ endif
+ endif
+ endif
+
+ if want_docs == 'all' or want_docs == 'html'
+ htmldir = join_paths(get_option('htmldir'), 'nvme')
+ sphinx_build = find_program('sphinx-build-3', 'sphinx-build')
+ if sphinx_build.found() and want_docs_build
+ custom_target(
+ 'generate_doc_html',
+ input: [static_sources, rst],
+ output: 'html',
+ command: [sphinx_build,
+ '-b', 'html',
+ '@OUTDIR@',
+ '@OUTDIR@' + '/html'],
+ install: true,
+ install_dir: htmldir)
+ else
+ # The HTML doc is not ready yet.
+ # if want_docs == 'all' or want_docs == 'html'
+ # install_subdir('html', install_dir: htmldir)
+ # endif
+ endif
+ endif
+endif
diff --git a/doc/mi.rst.in b/doc/mi.rst.in
new file mode 100644
index 0000000..a75fd69
--- /dev/null
+++ b/doc/mi.rst.in
@@ -0,0 +1,54 @@
+NVMe Management Interface (NVMe-MI) support
+===========================================
+
+This libnvme project also includes support for the NVMe Management Interface
+(NVMe-MI), currently over a Management Component Transport (MCTP)
+protocol link. This MCTP link will typically use i2c/SMBus as the
+hardware transport, enabling out-of-band management and control over NVMe
+devices using a simple SMBus interface.
+
+The MI interface is compiled into a separate shared object, ``libnvme-mi.so``.
+
+Most of the MI API is transport-agnostic, except for the endpoint constructor
+functions. Once an endpoint object (``nvme_mi_ep_t``) is created, the generic
+functions can be used to manage it.
+
+When endpoints are created (through one of the transport-specific functions,
+like ``nvme_mi_open_mctp()``), the endpoint hardware will be probed to
+see if any device-specific workarounds ("quirks") are required. This is
+implemented as an Identify Controller command, requesting a small amount of
+data on controller ID 0.
+
+To suppress this probe, the ``LIBNVME_MI_PROBE_ENABLED`` environment var can be
+set. Values of ``0``, ``false`` and ``disabled`` will disable the probe, and no
+quirks will be applied. Other values, or an unset environment variable, will
+enable the probe.
+
+MCTP Transport
+--------------
+
+The MI API is generally transport-agnostic, but the only currently-supported
+transport is MCTP, using the kernel ``AF_MCTP`` socket interface.
+
+MCTP endpoints are addressed by a (network-id, endpoint-id) pair. Endpoint
+IDs (EIDs) are defined by the MCTP standard as an 8-bit value. Since the
+address space is somewhat limited, the Linux `AF_MCTP` support allows for
+separate MCTP "networks", which provide separate address spaces. These networks
+each have a unique ``unsigned int`` as their ID.
+
+The default Network ID is 1; unless you have configured otherwise, MCTP
+endpoints will appear on this network.
+
+If compiled with D-Bus support, ``libnvme-mi`` can query the system MCTP daemon
+("``mctpd``") to find attached NVMe devices, via the ``nvme_mi_scan_mctp()``
+function. Calling this will establish a ``nvme_root_t`` object, populated
+with the results of that scan. Use the ``nvme_mi_for_each_endpoint`` macro
+to iterate through the scanned endpoints.
+
+Note that the MCTP daemon is provided separately, as part of the MCTP userspace
+tools, at https://github.com/CodeConstruct/mctp . ``mctpd`` is responsible for
+discovery and enumeration for MCTP endpoints on the system, and will query
+each for its protocol capabilities during enumeration. Consequently, NVMe-MI
+endpoints will need to report support for NVMe-MI-over-MCTP (protocol 0x4) in
+their supported protocols list (ie., as returned by the MCTP Get Message Type
+Support command) in order to be discovered.
diff --git a/doc/quickstart.rst.in b/doc/quickstart.rst.in
new file mode 100644
index 0000000..d356188
--- /dev/null
+++ b/doc/quickstart.rst.in
@@ -0,0 +1,5 @@
+==========
+Quickstart
+==========
+
+tbd \ No newline at end of file
diff --git a/doc/rst/fabrics.rst b/doc/rst/fabrics.rst
new file mode 100644
index 0000000..74d04e5
--- /dev/null
+++ b/doc/rst/fabrics.rst
@@ -0,0 +1,545 @@
+.. _fabrics.h:
+
+**fabrics.h**
+
+
+Fabrics-specific definitions.
+
+
+
+.. c:struct:: nvme_fabrics_config
+
+ Defines all linux nvme fabrics initiator options
+
+**Definition**
+
+::
+
+ struct nvme_fabrics_config {
+ char *host_traddr;
+ char *host_iface;
+ int queue_size;
+ int nr_io_queues;
+ int reconnect_delay;
+ int ctrl_loss_tmo;
+ int fast_io_fail_tmo;
+ int keep_alive_tmo;
+ int nr_write_queues;
+ int nr_poll_queues;
+ int tos;
+ int keyring;
+ int tls_key;
+ bool duplicate_connect;
+ bool disable_sqflow;
+ bool hdr_digest;
+ bool data_digest;
+ bool tls;
+ bool concat;
+ };
+
+**Members**
+
+``host_traddr``
+ Host transport address
+
+``host_iface``
+ Host interface name
+
+``queue_size``
+ Number of IO queue entries
+
+``nr_io_queues``
+ Number of controller IO queues to establish
+
+``reconnect_delay``
+ Time between two consecutive reconnect attempts.
+
+``ctrl_loss_tmo``
+ Override the default controller reconnect attempt timeout in seconds
+
+``fast_io_fail_tmo``
+ Set the fast I/O fail timeout in seconds.
+
+``keep_alive_tmo``
+ Override the default keep-alive-timeout to this value in seconds
+
+``nr_write_queues``
+ Number of queues to use for exclusively for writing
+
+``nr_poll_queues``
+ Number of queues to reserve for polling completions
+
+``tos``
+ Type of service
+
+``keyring``
+ Keyring to store and lookup keys
+
+``tls_key``
+ TLS PSK for the connection
+
+``duplicate_connect``
+ Allow multiple connections to the same target
+
+``disable_sqflow``
+ Disable controller sq flow control
+
+``hdr_digest``
+ Generate/verify header digest (TCP)
+
+``data_digest``
+ Generate/verify data digest (TCP)
+
+``tls``
+ Start TLS on the connection (TCP)
+
+``concat``
+ Enable secure concatenation (TCP)
+
+
+
+.. c:function:: const char * nvmf_trtype_str (__u8 trtype)
+
+ Decode TRTYPE field
+
+**Parameters**
+
+``__u8 trtype``
+ value to be decoded
+
+**Description**
+
+Decode the transport type field in the discovery
+log page entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: const char * nvmf_adrfam_str (__u8 adrfam)
+
+ Decode ADRFAM field
+
+**Parameters**
+
+``__u8 adrfam``
+ value to be decoded
+
+**Description**
+
+Decode the address family field in the discovery
+log page entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: const char * nvmf_subtype_str (__u8 subtype)
+
+ Decode SUBTYPE field
+
+**Parameters**
+
+``__u8 subtype``
+ value to be decoded
+
+**Description**
+
+Decode the subsystem type field in the discovery
+log page entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: const char * nvmf_treq_str (__u8 treq)
+
+ Decode TREQ field
+
+**Parameters**
+
+``__u8 treq``
+ value to be decoded
+
+**Description**
+
+Decode the transport requirements field in the
+discovery log page entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: const char * nvmf_eflags_str (__u16 eflags)
+
+ Decode EFLAGS field
+
+**Parameters**
+
+``__u16 eflags``
+ value to be decoded
+
+**Description**
+
+Decode the EFLAGS field in the discovery log page
+entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: const char * nvmf_sectype_str (__u8 sectype)
+
+ Decode SECTYPE field
+
+**Parameters**
+
+``__u8 sectype``
+ value to be decoded
+
+**Description**
+
+Decode the SECTYPE field in the discovery log page
+entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: const char * nvmf_prtype_str (__u8 prtype)
+
+ Decode RDMA Provider type field
+
+**Parameters**
+
+``__u8 prtype``
+ value to be decoded
+
+**Description**
+
+Decode the RDMA Provider type field in the discovery
+log page entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: const char * nvmf_qptype_str (__u8 qptype)
+
+ Decode RDMA QP Service type field
+
+**Parameters**
+
+``__u8 qptype``
+ value to be decoded
+
+**Description**
+
+Decode the RDMA QP Service type field in the discovery log page
+entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: const char * nvmf_cms_str (__u8 cms)
+
+ Decode RDMA connection management service field
+
+**Parameters**
+
+``__u8 cms``
+ value to be decoded
+
+**Description**
+
+Decode the RDMA connection management service field in the discovery
+log page entry.
+
+**Return**
+
+decoded string
+
+
+.. c:function:: void nvmf_default_config (struct nvme_fabrics_config *cfg)
+
+ Default values for fabrics configuration
+
+**Parameters**
+
+``struct nvme_fabrics_config *cfg``
+ config values to set
+
+**Description**
+
+Initializes **cfg** with default values.
+
+
+.. c:function:: void nvmf_update_config (nvme_ctrl_t c, const struct nvme_fabrics_config *cfg)
+
+ Update fabrics configuration values
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller to be modified
+
+``const struct nvme_fabrics_config *cfg``
+ Updated configuration values
+
+**Description**
+
+Updates the values from **c** with the configuration values from **cfg**;
+all non-default values from **cfg** will overwrite the values in **c**.
+
+
+.. c:function:: int nvmf_add_ctrl (nvme_host_t h, nvme_ctrl_t c, const struct nvme_fabrics_config *cfg)
+
+ Connect a controller and update topology
+
+**Parameters**
+
+``nvme_host_t h``
+ Host to which the controller should be attached
+
+``nvme_ctrl_t c``
+ Controller to be connected
+
+``const struct nvme_fabrics_config *cfg``
+ Default configuration for the controller
+
+**Description**
+
+Issues a 'connect' command to the NVMe-oF controller and inserts **c**
+into the topology using **h** as parent.
+**c** must be initialized and not connected to the topology.
+
+**Return**
+
+0 on success; on failure errno is set and -1 is returned.
+
+
+.. c:function:: int nvmf_get_discovery_log (nvme_ctrl_t c, struct nvmf_discovery_log **logp, int max_retries)
+
+ Return the discovery log page
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Discovery controller to use
+
+``struct nvmf_discovery_log **logp``
+ Pointer to the log page to be returned
+
+``int max_retries``
+ Number of retries in case of failure
+
+**Description**
+
+The memory allocated for the log page and returned in **logp**
+must be freed by the caller using free().
+
+**Note**
+
+Consider using nvmf_get_discovery_wargs() instead.
+
+**Return**
+
+0 on success; on failure -1 is returned and errno is set
+
+
+
+
+.. c:struct:: nvme_get_discovery_args
+
+ Arguments for nvmf_get_discovery_wargs()
+
+**Definition**
+
+::
+
+ struct nvme_get_discovery_args {
+ nvme_ctrl_t c;
+ int args_size;
+ int max_retries;
+ __u32 *result;
+ __u32 timeout;
+ __u8 lsp;
+ };
+
+**Members**
+
+``c``
+ Discovery controller
+
+``args_size``
+ Length of the structure
+
+``max_retries``
+ Number of retries in case of failure
+
+``result``
+ The command completion result from CQE dword0
+
+``timeout``
+ Timeout in ms (default: NVME_DEFAULT_IOCTL_TIMEOUT)
+
+``lsp``
+ Log specific field (See enum nvmf_log_discovery_lsp)
+
+
+
+.. c:function:: struct nvmf_discovery_log * nvmf_get_discovery_wargs (struct nvme_get_discovery_args *args)
+
+ Get the discovery log page with args
+
+**Parameters**
+
+``struct nvme_get_discovery_args *args``
+ Argument structure
+
+**Description**
+
+This function is similar to nvmf_get_discovery_log(), but
+takes an extensible **args** parameter. **args** provides more
+options than nvmf_get_discovery_log().
+
+This function performs a get discovery log page (DLP) command
+and returns the DLP. The memory allocated for the returned
+DLP must be freed by the caller using free().
+
+**Return**
+
+Pointer to the discovery log page (to be freed). NULL
+on failure and errno is set.
+
+
+.. c:function:: char * nvmf_hostnqn_generate ()
+
+ Generate a machine specific host nqn
+
+**Parameters**
+
+**Return**
+
+An nvm namespace qualified name string based on the machine
+identifier, or NULL if not successful.
+
+
+.. c:function:: char * nvmf_hostnqn_from_file ()
+
+ Reads the host nvm qualified name from the config default location
+
+**Parameters**
+
+**Description**
+
+
+Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme.
+$SYSCONFDIR is usually /etc.
+
+**Return**
+
+The host nqn, or NULL if unsuccessful. If found, the caller
+is responsible to free the string.
+
+
+.. c:function:: char * nvmf_hostid_from_file ()
+
+ Reads the host identifier from the config default location
+
+**Parameters**
+
+**Description**
+
+
+Retrieve the host idenditifer from the config file located in $SYSCONFDIR/nvme/.
+$SYSCONFDIR is usually /etc.
+
+**Return**
+
+The host identifier, or NULL if unsuccessful. If found, the caller
+ is responsible to free the string.
+
+
+.. c:function:: nvme_ctrl_t nvmf_connect_disc_entry (nvme_host_t h, struct nvmf_disc_log_entry *e, const struct nvme_fabrics_config *defcfg, bool *discover)
+
+ Connect controller based on the discovery log page entry
+
+**Parameters**
+
+``nvme_host_t h``
+ Host to which the controller should be connected
+
+``struct nvmf_disc_log_entry *e``
+ Discovery log page entry
+
+``const struct nvme_fabrics_config *defcfg``
+ Default configuration to be used for the new controller
+
+``bool *discover``
+ Set to 'true' if the new controller is a discovery controller
+
+**Return**
+
+Pointer to the new controller
+
+
+.. c:function:: bool nvmf_is_registration_supported (nvme_ctrl_t c)
+
+ check whether registration can be performed.
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Description**
+
+Only discovery controllers (DC) that comply with TP8010 support
+explicit registration with the DIM PDU. These can be identified by
+looking at the value of a dctype in the Identify command
+response. A value of 1 (DDC) or 2 (CDC) indicates that the DC
+supports explicit registration.
+
+**Return**
+
+true if controller supports explicit registration. false
+otherwise.
+
+
+.. c:function:: int nvmf_register_ctrl (nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result)
+
+ Perform registration task with a DC
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+``enum nvmf_dim_tas tas``
+ Task field of the Command Dword 10 (cdw10). Indicates whether to
+ perform a Registration, Deregistration, or Registration-update.
+
+``__u32 *result``
+ The command-specific result returned by the DC upon command
+ completion.
+
+**Description**
+
+Perform registration task with a Discovery Controller (DC). Three
+tasks are supported: register, deregister, and registration update.
+
+**Return**
+
+0 on success; on failure -1 is returned and errno is set
+
+
diff --git a/doc/rst/filters.rst b/doc/rst/filters.rst
new file mode 100644
index 0000000..3e8c997
--- /dev/null
+++ b/doc/rst/filters.rst
@@ -0,0 +1,142 @@
+.. _filters.h:
+
+**filters.h**
+
+
+libnvme directory filter
+
+.. c:function:: int nvme_namespace_filter (const struct dirent *d)
+
+ Filter for namespaces
+
+**Parameters**
+
+``const struct dirent *d``
+ dirent to check
+
+**Return**
+
+1 if **d** matches, 0 otherwise
+
+
+.. c:function:: int nvme_paths_filter (const struct dirent *d)
+
+ Filter for paths
+
+**Parameters**
+
+``const struct dirent *d``
+ dirent to check
+
+**Return**
+
+1 if **d** matches, 0 otherwise
+
+
+.. c:function:: int nvme_ctrls_filter (const struct dirent *d)
+
+ Filter for controllers
+
+**Parameters**
+
+``const struct dirent *d``
+ dirent to check
+
+**Return**
+
+1 if **d** matches, 0 otherwise
+
+
+.. c:function:: int nvme_subsys_filter (const struct dirent *d)
+
+ Filter for subsystems
+
+**Parameters**
+
+``const struct dirent *d``
+ dirent to check
+
+**Return**
+
+1 if **d** matches, 0 otherwise
+
+
+.. c:function:: int nvme_scan_subsystems (struct dirent ***subsys)
+
+ Scan for subsystems
+
+**Parameters**
+
+``struct dirent ***subsys``
+ Pointer to array of dirents
+
+**Return**
+
+number of entries in **subsys**
+
+
+.. c:function:: int nvme_scan_subsystem_namespaces (nvme_subsystem_t s, struct dirent ***ns)
+
+ Scan for namespaces in a subsystem
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ Subsystem to scan
+
+``struct dirent ***ns``
+ Pointer to array of dirents
+
+**Return**
+
+number of entries in **ns**
+
+
+.. c:function:: int nvme_scan_ctrls (struct dirent ***ctrls)
+
+ Scan for controllers
+
+**Parameters**
+
+``struct dirent ***ctrls``
+ Pointer to array of dirents
+
+**Return**
+
+number of entries in **ctrls**
+
+
+.. c:function:: int nvme_scan_ctrl_namespace_paths (nvme_ctrl_t c, struct dirent ***paths)
+
+ Scan for namespace paths in a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller to scan
+
+``struct dirent ***paths``
+ Pointer to array of dirents
+
+**Return**
+
+number of entries in **paths**
+
+
+.. c:function:: int nvme_scan_ctrl_namespaces (nvme_ctrl_t c, struct dirent ***ns)
+
+ Scan for namespaces in a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller to scan
+
+``struct dirent ***ns``
+ Pointer to array of dirents
+
+**Return**
+
+number of entries in **ns**
+
+
diff --git a/doc/rst/ioctl.rst b/doc/rst/ioctl.rst
new file mode 100644
index 0000000..4d8af34
--- /dev/null
+++ b/doc/rst/ioctl.rst
@@ -0,0 +1,5227 @@
+.. _ioctl.h:
+
+**ioctl.h**
+
+
+Linux NVMe ioctl interface functions
+
+
+
+.. c:struct:: nvme_passthru_cmd
+
+ nvme passthrough command structure
+
+**Definition**
+
+::
+
+ struct nvme_passthru_cmd {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u32 cdw10;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 result;
+ };
+
+**Members**
+
+``opcode``
+ Operation code, see :c:type:`enum nvme_io_opcodes <nvme_io_opcodes>` and :c:type:`enum nvme_admin_opcodes <nvme_admin_opcodes>`
+
+``flags``
+ Not supported: intended for command flags (eg: SGL, FUSE)
+
+``rsvd1``
+ Reserved for future use
+
+``nsid``
+ Namespace Identifier, or Fabrics type
+
+``cdw2``
+ Command Dword 2 (no spec defined use)
+
+``cdw3``
+ Command Dword 3 (no spec defined use)
+
+``metadata``
+ User space address to metadata buffer (NULL if not used)
+
+``addr``
+ User space address to data buffer (NULL if not used)
+
+``metadata_len``
+ Metadata buffer transfer length
+
+``data_len``
+ Data buffer transfer length
+
+``cdw10``
+ Command Dword 10 (command specific)
+
+``cdw11``
+ Command Dword 11 (command specific)
+
+``cdw12``
+ Command Dword 12 (command specific)
+
+``cdw13``
+ Command Dword 13 (command specific)
+
+``cdw14``
+ Command Dword 14 (command specific)
+
+``cdw15``
+ Command Dword 15 (command specific)
+
+``timeout_ms``
+ If non-zero, overrides system default timeout in milliseconds
+
+``result``
+ Set on completion to the command's CQE DWORD 0 controller response
+
+
+
+
+
+.. c:struct:: nvme_passthru_cmd64
+
+ 64-bit nvme passthrough command structure
+
+**Definition**
+
+::
+
+ struct nvme_passthru_cmd64 {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u32 cdw10;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 rsvd2;
+ __u64 result;
+ };
+
+**Members**
+
+``opcode``
+ Operation code, see :c:type:`enum nvme_io_opcodes <nvme_io_opcodes>` and :c:type:`enum nvme_admin_opcodes <nvme_admin_opcodes>`
+
+``flags``
+ Not supported: intended for command flags (eg: SGL, FUSE)
+
+``rsvd1``
+ Reserved for future use
+
+``nsid``
+ Namespace Identifier, or Fabrics type
+
+``cdw2``
+ Command Dword 2 (no spec defined use)
+
+``cdw3``
+ Command Dword 3 (no spec defined use)
+
+``metadata``
+ User space address to metadata buffer (NULL if not used)
+
+``addr``
+ User space address to data buffer (NULL if not used)
+
+``metadata_len``
+ Metadata buffer transfer length
+
+``data_len``
+ Data buffer transfer length
+
+``cdw10``
+ Command Dword 10 (command specific)
+
+``cdw11``
+ Command Dword 11 (command specific)
+
+``cdw12``
+ Command Dword 12 (command specific)
+
+``cdw13``
+ Command Dword 13 (command specific)
+
+``cdw14``
+ Command Dword 14 (command specific)
+
+``cdw15``
+ Command Dword 15 (command specific)
+
+``timeout_ms``
+ If non-zero, overrides system default timeout in milliseconds
+
+``rsvd2``
+ Reserved for future use (and fills an implicit struct pad
+
+``result``
+ Set on completion to the command's CQE DWORD 0-1 controller response
+
+
+
+
+
+.. c:struct:: nvme_uring_cmd
+
+ nvme uring command structure
+
+**Definition**
+
+::
+
+ struct nvme_uring_cmd {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u32 cdw10;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 rsvd2;
+ };
+
+**Members**
+
+``opcode``
+ Operation code, see :c:type:`enum nvme_io_opcodes <nvme_io_opcodes>` and :c:type:`enum nvme_admin_opcodes <nvme_admin_opcodes>`
+
+``flags``
+ Not supported: intended for command flags (eg: SGL, FUSE)
+
+``rsvd1``
+ Reserved for future use
+
+``nsid``
+ Namespace Identifier, or Fabrics type
+
+``cdw2``
+ Command Dword 2 (no spec defined use)
+
+``cdw3``
+ Command Dword 3 (no spec defined use)
+
+``metadata``
+ User space address to metadata buffer (NULL if not used)
+
+``addr``
+ User space address to data buffer (NULL if not used)
+
+``metadata_len``
+ Metadata buffer transfer length
+
+``data_len``
+ Data buffer transfer length
+
+``cdw10``
+ Command Dword 10 (command specific)
+
+``cdw11``
+ Command Dword 11 (command specific)
+
+``cdw12``
+ Command Dword 12 (command specific)
+
+``cdw13``
+ Command Dword 13 (command specific)
+
+``cdw14``
+ Command Dword 14 (command specific)
+
+``cdw15``
+ Command Dword 15 (command specific)
+
+``timeout_ms``
+ If non-zero, overrides system default timeout in milliseconds
+
+``rsvd2``
+ Reserved for future use (and fills an implicit struct pad
+
+
+
+.. c:macro:: sizeof_args
+
+``sizeof_args (type, member, align)``
+
+ Helper function used to determine structure sizes
+
+**Parameters**
+
+``type``
+ Argument structure type
+
+``member``
+ Member inside the type
+
+``align``
+ Alignment information
+
+
+.. c:function:: int nvme_submit_admin_passthru64 (int fd, struct nvme_passthru_cmd64 *cmd, __u64 *result)
+
+ Submit a 64-bit nvme passthrough admin command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_passthru_cmd64 *cmd``
+ The nvme admin command to send
+
+``__u64 *result``
+ Optional field to return the result from the CQE DW0-1
+
+**Description**
+
+Uses NVME_IOCTL_ADMIN64_CMD for the ioctl request.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_admin_passthru64 (int fd, __u8 opcode, __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len, void *metadata, __u32 timeout_ms, __u64 *result)
+
+ Submit a 64-bit nvme passthrough command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 opcode``
+ The nvme io command to send
+
+``__u8 flags``
+ NVMe command flags (not used)
+
+``__u16 rsvd``
+ Reserved for future use
+
+``__u32 nsid``
+ Namespace identifier
+
+``__u32 cdw2``
+ Command dword 2
+
+``__u32 cdw3``
+ Command dword 3
+
+``__u32 cdw10``
+ Command dword 10
+
+``__u32 cdw11``
+ Command dword 11
+
+``__u32 cdw12``
+ Command dword 12
+
+``__u32 cdw13``
+ Command dword 13
+
+``__u32 cdw14``
+ Command dword 14
+
+``__u32 cdw15``
+ Command dword 15
+
+``__u32 data_len``
+ Length of the data transferred in this command in bytes
+
+``void *data``
+ Pointer to user address of the data buffer
+
+``__u32 metadata_len``
+ Length of metadata transferred in this command
+
+``void *metadata``
+ Pointer to user address of the metadata buffer
+
+``__u32 timeout_ms``
+ How long the kernel waits for the command to complete
+
+``__u64 *result``
+ Optional field to return the result from the CQE dword 0
+
+**Description**
+
+Parameterized form of nvme_submit_admin_passthru64(). This sets up and
+submits a :c:type:`struct nvme_passthru_cmd64 <nvme_passthru_cmd64>`.
+
+Known values for **opcode** are defined in :c:type:`enum nvme_admin_opcode <nvme_admin_opcode>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_submit_admin_passthru (int fd, struct nvme_passthru_cmd *cmd, __u32 *result)
+
+ Submit an nvme passthrough admin command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_passthru_cmd *cmd``
+ The nvme admin command to send
+
+``__u32 *result``
+ Optional field to return the result from the CQE DW0
+
+**Description**
+
+Uses NVME_IOCTL_ADMIN_CMD for the ioctl request.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_admin_passthru (int fd, __u8 opcode, __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len, void *metadata, __u32 timeout_ms, __u32 *result)
+
+ Submit an nvme passthrough command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 opcode``
+ The nvme io command to send
+
+``__u8 flags``
+ NVMe command flags (not used)
+
+``__u16 rsvd``
+ Reserved for future use
+
+``__u32 nsid``
+ Namespace identifier
+
+``__u32 cdw2``
+ Command dword 2
+
+``__u32 cdw3``
+ Command dword 3
+
+``__u32 cdw10``
+ Command dword 10
+
+``__u32 cdw11``
+ Command dword 11
+
+``__u32 cdw12``
+ Command dword 12
+
+``__u32 cdw13``
+ Command dword 13
+
+``__u32 cdw14``
+ Command dword 14
+
+``__u32 cdw15``
+ Command dword 15
+
+``__u32 data_len``
+ Length of the data transferred in this command in bytes
+
+``void *data``
+ Pointer to user address of the data buffer
+
+``__u32 metadata_len``
+ Length of metadata transferred in this command
+
+``void *metadata``
+ Pointer to user address of the metadata buffer
+
+``__u32 timeout_ms``
+ How long the kernel waits for the command to complete
+
+``__u32 *result``
+ Optional field to return the result from the CQE dword 0
+
+**Description**
+
+Parameterized form of nvme_submit_admin_passthru(). This sets up and
+submits a :c:type:`struct nvme_passthru_cmd <nvme_passthru_cmd>`.
+
+Known values for **opcode** are defined in :c:type:`enum nvme_admin_opcode <nvme_admin_opcode>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_submit_io_passthru64 (int fd, struct nvme_passthru_cmd64 *cmd, __u64 *result)
+
+ Submit a 64-bit nvme passthrough command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_passthru_cmd64 *cmd``
+ The nvme io command to send
+
+``__u64 *result``
+ Optional field to return the result from the CQE DW0-1
+
+**Description**
+
+Uses NVME_IOCTL_IO64_CMD for the ioctl request.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_io_passthru64 (int fd, __u8 opcode, __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len, void *metadata, __u32 timeout_ms, __u64 *result)
+
+ Submit an nvme io passthrough command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 opcode``
+ The nvme io command to send
+
+``__u8 flags``
+ NVMe command flags (not used)
+
+``__u16 rsvd``
+ Reserved for future use
+
+``__u32 nsid``
+ Namespace identifier
+
+``__u32 cdw2``
+ Command dword 2
+
+``__u32 cdw3``
+ Command dword 3
+
+``__u32 cdw10``
+ Command dword 10
+
+``__u32 cdw11``
+ Command dword 11
+
+``__u32 cdw12``
+ Command dword 12
+
+``__u32 cdw13``
+ Command dword 13
+
+``__u32 cdw14``
+ Command dword 14
+
+``__u32 cdw15``
+ Command dword 15
+
+``__u32 data_len``
+ Length of the data transferred in this command in bytes
+
+``void *data``
+ Pointer to user address of the data buffer
+
+``__u32 metadata_len``
+ Length of metadata transferred in this command
+
+``void *metadata``
+ Pointer to user address of the metadata buffer
+
+``__u32 timeout_ms``
+ How long the kernel waits for the command to complete
+
+``__u64 *result``
+ Optional field to return the result from the CQE dword 0
+
+**Description**
+
+Parameterized form of nvme_submit_io_passthru64(). This sets up and submits
+a :c:type:`struct nvme_passthru_cmd64 <nvme_passthru_cmd64>`.
+
+Known values for **opcode** are defined in :c:type:`enum nvme_io_opcode <nvme_io_opcode>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_submit_io_passthru (int fd, struct nvme_passthru_cmd *cmd, __u32 *result)
+
+ Submit an nvme passthrough command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_passthru_cmd *cmd``
+ The nvme io command to send
+
+``__u32 *result``
+ Optional field to return the result from the CQE DW0
+
+**Description**
+
+Uses NVME_IOCTL_IO_CMD for the ioctl request.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_io_passthru (int fd, __u8 opcode, __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len, void *metadata, __u32 timeout_ms, __u32 *result)
+
+ Submit an nvme io passthrough command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 opcode``
+ The nvme io command to send
+
+``__u8 flags``
+ NVMe command flags (not used)
+
+``__u16 rsvd``
+ Reserved for future use
+
+``__u32 nsid``
+ Namespace identifier
+
+``__u32 cdw2``
+ Command dword 2
+
+``__u32 cdw3``
+ Command dword 3
+
+``__u32 cdw10``
+ Command dword 10
+
+``__u32 cdw11``
+ Command dword 11
+
+``__u32 cdw12``
+ Command dword 12
+
+``__u32 cdw13``
+ Command dword 13
+
+``__u32 cdw14``
+ Command dword 14
+
+``__u32 cdw15``
+ Command dword 15
+
+``__u32 data_len``
+ Length of the data transferred in this command in bytes
+
+``void *data``
+ Pointer to user address of the data buffer
+
+``__u32 metadata_len``
+ Length of metadata transferred in this command
+
+``void *metadata``
+ Pointer to user address of the metadata buffer
+
+``__u32 timeout_ms``
+ How long the kernel waits for the command to complete
+
+``__u32 *result``
+ Optional field to return the result from the CQE dword 0
+
+**Description**
+
+Parameterized form of nvme_submit_io_passthru(). This sets up and submits
+a :c:type:`struct nvme_passthru_cmd <nvme_passthru_cmd>`.
+
+Known values for **opcode** are defined in :c:type:`enum nvme_io_opcode <nvme_io_opcode>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_subsystem_reset (int fd)
+
+ Initiate a subsystem reset
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+**Description**
+
+This should only be sent to controller handles, not to namespaces.
+
+**Return**
+
+Zero if a subsystem reset was initiated or -1 with errno set
+otherwise.
+
+
+.. c:function:: int nvme_ctrl_reset (int fd)
+
+ Initiate a controller reset
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+**Description**
+
+This should only be sent to controller handles, not to namespaces.
+
+**Return**
+
+0 if a reset was initiated or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_ns_rescan (int fd)
+
+ Initiate a controller rescan
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+**Description**
+
+This should only be sent to controller handles, not to namespaces.
+
+**Return**
+
+0 if a rescan was initiated or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_nsid (int fd, __u32 *nsid)
+
+ Retrieve the NSID from a namespace file descriptor
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme namespace
+
+``__u32 *nsid``
+ User pointer to namespace id
+
+**Description**
+
+This should only be sent to namespace handles, not to controllers. The
+kernel's interface returns the nsid as the return value. This is unfortunate
+for many architectures that are incapable of allowing distinguishing a
+namespace id > 0x80000000 from a negative error number.
+
+**Return**
+
+0 if **nsid** was set successfully or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify (struct nvme_identify_args *args)
+
+ Send the NVMe Identify command
+
+**Parameters**
+
+``struct nvme_identify_args *args``
+ :c:type:`struct nvme_identify_args <nvme_identify_args>` argument structure
+
+**Description**
+
+The Identify command returns a data buffer that describes information about
+the NVM subsystem, the controller or the namespace(s).
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_ctrl (int fd, struct nvme_id_ctrl *id)
+
+ Retrieves nvme identify controller
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_id_ctrl *id``
+ User space destination address to transfer the data,
+
+**Description**
+
+Sends nvme identify with CNS value ``NVME_IDENTIFY_CNS_CTRL``.
+
+See :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>` for details on the data returned.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_ns (int fd, __u32 nsid, struct nvme_id_ns *ns)
+
+ Retrieves nvme identify namespace
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace to identify
+
+``struct nvme_id_ns *ns``
+ User space destination address to transfer the data
+
+**Description**
+
+If the Namespace Identifier (NSID) field specifies an active NSID, then the
+Identify Namespace data structure is returned to the host for that specified
+namespace.
+
+If the controller supports the Namespace Management capability and the NSID
+field is set to ``NVME_NSID_ALL``, then the controller returns an Identify Namespace
+data structure that specifies capabilities that are common across namespaces
+for this controller.
+
+See :c:type:`struct nvme_id_ns <nvme_id_ns>` for details on the structure returned.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_allocated_ns (int fd, __u32 nsid, struct nvme_id_ns *ns)
+
+ Same as nvme_identify_ns, but only for allocated namespaces
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace to identify
+
+``struct nvme_id_ns *ns``
+ User space destination address to transfer the data
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_active_ns_list (int fd, __u32 nsid, struct nvme_ns_list *list)
+
+ Retrieves active namespaces id list
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Return namespaces greater than this identifier
+
+``struct nvme_ns_list *list``
+ User space destination address to transfer the data
+
+**Description**
+
+A list of 1024 namespace IDs is returned to the host containing NSIDs in
+increasing order that are greater than the value specified in the Namespace
+Identifier (nsid) field of the command.
+
+See :c:type:`struct nvme_ns_list <nvme_ns_list>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_allocated_ns_list (int fd, __u32 nsid, struct nvme_ns_list *list)
+
+ Retrieves allocated namespace id list
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Return namespaces greater than this identifier
+
+``struct nvme_ns_list *list``
+ User space destination address to transfer the data
+
+**Description**
+
+A list of 1024 namespace IDs is returned to the host containing NSIDs in
+increasing order that are greater than the value specified in the Namespace
+Identifier (nsid) field of the command.
+
+See :c:type:`struct nvme_ns_list <nvme_ns_list>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_ctrl_list (int fd, __u16 cntid, struct nvme_ctrl_list *cntlist)
+
+ Retrieves identify controller list
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 cntid``
+ Starting CNTLID to return in the list
+
+``struct nvme_ctrl_list *cntlist``
+ User space destination address to transfer the data
+
+**Description**
+
+Up to 2047 controller identifiers is returned containing a controller
+identifier greater than or equal to the controller identifier specified in
+**cntid**.
+
+See :c:type:`struct nvme_ctrl_list <nvme_ctrl_list>` for a definition of the structure returned.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_nsid_ctrl_list (int fd, __u32 nsid, __u16 cntid, struct nvme_ctrl_list *cntlist)
+
+ Retrieves controller list attached to an nsid
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Return controllers that are attached to this nsid
+
+``__u16 cntid``
+ Starting CNTLID to return in the list
+
+``struct nvme_ctrl_list *cntlist``
+ User space destination address to transfer the data
+
+**Description**
+
+Up to 2047 controller identifiers are returned containing a controller
+identifier greater than or equal to the controller identifier specified in
+**cntid** attached to **nsid**.
+
+See :c:type:`struct nvme_ctrl_list <nvme_ctrl_list>` for a definition of the structure returned.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1
+
+
+.. c:function:: int nvme_identify_ns_descs (int fd, __u32 nsid, struct nvme_ns_id_desc *descs)
+
+ Retrieves namespace descriptor list
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ The namespace id to retrieve descriptors
+
+``struct nvme_ns_id_desc *descs``
+ User space destination address to transfer the data
+
+**Description**
+
+A list of Namespace Identification Descriptor structures is returned to the
+host for the namespace specified in the Namespace Identifier (NSID) field if
+it is an active NSID.
+
+The data returned is in the form of an array of 'struct nvme_ns_id_desc'.
+
+See :c:type:`struct nvme_ns_id_desc <nvme_ns_id_desc>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_nvmset_list (int fd, __u16 nvmsetid, struct nvme_id_nvmset_list *nvmset)
+
+ Retrieves NVM Set List
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 nvmsetid``
+ NVM Set Identifier
+
+``struct nvme_id_nvmset_list *nvmset``
+ User space destination address to transfer the data
+
+**Description**
+
+Retrieves an NVM Set List, :c:type:`struct nvme_id_nvmset_list <nvme_id_nvmset_list>`. The data structure
+is an ordered list by NVM Set Identifier, starting with the first NVM Set
+Identifier supported by the NVM subsystem that is equal to or greater than
+the NVM Set Identifier.
+
+See :c:type:`struct nvme_id_nvmset_list <nvme_id_nvmset_list>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_primary_ctrl (int fd, __u16 cntid, struct nvme_primary_ctrl_cap *cap)
+
+ Retrieve NVMe Primary Controller identification
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 cntid``
+ Return controllers starting at this identifier
+
+``struct nvme_primary_ctrl_cap *cap``
+ User space destination buffer address to transfer the data
+
+**Description**
+
+See :c:type:`struct nvme_primary_ctrl_cap <nvme_primary_ctrl_cap>` for the definition of the returned structure, **cap**.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_secondary_ctrl_list (int fd, __u16 cntid, struct nvme_secondary_ctrl_list *sc_list)
+
+ Retrieves secondary controller list
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 cntid``
+ Return controllers starting at this identifier
+
+``struct nvme_secondary_ctrl_list *sc_list``
+ User space destination address to transfer the data
+
+**Description**
+
+A Secondary Controller List is returned to the host for up to 127 secondary
+controllers associated with the primary controller processing this command.
+The list contains entries for controller identifiers greater than or equal
+to the value specified in the Controller Identifier (cntid).
+
+See :c:type:`struct nvme_secondary_ctrls_list <nvme_secondary_ctrls_list>` for a definition of the returned
+structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_ns_granularity (int fd, struct nvme_id_ns_granularity_list *gr_list)
+
+ Retrieves namespace granularity identification
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_id_ns_granularity_list *gr_list``
+ User space destination address to transfer the data
+
+**Description**
+
+If the controller supports reporting of Namespace Granularity, then a
+Namespace Granularity List is returned to the host for up to sixteen
+namespace granularity descriptors
+
+See :c:type:`struct nvme_id_ns_granularity_list <nvme_id_ns_granularity_list>` for the definition of the returned
+structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_uuid (int fd, struct nvme_id_uuid_list *uuid_list)
+
+ Retrieves device's UUIDs
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_id_uuid_list *uuid_list``
+ User space destination address to transfer the data
+
+**Description**
+
+Each UUID List entry is either 0h, the NVMe Invalid UUID, or a valid UUID.
+Valid UUIDs are those which are non-zero and are not the NVMe Invalid UUID.
+
+See :c:type:`struct nvme_id_uuid_list <nvme_id_uuid_list>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_ns_csi (int fd, __u32 nsid, __u8 uuidx, enum nvme_csi csi, void *data)
+
+ I/O command set specific identify namespace data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace to identify
+
+``__u8 uuidx``
+ UUID Index for differentiating vendor specific encoding
+
+``enum nvme_csi csi``
+ Command Set Identifier
+
+``void *data``
+ User space destination address to transfer the data
+
+**Description**
+
+An I/O Command Set specific Identify Namespace data structure is returned
+for the namespace specified in **nsid**.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_ctrl_csi (int fd, enum nvme_csi csi, void *data)
+
+ I/O command set specific Identify Controller data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_csi csi``
+ Command Set Identifier
+
+``void *data``
+ User space destination address to transfer the data
+
+**Description**
+
+An I/O Command Set specific Identify Controller data structure is returned
+to the host for the controller processing the command. The specific Identify
+Controller data structure to be returned is specified by **csi**.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_active_ns_list_csi (int fd, __u32 nsid, enum nvme_csi csi, struct nvme_ns_list *ns_list)
+
+ Active namespace ID list associated with a specified I/O command set
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Return namespaces greater than this identifier
+
+``enum nvme_csi csi``
+ Command Set Identifier
+
+``struct nvme_ns_list *ns_list``
+ User space destination address to transfer the data
+
+**Description**
+
+A list of 1024 namespace IDs is returned to the host containing active
+NSIDs in increasing order that are greater than the value specified in
+the Namespace Identifier (nsid) field of the command and matching the
+I/O Command Set specified in the **csi** argument.
+
+See :c:type:`struct nvme_ns_list <nvme_ns_list>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_allocated_ns_list_csi (int fd, __u32 nsid, enum nvme_csi csi, struct nvme_ns_list *ns_list)
+
+ Allocated namespace ID list associated with a specified I/O command set
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Return namespaces greater than this identifier
+
+``enum nvme_csi csi``
+ Command Set Identifier
+
+``struct nvme_ns_list *ns_list``
+ User space destination address to transfer the data
+
+**Description**
+
+A list of 1024 namespace IDs is returned to the host containing allocated
+NSIDs in increasing order that are greater than the value specified in
+the **nsid** field of the command and matching the I/O Command Set
+specified in the **csi** argument.
+
+See :c:type:`struct nvme_ns_list <nvme_ns_list>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_independent_identify_ns (int fd, __u32 nsid, struct nvme_id_independent_id_ns *ns)
+
+ I/O command set independent Identify namespace data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Return namespaces greater than this identifier
+
+``struct nvme_id_independent_id_ns *ns``
+ I/O Command Set Independent Identify Namespace data
+ structure
+
+**Description**
+
+The I/O command set independent Identify namespace data structure for
+the namespace identified with **ns** is returned to the host.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_ns_csi_user_data_format (int fd, __u16 user_data_format, __u8 uuidx, enum nvme_csi csi, void *data)
+
+ Identify namespace user data format
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 user_data_format``
+ Return namespaces capability of identifier
+
+``__u8 uuidx``
+ UUID selection, if supported
+
+``enum nvme_csi csi``
+ Command Set Identifier
+
+``void *data``
+ User space destination address to transfer the data
+
+**Description**
+
+Identify Namespace data structure for the specified User Data Format
+index containing the namespace capabilities for the NVM Command Set.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_iocs_ns_csi_user_data_format (int fd, __u16 user_data_format, __u8 uuidx, enum nvme_csi csi, void *data)
+
+ Identify I/O command set namespace data structure
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 user_data_format``
+ Return namespaces capability of identifier
+
+``__u8 uuidx``
+ UUID selection, if supported
+
+``enum nvme_csi csi``
+ Command Set Identifier
+
+``void *data``
+ User space destination address to transfer the data
+
+**Description**
+
+I/O Command Set specific Identify Namespace data structure for
+the specified User Data Format index containing the namespace
+capabilities for the I/O Command Set specified in the CSI field.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_nvm_identify_ctrl (int fd, struct nvme_id_ctrl_nvm *id)
+
+ Identify controller data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_id_ctrl_nvm *id``
+ User space destination address to transfer the data
+
+**Description**
+
+Return an identify controller data structure to the host of
+processing controller.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_domain_list (int fd, __u16 domid, struct nvme_id_domain_list *list)
+
+ Domain list data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 domid``
+ Domain ID
+
+``struct nvme_id_domain_list *list``
+ User space destination address to transfer data
+
+**Description**
+
+A list of 31 domain IDs is returned to the host containing domain
+attributes in increasing order that are greater than the value
+specified in the **domid** field.
+
+See :c:type:`struct nvme_identify_domain_attr <nvme_identify_domain_attr>` for the definition of the
+returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_endurance_group_list (int fd, __u16 endgrp_id, struct nvme_id_endurance_group_list *list)
+
+ Endurance group list data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 endgrp_id``
+ Endurance group identifier
+
+``struct nvme_id_endurance_group_list *list``
+ Array of endurance group identifiers
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_identify_iocs (int fd, __u16 cntlid, struct nvme_id_iocs *iocs)
+
+ I/O command set data structure
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 cntlid``
+ Controller ID
+
+``struct nvme_id_iocs *iocs``
+ User space destination address to transfer the data
+
+**Description**
+
+Retrieves list of the controller's supported io command set vectors. See
+:c:type:`struct nvme_id_iocs <nvme_id_iocs>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_zns_identify_ns (int fd, __u32 nsid, struct nvme_zns_id_ns *data)
+
+ ZNS identify namespace data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace to identify
+
+``struct nvme_zns_id_ns *data``
+ User space destination address to transfer the data
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_zns_identify_ctrl (int fd, struct nvme_zns_id_ctrl *id)
+
+ ZNS identify controller data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_zns_id_ctrl *id``
+ User space destination address to transfer the data
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log (struct nvme_get_log_args *args)
+
+ NVMe Admin Get Log command
+
+**Parameters**
+
+``struct nvme_get_log_args *args``
+ :c:type:`struct nvme_get_log_args <nvme_get_log_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_page (int fd, __u32 xfer_len, struct nvme_get_log_args *args)
+
+ Get log page data
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 xfer_len``
+ Max log transfer size per request to split the total.
+
+``struct nvme_get_log_args *args``
+ :c:type:`struct nvme_get_log_args <nvme_get_log_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_supported_log_pages (int fd, bool rae, struct nvme_supported_log_pages *log)
+
+ Retrieve nmve supported log pages
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_supported_log_pages *log``
+ Array of LID supported and Effects data structures
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_error (int fd, unsigned int nr_entries, bool rae, struct nvme_error_log_page *err_log)
+
+ Retrieve nvme error log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``unsigned int nr_entries``
+ Number of error log entries allocated
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_error_log_page *err_log``
+ Array of error logs of size 'entries'
+
+**Description**
+
+This log page describes extended error information for a command that
+completed with error, or may report an error that is not specific to a
+particular command.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_smart (int fd, __u32 nsid, bool rae, struct nvme_smart_log *smart_log)
+
+ Retrieve nvme smart log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Optional namespace identifier
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_smart_log *smart_log``
+ User address to store the smart log
+
+**Description**
+
+This log page provides SMART and general health information. The information
+provided is over the life of the controller and is retained across power
+cycles. To request the controller log page, the namespace identifier
+specified is FFFFFFFFh. The controller may also support requesting the log
+page on a per namespace basis, as indicated by bit 0 of the LPA field in the
+Identify Controller data structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_fw_slot (int fd, bool rae, struct nvme_firmware_slot *fw_log)
+
+ Retrieves the controller firmware log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_firmware_slot *fw_log``
+ User address to store the log page
+
+**Description**
+
+This log page describes the firmware revision stored in each firmware slot
+supported. The firmware revision is indicated as an ASCII string. The log
+page also indicates the active slot number.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_changed_ns_list (int fd, bool rae, struct nvme_ns_list *ns_log)
+
+ Retrieve namespace changed list
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_ns_list *ns_log``
+ User address to store the log page
+
+**Description**
+
+This log page describes namespaces attached to this controller that have
+changed since the last time the namespace was identified, been added, or
+deleted.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_cmd_effects (int fd, enum nvme_csi csi, struct nvme_cmd_effects_log *effects_log)
+
+ Retrieve nvme command effects log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_csi csi``
+ Command Set Identifier
+
+``struct nvme_cmd_effects_log *effects_log``
+ User address to store the effects log
+
+**Description**
+
+This log page describes the commands that the controller supports and the
+effects of those commands on the state of the NVM subsystem.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_device_self_test (int fd, struct nvme_self_test_log *log)
+
+ Retrieve the device self test log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_self_test_log *log``
+ Userspace address of the log payload
+
+**Description**
+
+The log page indicates the status of an in progress self test and the
+percent complete of that operation, and the results of the previous 20
+self-test operations.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_create_telemetry_host (int fd, struct nvme_telemetry_log *log)
+
+ Create host telemetry log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_telemetry_log *log``
+ Userspace address of the log payload
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_telemetry_host (int fd, __u64 offset, __u32 len, void *log)
+
+ Get Telemetry Host-Initiated log page
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u64 offset``
+ Offset into the telemetry data
+
+``__u32 len``
+ Length of provided user buffer to hold the log data in bytes
+
+``void *log``
+ User address for log page data
+
+**Description**
+
+Retrieves the Telemetry Host-Initiated log page at the requested offset
+using the previously existing capture.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_telemetry_ctrl (int fd, bool rae, __u64 offset, __u32 len, void *log)
+
+ Get Telemetry Controller-Initiated log page
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``__u64 offset``
+ Offset into the telemetry data
+
+``__u32 len``
+ Length of provided user buffer to hold the log data in bytes
+
+``void *log``
+ User address for log page data
+
+**Description**
+
+Retrieves the Telemetry Controller-Initiated log page at the requested offset
+using the previously existing capture.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_endurance_group (int fd, __u16 endgid, struct nvme_endurance_group_log *log)
+
+ Get Endurance Group log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 endgid``
+ Starting group identifier to return in the list
+
+``struct nvme_endurance_group_log *log``
+ User address to store the endurance log
+
+**Description**
+
+This log page indicates if an Endurance Group Event has occurred for a
+particular Endurance Group. If an Endurance Group Event has occurred, the
+details of the particular event are included in the Endurance Group
+Information log page for that Endurance Group. An asynchronous event is
+generated when an entry for an Endurance Group is newly added to this log
+page.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_predictable_lat_nvmset (int fd, __u16 nvmsetid, struct nvme_nvmset_predictable_lat_log *log)
+
+ Predictable Latency Per NVM Set
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 nvmsetid``
+ NVM set id
+
+``struct nvme_nvmset_predictable_lat_log *log``
+ User address to store the predictable latency log
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_predictable_lat_event (int fd, bool rae, __u32 offset, __u32 len, void *log)
+
+ Retrieve Predictable Latency Event Aggregate Log Page
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``__u32 offset``
+ Offset into the predictable latency event
+
+``__u32 len``
+ Length of provided user buffer to hold the log data in bytes
+
+``void *log``
+ User address for log page data
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_fdp_configurations (int fd, __u16 egid, __u32 offset, __u32 len, void *log)
+
+ Get list of Flexible Data Placement configurations
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 egid``
+ Endurance group identifier
+
+``__u32 offset``
+ Offset into log page
+
+``__u32 len``
+ Length (in bytes) of provided user buffer to hold the log data
+
+``void *log``
+ Log page data buffer
+
+
+.. c:function:: int nvme_get_log_reclaim_unit_handle_usage (int fd, __u16 egid, __u32 offset, __u32 len, void *log)
+
+ Get reclaim unit handle usage
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 egid``
+ Endurance group identifier
+
+``__u32 offset``
+ Offset into log page
+
+``__u32 len``
+ Length (in bytes) of provided user buffer to hold the log data
+
+``void *log``
+ Log page data buffer
+
+
+.. c:function:: int nvme_get_log_fdp_stats (int fd, __u16 egid, __u32 offset, __u32 len, void *log)
+
+ Get Flexible Data Placement statistics
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 egid``
+ Endurance group identifier
+
+``__u32 offset``
+ Offset into log page
+
+``__u32 len``
+ Length (in bytes) of provided user buffer to hold the log data
+
+``void *log``
+ Log page data buffer
+
+
+.. c:function:: int nvme_get_log_fdp_events (int fd, __u16 egid, bool host_events, __u32 offset, __u32 len, void *log)
+
+ Get Flexible Data Placement events
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 egid``
+ Endurance group identifier
+
+``bool host_events``
+ Whether to report host or controller events
+
+``__u32 offset``
+ Offset into log page
+
+``__u32 len``
+ Length (in bytes) of provided user buffer to hold the log data
+
+``void *log``
+ Log page data buffer
+
+
+.. c:function:: int nvme_get_log_ana (int fd, enum nvme_log_ana_lsp lsp, bool rae, __u64 offset, __u32 len, void *log)
+
+ Retrieve Asymmetric Namespace Access log page
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_log_ana_lsp lsp``
+ Log specific, see :c:type:`enum nvme_get_log_ana_lsp <nvme_get_log_ana_lsp>`
+
+``bool rae``
+ Retain asynchronous events
+
+``__u64 offset``
+ Offset to the start of the log page
+
+``__u32 len``
+ The allocated length of the log page
+
+``void *log``
+ User address to store the ana log
+
+**Description**
+
+This log consists of a header describing the log and descriptors containing
+the asymmetric namespace access information for ANA Groups that contain
+namespaces that are attached to the controller processing the command.
+
+See :c:type:`struct nvme_ana_rsp_hdr <nvme_ana_rsp_hdr>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_ana_groups (int fd, bool rae, __u32 len, struct nvme_ana_group_desc *log)
+
+ Retrieve Asymmetric Namespace Access groups only log page
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``__u32 len``
+ The allocated length of the log page
+
+``struct nvme_ana_group_desc *log``
+ User address to store the ana group log
+
+**Description**
+
+See :c:type:`struct nvme_ana_group_desc <nvme_ana_group_desc>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_lba_status (int fd, bool rae, __u64 offset, __u32 len, void *log)
+
+ Retrieve LBA Status
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``__u64 offset``
+ Offset to the start of the log page
+
+``__u32 len``
+ The allocated length of the log page
+
+``void *log``
+ User address to store the log page
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_endurance_grp_evt (int fd, bool rae, __u32 offset, __u32 len, void *log)
+
+ Retrieve Rotational Media Information
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``__u32 offset``
+ Offset to the start of the log page
+
+``__u32 len``
+ The allocated length of the log page
+
+``void *log``
+ User address to store the log page
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_fid_supported_effects (int fd, bool rae, struct nvme_fid_supported_effects_log *log)
+
+ Retrieve Feature Identifiers Supported and Effects
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_fid_supported_effects_log *log``
+ FID Supported and Effects data structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise
+
+
+.. c:function:: int nvme_get_log_mi_cmd_supported_effects (int fd, bool rae, struct nvme_mi_cmd_supported_effects_log *log)
+
+ displays the MI Commands Supported by the controller
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_mi_cmd_supported_effects_log *log``
+ MI Command Supported and Effects data structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise
+
+
+.. c:function:: int nvme_get_log_boot_partition (int fd, bool rae, __u8 lsp, __u32 len, struct nvme_boot_partition *part)
+
+ Retrieve Boot Partition
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``__u8 lsp``
+ The log specified field of LID
+
+``__u32 len``
+ The allocated size, minimum
+ struct nvme_boot_partition
+
+``struct nvme_boot_partition *part``
+ User address to store the log page
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise
+
+
+.. c:function:: int nvme_get_log_phy_rx_eom (int fd, __u8 lsp, __u16 controller, __u32 len, struct nvme_phy_rx_eom_log *log)
+
+ Retrieve Physical Interface Receiver Eye Opening Measurement Log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 lsp``
+ Log specific, controls action and measurement quality
+
+``__u16 controller``
+ Target controller ID
+
+``__u32 len``
+ The allocated size, minimum
+ struct nvme_phy_rx_eom_log
+
+``struct nvme_phy_rx_eom_log *log``
+ User address to store the log page
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise
+
+
+.. c:function:: int nvme_get_log_discovery (int fd, bool rae, __u32 offset, __u32 len, void *log)
+
+ Retrieve Discovery log page
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``__u32 offset``
+ Offset of this log to retrieve
+
+``__u32 len``
+ The allocated size for this portion of the log
+
+``void *log``
+ User address to store the discovery log
+
+**Description**
+
+Supported only by fabrics discovery controllers, returning discovery
+records.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_media_unit_stat (int fd, __u16 domid, struct nvme_media_unit_stat_log *mus)
+
+ Retrieve Media Unit Status
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 domid``
+ Domain Identifier selection, if supported
+
+``struct nvme_media_unit_stat_log *mus``
+ User address to store the Media Unit statistics log
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise
+
+
+.. c:function:: int nvme_get_log_support_cap_config_list (int fd, __u16 domid, struct nvme_supported_cap_config_list_log *cap)
+
+ Retrieve Supported Capacity Configuration List
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 domid``
+ Domain Identifier selection, if supported
+
+``struct nvme_supported_cap_config_list_log *cap``
+ User address to store supported capabilities config list
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise
+
+
+.. c:function:: int nvme_get_log_reservation (int fd, bool rae, struct nvme_resv_notification_log *log)
+
+ Retrieve Reservation Notification
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_resv_notification_log *log``
+ User address to store the reservation log
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise
+
+
+.. c:function:: int nvme_get_log_sanitize (int fd, bool rae, struct nvme_sanitize_log_page *log)
+
+ Retrieve Sanitize Status
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_sanitize_log_page *log``
+ User address to store the sanitize log
+
+**Description**
+
+The Sanitize Status log page reports sanitize operation time estimates and
+information about the most recent sanitize operation.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_zns_changed_zones (int fd, __u32 nsid, bool rae, struct nvme_zns_changed_zone_log *log)
+
+ Retrieve list of zones that have changed
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_zns_changed_zone_log *log``
+ User address to store the changed zone log
+
+**Description**
+
+The list of zones that have changed state due to an exceptional event.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_log_persistent_event (int fd, enum nvme_pevent_log_action action, __u32 size, void *pevent_log)
+
+ Retrieve Persistent Event Log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_pevent_log_action action``
+ Action the controller should take during processing this command
+
+``__u32 size``
+ Size of **pevent_log**
+
+``void *pevent_log``
+ User address to store the persistent event log
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features (struct nvme_set_features_args *args)
+
+ Set a feature attribute
+
+**Parameters**
+
+``struct nvme_set_features_args *args``
+ :c:type:`struct nvme_set_features_args <nvme_set_features_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_data (int fd, __u8 fid, __u32 nsid, __u32 cdw11, bool save, __u32 data_len, void *data, __u32 *result)
+
+ Helper function for **nvme_set_features\(\)**
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 fid``
+ Feature identifier
+
+``__u32 nsid``
+ Namespace ID, if applicable
+
+``__u32 cdw11``
+ Value to set the feature to
+
+``bool save``
+ Save value across power states
+
+``__u32 data_len``
+ Length of feature data, if applicable, in bytes
+
+``void *data``
+ User address of feature data, if applicable
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_simple (int fd, __u8 fid, __u32 nsid, __u32 cdw11, bool save, __u32 *result)
+
+ Helper function for **nvme_set_features\(\)**
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 fid``
+ Feature identifier
+
+``__u32 nsid``
+ Namespace ID, if applicable
+
+``__u32 cdw11``
+ Value to set the feature to
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_arbitration (int fd, __u8 ab, __u8 lpw, __u8 mpw, __u8 hpw, bool save, __u32 *result)
+
+ Set arbitration features
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 ab``
+ Arbitration Burst
+
+``__u8 lpw``
+ Low Priority Weight
+
+``__u8 mpw``
+ Medium Priority Weight
+
+``__u8 hpw``
+ High Priority Weight
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_power_mgmt (int fd, __u8 ps, __u8 wh, bool save, __u32 *result)
+
+ Set power management feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 ps``
+ Power State
+
+``__u8 wh``
+ Workload Hint
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_lba_range (int fd, __u32 nsid, __u8 nr_ranges, bool save, struct nvme_lba_range_type *data, __u32 *result)
+
+ Set LBA range feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``__u8 nr_ranges``
+ Number of ranges in **data**
+
+``bool save``
+ Save value across power states
+
+``struct nvme_lba_range_type *data``
+ User address of feature data
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_temp_thresh (int fd, __u16 tmpth, __u8 tmpsel, enum nvme_feat_tmpthresh_thsel thsel, bool save, __u32 *result)
+
+ Set temperature threshold feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 tmpth``
+ Temperature Threshold
+
+``__u8 tmpsel``
+ Threshold Temperature Select
+
+``enum nvme_feat_tmpthresh_thsel thsel``
+ Threshold Type Select
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_err_recovery (int fd, __u32 nsid, __u16 tler, bool dulbe, bool save, __u32 *result)
+
+ Set error recovery feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``__u16 tler``
+ Time-limited error recovery value
+
+``bool dulbe``
+ Deallocated or Unwritten Logical Block Error Enable
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_volatile_wc (int fd, bool wce, bool save, __u32 *result)
+
+ Set volatile write cache feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool wce``
+ Write cache enable
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_irq_coalesce (int fd, __u8 thr, __u8 time, bool save, __u32 *result)
+
+ Set IRQ coalesce feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 thr``
+ Aggregation Threshold
+
+``__u8 time``
+ Aggregation Time
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_irq_config (int fd, __u16 iv, bool cd, bool save, __u32 *result)
+
+ Set IRQ config feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 iv``
+ Interrupt Vector
+
+``bool cd``
+ Coalescing Disable
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_write_atomic (int fd, bool dn, bool save, __u32 *result)
+
+ Set write atomic feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool dn``
+ Disable Normal
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_async_event (int fd, __u32 events, bool save, __u32 *result)
+
+ Set asynchronous event feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 events``
+ Events to enable
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_auto_pst (int fd, bool apste, bool save, struct nvme_feat_auto_pst *apst, __u32 *result)
+
+ Set autonomous power state feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool apste``
+ Autonomous Power State Transition Enable
+
+``bool save``
+ Save value across power states
+
+``struct nvme_feat_auto_pst *apst``
+ Autonomous Power State Transition
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_timestamp (int fd, bool save, __u64 timestamp)
+
+ Set timestamp feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool save``
+ Save value across power states
+
+``__u64 timestamp``
+ The current timestamp value to assign to this feature
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_hctm (int fd, __u16 tmt2, __u16 tmt1, bool save, __u32 *result)
+
+ Set thermal management feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 tmt2``
+ Thermal Management Temperature 2
+
+``__u16 tmt1``
+ Thermal Management Temperature 1
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_nopsc (int fd, bool noppme, bool save, __u32 *result)
+
+ Set non-operational power state feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool noppme``
+ Non-Operational Power State Permissive Mode Enable
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_rrl (int fd, __u8 rrl, __u16 nvmsetid, bool save, __u32 *result)
+
+ Set read recovery level feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 rrl``
+ Read recovery level setting
+
+``__u16 nvmsetid``
+ NVM set id
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_plm_config (int fd, bool enable, __u16 nvmsetid, bool save, struct nvme_plm_config *data, __u32 *result)
+
+ Set predictable latency feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool enable``
+ Predictable Latency Enable
+
+``__u16 nvmsetid``
+ NVM Set Identifier
+
+``bool save``
+ Save value across power states
+
+``struct nvme_plm_config *data``
+ Pointer to structure nvme_plm_config
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_plm_window (int fd, enum nvme_feat_plm_window_select sel, __u16 nvmsetid, bool save, __u32 *result)
+
+ Set window select feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_feat_plm_window_select sel``
+ Window Select
+
+``__u16 nvmsetid``
+ NVM Set Identifier
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_lba_sts_interval (int fd, __u16 lsiri, __u16 lsipi, bool save, __u32 *result)
+
+ Set LBA status information feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 lsiri``
+ LBA Status Information Report Interval
+
+``__u16 lsipi``
+ LBA Status Information Poll Interval
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_host_behavior (int fd, bool save, struct nvme_feat_host_behavior *data)
+
+ Set host behavior feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool save``
+ Save value across power states
+
+``struct nvme_feat_host_behavior *data``
+ Pointer to structure nvme_feat_host_behavior
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_sanitize (int fd, bool nodrm, bool save, __u32 *result)
+
+ Set sanitize feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool nodrm``
+ No-Deallocate Response Mode
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_endurance_evt_cfg (int fd, __u16 endgid, __u8 egwarn, bool save, __u32 *result)
+
+ Set endurance event config feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 endgid``
+ Endurance Group Identifier
+
+``__u8 egwarn``
+ Flags to enable warning, see :c:type:`enum nvme_eg_critical_warning_flags <nvme_eg_critical_warning_flags>`
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_sw_progress (int fd, __u8 pbslc, bool save, __u32 *result)
+
+ Set pre-boot software load count feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u8 pbslc``
+ Pre-boot Software Load Count
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_host_id (int fd, bool exhid, bool save, __u8 *hostid)
+
+ Set enable extended host identifiers feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool exhid``
+ Enable Extended Host Identifier
+
+``bool save``
+ Save value across power states
+
+``__u8 *hostid``
+ Host ID to set
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_resv_mask (int fd, __u32 mask, bool save, __u32 *result)
+
+ Set reservation notification mask feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 mask``
+ Reservation Notification Mask Field
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+
+Deprecated: doesn't support specifying a NSID.
+Use nvme_set_features_resv_mask2() instead.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_resv_mask2 (int fd, __u32 nsid, __u32 mask, bool save, __u32 *result)
+
+ Set reservation notification mask feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``__u32 mask``
+ Reservation Notification Mask Field
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_resv_persist (int fd, bool ptpl, bool save, __u32 *result)
+
+ Set persist through power loss feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool ptpl``
+ Persist Through Power Loss
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+
+Deprecated: doesn't support specifying a NSID.
+Use nvme_set_features_resv_persist2() instead.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_resv_persist2 (int fd, __u32 nsid, bool ptpl, bool save, __u32 *result)
+
+ Set persist through power loss feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``bool ptpl``
+ Persist Through Power Loss
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_write_protect (int fd, enum nvme_feat_nswpcfg_state state, bool save, __u32 *result)
+
+ Set write protect feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_feat_nswpcfg_state state``
+ Write Protection State
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+
+Deprecated: doesn't support specifying a NSID.
+Use nvme_set_features_write_protect2() instead.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_write_protect2 (int fd, __u32 nsid, enum nvme_feat_nswpcfg_state state, bool save, __u32 *result)
+
+ Set write protect feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``enum nvme_feat_nswpcfg_state state``
+ Write Protection State
+
+``bool save``
+ Save value across power states
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_features_iocs_profile (int fd, __u16 iocsi, bool save)
+
+ Set I/O command set profile feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u16 iocsi``
+ I/O Command Set Combination Index
+
+``bool save``
+ Save value across power states
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features (struct nvme_get_features_args *args)
+
+ Retrieve a feature attribute
+
+**Parameters**
+
+``struct nvme_get_features_args *args``
+ :c:type:`struct nvme_get_features_args <nvme_get_features_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_data (int fd, enum nvme_features_id fid, __u32 nsid, __u32 data_len, void *data, __u32 *result)
+
+ Helper function for **nvme_get_features\(\)**
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_features_id fid``
+ Feature identifier
+
+``__u32 nsid``
+ Namespace ID, if applicable
+
+``__u32 data_len``
+ Length of feature data, if applicable, in bytes
+
+``void *data``
+ User address of feature data, if applicable
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_simple (int fd, enum nvme_features_id fid, __u32 nsid, __u32 *result)
+
+ Helper function for **nvme_get_features\(\)**
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_features_id fid``
+ Feature identifier
+
+``__u32 nsid``
+ Namespace ID, if applicable
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_arbitration (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get arbitration feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_power_mgmt (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get power management feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_lba_range (int fd, enum nvme_get_features_sel sel, struct nvme_lba_range_type *data, __u32 *result)
+
+ Get LBA range feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``struct nvme_lba_range_type *data``
+ User address of feature data, if applicable
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+
+Deprecated: doesn't support specifying a NSID.
+Use nvme_get_features_lba_range2() instead.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_lba_range2 (int fd, enum nvme_get_features_sel sel, __u32 nsid, struct nvme_lba_range_type *data, __u32 *result)
+
+ Get LBA range feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 nsid``
+ Namespace ID
+
+``struct nvme_lba_range_type *data``
+ Buffer to receive LBA Range Type data structure
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_temp_thresh (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get temperature threshold feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_err_recovery (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get error recovery feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+
+Deprecated: doesn't support specifying a NSID.
+Use nvme_get_features_err_recovery2() instead.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_err_recovery2 (int fd, enum nvme_get_features_sel sel, __u32 nsid, __u32 *result)
+
+ Get error recovery feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 nsid``
+ Namespace ID
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_volatile_wc (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get volatile write cache feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_num_queues (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get number of queues feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_irq_coalesce (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get IRQ coalesce feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_irq_config (int fd, enum nvme_get_features_sel sel, __u16 iv, __u32 *result)
+
+ Get IRQ config feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u16 iv``
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_write_atomic (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get write atomic feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_async_event (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get asynchronous event feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_auto_pst (int fd, enum nvme_get_features_sel sel, struct nvme_feat_auto_pst *apst, __u32 *result)
+
+ Get autonomous power state feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``struct nvme_feat_auto_pst *apst``
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_host_mem_buf (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get host memory buffer feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+
+Deprecated: doesn't fetch the Host Memory Buffer Attributes data structure.
+Use nvme_get_features_host_mem_buf2() instead.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_host_mem_buf2 (int fd, enum nvme_get_features_sel sel, struct nvme_host_mem_buf_attrs *attrs, __u32 *result)
+
+ Get host memory buffer feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``struct nvme_host_mem_buf_attrs *attrs``
+ Buffer for returned Host Memory Buffer Attributes
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_timestamp (int fd, enum nvme_get_features_sel sel, struct nvme_timestamp *ts)
+
+ Get timestamp feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``struct nvme_timestamp *ts``
+ Current timestamp
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_kato (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get keep alive timeout feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_hctm (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get thermal management feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_nopsc (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get non-operational power state feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_rrl (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get read recovery level feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_plm_config (int fd, enum nvme_get_features_sel sel, __u16 nvmsetid, struct nvme_plm_config *data, __u32 *result)
+
+ Get predictable latency feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u16 nvmsetid``
+ NVM set id
+
+``struct nvme_plm_config *data``
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_plm_window (int fd, enum nvme_get_features_sel sel, __u16 nvmsetid, __u32 *result)
+
+ Get window select feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u16 nvmsetid``
+ NVM set id
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_lba_sts_interval (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get LBA status information feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_host_behavior (int fd, enum nvme_get_features_sel sel, struct nvme_feat_host_behavior *data, __u32 *result)
+
+ Get host behavior feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``struct nvme_feat_host_behavior *data``
+ Pointer to structure nvme_feat_host_behavior
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_sanitize (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get sanitize feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_endurance_event_cfg (int fd, enum nvme_get_features_sel sel, __u16 endgid, __u32 *result)
+
+ Get endurance event config feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u16 endgid``
+ Endurance Group Identifier
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_sw_progress (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get software progress feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_host_id (int fd, enum nvme_get_features_sel sel, bool exhid, __u32 len, __u8 *hostid)
+
+ Get host id feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``bool exhid``
+ Enable Extended Host Identifier
+
+``__u32 len``
+ Length of **hostid**
+
+``__u8 *hostid``
+ Buffer for returned host ID
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_resv_mask (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get reservation mask feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+
+Deprecated: doesn't support specifying a NSID.
+Use nvme_get_features_resv_mask2() instead.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_resv_mask2 (int fd, enum nvme_get_features_sel sel, __u32 nsid, __u32 *result)
+
+ Get reservation mask feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 nsid``
+ Namespace ID
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_resv_persist (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get reservation persist feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+
+Deprecated: doesn't support specifying a NSID.
+Use nvme_get_features_resv_persist2() instead.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_resv_persist2 (int fd, enum nvme_get_features_sel sel, __u32 nsid, __u32 *result)
+
+ Get reservation persist feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 nsid``
+ Namespace ID
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_write_protect (int fd, __u32 nsid, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get write protect feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_features_iocs_profile (int fd, enum nvme_get_features_sel sel, __u32 *result)
+
+ Get IOCS profile feature
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_get_features_sel sel``
+ Select which type of attribute to return, see :c:type:`enum nvme_get_features_sel <nvme_get_features_sel>`
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_format_nvm (struct nvme_format_nvm_args *args)
+
+ Format nvme namespace(s)
+
+**Parameters**
+
+``struct nvme_format_nvm_args *args``
+ :c:type:`struct nvme_format_nvme_args <nvme_format_nvme_args>` argument structure
+
+**Description**
+
+The Format NVM command low level formats the NVM media. This command is used
+by the host to change the LBA data size and/or metadata size. A low level
+format may destroy all data and metadata associated with all namespaces or
+only the specific namespace associated with the command
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_ns_mgmt (struct nvme_ns_mgmt_args *args)
+
+ Issue a Namespace management command
+
+**Parameters**
+
+``struct nvme_ns_mgmt_args *args``
+ :c:type:`struct nvme_ns_mgmt_args <nvme_ns_mgmt_args>` Argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_ns_mgmt_create (int fd, struct nvme_id_ns *ns, __u32 *nsid, __u32 timeout, __u8 csi, struct nvme_ns_mgmt_host_sw_specified *data)
+
+ Create a non attached namespace
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_id_ns *ns``
+ Namespace identification that defines ns creation parameters
+
+``__u32 *nsid``
+ On success, set to the namespace id that was created
+
+``__u32 timeout``
+ Override the default timeout to this value in milliseconds;
+ set to 0 to use the system default.
+
+``__u8 csi``
+ Command Set Identifier
+
+``struct nvme_ns_mgmt_host_sw_specified *data``
+ Host Software Specified Fields that defines ns creation parameters
+
+**Description**
+
+On successful creation, the namespace exists in the subsystem, but is not
+attached to any controller. Use the nvme_ns_attach_ctrls() to assign the
+namespace to one or more controllers.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_ns_mgmt_delete (int fd, __u32 nsid)
+
+ Delete a non attached namespace
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace identifier to delete
+
+**Description**
+
+It is recommended that a namespace being deleted is not attached to any
+controller. Use the nvme_ns_detach_ctrls() first if the namespace is still
+attached.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_ns_attach (struct nvme_ns_attach_args *args)
+
+ Attach or detach namespace to controller(s)
+
+**Parameters**
+
+``struct nvme_ns_attach_args *args``
+ :c:type:`struct nvme_ns_attach_args <nvme_ns_attach_args>` Argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_ns_attach_ctrls (int fd, __u32 nsid, struct nvme_ctrl_list *ctrlist)
+
+ Attach namespace to controllers
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID to attach
+
+``struct nvme_ctrl_list *ctrlist``
+ Controller list to modify attachment state of nsid
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_ns_detach_ctrls (int fd, __u32 nsid, struct nvme_ctrl_list *ctrlist)
+
+ Detach namespace from controllers
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID to detach
+
+``struct nvme_ctrl_list *ctrlist``
+ Controller list to modify attachment state of nsid
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_fw_download (struct nvme_fw_download_args *args)
+
+ Download part or all of a firmware image to the controller
+
+**Parameters**
+
+``struct nvme_fw_download_args *args``
+ :c:type:`struct nvme_fw_download_args <nvme_fw_download_args>` argument structure
+
+**Description**
+
+The Firmware Image Download command downloads all or a portion of an image
+for a future update to the controller. The Firmware Image Download command
+downloads a new image (in whole or in part) to the controller.
+
+The image may be constructed of multiple pieces that are individually
+downloaded with separate Firmware Image Download commands. Each Firmware
+Image Download command includes a Dword Offset and Number of Dwords that
+specify a dword range.
+
+The new firmware image is not activated as part of the Firmware Image
+Download command. Use the nvme_fw_commit() to activate a newly downloaded
+image.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_fw_commit (struct nvme_fw_commit_args *args)
+
+ Commit firmware using the specified action
+
+**Parameters**
+
+``struct nvme_fw_commit_args *args``
+ :c:type:`struct nvme_fw_commit_args <nvme_fw_commit_args>` argument structure
+
+**Description**
+
+The Firmware Commit command modifies the firmware image or Boot Partitions.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise. The command
+status response may specify additional reset actions required to complete
+the commit process.
+
+
+.. c:function:: int nvme_security_send (struct nvme_security_send_args *args)
+
+ Security Send command
+
+**Parameters**
+
+``struct nvme_security_send_args *args``
+ :c:type:`struct nvme_security_send <nvme_security_send>` argument structure
+
+**Description**
+
+The Security Send command transfers security protocol data to the
+controller. The data structure transferred to the controller as part of this
+command contains security protocol specific commands to be performed by the
+controller. The data structure transferred may also contain data or
+parameters associated with the security protocol commands.
+
+The security data is protocol specific and is not defined by the NVMe
+specification.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_security_receive (struct nvme_security_receive_args *args)
+
+ Security Receive command
+
+**Parameters**
+
+``struct nvme_security_receive_args *args``
+ :c:type:`struct nvme_security_receive <nvme_security_receive>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_lba_status (struct nvme_get_lba_status_args *args)
+
+ Retrieve information on possibly unrecoverable LBAs
+
+**Parameters**
+
+``struct nvme_get_lba_status_args *args``
+ :c:type:`struct nvme_get_lba_status_args <nvme_get_lba_status_args>` argument structure
+
+**Description**
+
+The Get LBA Status command requests information about Potentially
+Unrecoverable LBAs. Refer to the specification for action type descriptions.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_send (struct nvme_directive_send_args *args)
+
+ Send directive command
+
+**Parameters**
+
+``struct nvme_directive_send_args *args``
+ :c:type:`struct nvme_directive_send_args <nvme_directive_send_args>` argument structure
+
+**Description**
+
+Directives is a mechanism to enable host and NVM subsystem or controller
+information exchange. The Directive Send command transfers data related to a
+specific Directive Type from the host to the controller.
+
+See the NVMe specification for more information.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_send_id_endir (int fd, __u32 nsid, bool endir, enum nvme_directive_dtype dtype, struct nvme_id_directives *id)
+
+ Directive Send Enable Directive
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace Identifier
+
+``bool endir``
+ Enable Directive
+
+``enum nvme_directive_dtype dtype``
+ Directive Type
+
+``struct nvme_id_directives *id``
+ Pointer to structure nvme_id_directives
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_send_stream_release_identifier (int fd, __u32 nsid, __u16 stream_id)
+
+ Directive Send Stream release
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``__u16 stream_id``
+ Stream identifier
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_send_stream_release_resource (int fd, __u32 nsid)
+
+ Directive Send Stream release resources
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_recv (struct nvme_directive_recv_args *args)
+
+ Receive directive specific data
+
+**Parameters**
+
+``struct nvme_directive_recv_args *args``
+ :c:type:`struct nvme_directive_recv_args <nvme_directive_recv_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_recv_identify_parameters (int fd, __u32 nsid, struct nvme_id_directives *id)
+
+ Directive receive identifier parameters
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``struct nvme_id_directives *id``
+ Identify parameters buffer
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_recv_stream_parameters (int fd, __u32 nsid, struct nvme_streams_directive_params *parms)
+
+ Directive receive stream parameters
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``struct nvme_streams_directive_params *parms``
+ Streams directive parameters buffer
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_recv_stream_status (int fd, __u32 nsid, unsigned int nr_entries, struct nvme_streams_directive_status *id)
+
+ Directive receive stream status
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``unsigned int nr_entries``
+ Number of streams to receive
+
+``struct nvme_streams_directive_status *id``
+ Stream status buffer
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_directive_recv_stream_allocate (int fd, __u32 nsid, __u16 nsr, __u32 *result)
+
+ Directive receive stream allocate
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``__u16 nsr``
+ Namespace Streams Requested
+
+``__u32 *result``
+ If successful, the CQE dword0 value
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_capacity_mgmt (struct nvme_capacity_mgmt_args *args)
+
+ Capacity management command
+
+**Parameters**
+
+``struct nvme_capacity_mgmt_args *args``
+ :c:type:`struct nvme_capacity_mgmt_args <nvme_capacity_mgmt_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_lockdown (struct nvme_lockdown_args *args)
+
+ Issue lockdown command
+
+**Parameters**
+
+``struct nvme_lockdown_args *args``
+ :c:type:`struct nvme_lockdown_args <nvme_lockdown_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_property (struct nvme_set_property_args *args)
+
+ Set controller property
+
+**Parameters**
+
+``struct nvme_set_property_args *args``
+ :c:type:`struct nvme_set_property_args <nvme_set_property_args>` argument structure
+
+**Description**
+
+This is an NVMe-over-Fabrics specific command, not applicable to PCIe. These
+properties align to the PCI MMIO controller registers.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_property (struct nvme_get_property_args *args)
+
+ Get a controller property
+
+**Parameters**
+
+``struct nvme_get_property_args *args``
+ :c:type:`struct nvme_get_propert_args <nvme_get_propert_args>` argument structure
+
+**Description**
+
+This is an NVMe-over-Fabrics specific command, not applicable to PCIe. These
+properties align to the PCI MMIO controller registers.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_sanitize_nvm (struct nvme_sanitize_nvm_args *args)
+
+ Start a sanitize operation
+
+**Parameters**
+
+``struct nvme_sanitize_nvm_args *args``
+ :c:type:`struct nvme_sanitize_nvm_args <nvme_sanitize_nvm_args>` argument structure
+
+**Description**
+
+A sanitize operation alters all user data in the NVM subsystem such that
+recovery of any previous user data from any cache, the non-volatile media,
+or any Controller Memory Buffer is not possible.
+
+The Sanitize command starts a sanitize operation or to recover from a
+previously failed sanitize operation. The sanitize operation types that may
+be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize
+operations are processed in the background, i.e., completion of the sanitize
+command does not indicate completion of the sanitize operation.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_dev_self_test (struct nvme_dev_self_test_args *args)
+
+ Start or abort a self test
+
+**Parameters**
+
+``struct nvme_dev_self_test_args *args``
+ :c:type:`struct nvme_dev_self_test <nvme_dev_self_test>` argument structure
+
+**Description**
+
+The Device Self-test command starts a device self-test operation or abort a
+device self-test operation. A device self-test operation is a diagnostic
+testing sequence that tests the integrity and functionality of the
+controller and may include testing of the media associated with namespaces.
+The controller may return a response to this command immediately while
+running the self-test in the background.
+
+Set the 'nsid' field to 0 to not include namespaces in the test. Set to
+0xffffffff to test all namespaces. All other values tests a specific
+namespace, if present.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_virtual_mgmt (struct nvme_virtual_mgmt_args *args)
+
+ Virtualization resource management
+
+**Parameters**
+
+``struct nvme_virtual_mgmt_args *args``
+ :c:type:`struct nvme_virtual_mgmt_args <nvme_virtual_mgmt_args>` argument structure
+
+**Description**
+
+The Virtualization Management command is supported by primary controllers
+that support the Virtualization Enhancements capability. This command is
+used for several functions:
+
+ - Modifying Flexible Resource allocation for the primary controller
+ - Assigning Flexible Resources for secondary controllers
+ - Setting the Online and Offline state for secondary controllers
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_flush (int fd, __u32 nsid)
+
+ Send an nvme flush command
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace identifier
+
+**Description**
+
+The Flush command requests that the contents of volatile write cache be made
+non-volatile.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_io (struct nvme_io_args *args, __u8 opcode)
+
+ Submit an nvme user I/O command
+
+**Parameters**
+
+``struct nvme_io_args *args``
+ :c:type:`struct nvme_io_args <nvme_io_args>` argument structure
+
+``__u8 opcode``
+ Opcode to execute
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_read (struct nvme_io_args *args)
+
+ Submit an nvme user read command
+
+**Parameters**
+
+``struct nvme_io_args *args``
+ :c:type:`struct nvme_io_args <nvme_io_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_write (struct nvme_io_args *args)
+
+ Submit an nvme user write command
+
+**Parameters**
+
+``struct nvme_io_args *args``
+ :c:type:`struct nvme_io_args <nvme_io_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_compare (struct nvme_io_args *args)
+
+ Submit an nvme user compare command
+
+**Parameters**
+
+``struct nvme_io_args *args``
+ :c:type:`struct nvme_io_args <nvme_io_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_write_zeros (struct nvme_io_args *args)
+
+ Submit an nvme write zeroes command
+
+**Parameters**
+
+``struct nvme_io_args *args``
+ :c:type:`struct nvme_io_args <nvme_io_args>` argument structure
+
+**Description**
+
+The Write Zeroes command sets a range of logical blocks to zero. After
+successful completion of this command, the value returned by subsequent
+reads of logical blocks in this range shall be all bytes cleared to 0h until
+a write occurs to this LBA range.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_write_uncorrectable (struct nvme_io_args *args)
+
+ Submit an nvme write uncorrectable command
+
+**Parameters**
+
+``struct nvme_io_args *args``
+ :c:type:`struct nvme_io_args <nvme_io_args>` argument structure
+
+**Description**
+
+The Write Uncorrectable command marks a range of logical blocks as invalid.
+When the specified logical block(s) are read after this operation, a failure
+is returned with Unrecovered Read Error status. To clear the invalid logical
+block status, a write operation on those logical blocks is required.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_verify (struct nvme_io_args *args)
+
+ Send an nvme verify command
+
+**Parameters**
+
+``struct nvme_io_args *args``
+ :c:type:`struct nvme_io_args <nvme_io_args>` argument structure
+
+**Description**
+
+The Verify command verifies integrity of stored information by reading data
+and metadata, if applicable, for the LBAs indicated without transferring any
+data or metadata to the host.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_dsm (struct nvme_dsm_args *args)
+
+ Send an nvme data set management command
+
+**Parameters**
+
+``struct nvme_dsm_args *args``
+ :c:type:`struct nvme_dsm_args <nvme_dsm_args>` argument structure
+
+**Description**
+
+The Dataset Management command is used by the host to indicate attributes
+for ranges of logical blocks. This includes attributes like frequency that
+data is read or written, access size, and other information that may be used
+to optimize performance and reliability, and may be used to
+deallocate/unmap/trim those logical blocks.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_copy (struct nvme_copy_args *args)
+
+ Copy command
+
+**Parameters**
+
+``struct nvme_copy_args *args``
+ :c:type:`struct nvme_copy_args <nvme_copy_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_resv_acquire (struct nvme_resv_acquire_args *args)
+
+ Send an nvme reservation acquire
+
+**Parameters**
+
+``struct nvme_resv_acquire_args *args``
+ :c:type:`struct nvme_resv_acquire <nvme_resv_acquire>` argument structure
+
+**Description**
+
+The Reservation Acquire command acquires a reservation on a namespace,
+preempt a reservation held on a namespace, and abort a reservation held on a
+namespace.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_resv_register (struct nvme_resv_register_args *args)
+
+ Send an nvme reservation register
+
+**Parameters**
+
+``struct nvme_resv_register_args *args``
+ :c:type:`struct nvme_resv_register_args <nvme_resv_register_args>` argument structure
+
+**Description**
+
+The Reservation Register command registers, unregisters, or replaces a
+reservation key.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_resv_release (struct nvme_resv_release_args *args)
+
+ Send an nvme reservation release
+
+**Parameters**
+
+``struct nvme_resv_release_args *args``
+ :c:type:`struct nvme_resv_release_args <nvme_resv_release_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_resv_report (struct nvme_resv_report_args *args)
+
+ Send an nvme reservation report
+
+**Parameters**
+
+``struct nvme_resv_report_args *args``
+ struct nvme_resv_report_args argument structure
+
+**Description**
+
+Returns a Reservation Status data structure to memory that describes the
+registration and reservation status of a namespace. See the definition for
+the returned structure, :c:type:`struct nvme_reservation_status <nvme_reservation_status>`, for more details.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_io_mgmt_recv (struct nvme_io_mgmt_recv_args *args)
+
+ I/O Management Receive command
+
+**Parameters**
+
+``struct nvme_io_mgmt_recv_args *args``
+ :c:type:`struct nvme_io_mgmt_recv_args <nvme_io_mgmt_recv_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_fdp_reclaim_unit_handle_status (int fd, __u32 nsid, __u32 data_len, void *data)
+
+ Get reclaim unit handle status
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace identifier
+
+``__u32 data_len``
+ Length of response buffer
+
+``void *data``
+ Response buffer
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_io_mgmt_send (struct nvme_io_mgmt_send_args *args)
+
+ I/O Management Send command
+
+**Parameters**
+
+``struct nvme_io_mgmt_send_args *args``
+ :c:type:`struct nvme_io_mgmt_send_args <nvme_io_mgmt_send_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_fdp_reclaim_unit_handle_update (int fd, __u32 nsid, unsigned int npids, __u16 *pids)
+
+ Update a list of reclaim unit handles
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace identifier
+
+``unsigned int npids``
+ Number of placement identifiers
+
+``__u16 *pids``
+ List of placement identifiers
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_zns_mgmt_send (struct nvme_zns_mgmt_send_args *args)
+
+ ZNS management send command
+
+**Parameters**
+
+``struct nvme_zns_mgmt_send_args *args``
+ :c:type:`struct nvme_zns_mgmt_send_args <nvme_zns_mgmt_send_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_zns_mgmt_recv (struct nvme_zns_mgmt_recv_args *args)
+
+ ZNS management receive command
+
+**Parameters**
+
+``struct nvme_zns_mgmt_recv_args *args``
+ :c:type:`struct nvme_zns_mgmt_recv_args <nvme_zns_mgmt_recv_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_zns_report_zones (int fd, __u32 nsid, __u64 slba, enum nvme_zns_report_options opts, bool extended, bool partial, __u32 data_len, void *data, __u32 timeout, __u32 *result)
+
+ Return the list of zones
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID
+
+``__u64 slba``
+ Starting LBA
+
+``enum nvme_zns_report_options opts``
+ Reporting options
+
+``bool extended``
+ Extended report
+
+``bool partial``
+ Partial report requested
+
+``__u32 data_len``
+ Length of the data buffer
+
+``void *data``
+ Userspace address of the report zones data
+
+``__u32 timeout``
+ timeout in ms
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_zns_append (struct nvme_zns_append_args *args)
+
+ Append data to a zone
+
+**Parameters**
+
+``struct nvme_zns_append_args *args``
+ :c:type:`struct nvme_zns_append_args <nvme_zns_append_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_dim_send (struct nvme_dim_args *args)
+
+ Send a Discovery Information Management (DIM) command
+
+**Parameters**
+
+``struct nvme_dim_args *args``
+ :c:type:`struct nvme_dim_args <nvme_dim_args>` argument structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: void nvme_set_debug (bool debug)
+
+ Set NVMe command debugging output
+
+**Parameters**
+
+``bool debug``
+ true to enable or false to disable
+
+
+.. c:function:: bool nvme_get_debug (void)
+
+ Get NVMe command debugging output
+
+**Parameters**
+
+``void``
+ no arguments
+
+**Return**
+
+false if disabled or true if enabled.
+
+
diff --git a/doc/rst/linux.rst b/doc/rst/linux.rst
new file mode 100644
index 0000000..819ee68
--- /dev/null
+++ b/doc/rst/linux.rst
@@ -0,0 +1,580 @@
+.. _linux.h:
+
+**linux.h**
+
+
+linux-specific utility functions
+
+.. c:function:: int nvme_fw_download_seq (int fd, __u32 size, __u32 xfer, __u32 offset, void *buf)
+
+ Firmware download sequence
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 size``
+ Total size of the firmware image to transfer
+
+``__u32 xfer``
+ Maximum size to send with each partial transfer
+
+``__u32 offset``
+ Starting offset to send with this firmware download
+
+``void *buf``
+ Address of buffer containing all or part of the firmware image.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+
+
+.. c:enum:: nvme_telemetry_da
+
+ Telemetry Log Data Area
+
+**Constants**
+
+``NVME_TELEMETRY_DA_1``
+ Data Area 1
+
+``NVME_TELEMETRY_DA_2``
+ Data Area 2
+
+``NVME_TELEMETRY_DA_3``
+ Data Area 3
+
+``NVME_TELEMETRY_DA_4``
+ Data Area 4
+
+
+.. c:function:: int nvme_get_telemetry_max (int fd, enum nvme_telemetry_da *da, size_t *max_data_tx)
+
+ Get telemetry limits
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``enum nvme_telemetry_da *da``
+ On success return max supported data area
+
+``size_t *max_data_tx``
+ On success set to max transfer chunk supported by the controller
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_telemetry_log (int fd, bool create, bool ctrl, bool rae, size_t max_data_tx, enum nvme_telemetry_da da, struct nvme_telemetry_log **log, size_t *size)
+
+ Get specified telemetry log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool create``
+ Generate new host initated telemetry capture
+
+``bool ctrl``
+ Get controller Initiated log
+
+``bool rae``
+ Retain asynchronous events
+
+``size_t max_data_tx``
+ Set the max data transfer size to be used retrieving telemetry.
+
+``enum nvme_telemetry_da da``
+ Log page data area, valid values: :c:type:`enum nvme_telemetry_da <nvme_telemetry_da>`.
+
+``struct nvme_telemetry_log **log``
+ On success, set to the value of the allocated and retrieved log.
+
+``size_t *size``
+ Ptr to the telemetry log size, so it can be returned
+
+**Description**
+
+The total size allocated can be calculated as:
+ (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_ctrl_telemetry (int fd, bool rae, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size)
+
+ Get controller telemetry log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_telemetry_log **log``
+ On success, set to the value of the allocated and retrieved log.
+
+``enum nvme_telemetry_da da``
+ Log page data area, valid values: :c:type:`enum nvme_telemetry_da <nvme_telemetry_da>`
+
+``size_t *size``
+ Ptr to the telemetry log size, so it can be returned
+
+**Description**
+
+The total size allocated can be calculated as:
+ (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_host_telemetry (int fd, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size)
+
+ Get host telemetry log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_telemetry_log **log``
+ On success, set to the value of the allocated and retrieved log.
+
+``enum nvme_telemetry_da da``
+ Log page data area, valid values: :c:type:`enum nvme_telemetry_da <nvme_telemetry_da>`
+
+``size_t *size``
+ Ptr to the telemetry log size, so it can be returned
+
+**Description**
+
+The total size allocated can be calculated as:
+ (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_new_host_telemetry (int fd, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size)
+
+ Get new host telemetry log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``struct nvme_telemetry_log **log``
+ On success, set to the value of the allocated and retrieved log.
+
+``enum nvme_telemetry_da da``
+ Log page data area, valid values: :c:type:`enum nvme_telemetry_da <nvme_telemetry_da>`
+
+``size_t *size``
+ Ptr to the telemetry log size, so it can be returned
+
+**Description**
+
+The total size allocated can be calculated as:
+ (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_ana_log_len (int fd, size_t *analen)
+
+ Retrieve size of the current ANA log
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``size_t *analen``
+ Pointer to where the length will be set on success
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_logical_block_size (int fd, __u32 nsid, int *blksize)
+
+ Retrieve block size
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace id
+
+``int *blksize``
+ Pointer to where the block size will be set on success
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_get_lba_status_log (int fd, bool rae, struct nvme_lba_status_log **log)
+
+ Retrieve the LBA Status log page
+
+**Parameters**
+
+``int fd``
+ File descriptor of the nvme device
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_lba_status_log **log``
+ On success, set to the value of the allocated and retrieved log.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_namespace_attach_ctrls (int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist)
+
+ Attach namespace to controller(s)
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID to attach
+
+``__u16 num_ctrls``
+ Number of controllers in ctrlist
+
+``__u16 *ctrlist``
+ List of controller IDs to perform the attach action
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_namespace_detach_ctrls (int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist)
+
+ Detach namespace from controller(s)
+
+**Parameters**
+
+``int fd``
+ File descriptor of nvme device
+
+``__u32 nsid``
+ Namespace ID to detach
+
+``__u16 num_ctrls``
+ Number of controllers in ctrlist
+
+``__u16 *ctrlist``
+ List of controller IDs to perform the detach action
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_open (const char *name)
+
+ Open an nvme controller or namespace device
+
+**Parameters**
+
+``const char *name``
+ The basename of the device to open
+
+**Description**
+
+This will look for the handle in /dev/ and validate the name and filetype
+match linux conventions.
+
+**Return**
+
+A file descriptor for the device on a successful open, or -1 with
+errno set otherwise.
+
+
+
+
+.. c:enum:: nvme_hmac_alg
+
+ HMAC algorithm
+
+**Constants**
+
+``NVME_HMAC_ALG_NONE``
+ No HMAC algorithm
+
+``NVME_HMAC_ALG_SHA2_256``
+ SHA2-256
+
+``NVME_HMAC_ALG_SHA2_384``
+ SHA2-384
+
+``NVME_HMAC_ALG_SHA2_512``
+ SHA2-512
+
+
+.. c:function:: int nvme_gen_dhchap_key (char *hostnqn, enum nvme_hmac_alg hmac, unsigned int key_len, unsigned char *secret, unsigned char *key)
+
+ DH-HMAC-CHAP key generation
+
+**Parameters**
+
+``char *hostnqn``
+ Host NVMe Qualified Name
+
+``enum nvme_hmac_alg hmac``
+ HMAC algorithm
+
+``unsigned int key_len``
+ Output key length
+
+``unsigned char *secret``
+ Secret to used for digest
+
+``unsigned char *key``
+ Generated DH-HMAC-CHAP key
+
+**Return**
+
+If key generation was successful the function returns 0 or
+-1 with errno set otherwise.
+
+
+.. c:function:: long nvme_lookup_keyring (const char *keyring)
+
+ Lookup keyring serial number
+
+**Parameters**
+
+``const char *keyring``
+ Keyring name
+
+**Description**
+
+Looks up the serial number of the keyring **keyring**.
+
+**Return**
+
+The key serial number of the keyring
+or 0 with errno set otherwise.
+
+
+.. c:function:: char * nvme_describe_key_serial (long key_id)
+
+ Return key description
+
+**Parameters**
+
+``long key_id``
+ Key serial number
+
+**Description**
+
+Fetches the description of the key or keyring identified
+by the serial number **key_id**.
+
+**Return**
+
+The description of **key_id** or NULL on failure.
+The returned string needs to be freed by the caller.
+
+
+.. c:function:: long nvme_lookup_key (const char *type, const char *identity)
+
+ Lookup key serial number
+
+**Parameters**
+
+``const char *type``
+ Key type
+
+``const char *identity``
+ Key description
+
+**Description**
+
+Looks up the serial number of the key **identity**
+with type ``type`` in the current session keyring.
+
+**Return**
+
+The key serial number of the key
+or 0 with errno set otherwise.
+
+
+.. c:function:: int nvme_set_keyring (long keyring_id)
+
+ Link keyring for lookup
+
+**Parameters**
+
+``long keyring_id``
+ Keyring id
+
+**Description**
+
+Links **keyring_id** into the session keyring such that
+its keys are available for further key lookups.
+
+**Return**
+
+0 on success, a negative number on error
+with errno set.
+
+
+.. c:function:: long nvme_insert_tls_key (const char *keyring, const char *key_type, const char *hostnqn, const char *subsysnqn, int hmac, unsigned char *configured_key, int key_len)
+
+ Derive and insert TLS key
+
+**Parameters**
+
+``const char *keyring``
+ Keyring to use
+
+``const char *key_type``
+ Type of the resulting key
+
+``const char *hostnqn``
+ Host NVMe Qualified Name
+
+``const char *subsysnqn``
+ Subsystem NVMe Qualified Name
+
+``int hmac``
+ HMAC algorithm
+
+``unsigned char *configured_key``
+ Configured key data to derive the key from
+
+``int key_len``
+ Length of **configured_key**
+
+**Description**
+
+Derives a 'retained' TLS key as specified in NVMe TCP 1.0a and
+stores it as type **key_type** in the keyring specified by **keyring**.
+
+**Return**
+
+The key serial number if the key could be inserted into
+the keyring or 0 with errno otherwise.
+
+
+.. c:function:: long nvme_insert_tls_key_versioned (const char *keyring, const char *key_type, const char *hostnqn, const char *subsysnqn, int version, int hmac, unsigned char *configured_key, int key_len)
+
+ Derive and insert TLS key
+
+**Parameters**
+
+``const char *keyring``
+ Keyring to use
+
+``const char *key_type``
+ Type of the resulting key
+
+``const char *hostnqn``
+ Host NVMe Qualified Name
+
+``const char *subsysnqn``
+ Subsystem NVMe Qualified Name
+
+``int version``
+ Key version to use
+
+``int hmac``
+ HMAC algorithm
+
+``unsigned char *configured_key``
+ Configured key data to derive the key from
+
+``int key_len``
+ Length of **configured_key**
+
+**Description**
+
+Derives a 'retained' TLS key as specified in NVMe TCP 1.0a (if
+**version** s set to '0') or NVMe TP8028 (if **version** is set to '1) and
+stores it as type **key_type** in the keyring specified by **keyring**.
+
+**Return**
+
+The key serial number if the key could be inserted into
+the keyring or 0 with errno otherwise.
+
+
+.. c:function:: char * nvme_generate_tls_key_identity (const char *hostnqn, const char *subsysnqn, int version, int hmac, unsigned char *configured_key, int key_len)
+
+ Generate the TLS key identity
+
+**Parameters**
+
+``const char *hostnqn``
+ Host NVMe Qualified Name
+
+``const char *subsysnqn``
+ Subsystem NVMe Qualified Name
+
+``int version``
+ Key version to use
+
+``int hmac``
+ HMAC algorithm
+
+``unsigned char *configured_key``
+ Configured key data to derive the key from
+
+``int key_len``
+ Length of **configured_key**
+
+**Description**
+
+Derives a 'retained' TLS key as specified in NVMe TCP and
+generate the corresponding TLs identity.
+
+**Return**
+
+The string containing the TLS identity. It is the responsibility
+of the caller to free the returned string.
+
+
diff --git a/doc/rst/log.rst b/doc/rst/log.rst
new file mode 100644
index 0000000..67911a5
--- /dev/null
+++ b/doc/rst/log.rst
@@ -0,0 +1,49 @@
+.. _log.h:
+
+**log.h**
+
+
+logging functions
+
+.. c:function:: void nvme_init_logging (nvme_root_t r, int lvl, bool log_pid, bool log_tstamp)
+
+ Initialize logging
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t context
+
+``int lvl``
+ Logging level to set
+
+``bool log_pid``
+ Boolean to enable logging of the PID
+
+``bool log_tstamp``
+ Boolean to enable logging of the timestamp
+
+**Description**
+
+Sets the default logging variables for the library.
+
+
+.. c:function:: void nvme_set_root (nvme_root_t r)
+
+ Set nvme_root_t context
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t context
+
+**Description**
+
+In order to be able to log from code paths where no root object is passed in
+via the arguments use the the default one which can be set via this call.
+When creating a new root object with **nvme_create_root** the global root object
+will be set as well. This means the global root object is always pointing to
+the latest created root object. Note the first **nvme_free_tree** call will reset
+the global root object.
+
+
diff --git a/doc/rst/meson.build b/doc/rst/meson.build
new file mode 100644
index 0000000..e54c381
--- /dev/null
+++ b/doc/rst/meson.build
@@ -0,0 +1,36 @@
+top_source_dir = meson.current_source_dir() + '/../../'
+
+want_docs = get_option('docs')
+
+if want_docs != 'false'
+ want_docs_build = get_option('docs-build')
+ rstdir = get_option('rstdir')
+ if want_docs_build
+ kernel_doc = find_program(top_source_dir + 'scripts/kernel-doc')
+
+ conf = configuration_data()
+ conf.set('SYSCONFDIR', sysconfdir)
+
+ if want_docs == 'all' or want_docs == 'rst' or want_docs == 'html'
+ foreach apif : api_files
+ afile = files(top_source_dir + 'src/nvme/' + apif)
+ subst = configure_file(
+ input: afile,
+ output: '@BASENAME@.subst',
+ configuration: conf)
+ rst = custom_target(
+ apif.underscorify() + '_rst',
+ input: subst,
+ output: '@BASENAME@.rst',
+ capture: true,
+ command: [kernel_doc,
+ '-rst',
+ '@INPUT@'],
+ install: true,
+ install_dir: rstdir)
+ endforeach
+ endif
+ else
+ # no prebuild docs
+ endif
+endif
diff --git a/doc/rst/mi.rst b/doc/rst/mi.rst
new file mode 100644
index 0000000..2aa7438
--- /dev/null
+++ b/doc/rst/mi.rst
@@ -0,0 +1,3239 @@
+.. _mi.h - NVMe Management Interface library (libnvme-mi) definitions.:
+
+**mi.h - NVMe Management Interface library (libnvme-mi) definitions.**
+
+
+These provide an abstraction for the MI messaging between controllers
+and a host, typically over an MCTP-over-i2c link to a NVMe device, used
+as part of the out-of-band management of a system.
+
+We have a few data structures define here to reflect the topology
+of a MI connection with an NVMe subsystem:
+
+ - :c:type:`nvme_mi_ep_t`: an MI endpoint - our mechanism of communication with a
+ NVMe subsystem. For MCTP, an endpoint will be the component that
+ holds the MCTP address (EID), and receives our request message.
+
+ endpoints are defined in the NVMe-MI spec, and are specific to the MI
+ interface.
+
+ Each endpoint will provide access to one or more of:
+
+ - :c:type:`nvme_mi_ctrl_t`: a NVMe controller, as defined by the NVMe base spec.
+ The controllers are responsible for processing any NVMe standard
+ commands (eg, the Admin command set). An endpoint (:c:type:`nvme_mi_ep_t`)
+ may provide access to multiple controllers - so each of the controller-
+ type commands will require a :c:type:`nvme_mi_ctrl_t` to be specified, rather than
+ an endpoint
+
+A couple of conventions with the libnvme-mi API:
+
+ - All types and functions have the nvme_mi prefix, to distinguish from
+ the libnvme core.
+
+ - We currently support either MI commands and Admin commands. The
+ former adds a _mi prefix, the latter an _admin prefix. [This does
+ result in the MI functions having a double _mi, like
+ :c:type:`nvme_mi_mi_subsystem_health_status_poll`, which is apparently amusing
+ for our German-speaking readers]
+
+For return values: unless specified in the per-function documentation,
+all functions:
+
+ - return 0 on success
+
+ - return -1, with errno set, for errors communicating with the MI device,
+ either in request or response data
+
+ - return >1 on MI status errors. This value is the 8-bit MI status
+ value, represented by :c:type:`enum nvme_mi_resp_status <nvme_mi_resp_status>`. Note that the
+ status values may be vendor-defined above 0xe0.
+
+For the second case, we have a few conventions for errno values:
+
+ - EPROTO: response data violated the MI protocol, and libnvme cannot
+ validly interpret the response
+
+ - EIO: Other I/O error communicating with device (eg., valid but
+ unexpected response data)
+
+ - EINVAL: invalid input arguments for a command
+
+In line with the core NVMe API, the Admin command functions take an
+`_args` structure to provide the command-specific parameters. However,
+for the MI interface, the fd and timeout members of these _args structs
+are ignored.
+
+References to the specifications here will either to be the NVM Express
+Management Interface ("NVMe-MI") or the NVM Express Base specification
+("NVMe"). At the time of writing, the versions we're referencing here
+are:
+ - NVMe-MI 1.2b
+ - NVMe 2.0b
+with a couple of accommodations for older spec types, particularly NVMe-MI
+1.1, where possible.
+
+.. c:macro:: NVME_MI_MSGTYPE_NVME
+
+``NVME_MI_MSGTYPE_NVME ()``
+
+ MCTP message type for NVMe-MI messages.
+
+**Parameters**
+
+**Description**
+
+
+This is defined by MCTP, but is referenced as part of the NVMe-MI message
+spec. This is the MCTP NVMe message type (0x4), with the message-integrity
+bit (0x80) set.
+
+
+
+
+.. c:enum:: nvme_mi_message_type
+
+ NVMe-MI message type field.
+
+**Constants**
+
+``NVME_MI_MT_CONTROL``
+ NVME-MI Control Primitive
+
+``NVME_MI_MT_MI``
+ NVMe-MI command
+
+``NVME_MI_MT_ADMIN``
+ NVMe Admin command
+
+``NVME_MI_MT_PCIE``
+ PCIe command
+
+**Description**
+
+Used as byte 1 of both request and response messages (NMIMT bits of NMP
+byte). Not to be confused with the MCTP message type in byte 0.
+
+
+
+
+.. c:enum:: nvme_mi_ror
+
+ Request or response field.
+
+**Constants**
+
+``NVME_MI_ROR_REQ``
+ request message
+
+``NVME_MI_ROR_RSP``
+ response message
+
+
+
+
+.. c:enum:: nvme_mi_resp_status
+
+ values for the response status field
+
+**Constants**
+
+``NVME_MI_RESP_SUCCESS``
+ success
+
+``NVME_MI_RESP_MPR``
+ More Processing Required
+
+``NVME_MI_RESP_INTERNAL_ERR``
+ Internal Error
+
+``NVME_MI_RESP_INVALID_OPCODE``
+ Invalid command opcode
+
+``NVME_MI_RESP_INVALID_PARAM``
+ Invalid command parameter
+
+``NVME_MI_RESP_INVALID_CMD_SIZE``
+ Invalid command size
+
+``NVME_MI_RESP_INVALID_INPUT_SIZE``
+ Invalid command input data size
+
+``NVME_MI_RESP_ACCESS_DENIED``
+ Access Denied
+
+``NVME_MI_RESP_VPD_UPDATES_EXCEEDED``
+ More VPD updates than allowed
+
+``NVME_MI_RESP_PCIE_INACCESSIBLE``
+ PCIe functionality currently unavailable
+
+``NVME_MI_RESP_MEB_SANITIZED``
+ MEB has been cleared due to sanitize
+
+``NVME_MI_RESP_ENC_SERV_FAILURE``
+ Enclosure services process failed
+
+``NVME_MI_RESP_ENC_SERV_XFER_FAILURE``
+ Transfer with enclosure services failed
+
+``NVME_MI_RESP_ENC_FAILURE``
+ Unreoverable enclosure failure
+
+``NVME_MI_RESP_ENC_XFER_REFUSED``
+ Enclosure services transfer refused
+
+``NVME_MI_RESP_ENC_FUNC_UNSUP``
+ Unsupported enclosure services function
+
+``NVME_MI_RESP_ENC_SERV_UNAVAIL``
+ Enclosure services unavailable
+
+``NVME_MI_RESP_ENC_DEGRADED``
+ Noncritical failure detected by enc. services
+
+``NVME_MI_RESP_SANITIZE_IN_PROGRESS``
+ Command prohibited during sanitize
+
+
+
+
+.. c:struct:: nvme_mi_msg_hdr
+
+ General MI message header.
+
+**Definition**
+
+::
+
+ struct nvme_mi_msg_hdr {
+ __u8 type;
+ __u8 nmp;
+ __u8 meb;
+ __u8 rsvd0;
+ };
+
+**Members**
+
+``type``
+ MCTP message type, will always be NVME_MI_MSGTYPE_NVME
+
+``nmp``
+ NVMe-MI message parameters (including MI message type)
+
+``meb``
+ Management Endpoint Buffer flag; unused for libnvme-mi implementation
+
+``rsvd0``
+ currently reserved
+
+
+**Description**
+
+Wire format shared by both request and response messages, per NVMe-MI
+section 3.1. This is used for all message types, MI and Admin.
+
+
+
+
+.. c:struct:: nvme_mi_msg_resp
+
+ Generic response type.
+
+**Definition**
+
+::
+
+ struct nvme_mi_msg_resp {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 rsvd0[3];
+ };
+
+**Members**
+
+``hdr``
+ the general request/response message header
+
+``status``
+ response status value (see :c:type:`enum nvme_mi_resp_status <nvme_mi_resp_status>`)
+
+``rsvd0``
+ reserved data, may be defined by specific response
+
+
+**Description**
+
+Every response will start with one of these; command-specific responses
+will define parts of the reserved data, and may add further fields.
+
+
+
+
+.. c:enum:: nvme_mi_mi_opcode
+
+ Operation code for supported NVMe-MI commands.
+
+**Constants**
+
+``nvme_mi_mi_opcode_mi_data_read``
+ Read NVMe-MI Data Structure
+
+``nvme_mi_mi_opcode_subsys_health_status_poll``
+ Subsystem Health Status Poll
+
+``nvme_mi_mi_opcode_configuration_set``
+ MI Configuration Set
+
+``nvme_mi_mi_opcode_configuration_get``
+ MI Configuration Get
+
+
+
+
+.. c:struct:: nvme_mi_mi_req_hdr
+
+ MI request message header.
+
+**Definition**
+
+::
+
+ struct nvme_mi_mi_req_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 opcode;
+ __u8 rsvd0[3];
+ __le32 cdw0, cdw1;
+ };
+
+**Members**
+
+``hdr``
+ generic MI message header
+
+``opcode``
+ opcode (OPC) for the specific MI command
+
+``rsvd0``
+ reserved bytes
+
+``cdw0``
+ Management Request Doubleword 0 - command specific usage
+
+``cdw1``
+ Management Request Doubleword 1 - command specific usage
+
+
+**Description**
+
+Wire format for MI request message headers, defined in section 5 of NVMe-MI.
+
+
+
+
+.. c:struct:: nvme_mi_mi_resp_hdr
+
+ MI response message header.
+
+**Definition**
+
+::
+
+ struct nvme_mi_mi_resp_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 nmresp[3];
+ };
+
+**Members**
+
+``hdr``
+ generic MI message header
+
+``status``
+ generic response status from command; non-zero on failure.
+
+``nmresp``
+ NVMe Management Response: command-type-specific response data
+
+
+**Description**
+
+Wire format for MI response message header, defined in section 5 of NVMe-MI.
+
+
+
+
+.. c:enum:: nvme_mi_dtyp
+
+ Data Structure Type field.
+
+**Constants**
+
+``nvme_mi_dtyp_subsys_info``
+ NVM Subsystem Information
+
+``nvme_mi_dtyp_port_info``
+ Port information
+
+``nvme_mi_dtyp_ctrl_list``
+ Controller List
+
+``nvme_mi_dtyp_ctrl_info``
+ Controller Information
+
+``nvme_mi_dtyp_opt_cmd_support``
+ Optionally Supported Command List
+
+``nvme_mi_dtyp_meb_support``
+ Management Endpoint Buffer Command Support List
+
+**Description**
+
+Data Structure Type field for Read NVMe-MI Data Structure command, used to
+indicate the particular structure to query from the endpoint.
+
+
+
+
+.. c:enum:: nvme_mi_config_id
+
+ NVMe-MI Configuration identifier.
+
+**Constants**
+
+``NVME_MI_CONFIG_SMBUS_FREQ``
+ Current SMBus/I2C frequency
+
+``NVME_MI_CONFIG_HEALTH_STATUS_CHANGE``
+ Health Status change - used to clear
+ health status bits in CCS bits of
+ status poll. Only for Set ops.
+
+``NVME_MI_CONFIG_MCTP_MTU``
+ MCTP maximum transmission unit size of port
+ specified in dw 0
+
+**Description**
+
+Configuration parameters for the MI Get/Set Configuration commands.
+
+See :c:type:`nvme_mi_mi_config_get`() and :c:type:`nvme_mi_config_set`().
+
+
+
+
+.. c:enum:: nvme_mi_config_smbus_freq
+
+ SMBus/I2C frequency values
+
+**Constants**
+
+``NVME_MI_CONFIG_SMBUS_FREQ_100kHz``
+ 100kHz
+
+``NVME_MI_CONFIG_SMBUS_FREQ_400kHz``
+ 400kHz
+
+``NVME_MI_CONFIG_SMBUS_FREQ_1MHz``
+ 1MHz
+
+**Description**
+
+Values used in the SMBus Frequency device configuration. See
+:c:type:`nvme_mi_mi_config_get_smbus_freq`() and :c:type:`nvme_mi_mi_config_set_smbus_freq`().
+
+
+
+
+.. c:struct:: nvme_mi_admin_req_hdr
+
+ Admin command request header.
+
+**Definition**
+
+::
+
+ struct nvme_mi_admin_req_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 opcode;
+ __u8 flags;
+ __le16 ctrl_id;
+ __le32 cdw1, cdw2, cdw3, cdw4, cdw5;
+ __le32 doff;
+ __le32 dlen;
+ __le32 rsvd0, rsvd1;
+ __le32 cdw10, cdw11, cdw12, cdw13, cdw14, cdw15;
+ };
+
+**Members**
+
+``hdr``
+ Generic MI message header
+
+``opcode``
+ Admin command opcode (using enum nvme_admin_opcode)
+
+``flags``
+ Command Flags, indicating dlen and doff validity; Only defined in
+ NVMe-MI version 1.1, no fields defined in 1.2 (where the dlen/doff
+ are always considered valid).
+
+``ctrl_id``
+ Controller ID target of command
+
+``cdw1``
+ Submission Queue Entry doubleword 1
+
+``cdw2``
+ Submission Queue Entry doubleword 2
+
+``cdw3``
+ Submission Queue Entry doubleword 3
+
+``cdw4``
+ Submission Queue Entry doubleword 4
+
+``cdw5``
+ Submission Queue Entry doubleword 5
+
+``doff``
+ Offset of data to return from command
+
+``dlen``
+ Length of sent/returned data
+
+``rsvd0``
+ Reserved
+
+``rsvd1``
+ Reserved
+
+``cdw10``
+ Submission Queue Entry doubleword 10
+
+``cdw11``
+ Submission Queue Entry doubleword 11
+
+``cdw12``
+ Submission Queue Entry doubleword 12
+
+``cdw13``
+ Submission Queue Entry doubleword 13
+
+``cdw14``
+ Submission Queue Entry doubleword 14
+
+``cdw15``
+ Submission Queue Entry doubleword 15
+
+
+**Description**
+
+Wire format for Admin command message headers, defined in section 6 of
+NVMe-MI.
+
+
+
+
+.. c:struct:: nvme_mi_admin_resp_hdr
+
+ Admin command response header.
+
+**Definition**
+
+::
+
+ struct nvme_mi_admin_resp_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 rsvd0[3];
+ __le32 cdw0, cdw1, cdw3;
+ };
+
+**Members**
+
+``hdr``
+ Generic MI message header
+
+``status``
+ Generic response code, non-zero on failure
+
+``rsvd0``
+ Reserved
+
+``cdw0``
+ Completion Queue Entry doubleword 0
+
+``cdw1``
+ Completion Queue Entry doubleword 1
+
+``cdw3``
+ Completion Queue Entry doubleword 3
+
+
+**Description**
+
+This is the generic response format with the three doublewords of completion
+queue data, plus optional response data.
+
+
+.. c:function:: const char * nvme_mi_status_to_string (int status)
+
+ return a string representation of the MI status.
+
+**Parameters**
+
+``int status``
+ MI response status
+
+**Description**
+
+Gives a string description of **status**, as per section 4.1.2 of the NVMe-MI
+spec. The status value should be of type NVME_STATUS_MI, and extracted
+from the return value using nvme_status_get_value().
+
+Returned string is const, and should not be free()ed.
+
+**Return**
+
+A string representing the status value
+
+
+.. c:function:: nvme_root_t nvme_mi_create_root (FILE *fp, int log_level)
+
+ Create top-level MI (root) handle.
+
+**Parameters**
+
+``FILE *fp``
+ File descriptor for logging messages
+
+``int log_level``
+ Logging level to use
+
+**Description**
+
+Create the top-level (library) handle for creating subsequent endpoint
+objects. Similar to nvme_create_root(), but we provide this to allow linking
+without the core libnvme.
+
+See :c:type:`nvme_create_root`.
+
+**Return**
+
+new root object, or NULL on failure.
+
+
+.. c:function:: void nvme_mi_free_root (nvme_root_t root)
+
+ Free root object.
+
+**Parameters**
+
+``nvme_root_t root``
+ root to free
+
+
+.. c:function:: void nvme_mi_set_probe_enabled (nvme_root_t root, bool enabled)
+
+ enable/disable the probe for new endpoints
+
+**Parameters**
+
+``nvme_root_t root``
+ :c:type:`nvme_root_t` object
+
+``bool enabled``
+ whether to probe new endpoints
+
+**Description**
+
+Controls whether newly-created endpoints are probed for quirks on creation.
+Defaults to enabled, which results in some initial messaging with the
+endpoint to determine model-specific details.
+
+
+
+
+.. c:type:: nvme_mi_ep_t
+
+ MI Endpoint object.
+
+**Description**
+
+
+Represents our communication endpoint on the remote MI-capable device.
+To be used for direct MI commands for the endpoint (through the
+nvme_mi_mi_* functions(), or to communicate with individual controllers
+(see :c:type:`nvme_mi_init_ctrl`).
+
+Endpoints are created through a transport-specific constructor; currently
+only MCTP-connected endpoints are supported, through :c:type:`nvme_mi_open_mctp`.
+Subsequent operations on the endpoint (and related controllers) are
+transport-independent.
+
+
+.. c:function:: nvme_mi_ep_t nvme_mi_first_endpoint (nvme_root_t m)
+
+ Start endpoint iterator
+
+**Parameters**
+
+``nvme_root_t m``
+ :c:type:`nvme_root_t` object
+
+**Return**
+
+first MI endpoint object under this root, or NULL if no endpoints
+ are present.
+
+**Description**
+
+See: :c:type:`nvme_mi_next_endpoint`, :c:type:`nvme_mi_for_each_endpoint`
+
+
+.. c:function:: nvme_mi_ep_t nvme_mi_next_endpoint (nvme_root_t m, nvme_mi_ep_t e)
+
+ Continue endpoint iterator
+
+**Parameters**
+
+``nvme_root_t m``
+ :c:type:`nvme_root_t` object
+
+``nvme_mi_ep_t e``
+ :c:type:`nvme_mi_ep_t` current position of iterator
+
+**Return**
+
+next endpoint MI endpoint object after **e** under this root, or NULL
+ if no further endpoints are present.
+
+**Description**
+
+See: :c:type:`nvme_mi_first_endpoint`, :c:type:`nvme_mi_for_each_endpoint`
+
+
+.. c:macro:: nvme_mi_for_each_endpoint
+
+``nvme_mi_for_each_endpoint (m, e)``
+
+ Iterator for NVMe-MI endpoints.
+
+**Parameters**
+
+``m``
+ :c:type:`nvme_root_t` containing endpoints
+
+``e``
+ :c:type:`nvme_mi_ep_t` object, set on each iteration
+
+
+.. c:macro:: nvme_mi_for_each_endpoint_safe
+
+``nvme_mi_for_each_endpoint_safe (m, e, _e)``
+
+ Iterator for NVMe-MI endpoints, allowing deletion during traversal
+
+**Parameters**
+
+``m``
+ :c:type:`nvme_root_t` containing endpoints
+
+``e``
+ :c:type:`nvme_mi_ep_t` object, set on each iteration
+
+``_e``
+ :c:type:`nvme_mi_ep_t` object used as temporary storage
+
+
+.. c:function:: int nvme_mi_ep_set_timeout (nvme_mi_ep_t ep, unsigned int timeout_ms)
+
+ set a timeout for NVMe-MI responses
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ MI endpoint object
+
+``unsigned int timeout_ms``
+ Timeout for MI responses, given in milliseconds
+
+
+.. c:function:: void nvme_mi_ep_set_mprt_max (nvme_mi_ep_t ep, unsigned int mprt_max_ms)
+
+ set the maximum wait time for a More Processing Required response
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ MI endpoint object
+
+``unsigned int mprt_max_ms``
+ Maximum more processing required wait time
+
+**Description**
+
+NVMe-MI endpoints may respond to a request with a "More Processing Required"
+response; this also includes a hint on the worst-case processing time for
+the eventual response data, with a specification-defined maximum of 65.535
+seconds.
+
+This function provides a way to limit the maximum time we're prepared to
+wait for the final response. Specify zero in **mprt_max_ms** for no limit.
+This should be larger than the command/response timeout set in
+:c:type:`nvme_mi_ep_set_timeout`().
+
+
+.. c:function:: unsigned int nvme_mi_ep_get_timeout (nvme_mi_ep_t ep)
+
+ get the current timeout value for NVMe-MI responses
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ MI endpoint object
+
+**Description**
+
+Returns the current timeout value, in milliseconds, for this endpoint.
+
+
+
+
+.. c:type:: nvme_mi_ctrl_t
+
+ NVMe-MI Controller object.
+
+**Description**
+
+
+Provides NVMe command functionality, through the MI interface.
+
+
+.. c:function:: nvme_mi_ctrl_t nvme_mi_first_ctrl (nvme_mi_ep_t ep)
+
+ Start controller iterator
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ :c:type:`nvme_mi_ep_t` object
+
+**Return**
+
+first MI controller object under this root, or NULL if no controllers
+ are present.
+
+**Description**
+
+See: :c:type:`nvme_mi_next_ctrl`, :c:type:`nvme_mi_for_each_ctrl`
+
+
+.. c:function:: nvme_mi_ctrl_t nvme_mi_next_ctrl (nvme_mi_ep_t ep, nvme_mi_ctrl_t c)
+
+ Continue ctrl iterator
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ :c:type:`nvme_mi_ep_t` object
+
+``nvme_mi_ctrl_t c``
+ :c:type:`nvme_mi_ctrl_t` current position of iterator
+
+**Return**
+
+next MI controller object after **c** under this endpoint, or NULL
+ if no further controllers are present.
+
+**Description**
+
+See: :c:type:`nvme_mi_first_ctrl`, :c:type:`nvme_mi_for_each_ctrl`
+
+
+.. c:macro:: nvme_mi_for_each_ctrl
+
+``nvme_mi_for_each_ctrl (ep, c)``
+
+ Iterator for NVMe-MI controllers.
+
+**Parameters**
+
+``ep``
+ :c:type:`nvme_mi_ep_t` containing endpoints
+
+``c``
+ :c:type:`nvme_mi_ctrl_t` object, set on each iteration
+
+**Description**
+
+Allows iteration of the list of controllers behind an endpoint. Unless the
+controllers have already been created explicitly, you'll probably want to
+call :c:type:`nvme_mi_scan_ep`() to scan for the controllers first.
+
+See: :c:type:`nvme_mi_scan_ep`()
+
+
+.. c:macro:: nvme_mi_for_each_ctrl_safe
+
+``nvme_mi_for_each_ctrl_safe (ep, c, _c)``
+
+ Iterator for NVMe-MI controllers, allowing deletion during traversal
+
+**Parameters**
+
+``ep``
+ :c:type:`nvme_mi_ep_t` containing controllers
+
+``c``
+ :c:type:`nvme_mi_ctrl_t` object, set on each iteration
+
+``_c``
+ :c:type:`nvme_mi_ctrl_t` object used as temporary storage
+
+**Description**
+
+Allows iteration of the list of controllers behind an endpoint, safe against
+deletion during iteration. Unless the controllers have already been created
+explicitly (or you're just iterating to destroy controllers) you'll probably
+want to call :c:type:`nvme_mi_scan_ep`() to scan for the controllers first.
+
+See: :c:type:`nvme_mi_scan_ep`()
+
+
+.. c:function:: nvme_mi_ep_t nvme_mi_open_mctp (nvme_root_t root, unsigned int netid, uint8_t eid)
+
+ Create an endpoint using a MCTP connection.
+
+**Parameters**
+
+``nvme_root_t root``
+ root object to create under
+
+``unsigned int netid``
+ MCTP network ID on this system
+
+``uint8_t eid``
+ MCTP endpoint ID
+
+**Description**
+
+Transport-specific endpoint initialization for MI-connected endpoints. Once
+an endpoint is created, the rest of the API is transport-independent.
+
+See :c:type:`nvme_mi_close`
+
+**Return**
+
+New endpoint object for **netid** & **eid**, or NULL on failure.
+
+
+.. c:function:: void nvme_mi_close (nvme_mi_ep_t ep)
+
+ Close an endpoint connection and release resources, including controller objects.
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ Endpoint object to close
+
+
+.. c:function:: nvme_root_t nvme_mi_scan_mctp (void)
+
+ look for MCTP-connected NVMe-MI endpoints.
+
+**Parameters**
+
+``void``
+ no arguments
+
+**Description**
+
+This function queries the system MCTP daemon ("mctpd") over
+D-Bus, to find MCTP endpoints that report support for NVMe-MI over MCTP.
+
+This requires libvnme-mi to be compiled with D-Bus support; if not, this
+will return NULL.
+
+**Return**
+
+A **nvme_root_t** populated with a set of MCTP-connected endpoints,
+ or NULL on failure
+
+
+.. c:function:: int nvme_mi_scan_ep (nvme_mi_ep_t ep, bool force_rescan)
+
+ query an endpoint for its NVMe controllers.
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ Endpoint to scan
+
+``bool force_rescan``
+ close existing controllers and rescan
+
+**Description**
+
+This function queries an MI endpoint for the controllers available, by
+performing an MI Read MI Data Structure command (requesting the
+controller list). The controllers are stored in the endpoint's internal
+list, and can be iterated with nvme_mi_for_each_ctrl.
+
+This will only scan the endpoint once, unless **force_rescan** is set. If
+so, all existing controller objects will be freed - the caller must not
+hold a reference to those across this call.
+
+See: :c:type:`nvme_mi_for_each_ctrl`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: nvme_mi_ctrl_t nvme_mi_init_ctrl (nvme_mi_ep_t ep, __u16 ctrl_id)
+
+ initialise a NVMe controller.
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ Endpoint to create under
+
+``__u16 ctrl_id``
+ ID of controller to initialize.
+
+**Description**
+
+Create a connection to a controller behind the endpoint specified in **ep**.
+Controller IDs may be queried from the endpoint through
+:c:type:`nvme_mi_mi_read_mi_data_ctrl_list`.
+
+See :c:type:`nvme_mi_close_ctrl`
+
+**Return**
+
+New controller object, or NULL on failure.
+
+
+.. c:function:: void nvme_mi_close_ctrl (nvme_mi_ctrl_t ctrl)
+
+ free a controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ controller to free
+
+
+.. c:function:: __u16 nvme_mi_ctrl_id (nvme_mi_ctrl_t ctrl)
+
+ get the ID of a controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ controller to query
+
+**Description**
+
+Retrieve the ID of the controller, as defined by hardware, and available
+in the Identify (Controller List) data. This is the value passed to
+**nvme_mi_init_ctrl**, but may have been created internally via
+**nvme_mi_scan_ep**.
+
+**Return**
+
+the (locally-stored) ID of this controller.
+
+
+.. c:function:: char * nvme_mi_endpoint_desc (nvme_mi_ep_t ep)
+
+ Get a string describing a MI endpoint.
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint to describe
+
+**Description**
+
+Generates a human-readable string describing the endpoint, with possibly
+transport-specific data. The string is allocated during the call, and the
+caller is responsible for free()-ing the string.
+
+**Return**
+
+a newly-allocated string containing the endpoint description, or
+ NULL on failure.
+
+
+.. c:function:: int nvme_mi_mi_read_mi_data_subsys (nvme_mi_ep_t ep, struct nvme_mi_read_nvm_ss_info *s)
+
+ Perform a Read MI Data Structure command, retrieving subsystem data.
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``struct nvme_mi_read_nvm_ss_info *s``
+ subsystem information to populate
+
+**Description**
+
+Retrieves the Subsystem information - number of external ports and
+NVMe version information. See :c:type:`struct nvme_mi_read_nvm_ss_info <nvme_mi_read_nvm_ss_info>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_read_mi_data_port (nvme_mi_ep_t ep, __u8 portid, struct nvme_mi_read_port_info *p)
+
+ Perform a Read MI Data Structure command, retrieving port data.
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u8 portid``
+ id of port data to retrieve
+
+``struct nvme_mi_read_port_info *p``
+ port information to populate
+
+**Description**
+
+Retrieves the Port information, for the specified port ID. The subsystem
+data (from :c:type:`nvme_mi_mi_read_mi_data_subsys`) nmp field contains the allowed
+range of port IDs.
+
+See :c:type:`struct nvme_mi_read_port_info <nvme_mi_read_port_info>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_read_mi_data_ctrl_list (nvme_mi_ep_t ep, __u8 start_ctrlid, struct nvme_ctrl_list *list)
+
+ Perform a Read MI Data Structure command, retrieving the list of attached controllers.
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u8 start_ctrlid``
+ starting controller ID
+
+``struct nvme_ctrl_list *list``
+ controller list to populate
+
+**Description**
+
+Retrieves the list of attached controllers, with IDs greater than or
+equal to **start_ctrlid**.
+
+See :c:type:`struct nvme_ctrl_list <nvme_ctrl_list>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_read_mi_data_ctrl (nvme_mi_ep_t ep, __u16 ctrl_id, struct nvme_mi_read_ctrl_info *ctrl)
+
+ Perform a Read MI Data Structure command, retrieving controller information
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u16 ctrl_id``
+ ID of controller to query
+
+``struct nvme_mi_read_ctrl_info *ctrl``
+ controller data to populate
+
+**Description**
+
+Retrieves the Controller Information Data Structure for the attached
+controller with ID **ctrlid**.
+
+See :c:type:`struct nvme_mi_read_ctrl_info <nvme_mi_read_ctrl_info>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_subsystem_health_status_poll (nvme_mi_ep_t ep, bool clear, struct nvme_mi_nvm_ss_health_status *nshds)
+
+ Read the Subsystem Health Data Structure from the NVM subsystem
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``bool clear``
+ flag to clear the Composite Controller Status state
+
+``struct nvme_mi_nvm_ss_health_status *nshds``
+ subsystem health status data to populate
+
+**Description**
+
+Retrieves the Subsystem Health Data Structure into **nshds**. If **clear** is
+set, requests that the Composite Controller Status bits are cleared after
+the read. See NVMe-MI section 5.6 for details on the CCS bits.
+
+See :c:type:`struct nvme_mi_nvm_ss_health_status <nvme_mi_nvm_ss_health_status>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_config_get (nvme_mi_ep_t ep, __u32 dw0, __u32 dw1, __u32 *nmresp)
+
+ query a configuration parameter
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u32 dw0``
+ management doubleword 0, containing configuration identifier, plus
+ config-specific fields
+
+``__u32 dw1``
+ management doubleword 0, config-specific.
+
+``__u32 *nmresp``
+ set to queried configuration data in NMRESP field of response.
+
+**Description**
+
+Performs a MI Configuration Get command, with the configuration identifier
+as the LSB of **dw0**. Other **dw0** and **dw1** data is configuration-identifier
+specific.
+
+On a successful Configuration Get, the **nmresp** pointer will be populated with
+the bytes from the 3-byte NMRESP field, converted to native endian.
+
+See :c:type:`enum nvme_mi_config_id <nvme_mi_config_id>` for identifiers.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_config_set (nvme_mi_ep_t ep, __u32 dw0, __u32 dw1)
+
+ set a configuration parameter
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u32 dw0``
+ management doubleword 0, containing configuration identifier, plus
+ config-specific fields
+
+``__u32 dw1``
+ management doubleword 0, config-specific.
+
+**Description**
+
+Performs a MI Configuration Set command, with the command as the LSB of
+**dw0**. Other **dw0** and **dw1** data is configuration-identifier specific.
+
+See :c:type:`enum nvme_mi_config_id <nvme_mi_config_id>` for identifiers.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_config_get_smbus_freq (nvme_mi_ep_t ep, __u8 port, enum nvme_mi_config_smbus_freq *freq)
+
+ get configuration: SMBus port frequency
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u8 port``
+ port ID to query
+
+``enum nvme_mi_config_smbus_freq *freq``
+ output value for current frequency configuration
+
+**Description**
+
+Performs a MI Configuration Get, to query the current SMBus frequency of
+the port specified in **port**. On success, populates **freq** with the port
+frequency
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_config_set_smbus_freq (nvme_mi_ep_t ep, __u8 port, enum nvme_mi_config_smbus_freq freq)
+
+ set configuration: SMBus port frequency
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u8 port``
+ port ID to set
+
+``enum nvme_mi_config_smbus_freq freq``
+ new frequency configuration
+
+**Description**
+
+Performs a MI Configuration Set, to update the current SMBus frequency of
+the port specified in **port**.
+
+See :c:type:`struct nvme_mi_read_port_info <nvme_mi_read_port_info>` for the maximum supported SMBus frequency
+for the port.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_config_set_health_status_change (nvme_mi_ep_t ep, __u32 mask)
+
+ clear CCS bits in health status
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u32 mask``
+ bitmask to clear
+
+**Description**
+
+Performs a MI Configuration Set, to update the current health status poll
+values of the Composite Controller Status bits. Bits set in **mask** will
+be cleared from future health status poll data, and may be re-triggered by
+a future health change event.
+
+See :c:type:`nvme_mi_mi_subsystem_health_status_poll`(), :c:type:`enum nvme_mi_ccs <nvme_mi_ccs>` for
+values in **mask**.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_config_get_mctp_mtu (nvme_mi_ep_t ep, __u8 port, __u16 *mtu)
+
+ get configuration: MCTP MTU
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u8 port``
+ port ID to query
+
+``__u16 *mtu``
+ output value for current MCTP MTU configuration
+
+**Description**
+
+Performs a MI Configuration Get, to query the current MCTP Maximum
+Transmission Unit size (MTU) of the port specified in **port**. On success,
+populates **mtu** with the MTU.
+
+The default reset value is 64, corresponding to the MCTP baseline MTU.
+
+Some controllers may also use this as the maximum receive unit size, and
+may not accept MCTP messages larger than the configured MTU.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_mi_config_set_mctp_mtu (nvme_mi_ep_t ep, __u8 port, __u16 mtu)
+
+ set configuration: MCTP MTU
+
+**Parameters**
+
+``nvme_mi_ep_t ep``
+ endpoint for MI communication
+
+``__u8 port``
+ port ID to set
+
+``__u16 mtu``
+ new MTU configuration
+
+**Description**
+
+Performs a MI Configuration Set, to update the current MCTP MTU value for
+the port specified in **port**.
+
+Some controllers may also use this as the maximum receive unit size, and
+may not accept MCTP messages larger than the configured MTU. When setting
+this value, you will likely need to change the MTU of the local MCTP
+interface(s) to match.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_admin_xfer (nvme_mi_ctrl_t ctrl, struct nvme_mi_admin_req_hdr *admin_req, size_t req_data_size, struct nvme_mi_admin_resp_hdr *admin_resp, off_t resp_data_offset, size_t *resp_data_size)
+
+ Raw admin transfer interface.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ controller to send the admin command to
+
+``struct nvme_mi_admin_req_hdr *admin_req``
+ request data
+
+``size_t req_data_size``
+ size of request data payload
+
+``struct nvme_mi_admin_resp_hdr *admin_resp``
+ buffer for response data
+
+``off_t resp_data_offset``
+ offset into request data to retrieve from controller
+
+``size_t *resp_data_size``
+ size of response data buffer, updated to received size
+
+**Description**
+
+Performs an arbitrary NVMe Admin command, using the provided request data,
+in **admin_req**. The size of the request data *payload* is specified in
+**req_data_size** - this does not include the standard header length (so a
+header-only request would have a size of 0).
+
+On success, response data is stored in **admin_resp**, which has an optional
+appended payload buffer of **resp_data_size** bytes. The actual payload
+transferred will be stored in **resp_data_size**. These sizes do not include
+the Admin request header, so 0 represents no payload.
+
+As with all Admin commands, we can request partial data from the Admin
+Response payload, offset by **resp_data_offset**.
+
+See: :c:type:`struct nvme_mi_admin_req_hdr <nvme_mi_admin_req_hdr>` and :c:type:`struct nvme_mi_admin_resp_hdr <nvme_mi_admin_resp_hdr>`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise..
+
+
+.. c:function:: int nvme_mi_admin_admin_passthru (nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len, void *metadata, __u32 timeout_ms, __u32 *result)
+
+ Submit an nvme admin passthrough command
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``__u8 opcode``
+ The nvme admin command to send
+
+``__u8 flags``
+ NVMe command flags (not used)
+
+``__u16 rsvd``
+ Reserved for future use
+
+``__u32 nsid``
+ Namespace identifier
+
+``__u32 cdw2``
+ Command dword 2
+
+``__u32 cdw3``
+ Command dword 3
+
+``__u32 cdw10``
+ Command dword 10
+
+``__u32 cdw11``
+ Command dword 11
+
+``__u32 cdw12``
+ Command dword 12
+
+``__u32 cdw13``
+ Command dword 13
+
+``__u32 cdw14``
+ Command dword 14
+
+``__u32 cdw15``
+ Command dword 15
+
+``__u32 data_len``
+ Length of the data transferred in this command in bytes
+
+``void *data``
+ Pointer to user address of the data buffer
+
+``__u32 metadata_len``
+ Length of metadata transferred in this command(not used)
+
+``void *metadata``
+ Pointer to user address of the metadata buffer(not used)
+
+``__u32 timeout_ms``
+ How long to wait for the command to complete
+
+``__u32 *result``
+ Optional field to return the result from the CQE dword 0
+
+**Description**
+
+Send a customized NVMe Admin command request message and get the corresponding
+response message.
+
+This interface supports no data, host to controller and controller to
+host but it doesn't support bidirectional data transfer.
+Also this interface only supports data transfer size range [0, 4096] (bytes)
+so the & data_len parameter must be less than 4097.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_partial (nvme_mi_ctrl_t ctrl, struct nvme_identify_args *args, off_t offset, size_t size)
+
+ Perform an Admin identify command, and retrieve partial response data.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``struct nvme_identify_args *args``
+ Identify command arguments
+
+``off_t offset``
+ offset of identify data to retrieve from response
+
+``size_t size``
+ size of identify data to return
+
+**Description**
+
+Perform an Identify command, using the Identify command parameters in **args**.
+The **offset** and **size** arguments allow the caller to retrieve part of
+the identify response. See NVMe-MI section 6.2 for the semantics (and some
+handy diagrams) of the offset & size parameters.
+
+Will return an error if the length of the response data (from the controller)
+did not match **size**.
+
+Unless you're performing a vendor-unique identify command, You'll probably
+want to use one of the identify helpers (nvme_mi_admin_identify,
+nvme_mi_admin_identify_cns_nsid, or nvme_mi_admin_identify_<type>) instead
+of this. If the type of your identify command is standardized but not
+yet supported by libnvme-mi, please contact the maintainers.
+
+See: :c:type:`struct nvme_identify_args <nvme_identify_args>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify (nvme_mi_ctrl_t ctrl, struct nvme_identify_args *args)
+
+ Perform an Admin identify command.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``struct nvme_identify_args *args``
+ Identify command arguments
+
+**Description**
+
+Perform an Identify command, using the Identify command parameters in **args**.
+Stores the identify data in ->data, and (if set) the result from cdw0
+into args->result.
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`.
+
+See: :c:type:`struct nvme_identify_args <nvme_identify_args>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_cns_nsid (nvme_mi_ctrl_t ctrl, enum nvme_identify_cns cns, __u32 nsid, void *data)
+
+ Perform an Admin identify command using specific CNS/NSID parameters.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``enum nvme_identify_cns cns``
+ Controller or Namespace Structure, specifying identified object
+
+``__u32 nsid``
+ namespace ID
+
+``void *data``
+ buffer for identify data response
+
+**Description**
+
+Perform an Identify command, using the CNS specifier **cns**, and the
+namespace ID **nsid** if required by the CNS type.
+
+Stores the identify data in **data**, which is expected to be a buffer of
+:c:type:`NVME_IDENTIFY_DATA_SIZE` bytes.
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_ns (nvme_mi_ctrl_t ctrl, __u32 nsid, struct nvme_id_ns *ns)
+
+ Perform an Admin identify command for a namespace
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u32 nsid``
+ namespace ID
+
+``struct nvme_id_ns *ns``
+ Namespace identification to populate
+
+**Description**
+
+Perform an Identify (namespace) command, setting the namespace id data
+in **ns**. The namespace is expected to active and allocated.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_ns_descs (nvme_mi_ctrl_t ctrl, __u32 nsid, struct nvme_ns_id_desc *descs)
+
+ Perform an Admin identify Namespace Identification Descriptor list command for a namespace
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u32 nsid``
+ Namespace ID
+
+``struct nvme_ns_id_desc *descs``
+ Namespace Identification Descriptor list to populate
+
+**Description**
+
+Perform an Identify namespace identification description list command,
+setting the namespace identification description list in **descs**
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_allocated_ns (nvme_mi_ctrl_t ctrl, __u32 nsid, struct nvme_id_ns *ns)
+
+ Perform an Admin identify command for an allocated namespace
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u32 nsid``
+ namespace ID
+
+``struct nvme_id_ns *ns``
+ Namespace identification to populate
+
+**Description**
+
+Perform an Identify (namespace) command, setting the namespace id data
+in **ns**.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_ctrl (nvme_mi_ctrl_t ctrl, struct nvme_id_ctrl *id)
+
+ Perform an Admin identify for a controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``struct nvme_id_ctrl *id``
+ Controller identify data to populate
+
+**Description**
+
+Perform an Identify command, for the controller specified by **ctrl**,
+writing identify data to **id**.
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`, so **id** will be
+fully populated on success.
+
+See: :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_ctrl_list (nvme_mi_ctrl_t ctrl, __u16 cntid, struct nvme_ctrl_list *list)
+
+ Perform an Admin identify for a controller list.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u16 cntid``
+ Controller ID to specify list start
+
+``struct nvme_ctrl_list *list``
+ List data to populate
+
+**Description**
+
+Perform an Identify command, for the controller list starting with
+IDs greater than or equal to **cntid**.
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`, so **id** will be
+fully populated on success.
+
+See: :c:type:`struct nvme_ctrl_list <nvme_ctrl_list>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_nsid_ctrl_list (nvme_mi_ctrl_t ctrl, __u32 nsid, __u16 cntid, struct nvme_ctrl_list *list)
+
+ Perform an Admin identify for a controller list with specific namespace ID
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u32 nsid``
+ Namespace identifier
+
+``__u16 cntid``
+ Controller ID to specify list start
+
+``struct nvme_ctrl_list *list``
+ List data to populate
+
+**Description**
+
+Perform an Identify command, for the controller list for **nsid**, starting
+with IDs greater than or equal to **cntid**.
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`, so **id** will be
+fully populated on success.
+
+See: :c:type:`struct nvme_ctrl_list <nvme_ctrl_list>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_allocated_ns_list (nvme_mi_ctrl_t ctrl, __u32 nsid, struct nvme_ns_list *list)
+
+ Perform an Admin identify for an allocated namespace list
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u32 nsid``
+ Namespace ID to specify list start
+
+``struct nvme_ns_list *list``
+ List data to populate
+
+**Description**
+
+Perform an Identify command, for the allocated namespace list starting with
+IDs greater than or equal to **nsid**. Specify :c:type:`NVME_NSID_NONE` for the start
+of the list.
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`, so **list** will be
+be fully populated on success.
+
+See: :c:type:`struct nvme_ns_list <nvme_ns_list>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_active_ns_list (nvme_mi_ctrl_t ctrl, __u32 nsid, struct nvme_ns_list *list)
+
+ Perform an Admin identify for an active namespace list
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u32 nsid``
+ Namespace ID to specify list start
+
+``struct nvme_ns_list *list``
+ List data to populate
+
+**Description**
+
+Perform an Identify command, for the active namespace list starting with
+IDs greater than or equal to **nsid**. Specify :c:type:`NVME_NSID_NONE` for the start
+of the list.
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`, so **list** will be
+be fully populated on success.
+
+See: :c:type:`struct nvme_ns_list <nvme_ns_list>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_identify_primary_ctrl (nvme_mi_ctrl_t ctrl, __u16 cntid, struct nvme_primary_ctrl_cap *cap)
+
+ Perform an Admin identify for primary controller capabilities data structure.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u16 cntid``
+ Controller ID to specify
+
+``struct nvme_primary_ctrl_cap *cap``
+ Primary Controller Capabilities data structure to populate
+
+**Description**
+
+Perform an Identify command to get the Primary Controller Capabilities data
+for the controller specified by **cntid**
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`, so **cap** will be
+be fully populated on success.
+
+See: :c:type:`struct nvme_primary_ctrl_cap <nvme_primary_ctrl_cap>`
+
+**Return**
+
+0 on success, non-zero on failure
+
+
+.. c:function:: int nvme_mi_admin_identify_secondary_ctrl_list (nvme_mi_ctrl_t ctrl, __u16 cntid, struct nvme_secondary_ctrl_list *list)
+
+ Perform an Admin identify for a secondary controller list.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to process identify command
+
+``__u16 cntid``
+ Controller ID to specify list start
+
+``struct nvme_secondary_ctrl_list *list``
+ List data to populate
+
+**Description**
+
+Perform an Identify command, for the secondary controllers associated with
+the current primary controller. Only entries with IDs greater than or
+equal to **cntid** are returned.
+
+Will return an error if the length of the response data (from the
+controller) is not a full :c:type:`NVME_IDENTIFY_DATA_SIZE`, so **list** will be
+be fully populated on success.
+
+See: :c:type:`struct nvme_secondary_ctrl_list <nvme_secondary_ctrl_list>`
+
+**Return**
+
+0 on success, non-zero on failure
+
+
+.. c:function:: int nvme_mi_admin_get_log_page (nvme_mi_ctrl_t ctrl, __u32 xfer_len, struct nvme_get_log_args *args)
+
+ Retrieve log page data from controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u32 xfer_len``
+ The chunk size of the read
+
+``struct nvme_get_log_args *args``
+ Get Log Page command arguments
+
+**Description**
+
+Performs a Get Log Page Admin command as specified by **args**. Response data
+is stored in **args->data**, which should be a buffer of **args->data_len** bytes.
+Resulting data length is stored in **args->data_len** on successful
+command completion.
+
+This request may be implemented as multiple log page commands, in order
+to fit within MI message-size limits.
+
+See: :c:type:`struct nvme_get_log_args <nvme_get_log_args>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log (nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args)
+
+ Retrieve log page data from controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``struct nvme_get_log_args *args``
+ Get Log Page command arguments
+
+**Description**
+
+Performs a Get Log Page Admin command as specified by **args**. Response data
+is stored in **args->data**, which should be a buffer of **args->data_len** bytes.
+Resulting data length is stored in **args->data_len** on successful
+command completion.
+
+This request may be implemented as multiple log page commands, in order
+to fit within MI message-size limits.
+
+See: :c:type:`struct nvme_get_log_args <nvme_get_log_args>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_nsid_log (nvme_mi_ctrl_t ctrl, bool rae, enum nvme_cmd_get_log_lid lid, __u32 nsid, __u32 len, void *log)
+
+ Helper for Get Log Page functions
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain Asynchronous Events
+
+``enum nvme_cmd_get_log_lid lid``
+ Log identifier
+
+``__u32 nsid``
+ Namespace ID
+
+``__u32 len``
+ length of log buffer
+
+``void *log``
+ pointer for resulting log data
+
+**Description**
+
+Performs a Get Log Page Admin command for a specific log ID **lid** and
+namespace ID **nsid**. Log data is expected to be **len** bytes, and is stored
+in **log** on success. The **rae** flag is passed as-is to the Get Log Page
+command, and is specific to the Log Page requested.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_simple (nvme_mi_ctrl_t ctrl, enum nvme_cmd_get_log_lid lid, __u32 len, void *log)
+
+ Helper for Get Log Page functions with no NSID or RAE requirements
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``enum nvme_cmd_get_log_lid lid``
+ Log identifier
+
+``__u32 len``
+ length of log buffer
+
+``void *log``
+ pointer for resulting log data
+
+**Description**
+
+Performs a Get Log Page Admin command for a specific log ID **lid**, using
+NVME_NSID_ALL for the namespace identifier, and rae set to false.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_supported_log_pages (nvme_mi_ctrl_t ctrl, bool rae, struct nvme_supported_log_pages *log)
+
+ Retrieve nmve supported log pages
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_supported_log_pages *log``
+ Array of LID supported and Effects data structures
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_error (nvme_mi_ctrl_t ctrl, unsigned int nr_entries, bool rae, struct nvme_error_log_page *err_log)
+
+ Retrieve nvme error log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``unsigned int nr_entries``
+ Number of error log entries allocated
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_error_log_page *err_log``
+ Array of error logs of size 'entries'
+
+**Description**
+
+This log page describes extended error information for a command that
+completed with error, or may report an error that is not specific to a
+particular command.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_smart (nvme_mi_ctrl_t ctrl, __u32 nsid, bool rae, struct nvme_smart_log *smart_log)
+
+ Retrieve nvme smart log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u32 nsid``
+ Optional namespace identifier
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_smart_log *smart_log``
+ User address to store the smart log
+
+**Description**
+
+This log page provides SMART and general health information. The information
+provided is over the life of the controller and is retained across power
+cycles. To request the controller log page, the namespace identifier
+specified is FFFFFFFFh. The controller may also support requesting the log
+page on a per namespace basis, as indicated by bit 0 of the LPA field in the
+Identify Controller data structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_fw_slot (nvme_mi_ctrl_t ctrl, bool rae, struct nvme_firmware_slot *fw_log)
+
+ Retrieves the controller firmware log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_firmware_slot *fw_log``
+ User address to store the log page
+
+**Description**
+
+This log page describes the firmware revision stored in each firmware slot
+supported. The firmware revision is indicated as an ASCII string. The log
+page also indicates the active slot number.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_changed_ns_list (nvme_mi_ctrl_t ctrl, bool rae, struct nvme_ns_list *ns_log)
+
+ Retrieve namespace changed list
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_ns_list *ns_log``
+ User address to store the log page
+
+**Description**
+
+This log page describes namespaces attached to this controller that have
+changed since the last time the namespace was identified, been added, or
+deleted.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_cmd_effects (nvme_mi_ctrl_t ctrl, enum nvme_csi csi, struct nvme_cmd_effects_log *effects_log)
+
+ Retrieve nvme command effects log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``enum nvme_csi csi``
+ Command Set Identifier
+
+``struct nvme_cmd_effects_log *effects_log``
+ User address to store the effects log
+
+**Description**
+
+This log page describes the commands that the controller supports and the
+effects of those commands on the state of the NVM subsystem.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_device_self_test (nvme_mi_ctrl_t ctrl, struct nvme_self_test_log *log)
+
+ Retrieve the device self test log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``struct nvme_self_test_log *log``
+ Userspace address of the log payload
+
+**Description**
+
+The log page indicates the status of an in progress self test and the
+percent complete of that operation, and the results of the previous 20
+self-test operations.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_create_telemetry_host (nvme_mi_ctrl_t ctrl, struct nvme_telemetry_log *log)
+
+ Create host telemetry log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``struct nvme_telemetry_log *log``
+ Userspace address of the log payload
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_telemetry_host (nvme_mi_ctrl_t ctrl, __u64 offset, __u32 len, void *log)
+
+ Get Telemetry Host-Initiated log page
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u64 offset``
+ Offset into the telemetry data
+
+``__u32 len``
+ Length of provided user buffer to hold the log data in bytes
+
+``void *log``
+ User address for log page data
+
+**Description**
+
+Retrieves the Telemetry Host-Initiated log page at the requested offset
+using the previously existing capture.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_telemetry_ctrl (nvme_mi_ctrl_t ctrl, bool rae, __u64 offset, __u32 len, void *log)
+
+ Get Telemetry Controller-Initiated log page
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``__u64 offset``
+ Offset into the telemetry data
+
+``__u32 len``
+ Length of provided user buffer to hold the log data in bytes
+
+``void *log``
+ User address for log page data
+
+**Description**
+
+Retrieves the Telemetry Controller-Initiated log page at the requested offset
+using the previously existing capture.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_endurance_group (nvme_mi_ctrl_t ctrl, __u16 endgid, struct nvme_endurance_group_log *log)
+
+ Get Endurance Group log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u16 endgid``
+ Starting group identifier to return in the list
+
+``struct nvme_endurance_group_log *log``
+ User address to store the endurance log
+
+**Description**
+
+This log page indicates if an Endurance Group Event has occurred for a
+particular Endurance Group. If an Endurance Group Event has occurred, the
+details of the particular event are included in the Endurance Group
+Information log page for that Endurance Group. An asynchronous event is
+generated when an entry for an Endurance Group is newly added to this log
+page.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_predictable_lat_nvmset (nvme_mi_ctrl_t ctrl, __u16 nvmsetid, struct nvme_nvmset_predictable_lat_log *log)
+
+ Predictable Latency Per NVM Set
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u16 nvmsetid``
+ NVM set id
+
+``struct nvme_nvmset_predictable_lat_log *log``
+ User address to store the predictable latency log
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_predictable_lat_event (nvme_mi_ctrl_t ctrl, bool rae, __u32 offset, __u32 len, void *log)
+
+ Retrieve Predictable Latency Event Aggregate Log Page
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``__u32 offset``
+ Offset into the predictable latency event
+
+``__u32 len``
+ Length of provided user buffer to hold the log data in bytes
+
+``void *log``
+ User address for log page data
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_ana (nvme_mi_ctrl_t ctrl, enum nvme_log_ana_lsp lsp, bool rae, __u64 offset, __u32 len, void *log)
+
+ Retrieve Asymmetric Namespace Access log page
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``enum nvme_log_ana_lsp lsp``
+ Log specific, see :c:type:`enum nvme_get_log_ana_lsp <nvme_get_log_ana_lsp>`
+
+``bool rae``
+ Retain asynchronous events
+
+``__u64 offset``
+ Offset to the start of the log page
+
+``__u32 len``
+ The allocated length of the log page
+
+``void *log``
+ User address to store the ana log
+
+**Description**
+
+This log consists of a header describing the log and descriptors containing
+the asymmetric namespace access information for ANA Groups that contain
+namespaces that are attached to the controller processing the command.
+
+See :c:type:`struct nvme_ana_rsp_hdr <nvme_ana_rsp_hdr>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_ana_groups (nvme_mi_ctrl_t ctrl, bool rae, __u32 len, struct nvme_ana_group_desc *log)
+
+ Retrieve Asymmetric Namespace Access groups only log page
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``__u32 len``
+ The allocated length of the log page
+
+``struct nvme_ana_group_desc *log``
+ User address to store the ana group log
+
+**Description**
+
+See :c:type:`struct nvme_ana_group_desc <nvme_ana_group_desc>` for the definition of the returned structure.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_lba_status (nvme_mi_ctrl_t ctrl, bool rae, __u64 offset, __u32 len, void *log)
+
+ Retrieve LBA Status
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``__u64 offset``
+ Offset to the start of the log page
+
+``__u32 len``
+ The allocated length of the log page
+
+``void *log``
+ User address to store the log page
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_endurance_grp_evt (nvme_mi_ctrl_t ctrl, bool rae, __u32 offset, __u32 len, void *log)
+
+ Retrieve Rotational Media Information
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``__u32 offset``
+ Offset to the start of the log page
+
+``__u32 len``
+ The allocated length of the log page
+
+``void *log``
+ User address to store the log page
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_fid_supported_effects (nvme_mi_ctrl_t ctrl, bool rae, struct nvme_fid_supported_effects_log *log)
+
+ Retrieve Feature Identifiers Supported and Effects
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_fid_supported_effects_log *log``
+ FID Supported and Effects data structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_mi_cmd_supported_effects (nvme_mi_ctrl_t ctrl, bool rae, struct nvme_mi_cmd_supported_effects_log *log)
+
+ displays the MI Commands Supported by the controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_mi_cmd_supported_effects_log *log``
+ MI Command Supported and Effects data structure
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_boot_partition (nvme_mi_ctrl_t ctrl, bool rae, __u8 lsp, __u32 len, struct nvme_boot_partition *part)
+
+ Retrieve Boot Partition
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``__u8 lsp``
+ The log specified field of LID
+
+``__u32 len``
+ The allocated size, minimum
+ struct nvme_boot_partition
+
+``struct nvme_boot_partition *part``
+ User address to store the log page
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_phy_rx_eom (nvme_mi_ctrl_t ctrl, __u8 lsp, __u16 controller, __u32 len, struct nvme_phy_rx_eom_log *log)
+
+ Retrieve Physical Interface Receiver Eye Opening Measurement Log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u8 lsp``
+ Log specific, controls action and measurement quality
+
+``__u16 controller``
+ Target controller ID
+
+``__u32 len``
+ The allocated size, minimum
+ struct nvme_phy_rx_eom_log
+
+``struct nvme_phy_rx_eom_log *log``
+ User address to store the log page
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise
+
+
+.. c:function:: int nvme_mi_admin_get_log_discovery (nvme_mi_ctrl_t ctrl, bool rae, __u32 offset, __u32 len, void *log)
+
+ Retrieve Discovery log page
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``__u32 offset``
+ Offset of this log to retrieve
+
+``__u32 len``
+ The allocated size for this portion of the log
+
+``void *log``
+ User address to store the discovery log
+
+**Description**
+
+Supported only by fabrics discovery controllers, returning discovery
+records.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_media_unit_stat (nvme_mi_ctrl_t ctrl, __u16 domid, struct nvme_media_unit_stat_log *mus)
+
+ Retrieve Media Unit Status
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u16 domid``
+ Domain Identifier selection, if supported
+
+``struct nvme_media_unit_stat_log *mus``
+ User address to store the Media Unit statistics log
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_support_cap_config_list (nvme_mi_ctrl_t ctrl, __u16 domid, struct nvme_supported_cap_config_list_log *cap)
+
+ Retrieve Supported Capacity Configuration List
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u16 domid``
+ Domain Identifier selection, if supported
+
+``struct nvme_supported_cap_config_list_log *cap``
+ User address to store supported capabilities config list
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_reservation (nvme_mi_ctrl_t ctrl, bool rae, struct nvme_resv_notification_log *log)
+
+ Retrieve Reservation Notification
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_resv_notification_log *log``
+ User address to store the reservation log
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_sanitize (nvme_mi_ctrl_t ctrl, bool rae, struct nvme_sanitize_log_page *log)
+
+ Retrieve Sanitize Status
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_sanitize_log_page *log``
+ User address to store the sanitize log
+
+**Description**
+
+The Sanitize Status log page reports sanitize operation time estimates and
+information about the most recent sanitize operation.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_zns_changed_zones (nvme_mi_ctrl_t ctrl, __u32 nsid, bool rae, struct nvme_zns_changed_zone_log *log)
+
+ Retrieve list of zones that have changed
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``__u32 nsid``
+ Namespace ID
+
+``bool rae``
+ Retain asynchronous events
+
+``struct nvme_zns_changed_zone_log *log``
+ User address to store the changed zone log
+
+**Description**
+
+The list of zones that have changed state due to an exceptional event.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_log_persistent_event (nvme_mi_ctrl_t ctrl, enum nvme_pevent_log_action action, __u32 size, void *pevent_log)
+
+ Retrieve Persistent Event Log
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to query
+
+``enum nvme_pevent_log_action action``
+ Action the controller should take during processing this command
+
+``__u32 size``
+ Size of **pevent_log**
+
+``void *pevent_log``
+ User address to store the persistent event log
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_security_send (nvme_mi_ctrl_t ctrl, struct nvme_security_send_args *args)
+
+ Perform a Security Send command on a controller.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_security_send_args *args``
+ Security Send command arguments
+
+**Description**
+
+Performs a Security Send Admin command as specified by **args**. Response data
+is stored in **args->data**, which should be a buffer of **args->data_len** bytes.
+Resulting data length is stored in **args->data_len** on successful
+command completion.
+
+Security Send data length should not be greater than 4096 bytes to
+comply with specification limits.
+
+See: :c:type:`struct nvme_get_log_args <nvme_get_log_args>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_security_recv (nvme_mi_ctrl_t ctrl, struct nvme_security_receive_args *args)
+
+ Perform a Security Receive command on a controller.
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_security_receive_args *args``
+ Security Receive command arguments
+
+**Description**
+
+Performs a Security Receive Admin command as specified by **args**. Response
+data is stored in **args->data**, which should be a buffer of **args->data_len**
+bytes. Resulting data length is stored in **args->data_len** on successful
+command completion.
+
+Security Receive data length should not be greater than 4096 bytes to
+comply with specification limits.
+
+See: :c:type:`struct nvme_get_log_args <nvme_get_log_args>`
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_features (nvme_mi_ctrl_t ctrl, struct nvme_get_features_args *args)
+
+ Perform a Get Feature command on a controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_get_features_args *args``
+ Get Features command arguments
+
+**Description**
+
+Performs a Get Features Admin command as specified by **args**. Returned
+feature data will be stored in **args->result** and **args->data**, depending
+on the specification of the feature itself; most features do not return
+additional data. See section 5.27.1 of the NVMe spec (v2.0b) for
+feature-specific information.
+
+On success, **args->data_len** will be updated with the actual data length
+received.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_features_data (nvme_mi_ctrl_t ctrl, enum nvme_features_id fid, __u32 nsid, __u32 data_len, void *data, __u32 *result)
+
+ Helper function for :c:type:`nvme_mi_admin_get_features`()
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``enum nvme_features_id fid``
+ Feature identifier
+
+``__u32 nsid``
+ Namespace ID, if applicable for **fid**
+
+``__u32 data_len``
+ Length of feature data, if applicable for **fid**, in bytes
+
+``void *data``
+ User address of feature data, if applicable
+
+``__u32 *result``
+ The command completion result from CQE dword0
+
+**Description**
+
+Helper for optionally features that optionally return data, using the
+SEL_CURRENT selector value.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_get_features_simple (nvme_mi_ctrl_t ctrl, enum nvme_features_id fid, __u32 nsid, __u32 *result)
+
+ Get a simple feature value with no data
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``enum nvme_features_id fid``
+ Feature identifier
+
+``__u32 nsid``
+ Namespace id, if required by **fid**
+
+``__u32 *result``
+ output feature data
+
+
+.. c:function:: int nvme_mi_admin_set_features (nvme_mi_ctrl_t ctrl, struct nvme_set_features_args *args)
+
+ Perform a Set Features command on a controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_set_features_args *args``
+ Set Features command arguments
+
+**Description**
+
+Performs a Set Features Admin command as specified by **args**. Result
+data will be stored in **args->result**.
+on the specification of the feature itself; most features do not return
+additional data. See section 5.27.1 of the NVMe spec (v2.0b) for
+feature-specific information.
+
+On success, **args->data_len** will be updated with the actual data length
+received.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_ns_mgmt (nvme_mi_ctrl_t ctrl, struct nvme_ns_mgmt_args *args)
+
+ Issue a Namespace Management command
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_ns_mgmt_args *args``
+ Namespace management command arguments
+
+**Description**
+
+Issues a Namespace Management command to **ctrl**, with arguments specified
+from **args**.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_ns_mgmt_create (nvme_mi_ctrl_t ctrl, struct nvme_id_ns *ns, __u8 csi, __u32 *nsid, struct nvme_ns_mgmt_host_sw_specified *data)
+
+ Helper for Namespace Management Create command
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_id_ns *ns``
+ New namespace parameters
+
+``__u8 csi``
+ Command Set Identifier for new NS
+
+``__u32 *nsid``
+ Set to new namespace ID on create
+
+``struct nvme_ns_mgmt_host_sw_specified *data``
+ Host Software Specified Fields that defines ns creation parameters
+
+**Description**
+
+Issues a Namespace Management (Create) command to **ctrl**, to create a
+new namespace specified by **ns**, using command set **csi**. On success,
+the new namespace ID will be written to **nsid**.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_ns_mgmt_delete (nvme_mi_ctrl_t ctrl, __u32 nsid)
+
+ Helper for Namespace Management Delete command
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``__u32 nsid``
+ Namespace ID to delete
+
+**Description**
+
+Issues a Namespace Management (Delete) command to **ctrl**, to delete the
+namespace with id **nsid**.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_ns_attach (nvme_mi_ctrl_t ctrl, struct nvme_ns_attach_args *args)
+
+ Attach or detach namespace to controller(s)
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_ns_attach_args *args``
+ Namespace Attach command arguments
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_ns_attach_ctrls (nvme_mi_ctrl_t ctrl, __u32 nsid, struct nvme_ctrl_list *ctrlist)
+
+ Attach namespace to controllers
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``__u32 nsid``
+ Namespace ID to attach
+
+``struct nvme_ctrl_list *ctrlist``
+ Controller list to modify attachment state of nsid
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_ns_detach_ctrls (nvme_mi_ctrl_t ctrl, __u32 nsid, struct nvme_ctrl_list *ctrlist)
+
+ Detach namespace from controllers
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``__u32 nsid``
+ Namespace ID to detach
+
+``struct nvme_ctrl_list *ctrlist``
+ Controller list to modify attachment state of nsid
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_fw_download (nvme_mi_ctrl_t ctrl, struct nvme_fw_download_args *args)
+
+ Download part or all of a firmware image to the controller
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send firmware data to
+
+``struct nvme_fw_download_args *args``
+ :c:type:`struct nvme_fw_download_args <nvme_fw_download_args>` argument structure
+
+**Description**
+
+The Firmware Image Download command downloads all or a portion of an image
+for a future update to the controller. The Firmware Image Download command
+downloads a new image (in whole or in part) to the controller.
+
+The image may be constructed of multiple pieces that are individually
+downloaded with separate Firmware Image Download commands. Each Firmware
+Image Download command includes a Dword Offset and Number of Dwords that
+specify a dword range.
+
+The new firmware image is not activated as part of the Firmware Image
+Download command. Use the nvme_mi_admin_fw_commit() to activate a newly
+downloaded image.
+
+**Return**
+
+0 on success, non-zero on failure
+
+
+.. c:function:: int nvme_mi_admin_fw_commit (nvme_mi_ctrl_t ctrl, struct nvme_fw_commit_args *args)
+
+ Commit firmware using the specified action
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send firmware data to
+
+``struct nvme_fw_commit_args *args``
+ :c:type:`struct nvme_fw_download_args <nvme_fw_download_args>` argument structure
+
+**Description**
+
+The Firmware Commit command modifies the firmware image or Boot Partitions.
+
+**Return**
+
+0 on success, non-zero on failure
+
+
+.. c:function:: int nvme_mi_admin_format_nvm (nvme_mi_ctrl_t ctrl, struct nvme_format_nvm_args *args)
+
+ Format NVMe namespace
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_format_nvm_args *args``
+ Format NVM command arguments
+
+**Description**
+
+Perform a low-level format to set the LBA data & metadata size. May destroy
+data & metadata on the specified namespaces
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
+.. c:function:: int nvme_mi_admin_sanitize_nvm (nvme_mi_ctrl_t ctrl, struct nvme_sanitize_nvm_args *args)
+
+ Start a subsystem Sanitize operation
+
+**Parameters**
+
+``nvme_mi_ctrl_t ctrl``
+ Controller to send command to
+
+``struct nvme_sanitize_nvm_args *args``
+ Sanitize command arguments
+
+**Description**
+
+A sanitize operation alters all user data in the NVM subsystem such that
+recovery of any previous user data from any cache, the non-volatile media,
+or any Controller Memory Buffer is not possible.
+
+The Sanitize command starts a sanitize operation or to recover from a
+previously failed sanitize operation. The sanitize operation types that may
+be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize
+operations are processed in the background, i.e., completion of the sanitize
+command does not indicate completion of the sanitize operation.
+
+**Return**
+
+The nvme command status if a response was received (see
+:c:type:`enum nvme_status_field <nvme_status_field>`) or -1 with errno set otherwise.
+
+
diff --git a/doc/rst/nbft.rst b/doc/rst/nbft.rst
new file mode 100644
index 0000000..93a3642
--- /dev/null
+++ b/doc/rst/nbft.rst
@@ -0,0 +1,1870 @@
+
+
+.. c:enum:: nbft_desc_type
+
+ NBFT Elements - Descriptor Types (Figure 5)
+
+**Constants**
+
+``NBFT_DESC_HEADER``
+ Header: an ACPI structure header with some additional
+ NBFT specific info.
+
+``NBFT_DESC_CONTROL``
+ Control Descriptor: indicates the location of host,
+ HFI, SSNS, security, and discovery descriptors.
+
+``NBFT_DESC_HOST``
+ Host Descriptor: host information.
+
+``NBFT_DESC_HFI``
+ HFI Descriptor: an indexable table of HFI Descriptors,
+ one for each fabric interface on the host.
+
+``NBFT_DESC_SSNS``
+ Subsystem Namespace Descriptor: an indexable table
+ of SSNS Descriptors.
+
+``NBFT_DESC_SECURITY``
+ Security Descriptor: an indexable table of Security
+ descriptors.
+
+``NBFT_DESC_DISCOVERY``
+ Discovery Descriptor: an indexable table of Discovery
+ Descriptors.
+
+``NBFT_DESC_HFI_TRINFO``
+ HFI Transport Descriptor: indicated by an HFI Descriptor,
+ corresponds to a specific transport for a single HFI.
+
+``NBFT_DESC_RESERVED_8``
+ Reserved.
+
+``NBFT_DESC_SSNS_EXT_INFO``
+ SSNS Extended Info Descriptor: indicated by an SSNS
+ Descriptor if required.
+
+
+
+
+.. c:enum:: nbft_trtype
+
+ NBFT Interface Transport Types (Figure 7)
+
+**Constants**
+
+``NBFT_TRTYPE_TCP``
+ NVMe/TCP (802.3 + TCP/IP). String Designator "tcp".
+
+
+
+
+.. c:struct:: nbft_heap_obj
+
+ NBFT Header Driver Signature
+
+**Definition**
+
+::
+
+ struct nbft_heap_obj {
+ __le32 offset;
+ __le16 length;
+ };
+
+**Members**
+
+``offset``
+ Offset in bytes of the heap object, if any, from byte offset 0h
+ of the NBFT Table Header.
+
+``length``
+ Length in bytes of the heap object, if any.
+
+
+
+
+
+.. c:struct:: nbft_header
+
+ NBFT Table - Header (Figure 8)
+
+**Definition**
+
+::
+
+ struct nbft_header {
+ char signature[4];
+ __le32 length;
+ __u8 major_revision;
+ __u8 checksum;
+ char oem_id[6];
+ char oem_table_id[8];
+ __le32 oem_revision;
+ __le32 creator_id;
+ __le32 creator_revision;
+ __le32 heap_offset;
+ __le32 heap_length;
+ struct nbft_heap_obj driver_dev_path_sig;
+ __u8 minor_revision;
+ __u8 reserved[13];
+ };
+
+**Members**
+
+``signature``
+ Signature: An ASCII string representation of the table
+ identifier. This field shall be set to the value 4E424654h
+ (i.e. "NBFT", see #NBFT_HEADER_SIG).
+
+``length``
+ Length: The length of the table, in bytes, including the
+ header, starting from offset 0h. This field is used to record
+ the size of the entire table.
+
+``major_revision``
+ Major Revision: The major revision of the structure
+ corresponding to the Signature field. Larger major revision
+ numbers should not be assumed backward compatible to lower
+ major revision numbers with the same signature.
+
+``checksum``
+ Checksum: The entire table, including the Checksum field,
+ shall sum to 0h to be considered valid.
+
+``oem_id``
+ OEMID shall be populated by the NBFT driver writer by
+ an OEM-supplied string that identifies the OEM. All
+ trailing bytes shall be NULL.
+
+``oem_table_id``
+ OEM Table ID: This field shall be populated by the NBFT
+ driver writer with an OEM-supplied string that the OEM
+ uses to identify the particular data table. This field is
+ particularly useful when defining a definition block to
+ distinguish definition block functions. The OEM assigns
+ each dissimilar table a new OEM Table ID.
+
+``oem_revision``
+ OEM Revision: An OEM-supplied revision number. Larger
+ numbers are assumed to be newer revisions.
+
+``creator_id``
+ Creator ID: Vendor ID of utility that created the table.
+ For instance, this may be the ID for the ASL Compiler.
+
+``creator_revision``
+ Creator Revision: Revision of utility that created the
+ table. For instance, this may be the ID for the ASL Compiler.
+
+``heap_offset``
+ Heap Offset (HO): This field indicates the offset in bytes
+ of the heap, if any, from byte offset 0h of the NBFT
+ Table Header.
+
+``heap_length``
+ Heap Length (HL): The length of the heap, if any.
+
+``driver_dev_path_sig``
+ Driver Signature Heap Object Reference: This field indicates
+ the offset in bytes of a heap object containing the Driver
+ Signature, if any, from byte offset 0h of the NBFT Table
+ Header.
+
+``minor_revision``
+ Minor Revision: The minor revision of the structure
+ corresponding to the Signature field. If the major revision
+ numbers are the same, any minor revision number differences
+ shall be backwards compatible with the same signature.
+
+``reserved``
+ Reserved.
+
+
+
+
+
+.. c:struct:: nbft_control
+
+ NBFT Table - Control Descriptor (Figure 8)
+
+**Definition**
+
+::
+
+ struct nbft_control {
+ __u8 structure_id;
+ __u8 major_revision;
+ __u8 minor_revision;
+ __u8 reserved1;
+ __le16 csl;
+ __u8 flags;
+ __u8 reserved2;
+ struct nbft_heap_obj hdesc;
+ __u8 hsv;
+ __u8 reserved3;
+ __le32 hfio;
+ __le16 hfil;
+ __u8 hfiv;
+ __u8 num_hfi;
+ __le32 ssnso;
+ __le16 ssnsl;
+ __u8 ssnsv;
+ __u8 num_ssns;
+ __le32 seco;
+ __le16 secl;
+ __u8 secv;
+ __u8 num_sec;
+ __le32 disco;
+ __le16 discl;
+ __u8 discv;
+ __u8 num_disc;
+ __u8 reserved4[16];
+ };
+
+**Members**
+
+``structure_id``
+ Structure ID: This field specifies the element (refer to
+ :c:type:`enum nbft_desc_type <nbft_desc_type>`). This field shall be set to 1h (i.e.,
+ Control, #NBFT_DESC_CONTROL).
+
+``major_revision``
+ Major Revision: The major revision of the structure corresponding
+ to the Signature field. Larger major revision numbers should
+ not be assumed backward compatible to lower major revision
+ numbers with the same signature.
+
+``minor_revision``
+ Minor Revision: The minor revision of the structure corresponding
+ to the signature field. If the major revision numbers are
+ the same, any minor revision number differences shall be backwards
+ compatible with the same signature.
+
+``reserved1``
+ Reserved.
+
+``csl``
+ Control Structure Length (CSL): This field indicates the length
+ in bytes of the Control Descriptor.
+
+``flags``
+ Flags, see :c:type:`enum nbft_control_flags <nbft_control_flags>`.
+
+``reserved2``
+ Reserved.
+
+``hdesc``
+ Host Descriptor (HDESC): This field indicates the location
+ and length of the Host Descriptor (see :c:type:`struct nbft_host <nbft_host>`).
+
+``hsv``
+ Host Descriptor Version (HSV): This field indicates the version
+ of the Host Descriptor.
+
+``reserved3``
+ Reserved.
+
+``hfio``
+ HFI Descriptor List Offset (HFIO): If this field is set to
+ a non-zero value, then this field indicates the offset in bytes
+ of the HFI Descriptor List, if any, from byte offset 0h of the
+ NBFT Table Header. If the **num_hfi** field is cleared to 0h,
+ then this field is reserved.
+
+``hfil``
+ HFI Descriptor Length (HFIL): This field indicates the length
+ in bytes of each HFI Descriptor, if any. If the **num_hfi** field
+ is cleared to 0h, then this field is reserved.
+
+``hfiv``
+ HFI Descriptor Version (HFIV): This field indicates the version
+ of each HFI Descriptor.
+
+``num_hfi``
+ Number of Host Fabric Interface Descriptors (NumHFI): This field
+ indicates the number of HFI Descriptors (see :c:type:`struct nbft_hfi <nbft_hfi>`)
+ in the HFI Descriptor List, if any. If no interfaces have been
+ configured, then this field shall be cleared to 0h.
+
+``ssnso``
+ SSNS Descriptor List Offset (SSNSO):: This field indicates
+ the offset in bytes of the SSNS Descriptor List, if any, from
+ byte offset 0h of the NBFT Table Header. If the **num_ssns** field
+ is cleared to 0h, then this field is reserved.
+
+``ssnsl``
+ SSNS Descriptor Length (SSNSL): This field indicates the length
+ in bytes of each SSNS Descriptor, if any. If the **num_ssns**
+ field is cleared to 0h, then this field is reserved.
+
+``ssnsv``
+ SSNS Descriptor Version (SSNSV): This field indicates the version
+ of the SSNS Descriptor.
+
+``num_ssns``
+ Number of Subsystem and Namespace Descriptors (NumSSNS): This
+ field indicates the number of Subsystem Namespace (SSNS)
+ Descriptors (see :c:type:`struct nbft_ssns <nbft_ssns>`) in the SSNS Descriptor List,
+ if any.
+
+``seco``
+ Security Profile Descriptor List Offset (SECO): This field
+ indicates the offset in bytes of the Security Profile Descriptor
+ List, if any, from byte offset 0h of the NBFT Table Header.
+ If the **num_sec** field is cleared to 0h, then this field
+ is reserved.
+
+``secl``
+ Security Profile Descriptor Length (SECL): This field indicates
+ the length in bytes of each Security Profile Descriptor, if any.
+ If the **num_sec** field is cleared to 0h, then this field
+ is reserved.
+
+``secv``
+ Security Profile Descriptor Version (SECV): This field indicates
+ the version of the Security Profile Descriptor.
+
+``num_sec``
+ Number of Security Profile Descriptors (NumSec): This field
+ indicates the number of Security Profile Descriptors
+ (see :c:type:`struct nbft_security <nbft_security>`), if any, in the Security Profile
+ Descriptor List.
+
+``disco``
+ Discovery Descriptor Offset (DISCO): This field indicates
+ the offset in bytes of the Discovery Descriptor List, if any,
+ from byte offset 0h of the NBFT Table Header. If the **num_disc**
+ field is cleared to 0h, then this field is reserved.
+
+``discl``
+ Discovery Descriptor Length (DISCL): This field indicates
+ the length in bytes of each Discovery Descriptor, if any.
+ If the **num_disc** field is cleared to 0h, then this field
+ is reserved.
+
+``discv``
+ Discovery Descriptor Version (DISCV): This field indicates
+ the version of the Discovery Descriptor.
+
+``num_disc``
+ Number of Discovery Descriptors (NumDisc): This field indicates
+ the number of Discovery Descriptors (see :c:type:`struct nbft_discovery <nbft_discovery>`),
+ if any, in the Discovery Descriptor List, if any.
+
+``reserved4``
+ Reserved.
+
+
+
+
+
+.. c:enum:: nbft_control_flags
+
+ Control Descriptor Flags
+
+**Constants**
+
+``NBFT_CONTROL_VALID``
+ Block Valid: indicates that the structure is valid.
+
+
+
+
+.. c:struct:: nbft_host
+
+ Host Descriptor (Figure 9)
+
+**Definition**
+
+::
+
+ struct nbft_host {
+ __u8 structure_id;
+ __u8 flags;
+ __u8 host_id[16];
+ struct nbft_heap_obj host_nqn_obj;
+ __u8 reserved[8];
+ };
+
+**Members**
+
+``structure_id``
+ Structure ID: This field shall be set to 2h (i.e.,
+ Host Descriptor; #NBFT_DESC_HOST).
+
+``flags``
+ Host Flags, see :c:type:`enum nbft_host_flags <nbft_host_flags>`.
+
+``host_id``
+ Host ID: This field shall be set to the Host Identifier. This
+ field shall not be empty if the NBFT and NVMe Boot are supported
+ by the Platform.
+
+``host_nqn_obj``
+ Host NQN Heap Object Reference: this field indicates a heap
+ object containing a Host NQN. This object shall not be empty
+ if the NBFT and NVMe Boot are supported by the Platform.
+
+``reserved``
+ Reserved.
+
+
+
+
+
+.. c:enum:: nbft_host_flags
+
+ Host Flags
+
+**Constants**
+
+``NBFT_HOST_VALID``
+ Descriptor Valid: If set to 1h, then this
+ descriptor is valid. If cleared to 0h, then
+ this descriptor is reserved.
+
+``NBFT_HOST_HOSTID_CONFIGURED``
+ HostID Configured: If set to 1h, then the
+ Host ID field contains an administratively-configured
+ value. If cleared to 0h, then the Host ID
+ field contains a driver default value.
+
+``NBFT_HOST_HOSTNQN_CONFIGURED``
+ Host NQN Configured: If set to 1h, then the
+ Host NQN indicated by the Host NQN Heap Object
+ Reference field (:c:type:`struct nbft_host <nbft_host>`.host_nqn)
+ contains an administratively-configured value.
+ If cleared to 0h, then the Host NQN indicated
+ by the Host NQN Offset field contains a driver
+ default value.
+
+``NBFT_HOST_PRIMARY_ADMIN_MASK``
+ Mask to get Primary Administrative Host Descriptor:
+ indicates whether the Host Descriptor in this
+ NBFT was selected as the primary NBFT for
+ administrative purposes of platform identity
+ as a hint to the OS. If multiple NBFT tables
+ are present, only one NBFT should be administratively
+ selected. There is no enforcement mechanism
+ for this to be coordinated between multiple NBFT
+ tables, but this field should be set to Selected
+ (#NBFT_HOST_PRIMARY_ADMIN_SELECTED) if
+ more than one NBFT is present.
+
+``NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED``
+ Not Indicated by Driver: The driver that created
+ this NBFT provided no administrative priority
+ hint for this NBFT.
+
+``NBFT_HOST_PRIMARY_ADMIN_UNSELECTED``
+ Unselected: The driver that created this NBFT
+ explicitly indicated that this NBFT should
+ not be prioritized over any other NBFT.
+
+``NBFT_HOST_PRIMARY_ADMIN_SELECTED``
+ Selected: The driver that created this NBFT
+ explicitly indicated that this NBFT should
+ be prioritized over any other NBFT.
+
+
+
+
+.. c:struct:: nbft_hfi
+
+ Host Fabric Interface (HFI) Descriptor (Figure 11)
+
+**Definition**
+
+::
+
+ struct nbft_hfi {
+ __u8 structure_id;
+ __u8 index;
+ __u8 flags;
+ __u8 trtype;
+ __u8 reserved1[12];
+ struct nbft_heap_obj trinfo_obj;
+ __u8 reserved2[10];
+ };
+
+**Members**
+
+``structure_id``
+ Structure ID: This field shall be set to 3h (i.e., Host Fabric
+ Interface Descriptor; #NBFT_DESC_HFI).
+
+``index``
+ HFI Descriptor Index: This field indicates the number of this
+ HFI Descriptor in the Host Fabric Interface Descriptor List.
+
+``flags``
+ HFI Descriptor Flags, see :c:type:`enum nbft_hfi_flags <nbft_hfi_flags>`.
+
+``trtype``
+ HFI Transport Type, see :c:type:`enum nbft_trtype <nbft_trtype>`.
+
+``reserved1``
+ Reserved.
+
+``trinfo_obj``
+ HFI Transport Info Descriptor Heap Object Reference: If this
+ field is set to a non-zero value, then this field indicates
+ the location and size of a heap object containing
+ a HFI Transport Info.
+
+``reserved2``
+ Reserved.
+
+
+
+
+
+.. c:enum:: nbft_hfi_flags
+
+ HFI Descriptor Flags
+
+**Constants**
+
+``NBFT_HFI_VALID``
+ Descriptor Valid: If set to 1h, then this descriptor is valid.
+ If cleared to 0h, then this descriptor is reserved.
+
+
+
+
+.. c:struct:: nbft_hfi_info_tcp
+
+ HFI Transport Info Descriptor - NVMe/TCP (Figure 13)
+
+**Definition**
+
+::
+
+ struct nbft_hfi_info_tcp {
+ __u8 structure_id;
+ __u8 version;
+ __u8 trtype;
+ __u8 trinfo_version;
+ __le16 hfi_index;
+ __u8 flags;
+ __le32 pci_sbdf;
+ __u8 mac_addr[6];
+ __le16 vlan;
+ __u8 ip_origin;
+ __u8 ip_address[16];
+ __u8 subnet_mask_prefix;
+ __u8 ip_gateway[16];
+ __u8 reserved1;
+ __le16 route_metric;
+ __u8 primary_dns[16];
+ __u8 secondary_dns[16];
+ __u8 dhcp_server[16];
+ struct nbft_heap_obj host_name_obj;
+ __u8 reserved2[18];
+ };
+
+**Members**
+
+``structure_id``
+ Structure ID: This field shall be set to 7h (i.e.,
+ HFI Transport Info; #NBFT_DESC_HFI_TRINFO).
+
+``version``
+ Version: This field shall be set to 1h.
+
+``trtype``
+ HFI Transport Type, see :c:type:`enum nbft_trtype <nbft_trtype>`: This field
+ shall be set to 03h (i.e., NVMe/TCP; #NBFT_TRTYPE_TCP).
+
+``trinfo_version``
+ Transport Info Version: Implementations compliant to this
+ specification shall set this field to 1h.
+
+``hfi_index``
+ HFI Descriptor Index: The value of the HFI Descriptor Index
+ field of the HFI Descriptor (see :c:type:`struct nbft_hfi <nbft_hfi>`.index)
+ whose HFI Transport Info Descriptor Heap Object Reference
+ field indicates this HFI Transport Info Descriptor.
+
+``flags``
+ HFI Transport Flags, see :c:type:`enum nbft_hfi_info_tcp_flags <nbft_hfi_info_tcp_flags>`.
+
+``pci_sbdf``
+ PCI Express Routing ID for the HFI Transport Function:
+ This field indicates the PCI Express Routing ID as specified
+ in the PCI Express Base Specification.
+
+``mac_addr``
+ MAC Address: The MAC address of this HFI, in EUI-48TM format,
+ as defined in the IEEE Guidelines for Use of Extended Unique
+ Identifiers. This field shall be set to a non-zero value.
+
+``vlan``
+ VLAN: If this field is set to a non-zero value, then this
+ field contains the VLAN identifier if the VLAN associated
+ with this HFI, as defined in IEEE 802.1q-2018. If no VLAN
+ is associated with this HFI, then this field shall be cleared
+ to 0h.
+
+``ip_origin``
+ IP Origin: If this field is set to a non-zero value, then
+ this field indicates the source of Ethernet L3 configuration
+ information used by the driver for this interface. Valid
+ values are defined in the Win 32 API: NL_PREFIX_ORIGIN
+ enumeration specification. This field should be cleared
+ to 0h if the IP Origin field is unused by driver.
+
+``ip_address``
+ IP Address: This field indicates the IPv4 or IPv6 address
+ of this HFI. This field shall be set to a non-zero value.
+
+``subnet_mask_prefix``
+ Subnet Mask Prefix: This field indicates the IPv4 or IPv6
+ subnet mask in CIDR routing prefix notation.
+
+``ip_gateway``
+ IP Gateway: If this field is set to a non-zero value, this
+ field indicates the IPv4 or IPv6 address of the IP gateway
+ for this HFI. If this field is cleared to 0h, then
+ no IP gateway is specified.
+
+``reserved1``
+ Reserved.
+
+``route_metric``
+ Route Metric: If this field is set to a non-zero value,
+ this field indicates the cost value for the route indicated
+ by this HF. This field contains the value utilized by the
+ pre-OS driver when chosing among all available routes. Lower
+ values relate to higher priority. Refer to IETF RFC 4249.
+ If the pre-OS driver supports routing and did not configure
+ a specific route metric for this interface, then the pre-OS
+ driver should set this value to 500. If the pre-OS driver
+ does not support routing, then this field should be cleared
+ to 0h.
+
+``primary_dns``
+ Primary DNS: If this field is set to a non-zero value,
+ this field indicates the IPv4 or IPv6 address of the
+ Primary DNS server for this HFI, if any, from byte offset
+ 0h of the NBFT Table Header. If this field is cleared to 0h,
+ then no Primary DNS is specified.
+
+``secondary_dns``
+ Secondary DNS: If this field is set to a non-zero value,
+ this field indicates the IPv4 or IPv6 address of
+ the Secondary DNS server for this HFI, if any, from byte
+ offset 0h of the NBFT Table Header. If this field is
+ cleared to 0h, then no Secondary DNS is specified.
+
+``dhcp_server``
+ DHCP Server: If the DHCP Override bit is set to 1h, then
+ this field indicates the IPv4 or IPv6 address of the DHCP
+ server used to assign this HFI address. If that bit is
+ cleared to 0h, then this field is reserved.
+
+``host_name_obj``
+ Host Name Heap Object Reference: If this field is set
+ to a non-zero value, then this field indicates the location
+ and size of a heap object containing a Host Name string.
+
+``reserved2``
+ Reserved.
+
+
+
+
+
+.. c:enum:: nbft_hfi_info_tcp_flags
+
+ HFI Transport Flags
+
+**Constants**
+
+``NBFT_HFI_INFO_TCP_VALID``
+ Descriptor Valid: if set to 1h, then this
+ descriptor is valid. If cleared to 0h, then
+ this descriptor is reserved.
+
+``NBFT_HFI_INFO_TCP_GLOBAL_ROUTE``
+ Global Route vs. Link Local Override Flag:
+ if set to 1h, then the BIOS utilized this
+ interface described by HFI to be the default
+ route with highest priority. If cleared to 0h,
+ then routes are local to their own scope.
+
+``NBFT_HFI_INFO_TCP_DHCP_OVERRIDE``
+ DHCP Override: if set to 1, then HFI information
+ was populated by consuming the DHCP on this
+ interface. If cleared to 0h, then the HFI
+ information was set administratively by
+ a configuration interface to the driver and
+ pre-OS envrionment.
+
+
+
+
+.. c:struct:: nbft_ssns
+
+ Subsystem Namespace (SSNS) Descriptor (Figure 15)
+
+**Definition**
+
+::
+
+ struct nbft_ssns {
+ __u8 structure_id;
+ __le16 index;
+ __le16 flags;
+ __u8 trtype;
+ __le16 trflags;
+ __u8 primary_discovery_ctrl_index;
+ __u8 reserved1;
+ struct nbft_heap_obj subsys_traddr_obj;
+ struct nbft_heap_obj subsys_trsvcid_obj;
+ __le16 subsys_port_id;
+ __le32 nsid;
+ __u8 nidt;
+ __u8 nid[16];
+ __u8 security_desc_index;
+ __u8 primary_hfi_desc_index;
+ __u8 reserved2;
+ struct nbft_heap_obj secondary_hfi_assoc_obj;
+ struct nbft_heap_obj subsys_ns_nqn_obj;
+ struct nbft_heap_obj ssns_extended_info_desc_obj;
+ __u8 reserved3[62];
+ };
+
+**Members**
+
+``structure_id``
+ Structure ID: This field shall be set to 4h
+ (i.e., SSNS; #NBFT_DESC_SSNS).
+
+``index``
+ SSNS Descriptor Index: This field indicates the number
+ of this Subsystem Namespace Descriptor in the
+ Subsystem Namespace Descriptor List.
+
+``flags``
+ SSNS Flags, see :c:type:`enum nbft_ssns_flags <nbft_ssns_flags>`.
+
+``trtype``
+ Transport Type, see :c:type:`enum nbft_trtype <nbft_trtype>`.
+
+``trflags``
+ Transport Specific Flags, see :c:type:`enum nbft_ssns_trflags <nbft_ssns_trflags>`.
+
+``primary_discovery_ctrl_index``
+ Primary Discovery Controller Index: The Discovery
+ Descriptor Index field of the Discovery Descriptor
+ (see :c:type:`struct nbft_discovery <nbft_discovery>`) that is associated with
+ this SSNS Descriptor. If a Discovery controller was
+ used to establish this record this value shall
+ be set to a non-zero value. If this namespace was
+ associated with multiple Discovery controllers,
+ those Discovery controllers shall have records
+ in the Discovery Descriptor to facilitate multi-path
+ rediscovery as required. If no Discovery controller
+ was utilized to inform this namespace record,
+ this field shall be cleared to 0h.
+
+``reserved1``
+ Reserved.
+
+``subsys_traddr_obj``
+ Subsystem Transport Address Heap Object Reference:
+ This field indicates the location and size of a heap
+ object containing the Subsystem Transport Address.
+ For IP based transports types, shall be an IP Address.
+
+``subsys_trsvcid_obj``
+ Subsystem Transport Service Identifier Heap Object Reference:
+ This field indicates the location and size of a heap
+ object containing an array of bytes indicating
+ the Subsystem Transport Service Identifier.
+ See :c:type:`enum nbft_trtype <nbft_trtype>`.
+
+``subsys_port_id``
+ Subsystem Port ID: Port in the NVM subsystem
+ associated with this transport address used by
+ the pre-OS driver.
+
+``nsid``
+ Namespace ID: This field indicates the namespace
+ identifier (NSID) of the namespace indicated by
+ this descriptor. This field shall be cleared to 0h
+ if not specified by the user. If this value is cleared
+ to 0h, then consumers of the NBFT shall rely
+ on the NID.
+
+``nidt``
+ Namespace Identifier Type (NIDT): This field
+ contains the value of the Namespace Identifier Type (NIDT)
+ field in the Namespace Identification Descriptor
+ for the namespace indicated by this descriptor.
+ If a namespace supports multiple NIDT entries
+ for uniqueness, the order of preference is NIDT field
+ value of 3h (i.e., UUID) before 2h (i.e., NSGUID),
+ and 2h before 1h (i.e., EUI-64).
+
+``nid``
+ Namespace Identifier (NID): This field contains
+ the value of the Namespace Identifier (NID) field
+ in the Namespace Identification Descriptor for
+ the namespace indicated by this descriptor.
+
+``security_desc_index``
+ Security Profile Descriptor Index: If the Use Security
+ Flag bit in the SSNS Flags field is set to 1h, then
+ this field indicates the value of the Security Profile
+ Descriptor Index field of the Security Profile
+ Descriptor (see :c:type:`struct nbft_security <nbft_security>`) associated
+ with this namespace. If the Use Security Flag bit
+ is cleared to 0h, then no Security Profile Descriptor
+ is associated with this namespace and this field
+ is reserved.
+
+``primary_hfi_desc_index``
+ Primary HFI Descriptor Index: This field indicates
+ the value of the HFI Descriptor Index field of the
+ HFI Descriptor (see :c:type:`struct nbft_hfi <nbft_hfi>`) for the
+ interface associated with this namespace. If multiple
+ HFIs are associated with this record, subsequent
+ interfaces should be populated in the Secondary
+ HFI Associations field.
+
+``reserved2``
+ Reserved.
+
+``secondary_hfi_assoc_obj``
+ Secondary HFI Associations Heap Object Reference:
+ If this field is set to a non-zero value, then
+ this field indicates an array of bytes, in which
+ each byte contains the value of the HFI Descriptor
+ Index field of an HFI Descriptor in the HFI Descriptor
+ List. If this field is cleared to 0h, then no
+ secondary HFI associations are specified.
+
+``subsys_ns_nqn_obj``
+ Subsystem and Namespace NQN Heap Object Reference:
+ This field indicates the location and size of
+ a heap object containing the Subsystem and Namespace NQN.
+
+``ssns_extended_info_desc_obj``
+ SSNS Extended Information Descriptor Heap Object
+ Reference: If the SSNS Extended Info In-use Flag
+ bit is set to 1h, then this field indicates the
+ offset in bytes of a heap object containing an
+ SSNS Extended Information Descriptor
+ (see :c:type:`struct nbft_ssns_ext_info <nbft_ssns_ext_info>`) heap object
+ from byte offset 0h of the NBFT Table Header.
+ If the SSNS Extended Info In-use Flag bit is cleared
+ to 0h, then this field is reserved.
+
+``reserved3``
+ Reserved.
+
+
+
+
+
+.. c:enum:: nbft_ssns_flags
+
+ Subsystem and Namespace Specific Flags Field (Figure 16)
+
+**Constants**
+
+``NBFT_SSNS_VALID``
+ Descriptor Valid: If set to 1h, then this descriptor
+ is valid. If cleared to 0h, then this descriptor
+ is not valid. A host that supports NVMe-oF Boot,
+ but does not currently have a remote Subsystem
+ and Namespace assigned may clear this bit to 0h.
+
+``NBFT_SSNS_NON_BOOTABLE_ENTRY``
+ Non-bootable Entry Flag: If set to 1h, this flag
+ indicates that this SSNS Descriptor contains
+ a namespace of administrative purpose to the boot
+ process, but the pre-OS may not have established
+ connectivity to or evaluated the contents of this
+ Descriptor. Such namespaces may contain supplemental
+ data deemed relevant by the Administrator as part
+ of the pre-OS to OS hand off. This may include
+ properties such as a UEFI device path that may
+ not have been created for this namespace. This means
+ an OS runtime may still require the contents
+ of such a namespace to complete later stages
+ of boot. If cleared to 0h, then this namespace did
+ not have any special administrative intent.
+
+``NBFT_SSNS_USE_SECURITY_FIELD``
+ Use Security Flag: If set to 1h, then there is
+ a Security Profile Descriptor associated with this
+ SSNS record and the Security Profile Descriptor Index
+ field is valid. If cleared to 0h, then there is
+ no Security Profile Descriptor associated with this
+ SSNS record and the Security Profile Descriptor Index
+ field is not valid.
+
+``NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE``
+ DHCP Root-Path Override Flag: If set to 1h, then
+ this SSNS descriptor was populated by consuming
+ the DHCP Root-Path on this interface. If cleared
+ to 0h, then the DHCP Root-Path was not used
+ in populating the SSNS descriptor.
+
+``NBFT_SSNS_EXTENDED_INFO_IN_USE``
+ SSNS Extended Info In-use Flag: If set to 1h,
+ then the SSNS Extended Information Offset field
+ and the SSNS Extended Information Length field
+ are valid. This flag, if set to 1h, indicates
+ that a Subsystem and Namespace Extended Information
+ Descriptor corresponding to this descriptor is present.
+
+``NBFT_SSNS_SEPARATE_DISCOVERY_CTRL``
+ Separate Discovery Controller Flag: If set to 1h,
+ then the Discovery controller associated with
+ this volume is on a different transport address
+ than the specified in the Subsystem Transport
+ Address Heap Object Reference. If cleared to 0h,
+ then the Discovery controller is the same as the
+ Subsystem Transport Address Heap Object Reference.
+
+``NBFT_SSNS_DISCOVERED_NAMESPACE``
+ Discovered Namespace Flag: If set to 1h, then
+ this namespace was acquired through discovery.
+ If cleared to 0h, then this namespace was
+ explicitly configured in the system.
+
+``NBFT_SSNS_UNAVAIL_NAMESPACE_MASK``
+ Mask to get Unavailable Namespace Flag: This
+ field indicates the availability of the namespace
+ at a specific point in time. Such use is only
+ a hint and its use does not guarantee the availability
+ of that referenced namespace at any future point in time.
+
+``NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND``
+ Not Indicated by Driver: No information is provided.
+
+``NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL``
+ Available: A referenced namespace described by this
+ flag was previously accessible by the pre-OS driver.
+
+``NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL``
+ Unavailable: This namespace was administratively
+ configured but unattempted, unavailable or
+ inaccessible when establishing connectivity
+ by the pre-OS driver.
+
+
+
+
+.. c:enum:: nbft_ssns_trflags
+
+ SSNS Transport Specific Flags Field (Figure 17)
+
+**Constants**
+
+``NBFT_SSNS_TRFLAG_VALID``
+ Transport Specific Flags in Use: If set to 1h, then
+ this descriptor is valid. If cleared to 0h, then
+ this descriptor is not valid.
+
+``NBFT_SSNS_PDU_HEADER_DIGEST``
+ PDU Header Digest (HDGST) Flag: If set to 1h, then
+ the host or administrator required the connection
+ described by this Subsystem and Namespace Descriptor
+ to use the NVM Header Digest Enabled. A consumer
+ of this information should attempt to use NVM Header
+ Digest when recreating this connection if enabled.
+ If cleared to 0h, then the host or administrator
+ did not require the connection described by this
+ Subsystem and Namespace Descriptor to use the
+ NVM Header Digest Enabled.
+
+``NBFT_SSNS_DATA_DIGEST``
+ Data Digest (DDGST) Flag: If set to 1h, then
+ the host or administrator required the connection
+ described by this Subsystem and Namespace Descriptor
+ to use the NVM Data Digest Enabled. If cleared
+ to 0h, then the host or administrator did not
+ require the connection described by this Subsystem
+ and Namespace Descriptor to use the NVM Data Digest
+ Enabled. A consumer of this field should attempt
+ to use NVM Data Digest when recreating this
+ connection if enabled.
+
+
+
+
+.. c:struct:: nbft_ssns_ext_info
+
+ Subsystem and Namespace Extended Information Descriptor (Figure 19)
+
+**Definition**
+
+::
+
+ struct nbft_ssns_ext_info {
+ __u8 structure_id;
+ __u8 version;
+ __le16 ssns_index;
+ __le32 flags;
+ __le16 cntlid;
+ __le16 asqsz;
+ struct nbft_heap_obj dhcp_root_path_str_obj;
+ };
+
+**Members**
+
+``structure_id``
+ Structure ID: This field shall be set to 9h
+ (i.e., SSNS Extended Info; #NBFT_DESC_SSNS_EXT_INFO).
+
+``version``
+ Version: This field shall be set to 1h.
+
+``ssns_index``
+ SSNS Descriptor Index: This field indicates the value
+ of the SSNS Descriptor Index field of the Subsystem
+ and Namespace Descriptor (see :c:type:`struct nbft_ssns <nbft_ssns>`) whose
+ SSNS Extended Information Descriptor Heap Object
+ Reference field indicates this descriptor.
+
+``flags``
+ Flags, see :c:type:`enum nbft_ssns_ext_info_flags <nbft_ssns_ext_info_flags>`.
+
+``cntlid``
+ Controller ID: The controller identifier of the first
+ controller associated with the Admin Queue by the driver.
+ If a controller identifier is not administratively
+ specified or direct configuration is not supported
+ by the driver, then this field shall be cleared to 0h.
+
+``asqsz``
+ Admin Submission Queue Size (ASQSZ): The Admin Submission
+ Queue Size utilized for the respective SSNS by the driver.
+
+``dhcp_root_path_str_obj``
+ DHCP Root Path String Heap Object Reference: If the
+ SSNS DHCP Root Path Override (#NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE)
+ flag bit is set to 1h, then this field indicates
+ the offset in bytes of a heap object containing
+ an DHCP Root Path String used by the driver. If the
+ SNSS DHCP Root Path Override flag bit is cleared to 0h,
+ then this field is reserved.
+
+
+
+
+
+.. c:enum:: nbft_ssns_ext_info_flags
+
+ Subsystem and Namespace Extended Information Descriptor Flags
+
+**Constants**
+
+``NBFT_SSNS_EXT_INFO_VALID``
+ Descriptor Valid: If set to 1h, then this descriptor
+ is valid. If cleared to 0h, then this descriptor
+ is reserved.
+
+``NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ``
+ Administrative ASQSZ: If set to 1h, then the value
+ of the ASQSZ field was provided by administrative
+ configuration for this SSNS record. If cleared
+ to 0h, then the value of the ASQSZ field was
+ either obtained by discovery or assumed
+ by the driver.
+
+
+
+
+.. c:struct:: nbft_security
+
+ Security Profile Descriptor (Figure 21)
+
+**Definition**
+
+::
+
+ struct nbft_security {
+ __u8 structure_id;
+ __u8 index;
+ __le16 flags;
+ __u8 secret_type;
+ __u8 reserved1;
+ struct nbft_heap_obj sec_chan_alg_obj;
+ struct nbft_heap_obj auth_proto_obj;
+ struct nbft_heap_obj cipher_suite_obj;
+ struct nbft_heap_obj dh_grp_obj;
+ struct nbft_heap_obj sec_hash_func_obj;
+ struct nbft_heap_obj sec_keypath_obj;
+ __u8 reserved2[22];
+ };
+
+**Members**
+
+``structure_id``
+ Structure ID: This field shall be set to 5h
+ (i.e., Security; #NBFT_DESC_SECURITY).
+
+``index``
+ Security Profile Descriptor Index: This field indicates
+ the number of this Security Profile Descriptor in the
+ Security Profile Descriptor List.
+
+``flags``
+ Security Profile Descriptor Flags, see :c:type:`enum nbft_security_flags <nbft_security_flags>`.
+
+``secret_type``
+ Secret Type, see :c:type:`enum nbft_security_secret_type <nbft_security_secret_type>`.
+
+``reserved1``
+ Reserved.
+
+``sec_chan_alg_obj``
+ Secure Channel Algorithm Heap Object Reference: If the
+ Security Policy List field is set to 1h, then this field
+ indicates the location and size of a heap object containing
+ a list of secure channel algorithms. The list is an array
+ of bytes and the values are defined in the Security Type
+ (SECTYPE) field in the Transport Specific Address Subtype
+ Definition in the NVMe TCP Transport Specification.
+ If the Security Policy List field is cleared to 0h, then
+ this field is reserved.
+
+``auth_proto_obj``
+ Authentication Protocols Heap Object Reference: If the
+ Authentication Policy List field is set to 1h, then this
+ field indicates the location and size of a heap object
+ containing a list of authentication protocol identifiers.
+ If the Authentication Policy List field is cleared to 0h,
+ then this field is reserved.
+
+``cipher_suite_obj``
+ Cipher Suite Offset Heap Object Reference: If the Cipher
+ Suites Restricted by Policy bit is set to 1h, then this
+ field indicates the location and size of a heap object
+ containing a list of cipher suite identifiers. The list,
+ if any, is an array of bytes and the values are defined
+ in the IANA TLS Parameters Registry. If the Cipher Suites
+ Restricted by Policy bit is cleared to 0h, then this field
+ is reserved.
+
+``dh_grp_obj``
+ DH Groups Heap Object Reference: If the Authentication DH Groups
+ Restricted by Policy List bit is set to 1h, then this field
+ indicates the location and size of a heap object containing
+ a list of DH-HMAC-CHAP Diffie-Hellman (DH) group identifiers.
+ If the Authentication DH Groups Restricted by Policy List
+ bit is cleared to 0h, then this field is reserved.
+
+``sec_hash_func_obj``
+ Secure Hash Functions Offset Heap Object Reference: If the
+ Secure Hash Functions Policy List bit is set to 1h, then
+ this field indicates the offset in bytes of a heap object
+ containing a list of DH-HMAC-CHAP hash function identifiers.
+ The list is an array of bytes and the values are defined
+ in the NVM Express Base Specification. If the Secure Hash
+ Functions Policy List bit is cleared to 0h, then this
+ field is reserved.
+
+``sec_keypath_obj``
+ Secret Keypath Offset Heap Object Reference: if this field
+ is set to a non-zero value, then this field indicates
+ the location and size of a heap object containing a URI.
+ The type of the URI is specified in the Secret Type field.
+ If this field is cleared to 0h, then this field is reserved.
+
+``reserved2``
+ Reserved.
+
+
+
+
+
+.. c:enum:: nbft_security_flags
+
+ Security Profile Descriptor Flags (Figure 22)
+
+**Constants**
+
+``NBFT_SECURITY_VALID``
+ Descriptor Valid: If set to 1h, then
+ this descriptor is valid. If cleared
+ to 0h, then this descriptor is not valid.
+
+``NBFT_SECURITY_IN_BAND_AUTH_MASK``
+ Mask to get the In-Band Authentication
+ Required field.
+
+``NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED``
+ In-band authentication is not supported
+ by the NVM subsystem.
+
+``NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED``
+ In-band authentication is supported by
+ the NVM subsystem and is not required.
+
+``NBFT_SECURITY_IN_BAND_AUTH_REQUIRED``
+ In-band authentication is supported by
+ the NVM subsystem and is required.
+
+``NBFT_SECURITY_AUTH_POLICY_LIST_MASK``
+ Mask to get the Authentication Policy List
+ flag: This field indicates whether
+ authentication protocols were indicated
+ by policy from driver defaults or
+ administrative configuration.
+
+``NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED``
+ Authentication Protocols Heap Object Reference
+ field Offset and Length are reserved.
+
+``NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER``
+ Authentication Protocols Offset field and
+ the Authentication Protocols Length field
+ indicate a list of authentication protocols
+ used by the driver.
+
+``NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN``
+ Authentication Protocols Offset field and
+ the Authentication Protocols Length field
+ indicate a list of authentication protocols
+ that were administratively set and used
+ by the driver.
+
+``NBFT_SECURITY_SEC_CHAN_NEG_MASK``
+ Mask to get the Secure Channel Negotiation
+ Required flag: This field indicates whether
+ secure channel negotiation (e.g. TLS)
+ is required.
+
+``NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED``
+ Secure channel negotiation is not supported
+ by the NVM subsystem.
+
+``NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED``
+ Secure channel negotiation is supported
+ by the NVM subsystem and is not required.
+
+``NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED``
+ Secure channel negotiation is supported
+ by the NVM subsystem and is required.
+
+``NBFT_SECURITY_SEC_POLICY_LIST_MASK``
+ Mask to get the Security Policy List flag:
+ This field indicates whether secure channel
+ protocols were indicated by policy from driver
+ defaults or administrative configuration.
+
+``NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED``
+ The Offset field and Length field in the
+ Secure Channel Algorithm Heap Object Reference
+ field are reserved.
+
+``NBFT_SECURITY_SEC_POLICY_LIST_DRIVER``
+ The Heap Object specified by the Secure Channel
+ Algorithm Heap Object Reference field indicates
+ a list of authentication protocols used
+ by the driver.
+
+``NBFT_SECURITY_SEC_POLICY_LIST_ADMIN``
+ The Heap Object specified by the Secure Channel
+ Algorithm Heap Object Reference field indicates
+ a list of authentication protocols that were
+ administratively set and used by the driver.
+
+``NBFT_SECURITY_CIPHER_RESTRICTED``
+ Cipher Suites Restricted by Policy: If set to 1h,
+ then the Cipher Suite Offset field and the
+ Ciper Suite Length field indicate a list
+ of supported cipher suites by the driver.
+ If cleared to 0h, then the Cipher Suite Offset
+ field and the Cipher Suite Length field
+ are reserved.
+
+``NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED``
+ Authentication DH Groups Restricted
+ by Policy List: If set to 1h, then connections
+ shall use one of the authentication DH groups
+ in the Authentication DH Groups List is required.
+ If cleared to 0h, then no Authentication DH Groups
+ List is indicated and use of an authentication
+ DH Group is not required.
+
+``NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST``
+ Secure Hash Functions Policy List: If set to 1h,
+ then connections shall use one of the secure
+ hash functions in the Secure Hash Functions
+ Policy List is required. If cleared to 0h,
+ then no Secure Hash Functions Policy
+ List is indicated and use of a secure
+ hash function is not required.
+
+
+
+
+.. c:enum:: nbft_security_secret_type
+
+ Security Profile Descriptor Secret Type
+
+**Constants**
+
+``NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI``
+ Redfish Host Interface URI:
+ If set to 1h, then the Secret Keypath
+ Object Reference is a URI pointing
+ to a Redfish Key Collection Object
+ that contains the PSK.
+
+
+
+
+.. c:struct:: nbft_discovery
+
+ Discovery Descriptor (Figure 24)
+
+**Definition**
+
+::
+
+ struct nbft_discovery {
+ __u8 structure_id;
+ __u8 flags;
+ __u8 index;
+ __u8 hfi_index;
+ __u8 sec_index;
+ __u8 reserved1;
+ struct nbft_heap_obj discovery_ctrl_addr_obj;
+ struct nbft_heap_obj discovery_ctrl_nqn_obj;
+ __u8 reserved2[14];
+ };
+
+**Members**
+
+``structure_id``
+ Structure ID: This field shall be set to 6h
+ (i.e., Discovery Descriptor; #NBFT_DESC_DISCOVERY).
+
+``flags``
+ Discovery Descriptor Flags, see :c:type:`enum nbft_discovery_flags <nbft_discovery_flags>`.
+
+``index``
+ Discovery Descriptor Index: This field indicates
+ the number of this Discovery Descriptor in
+ the Discovery Descriptor List.
+
+``hfi_index``
+ HFI Descriptor Index: This field indicates the value
+ of the HFI Descriptor Index field of the HFI Descriptor
+ associated with this Discovery Descriptor. If multiple
+ HFIs share a common Discovery controller, there shall
+ be multiple Discovery Descriptor entries with one per HFI.
+
+``sec_index``
+ Security Profile Descriptor Index: This field indicates
+ the value of the Security Profile Descriptor Index
+ field of the Security Descriptor associated with
+ this Discovery Descriptor.
+
+``reserved1``
+ Reserved.
+
+``discovery_ctrl_addr_obj``
+ Discovery Controller Address Heap Object Reference:
+ This field indicates the location and size of a heap
+ object containing a URI which indicates an NVMe Discovery
+ controller associated with this Discovery Descriptor.
+ If this field is cleared to 0h, then no URI is specified.
+
+``discovery_ctrl_nqn_obj``
+ Discovery Controller NQN Heap Object Reference:
+ If set to a non-zero value, this field indicates
+ the location and size of a heap object containing
+ an NVMe Discovery controller NQN. If the NVMe Discovery
+ controller referenced by this record requires secure
+ authentication with a well known Subsystem NQN, this
+ field indicates the unique NQN for that NVMe Discovery
+ controller. This record is involved formatted as an NQN
+ string. If this field is cleared to 0h, then this
+ field is reserved and the OS shall use the well
+ known discovery NQN for this record.
+
+``reserved2``
+ Reserved.
+
+
+
+
+
+.. c:enum:: nbft_discovery_flags
+
+ Discovery Descriptor Flags
+
+**Constants**
+
+``NBFT_DISCOVERY_VALID``
+ Descriptor Valid: if set to 1h, then this descriptor
+ is valid. If cleared to 0h, then this descriptor
+ is reserved.
+
+
+
+
+.. c:enum:: nbft_info_primary_admin_host_flag
+
+ Primary Administrative Host Descriptor Flags
+
+**Constants**
+
+``NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED``
+ Not Indicated by Driver: The driver
+ that created this NBFT provided no
+ administrative priority hint for
+ this NBFT.
+
+``NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED``
+ Unselected: The driver that created
+ this NBFT explicitly indicated that
+ this NBFT should not be prioritized
+ over any other NBFT.
+
+``NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED``
+ Selected: The driver that created
+ this NBFT explicitly indicated that
+ this NBFT should be prioritized over
+ any other NBFT.
+
+``NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED``
+ Reserved.
+
+
+
+
+.. c:struct:: nbft_info_host
+
+ Host Descriptor
+
+**Definition**
+
+::
+
+ struct nbft_info_host {
+ unsigned char *id;
+ char *nqn;
+ bool host_id_configured;
+ bool host_nqn_configured;
+ enum nbft_info_primary_admin_host_flag primary;
+ };
+
+**Members**
+
+``id``
+ Host ID (raw UUID, length = 16 bytes).
+
+``nqn``
+ Host NQN.
+
+``host_id_configured``
+ HostID Configured Flag: value of True indicates that **id**
+ contains administratively-configured value, or driver
+ default value if False.
+
+``host_nqn_configured``
+ Host NQN Configured Flag: value of True indicates that
+ **nqn** contains administratively-configured value,
+ or driver default value if False.
+
+``primary``
+ Primary Administrative Host Descriptor, see
+ :c:type:`enum nbft_info_primary_admin_host_flag <nbft_info_primary_admin_host_flag>`.
+
+
+
+
+
+.. c:struct:: nbft_info_hfi_info_tcp
+
+ HFI Transport Info Descriptor - NVMe/TCP
+
+**Definition**
+
+::
+
+ struct nbft_info_hfi_info_tcp {
+ __u32 pci_sbdf;
+ __u8 mac_addr[6];
+ __u16 vlan;
+ __u8 ip_origin;
+ char ipaddr[40];
+ __u8 subnet_mask_prefix;
+ char gateway_ipaddr[40];
+ __u16 route_metric;
+ char primary_dns_ipaddr[40];
+ char secondary_dns_ipaddr[40];
+ char dhcp_server_ipaddr[40];
+ char *host_name;
+ bool this_hfi_is_default_route;
+ bool dhcp_override;
+ };
+
+**Members**
+
+``pci_sbdf``
+ PCI Express Routing ID for the HFI Transport Function.
+
+``mac_addr``
+ MAC Address: The MAC address of this HFI,
+ in EUI-48TM format.
+
+``vlan``
+ The VLAN identifier if the VLAN is associated with
+ this HFI, as defined in IEEE 802.1q-2018 or zeroes
+ if no VLAN is associated with this HFI.
+
+``ip_origin``
+ The source of Ethernet L3 configuration information
+ used by the driver or 0 if not used.
+
+``ipaddr``
+ The IPv4 or IPv6 address of this HFI.
+
+``subnet_mask_prefix``
+ The IPv4 or IPv6 subnet mask in CIDR routing prefix
+ notation.
+
+``gateway_ipaddr``
+ The IPv4 or IPv6 address of the IP gateway for this
+ HFI or zeroes if no IP gateway is specified.
+
+``route_metric``
+ The cost value for the route indicated by this HFI.
+
+``primary_dns_ipaddr``
+ The IPv4 or IPv6 address of the Primary DNS server
+ for this HFI.
+
+``secondary_dns_ipaddr``
+ The IPv4 or IPv6 address of the Secondary DNS server
+ for this HFI.
+
+``dhcp_server_ipaddr``
+ The IPv4 or IPv6 address of the DHCP server used
+ to assign this HFI address.
+
+``host_name``
+ The Host Name string.
+
+``this_hfi_is_default_route``
+ If True, then the BIOS utilized this interface
+ described by HFI to be the default route with highest
+ priority. If False, then routes are local to their
+ own scope.
+
+``dhcp_override``
+ If True, then HFI information was populated
+ by consuming the DHCP on this interface. If False,
+ then the HFI information was set administratively
+ by a configuration interface to the driver and
+ pre-OS envrionment.
+
+
+
+
+
+.. c:struct:: nbft_info_hfi
+
+ Host Fabric Interface (HFI) Descriptor
+
+**Definition**
+
+::
+
+ struct nbft_info_hfi {
+ int index;
+ char transport[8];
+ struct nbft_info_hfi_info_tcp tcp_info;
+ };
+
+**Members**
+
+``index``
+ HFI Descriptor Index: indicates the number of this HFI Descriptor
+ in the Host Fabric Interface Descriptor List.
+
+``transport``
+ Transport Type string (e.g. 'tcp').
+
+``tcp_info``
+ The HFI Transport Info Descriptor, see :c:type:`struct nbft_info_hfi_info_tcp <nbft_info_hfi_info_tcp>`.
+
+
+
+
+
+.. c:struct:: nbft_info_discovery
+
+ Discovery Descriptor
+
+**Definition**
+
+::
+
+ struct nbft_info_discovery {
+ int index;
+ struct nbft_info_security *security;
+ struct nbft_info_hfi *hfi;
+ char *uri;
+ char *nqn;
+ };
+
+**Members**
+
+``index``
+ The number of this Discovery Descriptor in the Discovery
+ Descriptor List.
+
+``security``
+ The Security Profile Descriptor, see :c:type:`struct nbft_info_security <nbft_info_security>`.
+
+``hfi``
+ The HFI Descriptor associated with this Discovery Descriptor.
+ See :c:type:`struct nbft_info_hfi <nbft_info_hfi>`.
+
+``uri``
+ A URI which indicates an NVMe Discovery controller associated
+ with this Discovery Descriptor.
+
+``nqn``
+ An NVMe Discovery controller NQN.
+
+
+
+
+
+.. c:struct:: nbft_info_security
+
+ Security Profile Descriptor
+
+**Definition**
+
+::
+
+ struct nbft_info_security {
+ int index;
+ };
+
+**Members**
+
+``index``
+ The number of this Security Profile Descriptor in the Security
+ Profile Descriptor List.
+
+
+
+
+
+.. c:enum:: nbft_info_nid_type
+
+ Namespace Identifier Type (NIDT)
+
+**Constants**
+
+``NBFT_INFO_NID_TYPE_NONE``
+ No identifier available.
+
+``NBFT_INFO_NID_TYPE_EUI64``
+ The EUI-64 identifier.
+
+``NBFT_INFO_NID_TYPE_NGUID``
+ The NSGUID identifier.
+
+``NBFT_INFO_NID_TYPE_NS_UUID``
+ The UUID identifier.
+
+
+
+
+.. c:struct:: nbft_info_subsystem_ns
+
+ Subsystem Namespace (SSNS) info
+
+**Definition**
+
+::
+
+ struct nbft_info_subsystem_ns {
+ int index;
+ struct nbft_info_discovery *discovery;
+ struct nbft_info_security *security;
+ int num_hfis;
+ struct nbft_info_hfi **hfis;
+ char transport[8];
+ char traddr[40];
+ char *trsvcid;
+ __u16 subsys_port_id;
+ __u32 nsid;
+ enum nbft_info_nid_type nid_type;
+ __u8 *nid;
+ char *subsys_nqn;
+ bool pdu_header_digest_required;
+ bool data_digest_required;
+ int controller_id;
+ int asqsz;
+ char *dhcp_root_path_string;
+ };
+
+**Members**
+
+``index``
+ SSNS Descriptor Index in the descriptor list.
+
+``discovery``
+ Primary Discovery Controller associated with
+ this SSNS Descriptor.
+
+``security``
+ Security Profile Descriptor associated with
+ this namespace.
+
+``num_hfis``
+ Number of HFIs.
+
+``hfis``
+ List of HFIs associated with this namespace.
+ Includes the primary HFI at the first position
+ and all secondary HFIs. This array is null-terminated.
+
+``transport``
+ Transport Type string (e.g. 'tcp').
+
+``traddr``
+ Subsystem Transport Address.
+
+``trsvcid``
+ Subsystem Transport Service Identifier.
+
+``subsys_port_id``
+ The Subsystem Port ID.
+
+``nsid``
+ The Namespace ID of this descriptor or when **nid**
+ should be used instead.
+
+``nid_type``
+ Namespace Identifier Type, see :c:type:`enum nbft_info_nid_type <nbft_info_nid_type>`.
+
+``nid``
+ The Namespace Identifier value.
+
+``subsys_nqn``
+ Subsystem and Namespace NQN.
+
+``pdu_header_digest_required``
+ PDU Header Digest (HDGST) Flag: the use of NVM Header
+ Digest Enabled is required.
+
+``data_digest_required``
+ Data Digest (DDGST) Flag: the use of NVM Data Digest
+ Enabled is required.
+
+``controller_id``
+ Controller ID (SSNS Extended Information Descriptor):
+ The controller ID associated with the Admin Queue
+ or 0 if not supported.
+
+``asqsz``
+ Admin Submission Queue Size (SSNS Extended Information
+ Descriptor) or 0 if not supported.
+
+``dhcp_root_path_string``
+ DHCP Root Path Override string (SSNS Extended
+ Information Descriptor).
+
+
+
+
+
+.. c:struct:: nbft_info
+
+ The parsed NBFT table data.
+
+**Definition**
+
+::
+
+ struct nbft_info {
+ char *filename;
+ __u8 *raw_nbft;
+ ssize_t raw_nbft_size;
+ struct nbft_info_host host;
+ struct nbft_info_hfi **hfi_list;
+ struct nbft_info_security **security_list;
+ struct nbft_info_discovery **discovery_list;
+ struct nbft_info_subsystem_ns **subsystem_ns_list;
+ };
+
+**Members**
+
+``filename``
+ Path to the NBFT table.
+
+``raw_nbft``
+ The original NBFT table contents.
+
+``raw_nbft_size``
+ Size of **raw_nbft**.
+
+``host``
+ The Host Descriptor (should match other NBFTs).
+
+``hfi_list``
+ The HFI Descriptor List (null-terminated array).
+
+``security_list``
+ The Security Profile Descriptor List (null-terminated array).
+
+``discovery_list``
+ The Discovery Descriptor List (null-terminated array).
+
+``subsystem_ns_list``
+ The SSNS Descriptor List (null-terminated array).
+
+
+
+.. c:function:: int nvme_nbft_read (struct nbft_info **nbft, const char *filename)
+
+ Read and parse contents of an ACPI NBFT table
+
+**Parameters**
+
+``struct nbft_info **nbft``
+ Parsed NBFT table data.
+
+``const char *filename``
+ Filename of the raw NBFT table to read.
+
+**Description**
+
+Read and parse the specified NBFT file into a struct nbft_info.
+Free with nvme_nbft_free().
+
+**Return**
+
+0 on success, errno otherwise.
+
+
+.. c:function:: void nvme_nbft_free (struct nbft_info *nbft)
+
+ Free the struct nbft_info and its contents
+
+**Parameters**
+
+``struct nbft_info *nbft``
+ Parsed NBFT table data.
+
+
diff --git a/doc/rst/tree.rst b/doc/rst/tree.rst
new file mode 100644
index 0000000..b73ffae
--- /dev/null
+++ b/doc/rst/tree.rst
@@ -0,0 +1,2482 @@
+.. _tree.h:
+
+**tree.h**
+
+
+libnvme tree object interface
+
+.. c:function:: nvme_root_t nvme_create_root (FILE *fp, int log_level)
+
+ Initialize root object
+
+**Parameters**
+
+``FILE *fp``
+ File descriptor for logging messages
+
+``int log_level``
+ Logging level to use
+
+**Return**
+
+Initialized :c:type:`nvme_root_t` object
+
+
+.. c:function:: void nvme_root_set_application (nvme_root_t r, const char *a)
+
+ Specify managing application
+
+**Parameters**
+
+``nvme_root_t r``
+ :c:type:`nvme_root_t` object
+
+``const char *a``
+ Application string
+
+**Description**
+
+Sets the managing application string for **r**.
+
+
+.. c:function:: const char * nvme_root_get_application (nvme_root_t r)
+
+ Get managing application
+
+**Parameters**
+
+``nvme_root_t r``
+ :c:type:`nvme_root_t` object
+
+**Description**
+
+Returns the managing application string for **r** or NULL if not set.
+
+
+.. c:function:: void nvme_root_release_fds (nvme_root_t r)
+
+ Close all opened file descriptors in the tree
+
+**Parameters**
+
+``nvme_root_t r``
+ :c:type:`nvme_root_t` object
+
+**Description**
+
+Controller and Namespace objects cache the file descriptors
+of opened nvme devices. This API can be used to close and
+clear all cached fds in the tree.
+
+
+.. c:function:: void nvme_free_tree (nvme_root_t r)
+
+ Free root object
+
+**Parameters**
+
+``nvme_root_t r``
+ :c:type:`nvme_root_t` object
+
+**Description**
+
+Free an :c:type:`nvme_root_t` object and all attached objects
+
+
+.. c:function:: nvme_host_t nvme_first_host (nvme_root_t r)
+
+ Start host iterator
+
+**Parameters**
+
+``nvme_root_t r``
+ :c:type:`nvme_root_t` object
+
+**Return**
+
+First :c:type:`nvme_host_t` object in an iterator
+
+
+.. c:function:: nvme_host_t nvme_next_host (nvme_root_t r, nvme_host_t h)
+
+ Next host iterator
+
+**Parameters**
+
+``nvme_root_t r``
+ :c:type:`nvme_root_t` object
+
+``nvme_host_t h``
+ Previous :c:type:`nvme_host_t` iterator
+
+**Return**
+
+Next :c:type:`nvme_host_t` object in an iterator
+
+
+.. c:function:: nvme_root_t nvme_host_get_root (nvme_host_t h)
+
+ Returns nvme_root_t object
+
+**Parameters**
+
+``nvme_host_t h``
+ :c:type:`nvme_host_t` object
+
+**Return**
+
+:c:type:`nvme_root_t` object from **h**
+
+
+.. c:function:: nvme_host_t nvme_lookup_host (nvme_root_t r, const char *hostnqn, const char *hostid)
+
+ Lookup nvme_host_t object
+
+**Parameters**
+
+``nvme_root_t r``
+ :c:type:`nvme_root_t` object
+
+``const char *hostnqn``
+ Host NQN
+
+``const char *hostid``
+ Host ID
+
+**Description**
+
+Lookup a nvme_host_t object based on **hostnqn** and **hostid**
+or create one if not found.
+
+**Return**
+
+:c:type:`nvme_host_t` object
+
+
+.. c:function:: const char * nvme_host_get_dhchap_key (nvme_host_t h)
+
+ Return host key
+
+**Parameters**
+
+``nvme_host_t h``
+ Host for which the key should be returned
+
+**Return**
+
+DH-HMAC-CHAP host key or NULL if not set
+
+
+.. c:function:: void nvme_host_set_dhchap_key (nvme_host_t h, const char *key)
+
+ set host key
+
+**Parameters**
+
+``nvme_host_t h``
+ Host for which the key should be set
+
+``const char *key``
+ DH-HMAC-CHAP Key to set or NULL to clear existing key
+
+
+.. c:function:: void nvme_host_set_pdc_enabled (nvme_host_t h, bool enabled)
+
+ Set Persistent Discovery Controller flag
+
+**Parameters**
+
+``nvme_host_t h``
+ Host for which the falg should be set
+
+``bool enabled``
+ The bool to set the enabled flag
+
+**Description**
+
+When nvme_host_set_pdc_enabled() is not used to set the PDC flag,
+nvme_host_is_pdc_enabled() will return the default value which was
+passed into the function and not the undefined flag value.
+
+
+.. c:function:: bool nvme_host_is_pdc_enabled (nvme_host_t h, bool fallback)
+
+ Is Persistenct Discovery Controller enabled
+
+**Parameters**
+
+``nvme_host_t h``
+ Host which to check if PDC is enabled
+
+``bool fallback``
+ The fallback default value of the flag when
+ **nvme_host_set_pdc_enabled** has not be used
+ to set the flag.
+
+**Return**
+
+true if PDC is enabled for **h**, else false
+
+
+.. c:function:: nvme_host_t nvme_default_host (nvme_root_t r)
+
+ Initializes the default host
+
+**Parameters**
+
+``nvme_root_t r``
+ :c:type:`nvme_root_t` object
+
+**Description**
+
+Initializes the default host object based on the values in
+/etc/nvme/hostnqn and /etc/nvme/hostid and attaches it to **r**.
+
+**Return**
+
+:c:type:`nvme_host_t` object
+
+
+.. c:function:: nvme_subsystem_t nvme_first_subsystem (nvme_host_t h)
+
+ Start subsystem iterator
+
+**Parameters**
+
+``nvme_host_t h``
+ :c:type:`nvme_host_t` object
+
+**Return**
+
+first :c:type:`nvme_subsystem_t` object in an iterator
+
+
+.. c:function:: nvme_subsystem_t nvme_next_subsystem (nvme_host_t h, nvme_subsystem_t s)
+
+ Next subsystem iterator
+
+**Parameters**
+
+``nvme_host_t h``
+ :c:type:`nvme_host_t` object
+
+``nvme_subsystem_t s``
+ Previous :c:type:`nvme_subsystem_t` iterator
+
+**Return**
+
+next :c:type:`nvme_subsystem_t` object in an iterator
+
+
+.. c:function:: nvme_subsystem_t nvme_lookup_subsystem (struct nvme_host *h, const char *name, const char *subsysnqn)
+
+ Lookup nvme_subsystem_t object
+
+**Parameters**
+
+``struct nvme_host *h``
+ :c:type:`nvme_host_t` object
+
+``const char *name``
+ Name of the subsystem (may be NULL)
+
+``const char *subsysnqn``
+ Subsystem NQN
+
+**Description**
+
+Lookup a :c:type:`nvme_subsystem_t` object in **h** base on **name** (if present)
+and **subsysnqn** or create one if not found.
+
+**Return**
+
+nvme_subsystem_t object
+
+
+.. c:function:: void nvme_free_subsystem (struct nvme_subsystem *s)
+
+ Free a subsystem
+
+**Parameters**
+
+``struct nvme_subsystem *s``
+ subsystem
+
+**Description**
+
+Frees **s** and all related objects.
+
+
+.. c:function:: nvme_host_t nvme_subsystem_get_host (nvme_subsystem_t s)
+
+ Returns nvme_host_t object
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ subsystem
+
+**Return**
+
+:c:type:`nvme_host_t` object from **s**
+
+
+.. c:function:: nvme_ns_t nvme_ctrl_first_ns (nvme_ctrl_t c)
+
+ Start namespace iterator
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+First :c:type:`nvme_ns_t` object of an **c** iterator
+
+
+.. c:function:: nvme_ns_t nvme_ctrl_next_ns (nvme_ctrl_t c, nvme_ns_t n)
+
+ Next namespace iterator
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+``nvme_ns_t n``
+ Previous nvme_ns_t iterator
+
+**Return**
+
+Next nvme_ns_t object of an **c** iterator
+
+
+.. c:function:: nvme_path_t nvme_ctrl_first_path (nvme_ctrl_t c)
+
+ Start path iterator
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+First :c:type:`nvme_path_t` object of an **c** iterator
+
+
+.. c:function:: nvme_path_t nvme_ctrl_next_path (nvme_ctrl_t c, nvme_path_t p)
+
+ Next path iterator
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+``nvme_path_t p``
+ Previous :c:type:`nvme_path_t` object of an **c** iterator
+
+**Return**
+
+Next :c:type:`nvme_path_t` object of an **c** iterator
+
+
+.. c:function:: nvme_ctrl_t nvme_subsystem_first_ctrl (nvme_subsystem_t s)
+
+ First ctrl iterator
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ :c:type:`nvme_subsystem_t` object
+
+**Return**
+
+First controller of an **s** iterator
+
+
+.. c:function:: nvme_ctrl_t nvme_subsystem_next_ctrl (nvme_subsystem_t s, nvme_ctrl_t c)
+
+ Next ctrl iterator
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ :c:type:`nvme_subsystem_t` object
+
+``nvme_ctrl_t c``
+ Previous controller instance of an **s** iterator
+
+**Return**
+
+Next controller of an **s** iterator
+
+
+.. c:function:: nvme_path_t nvme_namespace_first_path (nvme_ns_t ns)
+
+ Start path iterator
+
+**Parameters**
+
+``nvme_ns_t ns``
+ Namespace instance
+
+**Return**
+
+First :c:type:`nvme_path_t` object of an **ns** iterator
+
+
+.. c:function:: nvme_path_t nvme_namespace_next_path (nvme_ns_t ns, nvme_path_t p)
+
+ Next path iterator
+
+**Parameters**
+
+``nvme_ns_t ns``
+ Namespace instance
+
+``nvme_path_t p``
+ Previous :c:type:`nvme_path_t` object of an **ns** iterator
+
+**Return**
+
+Next :c:type:`nvme_path_t` object of an **ns** iterator
+
+
+.. c:function:: nvme_ctrl_t nvme_lookup_ctrl (nvme_subsystem_t s, const char *transport, const char *traddr, const char *host_traddr, const char *host_iface, const char *trsvcid, nvme_ctrl_t p)
+
+ Lookup nvme_ctrl_t object
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ :c:type:`nvme_subsystem_t` object
+
+``const char *transport``
+ Transport name
+
+``const char *traddr``
+ Transport address
+
+``const char *host_traddr``
+ Host transport address
+
+``const char *host_iface``
+ Host interface name
+
+``const char *trsvcid``
+ Transport service identifier
+
+``nvme_ctrl_t p``
+ Previous controller instance
+
+**Description**
+
+Lookup a controller in **s** based on **transport**, **traddr**,
+**host_traddr**, **host_iface**, and **trsvcid**. **transport** must be specified,
+other fields may be required depending on the transport. A new
+object is created if none is found. If **p** is specified the lookup
+will start at **p** instead of the first controller.
+
+**Return**
+
+Controller instance
+
+
+.. c:function:: nvme_ctrl_t nvme_ctrl_find (nvme_subsystem_t s, const char *transport, const char *traddr, const char *trsvcid, const char *subsysnqn, const char *host_traddr, const char *host_iface)
+
+ Locate an existing controller
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ :c:type:`nvme_subsystem_t` object
+
+``const char *transport``
+ Transport name
+
+``const char *traddr``
+ Transport address
+
+``const char *trsvcid``
+ Transport service identifier
+
+``const char *subsysnqn``
+ Subsystem NQN
+
+``const char *host_traddr``
+ Host transport address
+
+``const char *host_iface``
+ Host interface name
+
+**Description**
+
+Lookup a controller in **s** based on **transport**, **traddr**, **trsvcid**,
+**subsysnqn**, **host_traddr**, and **host_iface**. **transport** must be specified,
+other fields may be required depending on the transport. Parameters set
+to NULL will be ignored.
+
+Unlike nvme_lookup_ctrl(), this function does not create a new object if
+an existing controller cannot be found.
+
+**Return**
+
+Controller instance on success, NULL otherwise.
+
+
+.. c:function:: bool nvme_ctrl_config_match (struct nvme_ctrl *c, const char *transport, const char *traddr, const char *trsvcid, const char *subsysnqn, const char *host_traddr, const char *host_iface)
+
+ Check if ctrl **c** matches config params
+
+**Parameters**
+
+``struct nvme_ctrl *c``
+ An existing controller instance
+
+``const char *transport``
+ Transport name
+
+``const char *traddr``
+ Transport address
+
+``const char *trsvcid``
+ Transport service identifier
+
+``const char *subsysnqn``
+ Subsystem NQN
+
+``const char *host_traddr``
+ Host transport address
+
+``const char *host_iface``
+ Host interface name
+
+**Description**
+
+Check that controller **c** matches parameters: **transport**, **traddr**,
+**trsvcid**, **subsysnqn**, **host_traddr**, and **host_iface**. Parameters set
+to NULL will be ignored.
+
+**Return**
+
+true if there's a match, false otherwise.
+
+
+.. c:function:: nvme_ctrl_t nvme_create_ctrl (nvme_root_t r, const char *subsysnqn, const char *transport, const char *traddr, const char *host_traddr, const char *host_iface, const char *trsvcid)
+
+ Allocate an unconnected NVMe controller
+
+**Parameters**
+
+``nvme_root_t r``
+ NVMe root element
+
+``const char *subsysnqn``
+ Subsystem NQN
+
+``const char *transport``
+ Transport type
+
+``const char *traddr``
+ Transport address
+
+``const char *host_traddr``
+ Host transport address
+
+``const char *host_iface``
+ Host interface name
+
+``const char *trsvcid``
+ Transport service ID
+
+**Description**
+
+Creates an unconnected controller to be used for nvme_add_ctrl().
+
+**Return**
+
+Controller instance
+
+
+.. c:function:: nvme_ns_t nvme_subsystem_first_ns (nvme_subsystem_t s)
+
+ Start namespace iterator
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ :c:type:`nvme_subsystem_t` object
+
+**Return**
+
+First :c:type:`nvme_ns_t` object of an **s** iterator
+
+
+.. c:function:: nvme_ns_t nvme_subsystem_next_ns (nvme_subsystem_t s, nvme_ns_t n)
+
+ Next namespace iterator
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ :c:type:`nvme_subsystem_t` object
+
+``nvme_ns_t n``
+ Previous :c:type:`nvme_ns_t` iterator
+
+**Return**
+
+Next :c:type:`nvme_ns_t` object of an **s** iterator
+
+
+.. c:macro:: nvme_for_each_host_safe
+
+``nvme_for_each_host_safe (r, h, _h)``
+
+ Traverse host list
+
+**Parameters**
+
+``r``
+ :c:type:`nvme_root_t` object
+
+``h``
+ :c:type:`nvme_host_t` object
+
+``_h``
+ Temporary :c:type:`nvme_host_t` object
+
+
+.. c:macro:: nvme_for_each_host
+
+``nvme_for_each_host (r, h)``
+
+ Traverse host list
+
+**Parameters**
+
+``r``
+ :c:type:`nvme_root_t` object
+
+``h``
+ :c:type:`nvme_host_t` object
+
+
+.. c:macro:: nvme_for_each_subsystem_safe
+
+``nvme_for_each_subsystem_safe (h, s, _s)``
+
+ Traverse subsystems
+
+**Parameters**
+
+``h``
+ :c:type:`nvme_host_t` object
+
+``s``
+ :c:type:`nvme_subsystem_t` object
+
+``_s``
+ Temporary :c:type:`nvme_subsystem_t` object
+
+
+.. c:macro:: nvme_for_each_subsystem
+
+``nvme_for_each_subsystem (h, s)``
+
+ Traverse subsystems
+
+**Parameters**
+
+``h``
+ :c:type:`nvme_host_t` object
+
+``s``
+ :c:type:`nvme_subsystem_t` object
+
+
+.. c:macro:: nvme_subsystem_for_each_ctrl_safe
+
+``nvme_subsystem_for_each_ctrl_safe (s, c, _c)``
+
+ Traverse controllers
+
+**Parameters**
+
+``s``
+ :c:type:`nvme_subsystem_t` object
+
+``c``
+ Controller instance
+
+``_c``
+ A :c:type:`nvme_ctrl_t_node` to use as temporary storage
+
+
+.. c:macro:: nvme_subsystem_for_each_ctrl
+
+``nvme_subsystem_for_each_ctrl (s, c)``
+
+ Traverse controllers
+
+**Parameters**
+
+``s``
+ :c:type:`nvme_subsystem_t` object
+
+``c``
+ Controller instance
+
+
+.. c:macro:: nvme_ctrl_for_each_ns_safe
+
+``nvme_ctrl_for_each_ns_safe (c, n, _n)``
+
+ Traverse namespaces
+
+**Parameters**
+
+``c``
+ Controller instance
+
+``n``
+ :c:type:`nvme_ns_t` object
+
+``_n``
+ A :c:type:`nvme_ns_t_node` to use as temporary storage
+
+
+.. c:macro:: nvme_ctrl_for_each_ns
+
+``nvme_ctrl_for_each_ns (c, n)``
+
+ Traverse namespaces
+
+**Parameters**
+
+``c``
+ Controller instance
+
+``n``
+ :c:type:`nvme_ns_t` object
+
+
+.. c:macro:: nvme_ctrl_for_each_path_safe
+
+``nvme_ctrl_for_each_path_safe (c, p, _p)``
+
+ Traverse paths
+
+**Parameters**
+
+``c``
+ Controller instance
+
+``p``
+ :c:type:`nvme_path_t` object
+
+``_p``
+ A :c:type:`nvme_path_t_node` to use as temporary storage
+
+
+.. c:macro:: nvme_ctrl_for_each_path
+
+``nvme_ctrl_for_each_path (c, p)``
+
+ Traverse paths
+
+**Parameters**
+
+``c``
+ Controller instance
+
+``p``
+ :c:type:`nvme_path_t` object
+
+
+.. c:macro:: nvme_subsystem_for_each_ns_safe
+
+``nvme_subsystem_for_each_ns_safe (s, n, _n)``
+
+ Traverse namespaces
+
+**Parameters**
+
+``s``
+ :c:type:`nvme_subsystem_t` object
+
+``n``
+ :c:type:`nvme_ns_t` object
+
+``_n``
+ A :c:type:`nvme_ns_t_node` to use as temporary storage
+
+
+.. c:macro:: nvme_subsystem_for_each_ns
+
+``nvme_subsystem_for_each_ns (s, n)``
+
+ Traverse namespaces
+
+**Parameters**
+
+``s``
+ :c:type:`nvme_subsystem_t` object
+
+``n``
+ :c:type:`nvme_ns_t` object
+
+
+.. c:macro:: nvme_namespace_for_each_path_safe
+
+``nvme_namespace_for_each_path_safe (n, p, _p)``
+
+ Traverse paths
+
+**Parameters**
+
+``n``
+ Namespace instance
+
+``p``
+ :c:type:`nvme_path_t` object
+
+``_p``
+ A :c:type:`nvme_path_t_node` to use as temporary storage
+
+
+.. c:macro:: nvme_namespace_for_each_path
+
+``nvme_namespace_for_each_path (n, p)``
+
+ Traverse paths
+
+**Parameters**
+
+``n``
+ Namespace instance
+
+``p``
+ :c:type:`nvme_path_t` object
+
+
+.. c:function:: int nvme_ns_get_fd (nvme_ns_t n)
+
+ Get associated file descriptor
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Description**
+
+libnvme will open() the file (if not already opened) and keep
+an internal copy of the file descriptor. Following calls to
+this API retrieve the internal cached copy of the file
+descriptor. The file will remain opened and the fd will
+remain cached until the ns object is deleted or
+nvme_ns_release_fd() is called.
+
+**Return**
+
+File descriptor associated with **n** or -1
+
+
+.. c:function:: void nvme_ns_release_fd (nvme_ns_t n)
+
+ Close fd and clear fd from ns object
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+
+.. c:function:: int nvme_ns_get_nsid (nvme_ns_t n)
+
+ NSID of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+NSID of **n**
+
+
+.. c:function:: int nvme_ns_get_lba_size (nvme_ns_t n)
+
+ LBA size of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+LBA size of **n**
+
+
+.. c:function:: int nvme_ns_get_meta_size (nvme_ns_t n)
+
+ Metadata size of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+Metadata size of **n**
+
+
+.. c:function:: uint64_t nvme_ns_get_lba_count (nvme_ns_t n)
+
+ LBA count of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+LBA count of **n**
+
+
+.. c:function:: uint64_t nvme_ns_get_lba_util (nvme_ns_t n)
+
+ LBA utilization of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+LBA utilization of **n**
+
+
+.. c:function:: enum nvme_csi nvme_ns_get_csi (nvme_ns_t n)
+
+ Command set identifier of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+The namespace's command set identifier in use
+
+
+.. c:function:: const uint8_t * nvme_ns_get_eui64 (nvme_ns_t n)
+
+ 64-bit eui of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+A pointer to the 64-bit eui
+
+
+.. c:function:: const uint8_t * nvme_ns_get_nguid (nvme_ns_t n)
+
+ 128-bit nguid of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+A pointer to the 128-bit nguid
+
+
+.. c:function:: void nvme_ns_get_uuid (nvme_ns_t n, unsigned char out[NVME_UUID_LEN])
+
+ UUID of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``unsigned char out[NVME_UUID_LEN]``
+ buffer for the UUID
+
+**Description**
+
+Copies the namespace's uuid into **out**
+
+
+.. c:function:: const char * nvme_ns_get_sysfs_dir (nvme_ns_t n)
+
+ sysfs directory of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+sysfs directory name of **n**
+
+
+.. c:function:: const char * nvme_ns_get_name (nvme_ns_t n)
+
+ sysfs name of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+sysfs name of **n**
+
+
+.. c:function:: const char * nvme_ns_get_generic_name (nvme_ns_t n)
+
+ Returns name of generic namespace chardev.
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+Name of generic namespace chardev
+
+
+.. c:function:: const char * nvme_ns_get_firmware (nvme_ns_t n)
+
+ Firmware string of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+Firmware string of **n**
+
+
+.. c:function:: const char * nvme_ns_get_serial (nvme_ns_t n)
+
+ Serial number of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+Serial number string of **n**
+
+
+.. c:function:: const char * nvme_ns_get_model (nvme_ns_t n)
+
+ Model of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+Model string of **n**
+
+
+.. c:function:: nvme_subsystem_t nvme_ns_get_subsystem (nvme_ns_t n)
+
+ :c:type:`nvme_subsystem_t` of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+nvme_subsystem_t object of **n**
+
+
+.. c:function:: nvme_ctrl_t nvme_ns_get_ctrl (nvme_ns_t n)
+
+ :c:type:`nvme_ctrl_t` of a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Description**
+
+nvme_ctrl_t object may be NULL for a multipathed namespace
+
+**Return**
+
+nvme_ctrl_t object of **n** if present
+
+
+.. c:function:: void nvme_free_ns (struct nvme_ns *n)
+
+ Free a namespace object
+
+**Parameters**
+
+``struct nvme_ns *n``
+ Namespace instance
+
+
+.. c:function:: int nvme_ns_read (nvme_ns_t n, void *buf, off_t offset, size_t count)
+
+ Read from a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``void *buf``
+ Buffer into which the data will be transferred
+
+``off_t offset``
+ LBA offset of **n**
+
+``size_t count``
+ Number of sectors in **buf**
+
+**Return**
+
+Number of sectors read or -1 on error.
+
+
+.. c:function:: int nvme_ns_write (nvme_ns_t n, void *buf, off_t offset, size_t count)
+
+ Write to a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``void *buf``
+ Buffer with data to be written
+
+``off_t offset``
+ LBA offset of **n**
+
+``size_t count``
+ Number of sectors in **buf**
+
+**Return**
+
+Number of sectors written or -1 on error
+
+
+.. c:function:: int nvme_ns_verify (nvme_ns_t n, off_t offset, size_t count)
+
+ Verify data on a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``off_t offset``
+ LBA offset of **n**
+
+``size_t count``
+ Number of sectors to be verified
+
+**Return**
+
+Number of sectors verified
+
+
+.. c:function:: int nvme_ns_compare (nvme_ns_t n, void *buf, off_t offset, size_t count)
+
+ Compare data on a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``void *buf``
+ Buffer with data to be compared
+
+``off_t offset``
+ LBA offset of **n**
+
+``size_t count``
+ Number of sectors in **buf**
+
+**Return**
+
+Number of sectors compared
+
+
+.. c:function:: int nvme_ns_write_zeros (nvme_ns_t n, off_t offset, size_t count)
+
+ Write zeros to a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``off_t offset``
+ LBA offset in **n**
+
+``size_t count``
+ Number of sectors to be written
+
+**Return**
+
+Number of sectors written
+
+
+.. c:function:: int nvme_ns_write_uncorrectable (nvme_ns_t n, off_t offset, size_t count)
+
+ Issus a 'write uncorrectable' command
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``off_t offset``
+ LBA offset in **n**
+
+``size_t count``
+ Number of sectors to be written
+
+**Return**
+
+Number of sectors written
+
+
+.. c:function:: int nvme_ns_flush (nvme_ns_t n)
+
+ Flush data to a namespace
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+**Return**
+
+0 on success, -1 on error.
+
+
+.. c:function:: int nvme_ns_identify (nvme_ns_t n, struct nvme_id_ns *ns)
+
+ Issue an 'identify namespace' command
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``struct nvme_id_ns *ns``
+ :c:type:`nvme_id_ns` buffer
+
+**Description**
+
+Writes the data returned by the 'identify namespace' command
+into **ns**.
+
+**Return**
+
+0 on success, -1 on error.
+
+
+.. c:function:: int nvme_ns_identify_descs (nvme_ns_t n, struct nvme_ns_id_desc *descs)
+
+ Issue an 'identify descriptors' command
+
+**Parameters**
+
+``nvme_ns_t n``
+ Namespace instance
+
+``struct nvme_ns_id_desc *descs``
+ List of identify descriptors
+
+**Description**
+
+Writes the data returned by the 'identify descriptors' command
+into **descs**.
+
+**Return**
+
+0 on success, -1 on error.
+
+
+.. c:function:: const char * nvme_path_get_name (nvme_path_t p)
+
+ sysfs name of an :c:type:`nvme_path_t` object
+
+**Parameters**
+
+``nvme_path_t p``
+ :c:type:`nvme_path_t` object
+
+**Return**
+
+sysfs name of **p**
+
+
+.. c:function:: const char * nvme_path_get_sysfs_dir (nvme_path_t p)
+
+ sysfs directory of an nvme_path_t object
+
+**Parameters**
+
+``nvme_path_t p``
+ :c:type:`nvme_path_t` object
+
+**Return**
+
+sysfs directory of **p**
+
+
+.. c:function:: const char * nvme_path_get_ana_state (nvme_path_t p)
+
+ ANA state of an nvme_path_t object
+
+**Parameters**
+
+``nvme_path_t p``
+ :c:type:`nvme_path_t` object
+
+**Return**
+
+ANA (Asynchronous Namespace Access) state of **p**
+
+
+.. c:function:: nvme_ctrl_t nvme_path_get_ctrl (nvme_path_t p)
+
+ Parent controller of an nvme_path_t object
+
+**Parameters**
+
+``nvme_path_t p``
+ :c:type:`nvme_path_t` object
+
+**Return**
+
+Parent controller if present
+
+
+.. c:function:: nvme_ns_t nvme_path_get_ns (nvme_path_t p)
+
+ Parent namespace of an nvme_path_t object
+
+**Parameters**
+
+``nvme_path_t p``
+ :c:type:`nvme_path_t` object
+
+**Return**
+
+Parent namespace if present
+
+
+.. c:function:: int nvme_ctrl_get_fd (nvme_ctrl_t c)
+
+ Get associated file descriptor
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Description**
+
+libnvme will open() the file (if not already opened) and keep
+an internal copy of the file descriptor. Following calls to
+this API retrieve the internal cached copy of the file
+descriptor. The file will remain opened and the fd will
+remain cached until the controller object is deleted or
+nvme_ctrl_release_fd() is called.
+
+**Return**
+
+File descriptor associated with **c** or -1
+
+
+.. c:function:: void nvme_ctrl_release_fd (nvme_ctrl_t c)
+
+ Close fd and clear fd from controller object
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+
+.. c:function:: const char * nvme_ctrl_get_name (nvme_ctrl_t c)
+
+ sysfs name of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+sysfs name of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_sysfs_dir (nvme_ctrl_t c)
+
+ sysfs directory of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+sysfs directory name of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_address (nvme_ctrl_t c)
+
+ Address string of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+NVMe-over-Fabrics address string of **c** or empty string
+of no address is present.
+
+
+.. c:function:: char * nvme_ctrl_get_src_addr (nvme_ctrl_t c, char *src_addr, size_t src_addr_len)
+
+ Extract src_addr from the c->address string
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+``char *src_addr``
+ Where to copy the src_addr. Size must be at least INET6_ADDRSTRLEN.
+
+``size_t src_addr_len``
+ Length of the buffer **src_addr**.
+
+**Return**
+
+Pointer to **src_addr** on success. NULL on failure to extract the src_addr.
+
+
+.. c:function:: const char * nvme_ctrl_get_phy_slot (nvme_ctrl_t c)
+
+ PCI physical slot number of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+PCI physical slot number of **c** or empty string if slot
+number is not present.
+
+
+.. c:function:: const char * nvme_ctrl_get_firmware (nvme_ctrl_t c)
+
+ Firmware string of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Firmware string of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_model (nvme_ctrl_t c)
+
+ Model of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Model string of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_state (nvme_ctrl_t c)
+
+ Running state of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+String indicating the running state of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_numa_node (nvme_ctrl_t c)
+
+ NUMA node of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+String indicating the NUMA node
+
+
+.. c:function:: const char * nvme_ctrl_get_queue_count (nvme_ctrl_t c)
+
+ Queue count of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Queue count of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_serial (nvme_ctrl_t c)
+
+ Serial number of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Serial number string of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_sqsize (nvme_ctrl_t c)
+
+ SQ size of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+SQ size (as string) of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_transport (nvme_ctrl_t c)
+
+ Transport type of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Transport type of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_subsysnqn (nvme_ctrl_t c)
+
+ Subsystem NQN of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Subsystem NQN of **c**
+
+
+.. c:function:: nvme_subsystem_t nvme_ctrl_get_subsystem (nvme_ctrl_t c)
+
+ Parent subsystem of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Parent nvme_subsystem_t object
+
+
+.. c:function:: const char * nvme_ctrl_get_traddr (nvme_ctrl_t c)
+
+ Transport address of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Transport address of **c**
+
+
+.. c:function:: const char * nvme_ctrl_get_trsvcid (nvme_ctrl_t c)
+
+ Transport service identifier of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Transport service identifier of **c** (if present)
+
+
+.. c:function:: const char * nvme_ctrl_get_host_traddr (nvme_ctrl_t c)
+
+ Host transport address of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Host transport address of **c** (if present)
+
+
+.. c:function:: const char * nvme_ctrl_get_host_iface (nvme_ctrl_t c)
+
+ Host interface name of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Host interface name of **c** (if present)
+
+
+.. c:function:: const char * nvme_ctrl_get_dhchap_host_key (nvme_ctrl_t c)
+
+ Return host key
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller to be checked
+
+**Return**
+
+DH-HMAC-CHAP host key or NULL if not set
+
+
+.. c:function:: void nvme_ctrl_set_dhchap_host_key (nvme_ctrl_t c, const char *key)
+
+ Set host key
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Host for which the key should be set
+
+``const char *key``
+ DH-HMAC-CHAP Key to set or NULL to clear existing key
+
+
+.. c:function:: const char * nvme_ctrl_get_dhchap_key (nvme_ctrl_t c)
+
+ Return controller key
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller for which the key should be set
+
+**Return**
+
+DH-HMAC-CHAP controller key or NULL if not set
+
+
+.. c:function:: void nvme_ctrl_set_dhchap_key (nvme_ctrl_t c, const char *key)
+
+ Set controller key
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller for which the key should be set
+
+``const char *key``
+ DH-HMAC-CHAP Key to set or NULL to clear existing key
+
+
+.. c:function:: struct nvme_fabrics_config * nvme_ctrl_get_config (nvme_ctrl_t c)
+
+ Fabrics configuration of a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Fabrics configuration of **c**
+
+
+.. c:function:: void nvme_ctrl_set_discovered (nvme_ctrl_t c, bool discovered)
+
+ Set the 'discovered' flag
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ nvme_ctrl_t object
+
+``bool discovered``
+ Value of the 'discovered' flag
+
+**Description**
+
+Set the 'discovered' flag of **c** to **discovered**
+
+
+.. c:function:: bool nvme_ctrl_is_discovered (nvme_ctrl_t c)
+
+ Returns the value of the 'discovered' flag
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Value of the 'discovered' flag of **c**
+
+
+.. c:function:: void nvme_ctrl_set_persistent (nvme_ctrl_t c, bool persistent)
+
+ Set the 'persistent' flag
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+``bool persistent``
+ value of the 'persistent' flag
+
+**Description**
+
+Set the 'persistent' flag of **c** to **persistent**
+
+
+.. c:function:: bool nvme_ctrl_is_persistent (nvme_ctrl_t c)
+
+ Returns the value of the 'persistent' flag
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Return**
+
+Value of the 'persistent' flag of **c**
+
+
+.. c:function:: void nvme_ctrl_set_discovery_ctrl (nvme_ctrl_t c, bool discovery)
+
+ Set the 'discovery_ctrl' flag
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller to be modified
+
+``bool discovery``
+ value of the discovery_ctrl flag
+
+**Description**
+
+Sets the 'discovery_ctrl' flag in **c** to specify whether
+**c** connects to a discovery subsystem.
+
+
+.. c:function:: bool nvme_ctrl_is_discovery_ctrl (nvme_ctrl_t c)
+
+ Check the 'discovery_ctrl' flag
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller to be checked
+
+**Description**
+
+Returns the value of the 'discovery_ctrl' flag which specifies whether
+**c** connects to a discovery subsystem.
+
+**Return**
+
+Value of the 'discover_ctrl' flag
+
+
+.. c:function:: void nvme_ctrl_set_unique_discovery_ctrl (nvme_ctrl_t c, bool unique)
+
+ Set the 'unique_discovery_ctrl' flag
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller to be modified
+
+``bool unique``
+ value of the unique_disc_ctrl flag
+
+**Description**
+
+Sets the 'unique_discovery_ctrl' flag in **c** to specify wheter
+**c** is a unique discovery controller
+
+
+.. c:function:: bool nvme_ctrl_is_unique_discovery_ctrl (nvme_ctrl_t c)
+
+ Check the 'unique_discovery_ctrl' flag
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller to be checked
+
+**Return**
+
+Value of the 'unique_discovery_ctrl' flag
+
+
+.. c:function:: int nvme_ctrl_identify (nvme_ctrl_t c, struct nvme_id_ctrl *id)
+
+ Issues an 'identify controller' command
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+``struct nvme_id_ctrl *id``
+ Identify controller data structure
+
+**Description**
+
+Issues an 'identify controller' command to **c** and copies the
+data into **id**.
+
+**Return**
+
+0 on success or -1 on failure.
+
+
+.. c:function:: int nvme_disconnect_ctrl (nvme_ctrl_t c)
+
+ Disconnect a controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+**Description**
+
+Issues a 'disconnect' fabrics command to **c**
+
+**Return**
+
+0 on success, -1 on failure.
+
+
+.. c:function:: nvme_ctrl_t nvme_scan_ctrl (nvme_root_t r, const char *name)
+
+ Scan on a controller
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t object
+
+``const char *name``
+ Name of the controller
+
+**Description**
+
+Scans a controller with sysfs name **name** and add it to **r**.
+
+**Return**
+
+nvme_ctrl_t object
+
+
+.. c:function:: void nvme_rescan_ctrl (nvme_ctrl_t c)
+
+ Rescan an existing controller
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+
+.. c:function:: int nvme_init_ctrl (nvme_host_t h, nvme_ctrl_t c, int instance)
+
+ Initialize nvme_ctrl_t object for an existing controller.
+
+**Parameters**
+
+``nvme_host_t h``
+ nvme_host_t object
+
+``nvme_ctrl_t c``
+ nvme_ctrl_t object
+
+``int instance``
+ Instance number (e.g. 1 for nvme1)
+
+**Return**
+
+The ioctl() return code. Typically 0 on success.
+
+
+.. c:function:: void nvme_free_ctrl (struct nvme_ctrl *c)
+
+ Free controller
+
+**Parameters**
+
+``struct nvme_ctrl *c``
+ Controller instance
+
+
+.. c:function:: void nvme_unlink_ctrl (struct nvme_ctrl *c)
+
+ Unlink controller
+
+**Parameters**
+
+``struct nvme_ctrl *c``
+ Controller instance
+
+
+.. c:function:: const char * nvme_subsystem_get_nqn (nvme_subsystem_t s)
+
+ Retrieve NQN from subsystem
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ nvme_subsystem_t object
+
+**Return**
+
+NQN of subsystem
+
+
+.. c:function:: const char * nvme_subsystem_get_sysfs_dir (nvme_subsystem_t s)
+
+ sysfs directory of an nvme_subsystem_t object
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ nvme_subsystem_t object
+
+**Return**
+
+sysfs directory name of **s**
+
+
+.. c:function:: const char * nvme_subsystem_get_name (nvme_subsystem_t s)
+
+ sysfs name of an nvme_subsystem_t object
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ nvme_subsystem_t object
+
+**Return**
+
+sysfs name of **s**
+
+
+.. c:function:: const char * nvme_subsystem_get_type (nvme_subsystem_t s)
+
+ Returns the type of a subsystem
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ nvme_subsystem_t object
+
+**Description**
+
+Returns the subsystem type of **s**.
+
+**Return**
+
+'nvm' or 'discovery'
+
+
+.. c:function:: const char * nvme_subsystem_get_application (nvme_subsystem_t s)
+
+ Return the application string
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ nvme_subsystem_t object
+
+**Return**
+
+Managing application string or NULL if not set.
+
+
+.. c:function:: void nvme_subsystem_set_application (nvme_subsystem_t s, const char *a)
+
+ Set the application string
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ nvme_subsystem_t object
+
+``const char *a``
+ application string
+
+**Description**
+
+Sets the managing application string for **s**.
+
+
+.. c:function:: const char * nvme_subsystem_get_iopolicy (nvme_subsystem_t s)
+
+ Return the IO policy of subsytem
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ nvme_subsystem_t object
+
+**Return**
+
+IO policy used by current subsystem
+
+
+.. c:function:: int nvme_scan_topology (nvme_root_t r, nvme_scan_filter_t f, void *f_args)
+
+ Scan NVMe topology and apply filter
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t object
+
+``nvme_scan_filter_t f``
+ filter to apply
+
+``void *f_args``
+ user-specified argument to **f**
+
+**Description**
+
+Scans the NVMe topology and filters out the resulting elements
+by applying **f**.
+
+**Return**
+
+Number of elements scanned
+
+
+.. c:function:: const char * nvme_host_get_hostnqn (nvme_host_t h)
+
+ Host NQN of an nvme_host_t object
+
+**Parameters**
+
+``nvme_host_t h``
+ nvme_host_t object
+
+**Return**
+
+Host NQN of **h**
+
+
+.. c:function:: const char * nvme_host_get_hostid (nvme_host_t h)
+
+ Host ID of an nvme_host_t object
+
+**Parameters**
+
+``nvme_host_t h``
+ nvme_host_t object
+
+**Return**
+
+Host ID of **h**
+
+
+.. c:function:: void nvme_host_release_fds (struct nvme_host *h)
+
+ Close all opened file descriptors under host
+
+**Parameters**
+
+``struct nvme_host *h``
+ nvme_host_t object
+
+**Description**
+
+Controller and Namespace objects cache the file descriptors
+of opened nvme devices. This API can be used to close and
+clear all cached fds under this host.
+
+
+.. c:function:: void nvme_free_host (nvme_host_t h)
+
+ Free nvme_host_t object
+
+**Parameters**
+
+``nvme_host_t h``
+ nvme_host_t object
+
+
+.. c:function:: nvme_root_t nvme_scan (const char *config_file)
+
+ Scan NVMe topology
+
+**Parameters**
+
+``const char *config_file``
+ Configuration file
+
+**Return**
+
+nvme_root_t object of found elements
+
+
+.. c:function:: int nvme_read_config (nvme_root_t r, const char *config_file)
+
+ Read NVMe JSON configuration file
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t object
+
+``const char *config_file``
+ JSON configuration file
+
+**Description**
+
+Read in the contents of **config_file** and merge them with
+the elements in **r**.
+
+**Return**
+
+0 on success, -1 on failure with errno set.
+
+
+.. c:function:: void nvme_refresh_topology (nvme_root_t r)
+
+ Refresh nvme_root_t object contents
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t object
+
+**Description**
+
+Removes all elements in **r** and rescans the existing topology.
+
+
+.. c:function:: int nvme_update_config (nvme_root_t r)
+
+ Update JSON configuration
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t object
+
+**Description**
+
+Updates the JSON configuration file with the contents of **r**.
+
+**Return**
+
+0 on success, -1 on failure.
+
+
+.. c:function:: int nvme_dump_config (nvme_root_t r)
+
+ Print the JSON configuration
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t object
+
+**Description**
+
+Prints the current contents of the JSON configuration
+file to stdout.
+
+**Return**
+
+0 on success, -1 on failure.
+
+
+.. c:function:: int nvme_dump_tree (nvme_root_t r)
+
+ Dump internal object tree
+
+**Parameters**
+
+``nvme_root_t r``
+ nvme_root_t object
+
+**Description**
+
+Prints the internal object tree in JSON format
+to stdout.
+
+**Return**
+
+0 on success, -1 on failure.
+
+
+.. c:function:: char * nvme_get_attr (const char *d, const char *attr)
+
+ Read sysfs attribute
+
+**Parameters**
+
+``const char *d``
+ sysfs directory
+
+``const char *attr``
+ sysfs attribute name
+
+**Return**
+
+String with the contents of **attr** or ``NULL`` in case of an empty value
+ or in case of an error (indicated by non-zero errno code).
+
+
+.. c:function:: char * nvme_get_subsys_attr (nvme_subsystem_t s, const char *attr)
+
+ Read subsystem sysfs attribute
+
+**Parameters**
+
+``nvme_subsystem_t s``
+ nvme_subsystem_t object
+
+``const char *attr``
+ sysfs attribute name
+
+**Return**
+
+String with the contents of **attr** or ``NULL`` in case of an empty value
+ or in case of an error (indicated by non-zero errno code).
+
+
+.. c:function:: char * nvme_get_ctrl_attr (nvme_ctrl_t c, const char *attr)
+
+ Read controller sysfs attribute
+
+**Parameters**
+
+``nvme_ctrl_t c``
+ Controller instance
+
+``const char *attr``
+ sysfs attribute name
+
+**Return**
+
+String with the contents of **attr** or ``NULL`` in case of an empty value
+ or in case of an error (indicated by non-zero errno code).
+
+
+.. c:function:: char * nvme_get_ns_attr (nvme_ns_t n, const char *attr)
+
+ Read namespace sysfs attribute
+
+**Parameters**
+
+``nvme_ns_t n``
+ nvme_ns_t object
+
+``const char *attr``
+ sysfs attribute name
+
+**Return**
+
+String with the contents of **attr** or ``NULL`` in case of an empty value
+ or in case of an error (indicated by non-zero errno code).
+
+
+.. c:function:: nvme_ns_t nvme_subsystem_lookup_namespace (struct nvme_subsystem *s, __u32 nsid)
+
+ lookup namespace by NSID
+
+**Parameters**
+
+``struct nvme_subsystem *s``
+ nvme_subsystem_t object
+
+``__u32 nsid``
+ Namespace id
+
+**Return**
+
+nvme_ns_t of the namespace with id **nsid** in subsystem **s**
+
+
+.. c:function:: void nvme_subsystem_release_fds (struct nvme_subsystem *s)
+
+ Close all opened fds under subsystem
+
+**Parameters**
+
+``struct nvme_subsystem *s``
+ nvme_subsystem_t object
+
+**Description**
+
+Controller and Namespace objects cache the file descriptors
+of opened nvme devices. This API can be used to close and
+clear all cached fds under this subsystem.
+
+
+.. c:function:: char * nvme_get_path_attr (nvme_path_t p, const char *attr)
+
+ Read path sysfs attribute
+
+**Parameters**
+
+``nvme_path_t p``
+ nvme_path_t object
+
+``const char *attr``
+ sysfs attribute name
+
+**Return**
+
+String with the contents of **attr** or ``NULL`` in case of an empty value
+ or in case of an error (indicated by non-zero errno code).
+
+
+.. c:function:: nvme_ns_t nvme_scan_namespace (const char *name)
+
+ scan namespace based on sysfs name
+
+**Parameters**
+
+``const char *name``
+ sysfs name of the namespace to scan
+
+**Return**
+
+nvme_ns_t object or NULL if not found.
+
+
+.. c:function:: const char * nvme_host_get_hostsymname (nvme_host_t h)
+
+ Get the host's symbolic name
+
+**Parameters**
+
+``nvme_host_t h``
+ Host for which the symbolic name should be returned.
+
+**Return**
+
+The symbolic name or NULL if a symbolic name hasn't been
+configure.
+
+
+.. c:function:: void nvme_host_set_hostsymname (nvme_host_t h, const char *hostsymname)
+
+ Set the host's symbolic name
+
+**Parameters**
+
+``nvme_host_t h``
+ Host for which the symbolic name should be set.
+
+``const char *hostsymname``
+ Symbolic name
+
+
diff --git a/doc/rst/types.rst b/doc/rst/types.rst
new file mode 100644
index 0000000..2aecd14
--- /dev/null
+++ b/doc/rst/types.rst
@@ -0,0 +1,12335 @@
+.. _types.h:
+
+**types.h**
+
+
+NVMe standard definitions
+
+.. c:macro:: NVME_GET
+
+``NVME_GET (value, name)``
+
+ extract field from complex value
+
+**Parameters**
+
+``value``
+ The original value of a complex field
+
+``name``
+ The name of the sub-field within an nvme value
+
+**Description**
+
+By convention, this library defines _SHIFT and _MASK such that mask can be
+applied after the shift to isolate a specific set of bits that decode to a
+sub-field.
+
+**Return**
+
+The 'name' field from 'value'
+
+
+.. c:macro:: NVME_SET
+
+``NVME_SET (value, name)``
+
+ set field into complex value
+
+**Parameters**
+
+``value``
+ The value to be set in its completed position
+
+``name``
+ The name of the sub-field within an nvme value
+
+**Return**
+
+The 'name' field from 'value'
+
+
+
+
+.. c:enum:: nvme_constants
+
+ A place to stash various constant nvme values
+
+**Constants**
+
+``NVME_NSID_ALL``
+ A broadcast value that is used to specify all
+ namespaces
+
+``NVME_NSID_NONE``
+ The invalid namespace id, for when the nsid
+ parameter is not used in a command
+
+``NVME_UUID_NONE``
+ Use to omit a uuid command parameter
+
+``NVME_CNTLID_NONE``
+ Use to omit a cntlid command parameter
+
+``NVME_CNSSPECID_NONE``
+ Use to omit a cns_specific_id command parameter
+
+``NVME_LOG_LSP_NONE``
+ Use to omit a log lsp command parameter
+
+``NVME_LOG_LSI_NONE``
+ Use to omit a log lsi command parameter
+
+``NVME_LOG_LPO_NONE``
+ Use to omit a log lpo command parameter
+
+``NVME_IDENTIFY_DATA_SIZE``
+ The transfer size for nvme identify commands
+
+``NVME_LOG_SUPPORTED_LOG_PAGES_MAX``
+ The largest possible index in the supported
+ log pages log.
+
+``NVME_ID_NVMSET_LIST_MAX``
+ The largest possible nvmset index in identify
+ nvmeset
+
+``NVME_ID_UUID_LIST_MAX``
+ The largest possible uuid index in identify
+ uuid list
+
+``NVME_ID_CTRL_LIST_MAX``
+ The largest possible controller index in
+ identify controller list
+
+``NVME_ID_NS_LIST_MAX``
+ The largest possible namespace index in
+ identify namespace list
+
+``NVME_ID_SECONDARY_CTRL_MAX``
+ The largest possible secondary controller index
+ in identify secondary controller
+
+``NVME_ID_DOMAIN_LIST_MAX``
+ The largest possible domain index in the
+ in domain list
+
+``NVME_ID_ENDURANCE_GROUP_LIST_MAX``
+ The largest possible endurance group
+ index in the endurance group list
+
+``NVME_ID_ND_DESCRIPTOR_MAX``
+ The largest possible namespace granularity
+ index in the namespace granularity descriptor
+ list
+
+``NVME_FEAT_LBA_RANGE_MAX``
+ The largest possible LBA range index in feature
+ lba range type
+
+``NVME_LOG_ST_MAX_RESULTS``
+ The largest possible self test result index in the
+ device self test log
+
+``NVME_LOG_TELEM_BLOCK_SIZE``
+ Specification defined size of Telemetry Data Blocks
+
+``NVME_LOG_FID_SUPPORTED_EFFECTS_MAX``
+ The largest possible FID index in the
+ feature identifiers effects log.
+
+``NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX``
+ The largest possible MI Command index
+ in the MI Command effects log.
+
+``NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED``
+ The reserved space in the MI Command
+ effects log.
+
+``NVME_DSM_MAX_RANGES``
+ The largest possible range index in a data-set
+ management command
+
+``NVME_NQN_LENGTH``
+ Max length for NVMe Qualified Name
+
+``NVMF_TRADDR_SIZE``
+ Max Transport Address size
+
+``NVMF_TSAS_SIZE``
+ Max Transport Specific Address Subtype size
+
+``NVME_ZNS_CHANGED_ZONES_MAX``
+ Max number of zones in the changed zones log
+ page
+
+
+
+
+.. c:enum:: nvme_csi
+
+ Defined command set indicators
+
+**Constants**
+
+``NVME_CSI_NVM``
+ NVM Command Set Indicator
+
+``NVME_CSI_KV``
+ Key Value Command Set
+
+``NVME_CSI_ZNS``
+ Zoned Namespace Command Set
+
+
+
+
+.. c:enum:: nvme_register_offsets
+
+ controller registers for all transports. This is the layout of BAR0/1 for PCIe, and properties for fabrics.
+
+**Constants**
+
+``NVME_REG_CAP``
+ Controller Capabilities
+
+``NVME_REG_VS``
+ Version
+
+``NVME_REG_INTMS``
+ Interrupt Mask Set
+
+``NVME_REG_INTMC``
+ Interrupt Mask Clear
+
+``NVME_REG_CC``
+ Controller Configuration
+
+``NVME_REG_CSTS``
+ Controller Status
+
+``NVME_REG_NSSR``
+ NVM Subsystem Reset
+
+``NVME_REG_AQA``
+ Admin Queue Attributes
+
+``NVME_REG_ASQ``
+ Admin SQ Base Address
+
+``NVME_REG_ACQ``
+ Admin CQ Base Address
+
+``NVME_REG_CMBLOC``
+ Controller Memory Buffer Location
+
+``NVME_REG_CMBSZ``
+ Controller Memory Buffer Size
+
+``NVME_REG_BPINFO``
+ Boot Partition Information
+
+``NVME_REG_BPRSEL``
+ Boot Partition Read Select
+
+``NVME_REG_BPMBL``
+ Boot Partition Memory Buffer Location
+
+``NVME_REG_CMBMSC``
+ Controller Memory Buffer Memory Space Control
+
+``NVME_REG_CMBSTS``
+ Controller Memory Buffer Status
+
+``NVME_REG_CMBEBS``
+ Controller Memory Buffer Elasticity Buffer Size
+
+``NVME_REG_CMBSWTP``
+ Controller Memory Buffer Sustained Write Throughput
+
+``NVME_REG_NSSD``
+ NVM Subsystem Shutdown
+
+``NVME_REG_CRTO``
+ Controller Ready Timeouts
+
+``NVME_REG_PMRCAP``
+ Persistent Memory Capabilities
+
+``NVME_REG_PMRCTL``
+ Persistent Memory Region Control
+
+``NVME_REG_PMRSTS``
+ Persistent Memory Region Status
+
+``NVME_REG_PMREBS``
+ Persistent Memory Region Elasticity Buffer Size
+
+``NVME_REG_PMRSWTP``
+ Memory Region Sustained Write Throughput
+
+``NVME_REG_PMRMSCL``
+ Persistent Memory Region Controller Memory Space Control Lower
+
+``NVME_REG_PMRMSCU``
+ Persistent Memory Region Controller Memory Space Control Upper
+
+
+.. c:function:: bool nvme_is_64bit_reg (__u32 offset)
+
+ Checks if offset of the controller register is a know 64bit value.
+
+**Parameters**
+
+``__u32 offset``
+ Offset of controller register field in bytes
+
+**Description**
+
+This function does not care about transport so that the offset is not going
+to be checked inside of this function for the unsupported fields in a
+specific transport. For example, BPMBL(Boot Partition Memory Buffer
+Location) register is not supported by fabrics, but it can be checked here.
+
+**Return**
+
+true if given offset is 64bit register, otherwise it returns false.
+
+
+.. c:function:: __u64 nvme_cmb_size (__u32 cmbsz)
+
+ Calculate size of the controller memory buffer
+
+**Parameters**
+
+``__u32 cmbsz``
+ Value from controller register ``NVME_REG_CMBSZ``
+
+**Return**
+
+size of controller memory buffer in bytes
+
+
+.. c:function:: __u64 nvme_pmr_size (__u32 pmrebs)
+
+ Calculate size of persistent memory region elasticity buffer
+
+**Parameters**
+
+``__u32 pmrebs``
+ Value from controller register ``NVME_REG_PMREBS``
+
+**Return**
+
+size of controller persistent memory buffer in bytes
+
+
+.. c:function:: __u64 nvme_pmr_throughput (__u32 pmrswtp)
+
+ Calculate throughput of persistent memory buffer
+
+**Parameters**
+
+``__u32 pmrswtp``
+ Value from controller register ``NVME_REG_PMRSWTP``
+
+**Return**
+
+throughput of controller persistent memory buffer in bytes/second
+
+
+
+
+.. c:enum:: nvme_psd_flags
+
+ Possible flag values in nvme power state descriptor
+
+**Constants**
+
+``NVME_PSD_FLAGS_MXPS``
+ Indicates the scale for the Maximum Power
+ field. If this bit is cleared, then the scale of the
+ Maximum Power field is in 0.01 Watts. If this bit is
+ set, then the scale of the Maximum Power field is in
+ 0.0001 Watts.
+
+``NVME_PSD_FLAGS_NOPS``
+ Indicates whether the controller processes I/O
+ commands in this power state. If this bit is cleared,
+ then the controller processes I/O commands in this
+ power state. If this bit is set, then the controller
+ does not process I/O commands in this power state.
+
+
+
+
+.. c:enum:: nvme_psd_ps
+
+ Known values for :c:type:`struct nvme_psd <nvme_psd>` ``ips`` and ``aps``. Use with nvme_psd_power_scale() to extract the power scale field to match this enum.
+
+**Constants**
+
+``NVME_PSD_PS_NOT_REPORTED``
+ Not reported
+
+``NVME_PSD_PS_100_MICRO_WATT``
+ 0.0001 watt scale
+
+``NVME_PSD_PS_10_MILLI_WATT``
+ 0.01 watt scale
+
+
+.. c:function:: unsigned int nvme_psd_power_scale (__u8 ps)
+
+ power scale occupies the upper 3 bits
+
+**Parameters**
+
+``__u8 ps``
+ power scale value
+
+**Return**
+
+power scale value
+
+
+
+
+.. c:enum:: nvme_psd_workload
+
+ Specifies a workload hint in the Power Management Feature (see :c:type:`struct nvme_psd <nvme_psd>`.apw) to inform the NVM subsystem or indicate the conditions for the active power level.
+
+**Constants**
+
+``NVME_PSD_WORKLOAD_NP``
+ The workload is unknown or not provided.
+
+``NVME_PSD_WORKLOAD_1``
+ Extended Idle Period with a Burst of Random Write
+ consists of five minutes of idle followed by
+ thirty-two random write commands of size 1 MiB
+ submitted to a single controller while all other
+ controllers in the NVM subsystem are idle, and then
+ thirty (30) seconds of idle.
+
+``NVME_PSD_WORKLOAD_2``
+ Heavy Sequential Writes consists of 80,000
+ sequential write commands of size 128 KiB submitted to
+ a single controller while all other controllers in the
+ NVM subsystem are idle. The submission queue(s)
+ should be sufficiently large allowing the host to
+ ensure there are multiple commands pending at all
+ times during the workload.
+
+
+
+
+.. c:struct:: nvme_id_psd
+
+ Power Management data structure
+
+**Definition**
+
+::
+
+ struct nvme_id_psd {
+ __le16 mp;
+ __u8 rsvd2;
+ __u8 flags;
+ __le32 enlat;
+ __le32 exlat;
+ __u8 rrt;
+ __u8 rrl;
+ __u8 rwt;
+ __u8 rwl;
+ __le16 idlp;
+ __u8 ips;
+ __u8 rsvd19;
+ __le16 actp;
+ __u8 apws;
+ __u8 rsvd23[9];
+ };
+
+**Members**
+
+``mp``
+ Maximum Power indicates the sustained maximum power consumed by the
+ NVM subsystem in this power state. The power in Watts is equal to
+ the value in this field multiplied by the scale specified in the Max
+ Power Scale bit (see :c:type:`enum nvme_psd_flags <nvme_psd_flags>`). A value of 0 indicates
+ Maximum Power is not reported.
+
+``rsvd2``
+ Reserved
+
+``flags``
+ Additional decoding flags, see :c:type:`enum nvme_psd_flags <nvme_psd_flags>`.
+
+``enlat``
+ Entry Latency indicates the maximum latency in microseconds
+ associated with entering this power state. A value of 0 indicates
+ Entry Latency is not reported.
+
+``exlat``
+ Exit Latency indicates the maximum latency in microseconds
+ associated with exiting this power state. A value of 0 indicates
+ Exit Latency is not reported.
+
+``rrt``
+ Relative Read Throughput indicates the read throughput rank
+ associated with this power state relative to others. The value in
+ this is less than the number of supported power states.
+
+``rrl``
+ Relative Read Latency indicates the read latency rank associated
+ with this power state relative to others. The value in this field is
+ less than the number of supported power states.
+
+``rwt``
+ Relative Write Throughput indicates write throughput rank associated
+ with this power state relative to others. The value in this field is
+ less than the number of supported power states
+
+``rwl``
+ Relative Write Latency indicates the write latency rank associated
+ with this power state relative to others. The value in this field is
+ less than the number of supported power states
+
+``idlp``
+ Idle Power indicates the typical power consumed by the NVM
+ subsystem over 30 seconds in this power state when idle.
+
+``ips``
+ Idle Power Scale indicates the scale for :c:type:`struct nvme_id_psd <nvme_id_psd>`.idlp,
+ see :c:type:`enum nvme_psd_ps <nvme_psd_ps>` for decoding this field.
+
+``rsvd19``
+ Reserved
+
+``actp``
+ Active Power indicates the largest average power consumed by the
+ NVM subsystem over a 10 second period in this power state with
+ the workload indicated in the Active Power Workload field.
+
+``apws``
+ Bits 7-6: Active Power Scale(APS) indicates the scale for the :c:type:`struct
+ nvme_id_psd <nvme_id_psd>`.actp, see :c:type:`enum nvme_psd_ps <nvme_psd_ps>` for decoding this value.
+ Bits 2-0: Active Power Workload(APW) indicates the workload
+ used to calculate maximum power for this power state.
+ See :c:type:`enum nvme_psd_workload <nvme_psd_workload>` for decoding this field.
+
+``rsvd23``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_id_ctrl
+
+ Identify Controller data structure
+
+**Definition**
+
+::
+
+ struct nvme_id_ctrl {
+ __le16 vid;
+ __le16 ssvid;
+ char sn[20];
+ char mn[40];
+ char fr[8];
+ __u8 rab;
+ __u8 ieee[3];
+ __u8 cmic;
+ __u8 mdts;
+ __le16 cntlid;
+ __le32 ver;
+ __le32 rtd3r;
+ __le32 rtd3e;
+ __le32 oaes;
+ __le32 ctratt;
+ __le16 rrls;
+ __u8 rsvd102[9];
+ __u8 cntrltype;
+ __u8 fguid[16];
+ __le16 crdt1;
+ __le16 crdt2;
+ __le16 crdt3;
+ __u8 rsvd134[119];
+ __u8 nvmsr;
+ __u8 vwci;
+ __u8 mec;
+ __le16 oacs;
+ __u8 acl;
+ __u8 aerl;
+ __u8 frmw;
+ __u8 lpa;
+ __u8 elpe;
+ __u8 npss;
+ __u8 avscc;
+ __u8 apsta;
+ __le16 wctemp;
+ __le16 cctemp;
+ __le16 mtfa;
+ __le32 hmpre;
+ __le32 hmmin;
+ __u8 tnvmcap[16];
+ __u8 unvmcap[16];
+ __le32 rpmbs;
+ __le16 edstt;
+ __u8 dsto;
+ __u8 fwug;
+ __le16 kas;
+ __le16 hctma;
+ __le16 mntmt;
+ __le16 mxtmt;
+ __le32 sanicap;
+ __le32 hmminds;
+ __le16 hmmaxd;
+ __le16 nsetidmax;
+ __le16 endgidmax;
+ __u8 anatt;
+ __u8 anacap;
+ __le32 anagrpmax;
+ __le32 nanagrpid;
+ __le32 pels;
+ __le16 domainid;
+ __u8 rsvd358[10];
+ __u8 megcap[16];
+ __u8 rsvd384[128];
+ __u8 sqes;
+ __u8 cqes;
+ __le16 maxcmd;
+ __le32 nn;
+ __le16 oncs;
+ __le16 fuses;
+ __u8 fna;
+ __u8 vwc;
+ __le16 awun;
+ __le16 awupf;
+ __u8 icsvscc;
+ __u8 nwpc;
+ __le16 acwu;
+ __le16 ocfs;
+ __le32 sgls;
+ __le32 mnan;
+ __u8 maxdna[16];
+ __le32 maxcna;
+ __le32 oaqd;
+ __u8 rsvd568[200];
+ char subnqn[NVME_NQN_LENGTH];
+ __u8 rsvd1024[768];
+ __le32 ioccsz;
+ __le32 iorcsz;
+ __le16 icdoff;
+ __u8 fcatt;
+ __u8 msdbd;
+ __le16 ofcs;
+ __u8 dctype;
+ __u8 rsvd1807[241];
+ struct nvme_id_psd psd[32];
+ __u8 vs[1024];
+ };
+
+**Members**
+
+``vid``
+ PCI Vendor ID, the company vendor identifier that is assigned by
+ the PCI SIG.
+
+``ssvid``
+ PCI Subsystem Vendor ID, the company vendor identifier that is
+ assigned by the PCI SIG for the subsystem.
+
+``sn``
+ Serial Number in ASCII
+
+``mn``
+ Model Number in ASCII
+
+``fr``
+ Firmware Revision in ASCII, the currently active firmware
+ revision for the NVM subsystem
+
+``rab``
+ Recommended Arbitration Burst, reported as a power of two
+
+``ieee``
+ IEEE assigned Organization Unique Identifier
+
+``cmic``
+ Controller Multipath IO and Namespace Sharing Capabilities of
+ the controller and NVM subsystem. See :c:type:`enum nvme_id_ctrl_cmic <nvme_id_ctrl_cmic>`.
+
+``mdts``
+ Max Data Transfer Size is the largest data transfer size. The
+ host should not submit a command that exceeds this maximum data
+ transfer size. The value is in units of the minimum memory page
+ size (CAP.MPSMIN) and is reported as a power of two
+
+``cntlid``
+ Controller ID, the NVM subsystem unique controller identifier
+ associated with the controller.
+
+``ver``
+ Version, this field contains the value reported in the Version
+ register, or property (see :c:type:`enum nvme_registers <nvme_registers>` ``NVME_REG_VS``).
+
+``rtd3r``
+ RTD3 Resume Latency, the expected latency in microseconds to resume
+ from Runtime D3
+
+``rtd3e``
+ RTD3 Exit Latency, the typical latency in microseconds to enter
+ Runtime D3.
+
+``oaes``
+ Optional Async Events Supported, see **enum** nvme_id_ctrl_oaes.
+
+``ctratt``
+ Controller Attributes, see **enum** nvme_id_ctrl_ctratt.
+
+``rrls``
+ Read Recovery Levels. If a bit is set, then the corresponding
+ Read Recovery Level is supported. If a bit is cleared, then the
+ corresponding Read Recovery Level is not supported.
+
+``rsvd102``
+ Reserved
+
+``cntrltype``
+ Controller Type, see :c:type:`enum nvme_id_ctrl_cntrltype <nvme_id_ctrl_cntrltype>`
+
+``fguid``
+ FRU GUID, a 128-bit value that is globally unique for a given
+ Field Replaceable Unit
+
+``crdt1``
+ Controller Retry Delay time in 100 millisecond units if CQE CRD
+ field is 1
+
+``crdt2``
+ Controller Retry Delay time in 100 millisecond units if CQE CRD
+ field is 2
+
+``crdt3``
+ Controller Retry Delay time in 100 millisecond units if CQE CRD
+ field is 3
+
+``rsvd134``
+ Reserved
+
+``nvmsr``
+ NVM Subsystem Report, see :c:type:`enum nvme_id_ctrl_nvmsr <nvme_id_ctrl_nvmsr>`
+
+``vwci``
+ VPD Write Cycle Information, see :c:type:`enum nvme_id_ctrl_vwci <nvme_id_ctrl_vwci>`
+
+``mec``
+ Management Endpoint Capabilities, see :c:type:`enum nvme_id_ctrl_mec <nvme_id_ctrl_mec>`
+
+``oacs``
+ Optional Admin Command Support,the optional Admin commands and
+ features supported by the controller, see :c:type:`enum nvme_id_ctrl_oacs <nvme_id_ctrl_oacs>`.
+
+``acl``
+ Abort Command Limit, the maximum number of concurrently
+ executing Abort commands supported by the controller. This is a
+ 0's based value.
+
+``aerl``
+ Async Event Request Limit, the maximum number of concurrently
+ outstanding Asynchronous Event Request commands supported by the
+ controller This is a 0's based value.
+
+``frmw``
+ Firmware Updates indicates capabilities regarding firmware
+ updates. See :c:type:`enum nvme_id_ctrl_frmw <nvme_id_ctrl_frmw>`.
+
+``lpa``
+ Log Page Attributes, see :c:type:`enum nvme_id_ctrl_lpa <nvme_id_ctrl_lpa>`.
+
+``elpe``
+ Error Log Page Entries, the maximum number of Error Information
+ log entries that are stored by the controller. This field is a
+ 0's based value.
+
+``npss``
+ Number of Power States Supported, the number of NVM Express
+ power states supported by the controller, indicating the number
+ of valid entries in :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.psd. This is a 0's
+ based value.
+
+``avscc``
+ Admin Vendor Specific Command Configuration, see
+ :c:type:`enum nvme_id_ctrl_avscc <nvme_id_ctrl_avscc>`.
+
+``apsta``
+ Autonomous Power State Transition Attributes, see
+ :c:type:`enum nvme_id_ctrl_apsta <nvme_id_ctrl_apsta>`.
+
+``wctemp``
+ Warning Composite Temperature Threshold indicates
+ the minimum Composite Temperature field value (see :c:type:`struct
+ nvme_smart_log <nvme_smart_log>`.critical_comp_time) that indicates an overheating
+ condition during which controller operation continues.
+
+``cctemp``
+ Critical Composite Temperature Threshold, field indicates the
+ minimum Composite Temperature field value (see :c:type:`struct
+ nvme_smart_log <nvme_smart_log>`.critical_comp_time) that indicates a critical
+ overheating condition.
+
+``mtfa``
+ Maximum Time for Firmware Activation indicates the maximum time
+ the controller temporarily stops processing commands to activate
+ the firmware image, specified in 100 millisecond units. This
+ field is always valid if the controller supports firmware
+ activation without a reset.
+
+``hmpre``
+ Host Memory Buffer Preferred Size indicates the preferred size
+ that the host is requested to allocate for the Host Memory
+ Buffer feature in 4 KiB units.
+
+``hmmin``
+ Host Memory Buffer Minimum Size indicates the minimum size that
+ the host is requested to allocate for the Host Memory Buffer
+ feature in 4 KiB units.
+
+``tnvmcap``
+ Total NVM Capacity, the total NVM capacity in the NVM subsystem.
+ The value is in bytes.
+
+``unvmcap``
+ Unallocated NVM Capacity, the unallocated NVM capacity in the
+ NVM subsystem. The value is in bytes.
+
+``rpmbs``
+ Replay Protected Memory Block Support, see
+ :c:type:`enum nvme_id_ctrl_rpmbs <nvme_id_ctrl_rpmbs>`.
+
+``edstt``
+ Extended Device Self-test Time, if Device Self-test command is
+ supported (see :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.oacs, ``NVME_CTRL_OACS_SELF_TEST``),
+ then this field indicates the nominal amount of time in one
+ minute units that the controller takes to complete an extended
+ device self-test operation when in power state 0.
+
+``dsto``
+ Device Self-test Options, see :c:type:`enum nvme_id_ctrl_dsto <nvme_id_ctrl_dsto>`.
+
+``fwug``
+ Firmware Update Granularity indicates the granularity and
+ alignment requirement of the firmware image being updated by the
+ Firmware Image Download command. The value is reported in 4 KiB
+ units. A value of 0h indicates no information on granularity is
+ provided. A value of FFh indicates no restriction
+
+``kas``
+ Keep Alive Support indicates the granularity of the Keep Alive
+ Timer in 100 millisecond units.
+
+``hctma``
+ Host Controlled Thermal Management Attributes, see
+ :c:type:`enum nvme_id_ctrl_hctm <nvme_id_ctrl_hctm>`.
+
+``mntmt``
+ Minimum Thermal Management Temperature indicates the minimum
+ temperature, in degrees Kelvin, that the host may request in the
+ Thermal Management Temperature 1 field and Thermal Management
+ Temperature 2 field of a Set Features command with the Feature
+ Identifier field set to ``NVME_FEAT_FID_HCTM``.
+
+``mxtmt``
+ Maximum Thermal Management Temperature indicates the maximum
+ temperature, in degrees Kelvin, that the host may request in the
+ Thermal Management Temperature 1 field and Thermal Management
+ Temperature 2 field of the Set Features command with the Feature
+ Identifier set to ``NVME_FEAT_FID_HCTM``.
+
+``sanicap``
+ Sanitize Capabilities, see :c:type:`enum nvme_id_ctrl_sanicap <nvme_id_ctrl_sanicap>`
+
+``hmminds``
+ Host Memory Buffer Minimum Descriptor Entry Size indicates the
+ minimum usable size of a Host Memory Buffer Descriptor Entry in
+ 4 KiB units.
+
+``hmmaxd``
+ Host Memory Maximum Descriptors Entries indicates the number of
+ usable Host Memory Buffer Descriptor Entries.
+
+``nsetidmax``
+ NVM Set Identifier Maximum, defines the maximum value of a valid
+ NVM Set Identifier for any controller in the NVM subsystem.
+
+``endgidmax``
+ Endurance Group Identifier Maximum, defines the maximum value of
+ a valid Endurance Group Identifier for any controller in the NVM
+ subsystem.
+
+``anatt``
+ ANA Transition Time indicates the maximum amount of time, in
+ seconds, for a transition between ANA states or the maximum
+ amount of time, in seconds, that the controller reports the ANA
+ change state.
+
+``anacap``
+ Asymmetric Namespace Access Capabilities, see
+ :c:type:`enum nvme_id_ctrl_anacap <nvme_id_ctrl_anacap>`.
+
+``anagrpmax``
+ ANA Group Identifier Maximum indicates the maximum value of a
+ valid ANA Group Identifier for any controller in the NVM
+ subsystem.
+
+``nanagrpid``
+ Number of ANA Group Identifiers indicates the number of ANA
+ groups supported by this controller.
+
+``pels``
+ Persistent Event Log Size indicates the maximum reportable size
+ for the Persistent Event Log.
+
+``domainid``
+ Domain Identifier indicates the identifier of the domain
+ that contains this controller.
+
+``rsvd358``
+ Reserved
+
+``megcap``
+ Max Endurance Group Capacity indicates the maximum capacity
+ of a single Endurance Group.
+
+``rsvd384``
+ Reserved
+
+``sqes``
+ Submission Queue Entry Size, see :c:type:`enum nvme_id_ctrl_sqes <nvme_id_ctrl_sqes>`.
+
+``cqes``
+ Completion Queue Entry Size, see :c:type:`enum nvme_id_ctrl_cqes <nvme_id_ctrl_cqes>`.
+
+``maxcmd``
+ Maximum Outstanding Commands indicates the maximum number of
+ commands that the controller processes at one time for a
+ particular queue.
+
+``nn``
+ Number of Namespaces indicates the maximum value of a valid
+ nsid for the NVM subsystem. If the MNAN (:c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.mnan
+ field is cleared to 0h, then this field also indicates the
+ maximum number of namespaces supported by the NVM subsystem.
+
+``oncs``
+ Optional NVM Command Support, see :c:type:`enum nvme_id_ctrl_oncs <nvme_id_ctrl_oncs>`.
+
+``fuses``
+ Fused Operation Support, see :c:type:`enum nvme_id_ctrl_fuses <nvme_id_ctrl_fuses>`.
+
+``fna``
+ Format NVM Attributes, see :c:type:`enum nvme_id_ctrl_fna <nvme_id_ctrl_fna>`.
+
+``vwc``
+ Volatile Write Cache, see :c:type:`enum nvme_id_ctrl_vwc <nvme_id_ctrl_vwc>`.
+
+``awun``
+ Atomic Write Unit Normal indicates the size of the write
+ operation guaranteed to be written atomically to the NVM across
+ all namespaces with any supported namespace format during normal
+ operation. This field is specified in logical blocks and is a
+ 0's based value.
+
+``awupf``
+ Atomic Write Unit Power Fail indicates the size of the write
+ operation guaranteed to be written atomically to the NVM across
+ all namespaces with any supported namespace format during a
+ power fail or error condition. This field is specified in
+ logical blocks and is a 0’s based value.
+
+``icsvscc``
+ NVM Vendor Specific Command Configuration, see
+ :c:type:`enum nvme_id_ctrl_nvscc <nvme_id_ctrl_nvscc>`.
+
+``nwpc``
+ Namespace Write Protection Capabilities, see
+ :c:type:`enum nvme_id_ctrl_nwpc <nvme_id_ctrl_nwpc>`.
+
+``acwu``
+ Atomic Compare & Write Unit indicates the size of the write
+ operation guaranteed to be written atomically to the NVM across
+ all namespaces with any supported namespace format for a Compare
+ and Write fused operation. This field is specified in logical
+ blocks and is a 0’s based value.
+
+``ocfs``
+ Optional Copy Formats Supported, each bit n means controller
+ supports Copy Format n.
+
+``sgls``
+ SGL Support, see :c:type:`enum nvme_id_ctrl_sgls <nvme_id_ctrl_sgls>`
+
+``mnan``
+ Maximum Number of Allowed Namespaces indicates the maximum
+ number of namespaces supported by the NVM subsystem.
+
+``maxdna``
+ Maximum Domain Namespace Attachments indicates the maximum
+ of the sum of the number of namespaces attached to each I/O
+ controller in the Domain.
+
+``maxcna``
+ Maximum I/O Controller Namespace Attachments indicates the
+ maximum number of namespaces that are allowed to be attached to
+ this I/O controller.
+
+``oaqd``
+ Optimal Aggregated Queue Depth indicates the recommended maximum
+ total number of outstanding I/O commands across all I/O queues
+ on the controller for optimal operation.
+
+``rsvd568``
+ Reserved
+
+``subnqn``
+ NVM Subsystem NVMe Qualified Name, UTF-8 null terminated string
+
+``rsvd1024``
+ Reserved
+
+``ioccsz``
+ I/O Queue Command Capsule Supported Size, defines the maximum
+ I/O command capsule size in 16 byte units.
+
+``iorcsz``
+ I/O Queue Response Capsule Supported Size, defines the maximum
+ I/O response capsule size in 16 byte units.
+
+``icdoff``
+ In Capsule Data Offset, defines the offset where data starts
+ within a capsule. This value is applicable to I/O Queues only.
+
+``fcatt``
+ Fabrics Controller Attributes, see :c:type:`enum nvme_id_ctrl_fcatt <nvme_id_ctrl_fcatt>`.
+
+``msdbd``
+ Maximum SGL Data Block Descriptors indicates the maximum
+ number of SGL Data Block or Keyed SGL Data Block descriptors
+ that a host is allowed to place in a capsule. A value of 0h
+ indicates no limit.
+
+``ofcs``
+ Optional Fabric Commands Support, see :c:type:`enum nvme_id_ctrl_ofcs <nvme_id_ctrl_ofcs>`.
+
+``dctype``
+ Discovery Controller Type (DCTYPE). This field indicates what
+ type of Discovery controller the controller is (see enum
+ nvme_id_ctrl_dctype)
+
+``rsvd1807``
+ Reserved
+
+``psd``
+ Power State Descriptors, see :c:type:`struct nvme_id_psd <nvme_id_psd>`.
+
+``vs``
+ Vendor Specific
+
+
+
+
+
+.. c:enum:: nvme_id_ctrl_cmic
+
+ Controller Multipath IO and Namespace Sharing Capabilities of the controller and NVM subsystem.
+
+**Constants**
+
+``NVME_CTRL_CMIC_MULTI_PORT``
+ If set, then the NVM subsystem may contain
+ more than one NVM subsystem port, otherwise
+ the NVM subsystem contains only a single
+ NVM subsystem port.
+
+``NVME_CTRL_CMIC_MULTI_CTRL``
+ If set, then the NVM subsystem may contain
+ two or more controllers, otherwise the
+ NVM subsystem contains only a single
+ controller. An NVM subsystem that contains
+ multiple controllers may be used by
+ multiple hosts, or may provide multiple
+ paths for a single host.
+
+``NVME_CTRL_CMIC_MULTI_SRIOV``
+ If set, then the controller is associated
+ with an SR-IOV Virtual Function, otherwise
+ it is associated with a PCI Function
+ or a Fabrics connection.
+
+``NVME_CTRL_CMIC_MULTI_ANA_REPORTING``
+ If set, then the NVM subsystem supports
+ Asymmetric Namespace Access Reporting.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_oaes
+
+ Optional Asynchronous Events Supported
+
+**Constants**
+
+``NVME_CTRL_OAES_NA``
+ Namespace Attribute Notices event supported
+
+``NVME_CTRL_OAES_FA``
+ Firmware Activation Notices event supported
+
+``NVME_CTRL_OAES_ANA``
+ ANA Change Notices supported
+
+``NVME_CTRL_OAES_PLEA``
+ Predictable Latency Event Aggregate Log
+ Change Notices event supported
+
+``NVME_CTRL_OAES_LBAS``
+ LBA Status Information Notices event supported
+
+``NVME_CTRL_OAES_EGE``
+ Endurance Group Events Aggregate Log Change
+ Notices event supported
+
+``NVME_CTRL_OAES_NS``
+ Normal NVM Subsystem Shutdown event supported
+
+``NVME_CTRL_OAES_ZD``
+ Zone Descriptor Change Notifications supported
+
+``NVME_CTRL_OAES_DL``
+ Discover Log Page Change Notifications supported
+
+
+
+
+.. c:enum:: nvme_id_ctrl_ctratt
+
+ Controller attributes
+
+**Constants**
+
+``NVME_CTRL_CTRATT_128_ID``
+ 128-bit Host Identifier supported
+
+``NVME_CTRL_CTRATT_NON_OP_PSP``
+ Non-Operational Poser State Permissive Mode
+ supported
+
+``NVME_CTRL_CTRATT_NVM_SETS``
+ NVM Sets supported
+
+``NVME_CTRL_CTRATT_READ_RECV_LVLS``
+ Read Recovery Levels supported
+
+``NVME_CTRL_CTRATT_ENDURANCE_GROUPS``
+ Endurance Groups supported
+
+``NVME_CTRL_CTRATT_PREDICTABLE_LAT``
+ Predictable Latency Mode supported
+
+``NVME_CTRL_CTRATT_TBKAS``
+ Traffic Based Keep Alive Support
+
+``NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY``
+ Namespace Granularity reporting
+ supported
+
+``NVME_CTRL_CTRATT_SQ_ASSOCIATIONS``
+ SQ Associations supported
+
+``NVME_CTRL_CTRATT_UUID_LIST``
+ UUID List reporting supported
+
+``NVME_CTRL_CTRATT_MDS``
+ Multi-Domain Subsystem supported
+
+``NVME_CTRL_CTRATT_FIXED_CAP``
+ Fixed Capacity Management supported
+
+``NVME_CTRL_CTRATT_VARIABLE_CAP``
+ Variable Capacity Management supported
+
+``NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS``
+ Delete Endurance Groups supported
+
+``NVME_CTRL_CTRATT_DEL_NVM_SETS``
+ Delete NVM Sets supported
+
+``NVME_CTRL_CTRATT_ELBAS``
+ Extended LBA Formats supported
+
+``NVME_CTRL_CTRATT_FDPS``
+ Flexible Data Placement supported
+
+
+
+
+.. c:enum:: nvme_id_ctrl_cntrltype
+
+ Controller types
+
+**Constants**
+
+``NVME_CTRL_CNTRLTYPE_IO``
+ NVM I/O controller
+
+``NVME_CTRL_CNTRLTYPE_DISCOVERY``
+ Discovery controller
+
+``NVME_CTRL_CNTRLTYPE_ADMIN``
+ Admin controller
+
+
+
+
+.. c:enum:: nvme_id_ctrl_dctype
+
+ Discovery Controller types
+
+**Constants**
+
+``NVME_CTRL_DCTYPE_NOT_REPORTED``
+ Not reported (I/O, Admin, and pre-TP8010)
+
+``NVME_CTRL_DCTYPE_DDC``
+ Direct Discovery controller
+
+``NVME_CTRL_DCTYPE_CDC``
+ Central Discovery controller
+
+
+
+
+.. c:enum:: nvme_id_ctrl_nvmsr
+
+ This field reports information associated with the NVM Subsystem, see :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.nvmsr.
+
+**Constants**
+
+``NVME_CTRL_NVMSR_NVMESD``
+ If set, then the NVM Subsystem is part of an NVMe
+ Storage Device; if cleared, then the NVM Subsystem
+ is not part of an NVMe Storage Device.
+
+``NVME_CTRL_NVMSR_NVMEE``
+ If set’, then the NVM Subsystem is part of an NVMe
+ Enclosure; if cleared, then the NVM Subsystem is
+ not part of an NVMe Enclosure.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_vwci
+
+ This field indicates information about remaining number of times that VPD contents are able to be updated using the VPD Write command, see :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.vwci.
+
+**Constants**
+
+``NVME_CTRL_VWCI_VWCR``
+ Mask to get value of VPD Write Cycles Remaining. If
+ the VPD Write Cycle Remaining Valid bit is set, then
+ this field contains a value indicating the remaining
+ number of times that VPD contents are able to be
+ updated using the VPD Write command. If this field is
+ set to 7Fh, then the remaining number of times that
+ VPD contents are able to be updated using the VPD
+ Write command is greater than or equal to 7Fh.
+
+``NVME_CTRL_VWCI_VWCRV``
+ VPD Write Cycle Remaining Valid. If this bit is set,
+ then the VPD Write Cycle Remaining field is valid. If
+ this bit is cleared, then the VPD Write Cycles
+ Remaining field is invalid and cleared to 0h.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_mec
+
+ Flags indicating the capabilities of the Management Endpoint in the Controller, :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.mec.
+
+**Constants**
+
+``NVME_CTRL_MEC_SMBUSME``
+ If set, then the NVM Subsystem contains a Management
+ Endpoint on an SMBus/I2C port.
+
+``NVME_CTRL_MEC_PCIEME``
+ If set, then the NVM Subsystem contains a Management
+ Endpoint on a PCIe port.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_oacs
+
+ Flags indicating the optional Admin commands and features supported by the controller, see :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.oacs.
+
+**Constants**
+
+``NVME_CTRL_OACS_SECURITY``
+ If set, then the controller supports the
+ Security Send and Security Receive commands.
+
+``NVME_CTRL_OACS_FORMAT``
+ If set then the controller supports the Format
+ NVM command.
+
+``NVME_CTRL_OACS_FW``
+ If set, then the controller supports the
+ Firmware Commit and Firmware Image Download commands.
+
+``NVME_CTRL_OACS_NS_MGMT``
+ If set, then the controller supports the
+ Namespace Management capability
+
+``NVME_CTRL_OACS_SELF_TEST``
+ If set, then the controller supports the Device
+ Self-test command.
+
+``NVME_CTRL_OACS_DIRECTIVES``
+ If set, then the controller supports Directives
+ and the Directive Send and Directive Receive
+ commands.
+
+``NVME_CTRL_OACS_NVME_MI``
+ If set, then the controller supports the NVMe-MI
+ Send and NVMe-MI Receive commands.
+
+``NVME_CTRL_OACS_VIRT_MGMT``
+ If set, then the controller supports the
+ Virtualization Management command.
+
+``NVME_CTRL_OACS_DBBUF_CFG``
+ If set, then the controller supports the
+ Doorbell Buffer Config command.
+
+``NVME_CTRL_OACS_LBA_STATUS``
+ If set, then the controller supports the Get LBA
+ Status capability.
+
+``NVME_CTRL_OACS_CMD_FEAT_LD``
+ If set, then the controller supports the command
+ and feature lockdown capability.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_frmw
+
+ Flags and values indicates capabilities regarding firmware updates from :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.frmw.
+
+**Constants**
+
+``NVME_CTRL_FRMW_1ST_RO``
+ If set, the first firmware slot is readonly
+
+``NVME_CTRL_FRMW_NR_SLOTS``
+ Mask to get the value of the number of
+ firmware slots that the controller supports.
+
+``NVME_CTRL_FRMW_FW_ACT_NO_RESET``
+ If set, the controller supports firmware
+ activation without a reset.
+
+``NVME_CTRL_FRMW_MP_UP_DETECTION``
+ If set, the controller is able to detect
+ overlapping firmware/boot partition
+ image update.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_lpa
+
+ Flags indicating optional attributes for log pages that are accessed via the Get Log Page command.
+
+**Constants**
+
+``NVME_CTRL_LPA_SMART_PER_NS``
+ If set, controller supports SMART/Health log
+ page on a per namespace basis.
+
+``NVME_CTRL_LPA_CMD_EFFECTS``
+ If Set, the controller supports the commands
+ supported and effects log page.
+
+``NVME_CTRL_LPA_EXTENDED``
+ If set, the controller supports extended data
+ for log page command including extended number
+ of dwords and log page offset fields.
+
+``NVME_CTRL_LPA_TELEMETRY``
+ If set, the controller supports the telemetry
+ host-initiated and telemetry controller-initiated
+ log pages and sending telemetry log notices.
+
+``NVME_CTRL_LPA_PERSETENT_EVENT``
+ If set, the controller supports
+ persistent event log.
+
+``NVME_CTRL_LPA_LI0_LI5_LI12_LI13``
+ If set, the controller supports
+ - log pages log page.
+ - returning scope of each command in
+ commands supported and effects log
+ page.
+ - feature identifiers supported and
+ effects log page.
+ - NVMe-MI commands supported and
+ effects log page.
+
+``NVME_CTRL_LPA_DA4_TELEMETRY``
+ If set, the controller supports data
+ area 4 for telemetry host-initiated and
+ telemetry.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_avscc
+
+ Flags indicating the configuration settings for Admin Vendor Specific command handling.
+
+**Constants**
+
+``NVME_CTRL_AVSCC_AVS``
+ If set, all Admin Vendor Specific Commands use the
+ optional vendor specific command format with NDT and
+ NDM fields.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_apsta
+
+ Flags indicating the attributes of the autonomous power state transition feature.
+
+**Constants**
+
+``NVME_CTRL_APSTA_APST``
+ If set, then the controller supports autonomous power
+ state transitions.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_rpmbs
+
+ This field indicates if the controller supports one or more Replay Protected Memory Blocks, from :c:type:`struct nvme_id_ctrl <nvme_id_ctrl>`.rpmbs.
+
+**Constants**
+
+``NVME_CTRL_RPMBS_NR_UNITS``
+ Mask to get the value of the Number of RPMB Units
+
+``NVME_CTRL_RPMBS_AUTH_METHOD``
+ Mask to get the value of the Authentication Method
+
+``NVME_CTRL_RPMBS_TOTAL_SIZE``
+ Mask to get the value of Total Size
+
+``NVME_CTRL_RPMBS_ACCESS_SIZE``
+ Mask to get the value of Access Size
+
+
+
+
+.. c:enum:: nvme_id_ctrl_dsto
+
+ Flags indicating the optional Device Self-test command or operation behaviors supported by the controller or NVM subsystem.
+
+**Constants**
+
+``NVME_CTRL_DSTO_ONE_DST``
+ If set, then the NVM subsystem supports only one
+ device self-test operation in progress at a time.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_hctm
+
+ Flags indicate the attributes of the host controlled thermal management feature
+
+**Constants**
+
+``NVME_CTRL_HCTMA_HCTM``
+ then the controller supports host controlled thermal
+ management, and the Set Features command and Get
+ Features command with the Feature Identifier field
+ set to ``NVME_FEAT_FID_HCTM``.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_sanicap
+
+ Indicates attributes for sanitize operations.
+
+**Constants**
+
+``NVME_CTRL_SANICAP_CES``
+ Crypto Erase Support. If set, then the
+ controller supports the Crypto Erase sanitize operation.
+
+``NVME_CTRL_SANICAP_BES``
+ Block Erase Support. If set, then the controller
+ supports the Block Erase sanitize operation.
+
+``NVME_CTRL_SANICAP_OWS``
+ Overwrite Support. If set, then the controller
+ supports the Overwrite sanitize operation.
+
+``NVME_CTRL_SANICAP_NDI``
+ No-Deallocate Inhibited. If set and the No-
+ Deallocate Response Mode bit is set, then the
+ controller deallocates after the sanitize
+ operation even if the No-Deallocate After
+ Sanitize bit is set in a Sanitize command.
+
+``NVME_CTRL_SANICAP_NODMMAS``
+ No-Deallocate Modifies Media After Sanitize,
+ mask to extract value.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_anacap
+
+ This field indicates the capabilities associated with Asymmetric Namespace Access Reporting.
+
+**Constants**
+
+``NVME_CTRL_ANACAP_OPT``
+ If set, then the controller is able to
+ report ANA Optimized state.
+
+``NVME_CTRL_ANACAP_NON_OPT``
+ If set, then the controller is able to
+ report ANA Non-Optimized state.
+
+``NVME_CTRL_ANACAP_INACCESSIBLE``
+ If set, then the controller is able to
+ report ANA Inaccessible state.
+
+``NVME_CTRL_ANACAP_PERSISTENT_LOSS``
+ If set, then the controller is able to
+ report ANA Persistent Loss state.
+
+``NVME_CTRL_ANACAP_CHANGE``
+ If set, then the controller is able to
+ report ANA Change state.
+
+``NVME_CTRL_ANACAP_GRPID_NO_CHG``
+ If set, then the ANAGRPID field in the
+ Identify Namespace data structure
+ (:c:type:`struct nvme_id_ns <nvme_id_ns>`.anagrpid), does not
+ change while the namespace is attached to
+ any controller.
+
+``NVME_CTRL_ANACAP_GRPID_MGMT``
+ If set, then the controller supports a
+ non-zero value in the ANAGRPID field of
+ the Namespace Management command.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_sqes
+
+ Defines the required and maximum Submission Queue entry size when using the NVM Command Set.
+
+**Constants**
+
+``NVME_CTRL_SQES_MIN``
+ Mask to get the value of the required Submission Queue
+ Entry size when using the NVM Command Set.
+
+``NVME_CTRL_SQES_MAX``
+ Mask to get the value of the maximum Submission Queue
+ entry size when using the NVM Command Set.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_cqes
+
+ Defines the required and maximum Completion Queue entry size when using the NVM Command Set.
+
+**Constants**
+
+``NVME_CTRL_CQES_MIN``
+ Mask to get the value of the required Completion Queue
+ Entry size when using the NVM Command Set.
+
+``NVME_CTRL_CQES_MAX``
+ Mask to get the value of the maximum Completion Queue
+ entry size when using the NVM Command Set.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_oncs
+
+ This field indicates the optional NVM commands and features supported by the controller.
+
+**Constants**
+
+``NVME_CTRL_ONCS_COMPARE``
+ If set, then the controller supports
+ the Compare command.
+
+``NVME_CTRL_ONCS_WRITE_UNCORRECTABLE``
+ If set, then the controller supports
+ the Write Uncorrectable command.
+
+``NVME_CTRL_ONCS_DSM``
+ If set, then the controller supports
+ the Dataset Management command.
+
+``NVME_CTRL_ONCS_WRITE_ZEROES``
+ If set, then the controller supports
+ the Write Zeroes command.
+
+``NVME_CTRL_ONCS_SAVE_FEATURES``
+ If set, then the controller supports
+ the Save field set to a non-zero value
+ in the Set Features command and the
+ Select field set to a non-zero value in
+ the Get Features command.
+
+``NVME_CTRL_ONCS_RESERVATIONS``
+ If set, then the controller supports
+ reservations.
+
+``NVME_CTRL_ONCS_TIMESTAMP``
+ If set, then the controller supports
+ the Timestamp feature.
+
+``NVME_CTRL_ONCS_VERIFY``
+ If set, then the controller supports
+ the Verify command.
+
+``NVME_CTRL_ONCS_COPY``
+ If set, then the controller supports
+ the copy command.
+
+``NVME_CTRL_ONCS_COPY_SINGLE_ATOMICITY``
+ If set, then the write portion of a
+ Copy command is performed as a single
+ write command to which the same
+ atomicity requirements that apply to
+ a write command apply.
+
+``NVME_CTRL_ONCS_ALL_FAST_COPY``
+ If set, then all copy operations for
+ the Copy command are fast copy
+ operations.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_fuses
+
+ This field indicates the fused operations that the controller supports.
+
+**Constants**
+
+``NVME_CTRL_FUSES_COMPARE_AND_WRITE``
+ If set, then the controller supports the
+ Compare and Write fused operation.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_fna
+
+ This field indicates attributes for the Format NVM command.
+
+**Constants**
+
+``NVME_CTRL_FNA_FMT_ALL_NAMESPACES``
+ If set, then all namespaces in an NVM
+ subsystem shall be configured with the
+ same attributes and a format (excluding
+ secure erase) of any namespace results in
+ a format of all namespaces in an NVM
+ subsystem. If cleared, then the
+ controller supports format on a per
+ namespace basis.
+
+``NVME_CTRL_FNA_SEC_ALL_NAMESPACES``
+ If set, then any secure erase performed
+ as part of a format operation results in
+ a secure erase of all namespaces in the
+ NVM subsystem. If cleared, then any
+ secure erase performed as part of a
+ format results in a secure erase of the
+ particular namespace specified.
+
+``NVME_CTRL_FNA_CRYPTO_ERASE``
+ If set, then cryptographic erase is
+ supported. If cleared, then cryptographic
+ erase is not supported.
+
+``NVME_CTRL_FNA_NSID_FFFFFFFF``
+ If set, then format does not support
+ nsid value set to FFFFFFFFh. If cleared,
+ format supports nsid value set to
+ FFFFFFFFh.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_vwc
+
+ Volatile write cache
+
+**Constants**
+
+``NVME_CTRL_VWC_PRESENT``
+ If set, indicates a volatile write cache is present.
+ If a volatile write cache is present, then the host
+ controls whether the volatile write cache is enabled
+ with a Set Features command specifying the value
+ ``NVME_FEAT_FID_VOLATILE_WC``.
+
+``NVME_CTRL_VWC_FLUSH``
+ Mask to get the value of the flush command behavior.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_nvscc
+
+ This field indicates the configuration settings for NVM Vendor Specific command handling.
+
+**Constants**
+
+``NVME_CTRL_NVSCC_FMT``
+ If set, all NVM Vendor Specific Commands use the
+ format with NDT and NDM fields.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_nwpc
+
+ This field indicates the optional namespace write protection capabilities supported by the controller.
+
+**Constants**
+
+``NVME_CTRL_NWPC_WRITE_PROTECT``
+ If set, then the controller shall
+ support the No Write Protect and
+ Write Protect namespace write
+ protection states and may support
+ the Write Protect Until Power
+ Cycle state and Permanent Write
+ Protect namespace write
+ protection states.
+
+``NVME_CTRL_NWPC_WRITE_PROTECT_POWER_CYCLE``
+ If set, then the controller
+ supports the Write Protect Until
+ Power Cycle state.
+
+``NVME_CTRL_NWPC_WRITE_PROTECT_PERMANENT``
+ If set, then the controller
+ supports the Permanent Write
+ Protect state.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_sgls
+
+ This field indicates if SGLs are supported for the NVM Command Set and the particular SGL types supported.
+
+**Constants**
+
+``NVME_CTRL_SGLS_SUPPORTED``
+
+``NVME_CTRL_SGLS_KEYED``
+
+``NVME_CTRL_SGLS_BIT_BUCKET``
+
+``NVME_CTRL_SGLS_MPTR_BYTE_ALIGNED``
+
+``NVME_CTRL_SGLS_OVERSIZE``
+
+``NVME_CTRL_SGLS_MPTR_SGL``
+
+``NVME_CTRL_SGLS_OFFSET``
+
+``NVME_CTRL_SGLS_TPORT``
+
+
+
+
+.. c:enum:: nvme_id_ctrl_fcatt
+
+ This field indicates attributes of the controller that are specific to NVMe over Fabrics.
+
+**Constants**
+
+``NVME_CTRL_FCATT_DYNAMIC``
+ If cleared, then the NVM subsystem uses a dynamic
+ controller model. If set, then the NVM subsystem
+ uses a static controller model.
+
+
+
+
+.. c:enum:: nvme_id_ctrl_ofcs
+
+ Indicate whether the controller supports optional fabric commands.
+
+**Constants**
+
+``NVME_CTRL_OFCS_DISCONNECT``
+ If set, then the controller supports the
+ Disconnect command and deletion of individual
+ I/O Queues.
+
+
+
+
+.. c:struct:: nvme_lbaf
+
+ LBA Format Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_lbaf {
+ __le16 ms;
+ __u8 ds;
+ __u8 rp;
+ };
+
+**Members**
+
+``ms``
+ Metadata Size indicates the number of metadata bytes provided per LBA
+ based on the LBA Data Size indicated.
+
+``ds``
+ LBA Data Size indicates the LBA data size supported, reported as a
+ power of two.
+
+``rp``
+ Relative Performance, see :c:type:`enum nvme_lbaf_rp <nvme_lbaf_rp>`.
+
+
+
+
+
+.. c:enum:: nvme_lbaf_rp
+
+ This field indicates the relative performance of the LBA format indicated relative to other LBA formats supported by the controller.
+
+**Constants**
+
+``NVME_LBAF_RP_BEST``
+ Best performance
+
+``NVME_LBAF_RP_BETTER``
+ Better performance
+
+``NVME_LBAF_RP_GOOD``
+ Good performance
+
+``NVME_LBAF_RP_DEGRADED``
+ Degraded performance
+
+``NVME_LBAF_RP_MASK``
+ Mask to get the relative performance value from the
+ field
+
+
+
+
+.. c:struct:: nvme_id_ns
+
+ Identify Namespace data structure
+
+**Definition**
+
+::
+
+ struct nvme_id_ns {
+ __le64 nsze;
+ __le64 ncap;
+ __le64 nuse;
+ __u8 nsfeat;
+ __u8 nlbaf;
+ __u8 flbas;
+ __u8 mc;
+ __u8 dpc;
+ __u8 dps;
+ __u8 nmic;
+ __u8 rescap;
+ __u8 fpi;
+ __u8 dlfeat;
+ __le16 nawun;
+ __le16 nawupf;
+ __le16 nacwu;
+ __le16 nabsn;
+ __le16 nabo;
+ __le16 nabspf;
+ __le16 noiob;
+ __u8 nvmcap[16];
+ __le16 npwg;
+ __le16 npwa;
+ __le16 npdg;
+ __le16 npda;
+ __le16 nows;
+ __le16 mssrl;
+ __le32 mcl;
+ __u8 msrc;
+ __u8 rsvd81;
+ __u8 nulbaf;
+ __u8 rsvd83[9];
+ __le32 anagrpid;
+ __u8 rsvd96[3];
+ __u8 nsattr;
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 nguid[16];
+ __u8 eui64[8];
+ struct nvme_lbaf lbaf[64];
+ __u8 vs[3712];
+ };
+
+**Members**
+
+``nsze``
+ Namespace Size indicates the total size of the namespace in
+ logical blocks. The number of logical blocks is based on the
+ formatted LBA size.
+
+``ncap``
+ Namespace Capacity indicates the maximum number of logical blocks
+ that may be allocated in the namespace at any point in time. The
+ number of logical blocks is based on the formatted LBA size.
+
+``nuse``
+ Namespace Utilization indicates the current number of logical
+ blocks allocated in the namespace. This field is smaller than or
+ equal to the Namespace Capacity. The number of logical blocks is
+ based on the formatted LBA size.
+
+``nsfeat``
+ Namespace Features, see :c:type:`enum nvme_id_nsfeat <nvme_id_nsfeat>`.
+
+``nlbaf``
+ Number of LBA Formats defines the number of supported LBA data
+ size and metadata size combinations supported by the namespace
+ and the highest possible index to :c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf.
+
+``flbas``
+ Formatted LBA Size, see :c:type:`enum nvme_id_ns_flbas <nvme_id_ns_flbas>`.
+
+``mc``
+ Metadata Capabilities, see :c:type:`enum nvme_id_ns_mc <nvme_id_ns_mc>`.
+
+``dpc``
+ End-to-end Data Protection Capabilities, see
+ :c:type:`enum nvme_id_ns_dpc <nvme_id_ns_dpc>`.
+
+``dps``
+ End-to-end Data Protection Type Settings, see
+ :c:type:`enum nvme_id_ns_dps <nvme_id_ns_dps>`.
+
+``nmic``
+ Namespace Multi-path I/O and Namespace Sharing Capabilities, see
+ :c:type:`enum nvme_id_ns_nmic <nvme_id_ns_nmic>`.
+
+``rescap``
+ Reservation Capabilities, see :c:type:`enum nvme_id_ns_rescap <nvme_id_ns_rescap>`.
+
+``fpi``
+ Format Progress Indicator, see :c:type:`enum nvme_nd_ns_fpi <nvme_nd_ns_fpi>`.
+
+``dlfeat``
+ Deallocate Logical Block Features, see :c:type:`enum nvme_id_ns_dlfeat <nvme_id_ns_dlfeat>`.
+
+``nawun``
+ Namespace Atomic Write Unit Normal indicates the
+ namespace specific size of the write operation guaranteed to be
+ written atomically to the NVM during normal operation.
+
+``nawupf``
+ Namespace Atomic Write Unit Power Fail indicates the
+ namespace specific size of the write operation guaranteed to be
+ written atomically to the NVM during a power fail or error
+ condition.
+
+``nacwu``
+ Namespace Atomic Compare & Write Unit indicates the namespace
+ specific size of the write operation guaranteed to be written
+ atomically to the NVM for a Compare and Write fused command.
+
+``nabsn``
+ Namespace Atomic Boundary Size Normal indicates the atomic
+ boundary size for this namespace for the NAWUN value. This field
+ is specified in logical blocks.
+
+``nabo``
+ Namespace Atomic Boundary Offset indicates the LBA on this
+ namespace where the first atomic boundary starts.
+
+``nabspf``
+ Namespace Atomic Boundary Size Power Fail indicates the atomic
+ boundary size for this namespace specific to the Namespace Atomic
+ Write Unit Power Fail value. This field is specified in logical
+ blocks.
+
+``noiob``
+ Namespace Optimal I/O Boundary indicates the optimal I/O boundary
+ for this namespace. This field is specified in logical blocks.
+ The host should construct Read and Write commands that do not
+ cross the I/O boundary to achieve optimal performance.
+
+``nvmcap``
+ NVM Capacity indicates the total size of the NVM allocated to
+ this namespace. The value is in bytes.
+
+``npwg``
+ Namespace Preferred Write Granularity indicates the smallest
+ recommended write granularity in logical blocks for this
+ namespace. This is a 0's based value.
+
+``npwa``
+ Namespace Preferred Write Alignment indicates the recommended
+ write alignment in logical blocks for this namespace. This is a
+ 0's based value.
+
+``npdg``
+ Namespace Preferred Deallocate Granularity indicates the
+ recommended granularity in logical blocks for the Dataset
+ Management command with the Attribute - Deallocate bit.
+
+``npda``
+ Namespace Preferred Deallocate Alignment indicates the
+ recommended alignment in logical blocks for the Dataset
+ Management command with the Attribute - Deallocate bit
+
+``nows``
+ Namespace Optimal Write Size indicates the size in logical blocks
+ for optimal write performance for this namespace. This is a 0's
+ based value.
+
+``mssrl``
+ Maximum Single Source Range Length indicates the maximum number
+ of logical blocks that may be specified in each valid Source Range
+ field of a Copy command.
+
+``mcl``
+ Maximum Copy Length indicates the maximum number of logical
+ blocks that may be specified in a Copy command.
+
+``msrc``
+ Maximum Source Range Count indicates the maximum number of Source
+ Range entries that may be used to specify source data in a Copy
+ command. This is a 0’s based value.
+
+``rsvd81``
+ Reserved
+
+``nulbaf``
+ Number of Unique Capability LBA Formats defines the number of
+ supported user data size and metadata size combinations supported
+ by the namespace that may not share the same capabilities. LBA
+ formats shall be allocated in order and packed sequentially.
+
+``rsvd83``
+ Reserved
+
+``anagrpid``
+ ANA Group Identifier indicates the ANA Group Identifier of the
+ ANA group of which the namespace is a member.
+
+``rsvd96``
+ Reserved
+
+``nsattr``
+ Namespace Attributes, see :c:type:`enum nvme_id_ns_attr <nvme_id_ns_attr>`.
+
+``nvmsetid``
+ NVM Set Identifier indicates the NVM Set with which this
+ namespace is associated.
+
+``endgid``
+ Endurance Group Identifier indicates the Endurance Group with
+ which this namespace is associated.
+
+``nguid``
+ Namespace Globally Unique Identifier contains a 128-bit value
+ that is globally unique and assigned to the namespace when the
+ namespace is created. This field remains fixed throughout the
+ life of the namespace and is preserved across namespace and
+ controller operations
+
+``eui64``
+ IEEE Extended Unique Identifier contains a 64-bit IEEE Extended
+ Unique Identifier (EUI-64) that is globally unique and assigned
+ to the namespace when the namespace is created. This field
+ remains fixed throughout the life of the namespace and is
+ preserved across namespace and controller operations
+
+``lbaf``
+ LBA Format, see :c:type:`struct nvme_lbaf <nvme_lbaf>`.
+
+``vs``
+ Vendor Specific
+
+
+
+
+
+.. c:enum:: nvme_id_nsfeat
+
+ This field defines features of the namespace.
+
+**Constants**
+
+``NVME_NS_FEAT_THIN``
+ If set, indicates that the namespace supports thin
+ provisioning. Specifically, the Namespace Capacity
+ reported may be less than the Namespace Size.
+
+``NVME_NS_FEAT_NATOMIC``
+ If set, indicates that the fields NAWUN, NAWUPF, and
+ NACWU are defined for this namespace and should be
+ used by the host for this namespace instead of the
+ AWUN, AWUPF, and ACWU fields in the Identify
+ Controller data structure.
+
+``NVME_NS_FEAT_DULBE``
+ If set, indicates that the controller supports the
+ Deallocated or Unwritten Logical Block error for
+ this namespace.
+
+``NVME_NS_FEAT_ID_REUSE``
+ If set, indicates that the value in the NGUID field
+ for this namespace, if non- zero, is never reused by
+ the controller and that the value in the EUI64 field
+ for this namespace, if non-zero, is never reused by
+ the controller.
+
+``NVME_NS_FEAT_IO_OPT``
+ If set, indicates that the fields NPWG, NPWA, NPDG,
+ NPDA, and NOWS are defined for this namespace and
+ should be used by the host for I/O optimization
+
+
+
+
+.. c:enum:: nvme_id_ns_flbas
+
+ This field indicates the LBA data size & metadata size combination that the namespace has been formatted with
+
+**Constants**
+
+``NVME_NS_FLBAS_LOWER_MASK``
+ Mask to get the index of one of the supported
+ LBA Formats's least significant
+ 4bits indicated in
+ :c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf.
+
+``NVME_NS_FLBAS_META_EXT``
+ Applicable only if format contains metadata. If
+ this bit is set, indicates that the metadata is
+ transferred at the end of the data LBA, creating an
+ extended data LBA. If cleared, indicates that all
+ of the metadata for a command is transferred as a
+ separate contiguous buffer of data.
+
+``NVME_NS_FLBAS_HIGHER_MASK``
+ Mask to get the index of one of
+ the supported LBA Formats's most significant
+ 2bits indicated in
+ :c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf.
+
+
+
+
+.. c:enum:: nvme_nvm_id_ns_elbaf
+
+ This field indicates the extended LBA format
+
+**Constants**
+
+``NVME_NVM_ELBAF_STS_MASK``
+ Mask to get the storage tag size used to determine
+ the variable-sized storage tag/reference tag fields
+
+``NVME_NVM_ELBAF_PIF_MASK``
+ Mask to get the protection information format for
+ the extended LBA format.
+
+
+
+
+.. c:enum:: nvme_id_ns_mc
+
+ This field indicates the capabilities for metadata.
+
+**Constants**
+
+``NVME_NS_MC_EXTENDED``
+ If set, indicates the namespace supports the metadata
+ being transferred as part of a separate buffer that is
+ specified in the Metadata Pointer.
+
+``NVME_NS_MC_SEPARATE``
+ If set, indicates that the namespace supports the
+ metadata being transferred as part of an extended data LBA.
+
+
+
+
+.. c:enum:: nvme_id_ns_dpc
+
+ This field indicates the capabilities for the end-to-end data protection feature.
+
+**Constants**
+
+``NVME_NS_DPC_PI_TYPE1``
+ If set, indicates that the namespace supports
+ Protection Information Type 1.
+
+``NVME_NS_DPC_PI_TYPE2``
+ If set, indicates that the namespace supports
+ Protection Information Type 2.
+
+``NVME_NS_DPC_PI_TYPE3``
+ If set, indicates that the namespace supports
+ Protection Information Type 3.
+
+``NVME_NS_DPC_PI_FIRST``
+ If set, indicates that the namespace supports
+ protection information transferred as the first eight
+ bytes of metadata.
+
+``NVME_NS_DPC_PI_LAST``
+ If set, indicates that the namespace supports
+ protection information transferred as the last eight
+ bytes of metadata.
+
+
+
+
+.. c:enum:: nvme_id_ns_dps
+
+ This field indicates the Type settings for the end-to-end data protection feature.
+
+**Constants**
+
+``NVME_NS_DPS_PI_NONE``
+ Protection information is not enabled
+
+``NVME_NS_DPS_PI_TYPE1``
+ Protection information is enabled, Type 1
+
+``NVME_NS_DPS_PI_TYPE2``
+ Protection information is enabled, Type 2
+
+``NVME_NS_DPS_PI_TYPE3``
+ Protection information is enabled, Type 3
+
+``NVME_NS_DPS_PI_MASK``
+ Mask to get the value of the PI type
+
+``NVME_NS_DPS_PI_FIRST``
+ If set, indicates that the protection information, if
+ enabled, is transferred as the first eight bytes of
+ metadata.
+
+
+
+
+.. c:enum:: nvme_id_ns_nmic
+
+ This field specifies multi-path I/O and namespace sharing capabilities of the namespace.
+
+**Constants**
+
+``NVME_NS_NMIC_SHARED``
+ If set, then the namespace may be attached to two or
+ more controllers in the NVM subsystem concurrently
+
+
+
+
+.. c:enum:: nvme_id_ns_rescap
+
+ This field indicates the reservation capabilities of the namespace.
+
+**Constants**
+
+``NVME_NS_RESCAP_PTPL``
+ If set, indicates that the namespace supports the
+ Persist Through Power Loss capability.
+
+``NVME_NS_RESCAP_WE``
+ If set, indicates that the namespace supports the
+ Write Exclusive reservation type.
+
+``NVME_NS_RESCAP_EA``
+ If set, indicates that the namespace supports the
+ Exclusive Access reservation type.
+
+``NVME_NS_RESCAP_WERO``
+ If set, indicates that the namespace supports the
+ Write Exclusive - Registrants Only reservation type.
+
+``NVME_NS_RESCAP_EARO``
+ If set, indicates that the namespace supports the
+ Exclusive Access - Registrants Only reservation type.
+
+``NVME_NS_RESCAP_WEAR``
+ If set, indicates that the namespace supports the
+ Write Exclusive - All Registrants reservation type.
+
+``NVME_NS_RESCAP_EAAR``
+ If set, indicates that the namespace supports the
+ Exclusive Access - All Registrants reservation type.
+
+``NVME_NS_RESCAP_IEK_13``
+ If set, indicates that Ignore Existing Key is used
+ as defined in revision 1.3 or later of this specification.
+
+
+
+
+.. c:enum:: nvme_nd_ns_fpi
+
+ If a format operation is in progress, this field indicates the percentage of the namespace that remains to be formatted.
+
+**Constants**
+
+``NVME_NS_FPI_REMAINING``
+ Mask to get the format percent remaining value
+
+``NVME_NS_FPI_SUPPORTED``
+ If set, indicates that the namespace supports the
+ Format Progress Indicator defined for the field.
+
+
+
+
+.. c:enum:: nvme_id_ns_dlfeat
+
+ This field indicates information about features that affect deallocating logical blocks for this namespace.
+
+**Constants**
+
+``NVME_NS_DLFEAT_RB``
+ Mask to get the value of the read behavior
+
+``NVME_NS_DLFEAT_RB_NR``
+ Read behvaior is not reported
+
+``NVME_NS_DLFEAT_RB_ALL_0S``
+ A deallocated logical block returns all bytes
+ cleared to 0h.
+
+``NVME_NS_DLFEAT_RB_ALL_FS``
+ A deallocated logical block returns all bytes
+ set to FFh.
+
+``NVME_NS_DLFEAT_WRITE_ZEROES``
+ If set, indicates that the controller supports
+ the Deallocate bit in the Write Zeroes command
+ for this namespace.
+
+``NVME_NS_DLFEAT_CRC_GUARD``
+ If set, indicates that the Guard field for
+ deallocated logical blocks that contain
+ protection information is set to the CRC for
+ the value read from the deallocated logical
+ block and its metadata
+
+
+
+
+.. c:enum:: nvme_id_ns_attr
+
+ Specifies attributes of the namespace.
+
+**Constants**
+
+``NVME_NS_NSATTR_WRITE_PROTECTED``
+ If set, then the namespace is currently
+ write protected and all write access to the
+ namespace shall fail.
+
+
+
+
+.. c:struct:: nvme_ns_id_desc
+
+ Namespace identifier type descriptor
+
+**Definition**
+
+::
+
+ struct nvme_ns_id_desc {
+ __u8 nidt;
+ __u8 nidl;
+ __le16 rsvd;
+ __u8 nid[];
+ };
+
+**Members**
+
+``nidt``
+ Namespace Identifier Type, see :c:type:`enum nvme_ns_id_desc_nidt <nvme_ns_id_desc_nidt>`
+
+``nidl``
+ Namespace Identifier Length contains the length in bytes of the
+ :c:type:`struct nvme_id_ns <nvme_id_ns>`.nid.
+
+``rsvd``
+ Reserved
+
+``nid``
+ Namespace Identifier contains a value that is globally unique and
+ assigned to the namespace when the namespace is created. The length
+ is defined in :c:type:`struct nvme_id_ns <nvme_id_ns>`.nidl.
+
+
+
+
+
+.. c:enum:: nvme_ns_id_desc_nidt
+
+ Known namespace identifier types
+
+**Constants**
+
+``NVME_NIDT_EUI64``
+ IEEE Extended Unique Identifier, the NID field contains a
+ copy of the EUI64 field in the struct nvme_id_ns.eui64.
+
+``NVME_NIDT_NGUID``
+ Namespace Globally Unique Identifier, the NID field
+ contains a copy of the NGUID field in struct nvme_id_ns.nguid.
+
+``NVME_NIDT_UUID``
+ The NID field contains a 128-bit Universally Unique
+ Identifier (UUID) as specified in RFC 4122.
+
+``NVME_NIDT_CSI``
+ The NID field contains the command set identifier.
+
+
+
+
+.. c:struct:: nvme_nvmset_attr
+
+ NVM Set Attributes Entry
+
+**Definition**
+
+::
+
+ struct nvme_nvmset_attr {
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 rsvd4[4];
+ __le32 rr4kt;
+ __le32 ows;
+ __u8 tnvmsetcap[16];
+ __u8 unvmsetcap[16];
+ __u8 rsvd48[80];
+ };
+
+**Members**
+
+``nvmsetid``
+ NVM Set Identifier
+
+``endgid``
+ Endurance Group Identifier
+
+``rsvd4``
+ Reserved
+
+``rr4kt``
+ Random 4 KiB Read Typical indicates the typical
+ time to complete a 4 KiB random read in 100 nanosecond units
+ when the NVM Set is in a Predictable Latency Mode Deterministic
+ Window and there is 1 outstanding command per NVM Set.
+
+``ows``
+ Optimal Write Size
+
+``tnvmsetcap``
+ Total NVM Set Capacity
+
+``unvmsetcap``
+ Unallocated NVM Set Capacity
+
+``rsvd48``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_id_nvmset_list
+
+ NVM set list
+
+**Definition**
+
+::
+
+ struct nvme_id_nvmset_list {
+ __u8 nid;
+ __u8 rsvd1[127];
+ struct nvme_nvmset_attr ent[NVME_ID_NVMSET_LIST_MAX];
+ };
+
+**Members**
+
+``nid``
+ Nvmset id
+
+``rsvd1``
+ Reserved
+
+``ent``
+ nvmset id list
+
+
+
+
+
+.. c:struct:: nvme_id_independent_id_ns
+
+ Identify - I/O Command Set Independent Identify Namespace Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_id_independent_id_ns {
+ __u8 nsfeat;
+ __u8 nmic;
+ __u8 rescap;
+ __u8 fpi;
+ __le32 anagrpid;
+ __u8 nsattr;
+ __u8 rsvd9;
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 nstat;
+ __u8 rsvd15[4081];
+ };
+
+**Members**
+
+``nsfeat``
+ common namespace features
+
+``nmic``
+ Namespace Multi-path I/O and Namespace
+ Sharing Capabilities
+
+``rescap``
+ Reservation Capabilities
+
+``fpi``
+ Format Progress Indicator
+
+``anagrpid``
+ ANA Group Identifier
+
+``nsattr``
+ Namespace Attributes
+
+``rsvd9``
+ reserved
+
+``nvmsetid``
+ NVM Set Identifier
+
+``endgid``
+ Endurance Group Identifier
+
+``nstat``
+ Namespace Status
+
+``rsvd15``
+ reserved
+
+
+
+
+
+.. c:struct:: nvme_id_ns_granularity_desc
+
+ Namespace Granularity Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_id_ns_granularity_desc {
+ __le64 nszegran;
+ __le64 ncapgran;
+ };
+
+**Members**
+
+``nszegran``
+ Namespace Size Granularity
+
+``ncapgran``
+ Namespace Capacity Granularity
+
+
+
+
+
+.. c:struct:: nvme_id_ns_granularity_list
+
+ Namespace Granularity List
+
+**Definition**
+
+::
+
+ struct nvme_id_ns_granularity_list {
+ __le32 attributes;
+ __u8 num_descriptors;
+ __u8 rsvd5[27];
+ struct nvme_id_ns_granularity_desc entry[NVME_ID_ND_DESCRIPTOR_MAX];
+ __u8 rsvd288[3808];
+ };
+
+**Members**
+
+``attributes``
+ Namespace Granularity Attributes
+
+``num_descriptors``
+ Number of Descriptors
+
+``rsvd5``
+ reserved
+
+``entry``
+ Namespace Granularity Descriptor
+
+``rsvd288``
+ reserved
+
+
+
+
+
+.. c:struct:: nvme_id_uuid_list_entry
+
+ UUID List Entry
+
+**Definition**
+
+::
+
+ struct nvme_id_uuid_list_entry {
+ __u8 header;
+ __u8 rsvd1[15];
+ __u8 uuid[16];
+ };
+
+**Members**
+
+``header``
+ UUID Lists Entry Header
+
+``rsvd1``
+ reserved
+
+``uuid``
+ 128-bit Universally Unique Identifier
+
+
+
+
+
+.. c:enum:: nvme_id_uuid
+
+ Identifier Association
+
+**Constants**
+
+``NVME_ID_UUID_HDR_ASSOCIATION_MASK``
+
+``NVME_ID_UUID_ASSOCIATION_NONE``
+
+``NVME_ID_UUID_ASSOCIATION_VENDOR``
+
+``NVME_ID_UUID_ASSOCIATION_SUBSYSTEM_VENDOR``
+
+
+
+
+.. c:struct:: nvme_id_uuid_list
+
+ UUID list
+
+**Definition**
+
+::
+
+ struct nvme_id_uuid_list {
+ __u8 rsvd0[32];
+ struct nvme_id_uuid_list_entry entry[NVME_ID_UUID_LIST_MAX];
+ };
+
+**Members**
+
+``rsvd0``
+ reserved
+
+``entry``
+ UUID list entry
+
+
+
+
+
+.. c:struct:: nvme_ctrl_list
+
+ Controller List
+
+**Definition**
+
+::
+
+ struct nvme_ctrl_list {
+ __le16 num;
+ __le16 identifier[NVME_ID_CTRL_LIST_MAX];
+ };
+
+**Members**
+
+``num``
+ Number of Identifiers
+
+``identifier``
+ NVM subsystem unique controller identifier
+
+
+
+
+
+.. c:struct:: nvme_ns_list
+
+ Namespace List
+
+**Definition**
+
+::
+
+ struct nvme_ns_list {
+ __le32 ns[NVME_ID_NS_LIST_MAX];
+ };
+
+**Members**
+
+``ns``
+ Namespace Identifier
+
+
+
+
+
+.. c:struct:: nvme_id_ctrl_nvm
+
+ I/O Command Set Specific Identify Controller data structure
+
+**Definition**
+
+::
+
+ struct nvme_id_ctrl_nvm {
+ __u8 vsl;
+ __u8 wzsl;
+ __u8 wusl;
+ __u8 dmrl;
+ __le32 dmrsl;
+ __le64 dmsl;
+ __u8 rsvd16[4080];
+ };
+
+**Members**
+
+``vsl``
+ Verify Size Limit
+
+``wzsl``
+ Write Zeroes Size Limit
+
+``wusl``
+ Write Uncorrectable Size Limit
+
+``dmrl``
+ Dataset Management Ranges Limit
+
+``dmrsl``
+ Dataset Management Range Size Limit
+
+``dmsl``
+ Dataset Management Size Limit
+
+``rsvd16``
+ reserved
+
+
+
+
+
+.. c:struct:: nvme_nvm_id_ns
+
+ NVME Command Set I/O Command Set Specific Identify Namespace Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_nvm_id_ns {
+ __le64 lbstm;
+ __u8 pic;
+ __u8 rsvd9[3];
+ __le32 elbaf[64];
+ __u8 rsvd268[3828];
+ };
+
+**Members**
+
+``lbstm``
+ Logical Block Storage Tag Mask
+
+``pic``
+ Protection Information Capabilities
+
+``rsvd9``
+ Reserved
+
+``elbaf``
+ List of Extended LBA Format Support
+
+``rsvd268``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_zns_lbafe
+
+ LBA Format Extension Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_zns_lbafe {
+ __le64 zsze;
+ __u8 zdes;
+ __u8 rsvd9[7];
+ };
+
+**Members**
+
+``zsze``
+ Zone Size
+
+``zdes``
+ Zone Descriptor Extension Size
+
+``rsvd9``
+ reserved
+
+
+
+
+
+.. c:struct:: nvme_zns_id_ns
+
+ Zoned Namespace Command Set Specific Identify Namespace Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_zns_id_ns {
+ __le16 zoc;
+ __le16 ozcs;
+ __le32 mar;
+ __le32 mor;
+ __le32 rrl;
+ __le32 frl;
+ __le32 rrl1;
+ __le32 rrl2;
+ __le32 rrl3;
+ __le32 frl1;
+ __le32 frl2;
+ __le32 frl3;
+ __le32 numzrwa;
+ __le16 zrwafg;
+ __le16 zrwasz;
+ __u8 zrwacap;
+ __u8 rsvd53[2763];
+ struct nvme_zns_lbafe lbafe[64];
+ __u8 vs[256];
+ };
+
+**Members**
+
+``zoc``
+ Zone Operation Characteristics
+
+``ozcs``
+ Optional Zoned Command Support
+
+``mar``
+ Maximum Active Resources
+
+``mor``
+ Maximum Open Resources
+
+``rrl``
+ Reset Recommended Limit
+
+``frl``
+ Finish Recommended Limit
+
+``rrl1``
+ Reset Recommended Limit 1
+
+``rrl2``
+ Reset Recommended Limit 2
+
+``rrl3``
+ Reset Recommended Limit 3
+
+``frl1``
+ Finish Recommended Limit 1
+
+``frl2``
+ Finish Recommended Limit 2
+
+``frl3``
+ Finish Recommended Limit 3
+
+``numzrwa``
+ Number of ZRWA Resources
+
+``zrwafg``
+ ZRWA Flush Granularity
+
+``zrwasz``
+ ZRWA Size
+
+``zrwacap``
+ ZRWA Capability
+
+``rsvd53``
+ Reserved
+
+``lbafe``
+ LBA Format Extension
+
+``vs``
+ Vendor Specific
+
+
+
+
+
+.. c:struct:: nvme_zns_id_ctrl
+
+ I/O Command Set Specific Identify Controller Data Structure for the Zoned Namespace Command Set
+
+**Definition**
+
+::
+
+ struct nvme_zns_id_ctrl {
+ __u8 zasl;
+ __u8 rsvd1[4095];
+ };
+
+**Members**
+
+``zasl``
+ Zone Append Size Limit
+
+``rsvd1``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_primary_ctrl_cap
+
+ Identify - Controller Capabilities Structure
+
+**Definition**
+
+::
+
+ struct nvme_primary_ctrl_cap {
+ __le16 cntlid;
+ __le16 portid;
+ __u8 crt;
+ __u8 rsvd5[27];
+ __le32 vqfrt;
+ __le32 vqrfa;
+ __le16 vqrfap;
+ __le16 vqprt;
+ __le16 vqfrsm;
+ __le16 vqgran;
+ __u8 rsvd48[16];
+ __le32 vifrt;
+ __le32 virfa;
+ __le16 virfap;
+ __le16 viprt;
+ __le16 vifrsm;
+ __le16 vigran;
+ __u8 rsvd80[4016];
+ };
+
+**Members**
+
+``cntlid``
+ Controller Identifier
+
+``portid``
+ Port Identifier
+
+``crt``
+ Controller Resource Types
+
+``rsvd5``
+ reserved
+
+``vqfrt``
+ VQ Resources Flexible Total
+
+``vqrfa``
+ VQ Resources Flexible Assigned
+
+``vqrfap``
+ VQ Resources Flexible Allocated to Primary
+
+``vqprt``
+ VQ Resources Private Total
+
+``vqfrsm``
+ VQ Resources Flexible Secondary Maximum
+
+``vqgran``
+ VQ Flexible Resource Preferred Granularity
+
+``rsvd48``
+ reserved
+
+``vifrt``
+ VI Resources Flexible Total
+
+``virfa``
+ VI Resources Flexible Assigned
+
+``virfap``
+ VI Resources Flexible Allocated to Primary
+
+``viprt``
+ VI Resources Private Total
+
+``vifrsm``
+ VI Resources Flexible Secondary Maximum
+
+``vigran``
+ VI Flexible Resource Preferred Granularity
+
+``rsvd80``
+ reserved
+
+
+
+
+
+.. c:struct:: nvme_secondary_ctrl
+
+ Secondary Controller Entry
+
+**Definition**
+
+::
+
+ struct nvme_secondary_ctrl {
+ __le16 scid;
+ __le16 pcid;
+ __u8 scs;
+ __u8 rsvd5[3];
+ __le16 vfn;
+ __le16 nvq;
+ __le16 nvi;
+ __u8 rsvd14[18];
+ };
+
+**Members**
+
+``scid``
+ Secondary Controller Identifier
+
+``pcid``
+ Primary Controller Identifier
+
+``scs``
+ Secondary Controller State
+
+``rsvd5``
+ Reserved
+
+``vfn``
+ Virtual Function Number
+
+``nvq``
+ Number of VQ Flexible Resources Assigned
+
+``nvi``
+ Number of VI Flexible Resources Assigned
+
+``rsvd14``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_secondary_ctrl_list
+
+ Secondary Controller List
+
+**Definition**
+
+::
+
+ struct nvme_secondary_ctrl_list {
+ __u8 num;
+ __u8 rsvd[31];
+ struct nvme_secondary_ctrl sc_entry[NVME_ID_SECONDARY_CTRL_MAX];
+ };
+
+**Members**
+
+``num``
+ Number of Identifiers
+
+``rsvd``
+ Reserved
+
+``sc_entry``
+ Secondary Controller Entry
+
+
+
+
+
+.. c:struct:: nvme_id_iocs
+
+ NVMe Identify IO Command Set data structure
+
+**Definition**
+
+::
+
+ struct nvme_id_iocs {
+ __le64 iocsc[512];
+ };
+
+**Members**
+
+``iocsc``
+ List of supported IO Command Set Combination vectors
+
+
+
+
+
+.. c:struct:: nvme_id_domain_attr
+
+ Domain Attributes Entry
+
+**Definition**
+
+::
+
+ struct nvme_id_domain_attr {
+ __le16 dom_id;
+ __u8 rsvd2[14];
+ __u8 dom_cap[16];
+ __u8 unalloc_dom_cap[16];
+ __u8 max_egrp_dom_cap[16];
+ __u8 rsvd64[64];
+ };
+
+**Members**
+
+``dom_id``
+ Domain Identifier
+
+``rsvd2``
+ Reserved
+
+``dom_cap``
+ Total Domain Capacity
+
+``unalloc_dom_cap``
+ Unallocated Domain Capacity
+
+``max_egrp_dom_cap``
+ Max Endurance Group Domain Capacity
+
+``rsvd64``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_id_domain_list
+
+ Domain List
+
+**Definition**
+
+::
+
+ struct nvme_id_domain_list {
+ __u8 num;
+ __u8 rsvd[127];
+ struct nvme_id_domain_attr domain_attr[NVME_ID_DOMAIN_LIST_MAX];
+ };
+
+**Members**
+
+``num``
+ Number of domain attributes
+
+``rsvd``
+ Reserved
+
+``domain_attr``
+ List of domain attributes
+
+
+
+
+
+.. c:struct:: nvme_id_endurance_group_list
+
+ Endurance Group List
+
+**Definition**
+
+::
+
+ struct nvme_id_endurance_group_list {
+ __le16 num;
+ __le16 identifier[NVME_ID_ENDURANCE_GROUP_LIST_MAX];
+ };
+
+**Members**
+
+``num``
+ Number of Identifiers
+
+``identifier``
+ Endurance Group Identifier
+
+
+
+
+
+.. c:struct:: nvme_supported_log_pages
+
+ Supported Log Pages - Log
+
+**Definition**
+
+::
+
+ struct nvme_supported_log_pages {
+ __le32 lid_support[NVME_LOG_SUPPORTED_LOG_PAGES_MAX];
+ };
+
+**Members**
+
+``lid_support``
+ Log Page Identifier Supported
+
+
+**Description**
+
+Supported Log Pages (Log Identifier 00h)
+
+
+
+
+.. c:struct:: nvme_error_log_page
+
+ Error Information Log Entry (Log Identifier 01h)
+
+**Definition**
+
+::
+
+ struct nvme_error_log_page {
+ __le64 error_count;
+ __le16 sqid;
+ __le16 cmdid;
+ __le16 status_field;
+ __le16 parm_error_location;
+ __le64 lba;
+ __le32 nsid;
+ __u8 vs;
+ __u8 trtype;
+ __u8 csi;
+ __u8 opcode;
+ __le64 cs;
+ __le16 trtype_spec_info;
+ __u8 rsvd[21];
+ __u8 log_page_version;
+ };
+
+**Members**
+
+``error_count``
+ Error Count: a 64-bit incrementing error count,
+ indicating a unique identifier for this error. The error
+ count starts at ``1h``, is incremented for each unique error
+ log entry, and is retained across power off conditions.
+ A value of ``0h`` indicates an invalid entry; this value
+ is used when there are lost entries or when there are
+ fewer errors than the maximum number of entries the
+ controller supports. If the value of this field is
+ ``FFFFFFFFh``, then the field shall be set to 1h when
+ incremented (i.e., rolls over to ``1h``). Prior to NVMe
+ 1.4, processing of incrementing beyond ``FFFFFFFFh`` is
+ unspecified.
+
+``sqid``
+ Submission Queue ID: indicates the Submission Queue
+ Identifier of the command that the error information is
+ associated with. If the error is not specific to
+ a particular command, then this field shall be set to
+ ``FFFFh``.
+
+``cmdid``
+ Command ID: indicates the Command Identifier of the
+ command that the error is associated with. If the error
+ is not specific to a particular command, then this field
+ shall be set to ``FFFFh``.
+
+``status_field``
+ Bits 15-1: Status Field: indicates the Status Field for
+ the command that completed. If the error is not specific
+ to a particular command, then this field reports the most
+ applicable status value.
+ Bit 0: Phase Tag: may indicate the Phase Tag posted for
+ the command.
+
+``parm_error_location``
+ Parameter Error Location: indicates the byte and bit of
+ the command parameter that the error is associated with,
+ if applicable. If the parameter spans multiple bytes or
+ bits, then the location indicates the first byte and bit
+ of the parameter.
+ Bits 10-8: Bit in command that contained the error.
+ Valid values are 0 to 7.
+ Bits 7-0: Byte in command that contained the error.
+ Valid values are 0 to 63.
+
+``lba``
+ LBA: This field indicates the first LBA that experienced
+ the error condition, if applicable.
+
+``nsid``
+ Namespace: This field indicates the NSID of the namespace
+ that the error is associated with, if applicable.
+
+``vs``
+ Vendor Specific Information Available: If there is
+ additional vendor specific error information available,
+ this field provides the log page identifier associated
+ with that page. A value of ``0h`` indicates that no additional
+ information is available. Valid values are in the range
+ of ``80h`` to ``FFh``.
+
+``trtype``
+ Transport Type (TRTYPE): indicates the Transport Type of
+ the transport associated with the error. The values in
+ this field are the same as the TRTYPE values in the
+ Discovery Log Page Entry. If the error is not transport
+ related, this field shall be cleared to ``0h``. If the error
+ is transport related, this field shall be set to the type
+ of the transport - see :c:type:`enum nvme_trtype <nvme_trtype>`.
+
+``csi``
+ Command Set Indicator: This field contains command set
+ indicator for the command that the error is associated
+ with.
+
+``opcode``
+ Opcode: This field contains opcode for the command that
+ the error is associated with.
+
+``cs``
+ Command Specific Information: This field contains command
+ specific information. If used, the command definition
+ specifies the information returned.
+
+``trtype_spec_info``
+ Transport Type Specific Information
+
+``rsvd``
+ Reserved: [62:42]
+
+``log_page_version``
+ This field shall be set to 1h. If set, **csi** and **opcode**
+ will have valid values.
+
+
+
+
+
+.. c:struct:: nvme_smart_log
+
+ SMART / Health Information Log (Log Identifier 02h)
+
+**Definition**
+
+::
+
+ struct nvme_smart_log {
+ __u8 critical_warning;
+ __u8 temperature[2];
+ __u8 avail_spare;
+ __u8 spare_thresh;
+ __u8 percent_used;
+ __u8 endu_grp_crit_warn_sumry;
+ __u8 rsvd7[25];
+ __u8 data_units_read[16];
+ __u8 data_units_written[16];
+ __u8 host_reads[16];
+ __u8 host_writes[16];
+ __u8 ctrl_busy_time[16];
+ __u8 power_cycles[16];
+ __u8 power_on_hours[16];
+ __u8 unsafe_shutdowns[16];
+ __u8 media_errors[16];
+ __u8 num_err_log_entries[16];
+ __le32 warning_temp_time;
+ __le32 critical_comp_time;
+ __le16 temp_sensor[8];
+ __le32 thm_temp1_trans_count;
+ __le32 thm_temp2_trans_count;
+ __le32 thm_temp1_total_time;
+ __le32 thm_temp2_total_time;
+ __u8 rsvd232[280];
+ };
+
+**Members**
+
+``critical_warning``
+ This field indicates critical warnings for the state
+ of the controller. Critical warnings may result in an
+ asynchronous event notification to the host. Bits in
+ this field represent the current associated state and
+ are not persistent (see :c:type:`enum nvme_smart_crit <nvme_smart_crit>`).
+
+``temperature``
+ Composite Temperature: Contains a value corresponding
+ to a temperature in Kelvins that represents the current
+ composite temperature of the controller and namespace(s)
+ associated with that controller. The manner in which
+ this value is computed is implementation specific and
+ may not represent the actual temperature of any physical
+ point in the NVM subsystem. Warning and critical
+ overheating composite temperature threshold values are
+ reported by the WCTEMP and CCTEMP fields in the Identify
+ Controller data structure.
+
+``avail_spare``
+ Available Spare: Contains a normalized percentage (0%
+ to 100%) of the remaining spare capacity available.
+
+``spare_thresh``
+ Available Spare Threshold: When the Available Spare
+ falls below the threshold indicated in this field, an
+ asynchronous event completion may occur. The value is
+ indicated as a normalized percentage (0% to 100%).
+ The values 101 to 255 are reserved.
+
+``percent_used``
+ Percentage Used: Contains a vendor specific estimate
+ of the percentage of NVM subsystem life used based on
+ the actual usage and the manufacturer's prediction of
+ NVM life. A value of 100 indicates that the estimated
+ endurance of the NVM in the NVM subsystem has been
+ consumed, but may not indicate an NVM subsystem failure.
+ The value is allowed to exceed 100. Percentages greater
+ than 254 shall be represented as 255. This value shall
+ be updated once per power-on hour (when the controller
+ is not in a sleep state).
+
+``endu_grp_crit_warn_sumry``
+ Endurance Group Critical Warning Summary: This field
+ indicates critical warnings for the state of Endurance
+ Groups. Bits in this field represent the current associated
+ state and are not persistent (see :c:type:`enum nvme_smart_egcw <nvme_smart_egcw>`).
+
+``rsvd7``
+ Reserved
+
+``data_units_read``
+ Data Units Read: Contains the number of 512 byte data
+ units the host has read from the controller; this value
+ does not include metadata. This value is reported in
+ thousands (i.e., a value of 1 corresponds to 1000
+ units of 512 bytes read) and is rounded up (e.g., one
+ indicates the that number of 512 byte data units read
+ is from 1 to 1000, three indicates that the number of
+ 512 byte data units read is from 2001 to 3000). When
+ the LBA size is a value other than 512 bytes, the
+ controller shall convert the amount of data read to
+ 512 byte units. For the NVM command set, logical blocks
+ read as part of Compare, Read, and Verify operations
+ shall be included in this value. A value of ``0h`` in
+ this field indicates that the number of Data Units Read
+ is not reported.
+
+``data_units_written``
+ Data Units Written: Contains the number of 512 byte
+ data units the host has written to the controller;
+ this value does not include metadata. This value is
+ reported in thousands (i.e., a value of 1 corresponds
+ to 1000 units of 512 bytes written) and is rounded up
+ (e.g., one indicates that the number of 512 byte data
+ units written is from 1 to 1,000, three indicates that
+ the number of 512 byte data units written is from 2001
+ to 3000). When the LBA size is a value other than 512
+ bytes, the controller shall convert the amount of data
+ written to 512 byte units. For the NVM command set,
+ logical blocks written as part of Write operations shall
+ be included in this value. Write Uncorrectable commands
+ and Write Zeroes commands shall not impact this value.
+ A value of ``0h`` in this field indicates that the number
+ of Data Units Written is not reported.
+
+``host_reads``
+ Host Read Commands: Contains the number of read commands
+ completed by the controller. For the NVM command set,
+ this value is the sum of the number of Compare commands
+ and the number of Read commands.
+
+``host_writes``
+ Host Write Commands: Contains the number of write
+ commands completed by the controller. For the NVM
+ command set, this is the number of Write commands.
+
+``ctrl_busy_time``
+ Controller Busy Time: Contains the amount of time the
+ controller is busy with I/O commands. The controller
+ is busy when there is a command outstanding to an I/O
+ Queue (specifically, a command was issued via an I/O
+ Submission Queue Tail doorbell write and the corresponding
+ completion queue entry has not been posted yet to the
+ associated I/O Completion Queue). This value is
+ reported in minutes.
+
+``power_cycles``
+ Power Cycles: Contains the number of power cycles.
+
+``power_on_hours``
+ Power On Hours: Contains the number of power-on hours.
+ This may not include time that the controller was
+ powered and in a non-operational power state.
+
+``unsafe_shutdowns``
+ Unsafe Shutdowns: Contains the number of unsafe
+ shutdowns. This count is incremented when a Shutdown
+ Notification (CC.SHN) is not received prior to loss of power.
+
+``media_errors``
+ Media and Data Integrity Errors: Contains the number
+ of occurrences where the controller detected an
+ unrecovered data integrity error. Errors such as
+ uncorrectable ECC, CRC checksum failure, or LBA tag
+ mismatch are included in this field. Errors introduced
+ as a result of a Write Uncorrectable command may or
+ may not be included in this field.
+
+``num_err_log_entries``
+ Number of Error Information Log Entries: Contains the
+ number of Error Information log entries over the life
+ of the controller.
+
+``warning_temp_time``
+ Warning Composite Temperature Time: Contains the amount
+ of time in minutes that the controller is operational
+ and the Composite Temperature is greater than or equal
+ to the Warning Composite Temperature Threshold (WCTEMP)
+ field and less than the Critical Composite Temperature
+ Threshold (CCTEMP) field in the Identify Controller
+ data structure. If the value of the WCTEMP or CCTEMP
+ field is ``0h``, then this field is always cleared to ``0h``
+ regardless of the Composite Temperature value.
+
+``critical_comp_time``
+ Critical Composite Temperature Time: Contains the amount
+ of time in minutes that the controller is operational
+ and the Composite Temperature is greater than or equal
+ to the Critical Composite Temperature Threshold (CCTEMP)
+ field in the Identify Controller data structure. If
+ the value of the CCTEMP field is ``0h``, then this field
+ is always cleared to 0h regardless of the Composite
+ Temperature value.
+
+``temp_sensor``
+ Temperature Sensor 1-8: Contains the current temperature
+ in degrees Kelvin reported by temperature sensors 1-8.
+ The physical point in the NVM subsystem whose temperature
+ is reported by the temperature sensor and the temperature
+ accuracy is implementation specific. An implementation
+ that does not implement the temperature sensor reports
+ a value of ``0h``.
+
+``thm_temp1_trans_count``
+ Thermal Management Temperature 1 Transition Count:
+ Contains the number of times the controller transitioned
+ to lower power active power states or performed vendor
+ specific thermal management actions while minimizing
+ the impact on performance in order to attempt to reduce
+ the Composite Temperature because of the host controlled
+ thermal management feature (i.e., the Composite
+ Temperature rose above the Thermal Management
+ Temperature 1). This counter shall not wrap once the
+ value ``FFFFFFFFh`` is reached. A value of ``0h``, indicates
+ that this transition has never occurred or this field
+ is not implemented.
+
+``thm_temp2_trans_count``
+ Thermal Management Temperature 2 Transition Count
+
+``thm_temp1_total_time``
+ Total Time For Thermal Management Temperature 1:
+ Contains the number of seconds that the controller
+ had transitioned to lower power active power states or
+ performed vendor specific thermal management actions
+ while minimizing the impact on performance in order to
+ attempt to reduce the Composite Temperature because of
+ the host controlled thermal management feature. This
+ counter shall not wrap once the value ``FFFFFFFFh`` is
+ reached. A value of ``0h``, indicates that this transition
+ has never occurred or this field is not implemented.
+
+``thm_temp2_total_time``
+ Total Time For Thermal Management Temperature 2
+
+``rsvd232``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_smart_crit
+
+ Critical Warning
+
+**Constants**
+
+``NVME_SMART_CRIT_SPARE``
+ If set, then the available spare capacity has fallen
+ below the threshold.
+
+``NVME_SMART_CRIT_TEMPERATURE``
+ If set, then a temperature is either greater
+ than or equal to an over temperature threshold; or
+ less than or equal to an under temperature threshold.
+
+``NVME_SMART_CRIT_DEGRADED``
+ If set, then the NVM subsystem reliability has
+ been degraded due to significant media related errors
+ or any internal error that degrades NVM subsystem
+ reliability.
+
+``NVME_SMART_CRIT_MEDIA``
+ If set, then all of the media has been placed in read
+ only mode. The controller shall not set this bit if
+ the read-only condition on the media is a result of
+ a change in the write protection state of a namespace.
+
+``NVME_SMART_CRIT_VOLATILE_MEMORY``
+ If set, then the volatile memory backup
+ device has failed. This field is only valid if the
+ controller has a volatile memory backup solution.
+
+``NVME_SMART_CRIT_PMR_RO``
+ If set, then the Persistent Memory Region has become
+ read-only or unreliable.
+
+
+
+
+.. c:enum:: nvme_smart_egcw
+
+ Endurance Group Critical Warning Summary
+
+**Constants**
+
+``NVME_SMART_EGCW_SPARE``
+ If set, then the available spare capacity of one or
+ more Endurance Groups has fallen below the threshold.
+
+``NVME_SMART_EGCW_DEGRADED``
+ If set, then the reliability of one or more
+ Endurance Groups has been degraded due to significant
+ media related errors or any internal error that
+ degrades NVM subsystem reliability.
+
+``NVME_SMART_EGCW_RO``
+ If set, then the namespaces in one or more Endurance
+ Groups have been placed in read only mode not as
+ a result of a change in the write protection state
+ of a namespace.
+
+
+
+
+.. c:struct:: nvme_firmware_slot
+
+ Firmware Slot Information Log
+
+**Definition**
+
+::
+
+ struct nvme_firmware_slot {
+ __u8 afi;
+ __u8 rsvd1[7];
+ char frs[7][8];
+ __u8 rsvd2[448];
+ };
+
+**Members**
+
+``afi``
+ Active Firmware Info
+
+``rsvd1``
+ Reserved
+
+``frs``
+ Firmware Revision for Slot
+
+``rsvd2``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_cmd_effects_log
+
+ Commands Supported and Effects Log
+
+**Definition**
+
+::
+
+ struct nvme_cmd_effects_log {
+ __le32 acs[256];
+ __le32 iocs[256];
+ __u8 rsvd[2048];
+ };
+
+**Members**
+
+``acs``
+ Admin Command Supported
+
+``iocs``
+ I/O Command Supported
+
+``rsvd``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_cmd_effects
+
+ Commands Supported and Effects
+
+**Constants**
+
+``NVME_CMD_EFFECTS_CSUPP``
+ Command Supported
+
+``NVME_CMD_EFFECTS_LBCC``
+ Logical Block Content Change
+
+``NVME_CMD_EFFECTS_NCC``
+ Namespace Capability Change
+
+``NVME_CMD_EFFECTS_NIC``
+ Namespace Inventory Change
+
+``NVME_CMD_EFFECTS_CCC``
+ Controller Capability Change
+
+``NVME_CMD_EFFECTS_CSE_MASK``
+ Command Submission and Execution
+
+``NVME_CMD_EFFECTS_UUID_SEL``
+ UUID Selection Supported
+
+
+
+
+.. c:struct:: nvme_st_result
+
+ Self-test Result
+
+**Definition**
+
+::
+
+ struct nvme_st_result {
+ __u8 dsts;
+ __u8 seg;
+ __u8 vdi;
+ __u8 rsvd;
+ __le64 poh;
+ __le32 nsid;
+ __le64 flba;
+ __u8 sct;
+ __u8 sc;
+ __u8 vs[2];
+ };
+
+**Members**
+
+``dsts``
+ Device Self-test Status: Indicates the device self-test code and the
+ status of the operation (see :c:type:`enum nvme_status_result <nvme_status_result>` and :c:type:`enum nvme_st_code <nvme_st_code>`).
+
+``seg``
+ Segment Number: Iindicates the segment number where the first self-test
+ failure occurred. If Device Self-test Status (**dsts**) is not set to
+ #NVME_ST_RESULT_KNOWN_SEG_FAIL, then this field should be ignored.
+
+``vdi``
+ Valid Diagnostic Information: Indicates the diagnostic failure
+ information that is reported. See :c:type:`enum nvme_st_valid_diag_info <nvme_st_valid_diag_info>`.
+
+``rsvd``
+ Reserved
+
+``poh``
+ Power On Hours (POH): Indicates the number of power-on hours at the
+ time the device self-test operation was completed or aborted. This
+ does not include time that the controller was powered and in a low
+ power state condition.
+
+``nsid``
+ Namespace Identifier (NSID): Indicates the namespace that the Failing
+ LBA occurred on. Valid only when the NSID Valid bit
+ (#NVME_ST_VALID_DIAG_INFO_NSID) is set in the Valid Diagnostic
+ Information (**vdi**) field.
+
+``flba``
+ Failing LBA: indicates the LBA of the logical block that caused the
+ test to fail. If the device encountered more than one failed logical
+ block during the test, then this field only indicates one of those
+ failed logical blocks. Valid only when the NSID Valid bit
+ (#NVME_ST_VALID_DIAG_INFO_FLBA) is set in the Valid Diagnostic
+ Information (**vdi**) field.
+
+``sct``
+ Status Code Type: This field may contain additional information related
+ to errors or conditions. Bits 2:0 may contain additional information
+ relating to errors or conditions that occurred during the device
+ self-test operation represented in the same format used in the Status
+ Code Type field of the completion queue entry (refer to :c:type:`enum nvme_status_field <nvme_status_field>`).
+ Valid only when the NSID Valid bit (#NVME_ST_VALID_DIAG_INFO_SCT) is
+ set in the Valid Diagnostic Information (**vdi**) field.
+
+``sc``
+ Status Code: This field may contain additional information relating
+ to errors or conditions that occurred during the device self-test
+ operation represented in the same format used in the Status Code field
+ of the completion queue entry. Valid only when the SCT Valid bit
+ (#NVME_ST_VALID_DIAG_INFO_SC) is set in the Valid Diagnostic
+ Information (**vdi**) field.
+
+``vs``
+ Vendor Specific.
+
+
+
+
+
+.. c:enum:: nvme_status_result
+
+ Result of the device self-test operation
+
+**Constants**
+
+``NVME_ST_RESULT_NO_ERR``
+ Operation completed without error.
+
+``NVME_ST_RESULT_ABORTED``
+ Operation was aborted by a Device Self-test command.
+
+``NVME_ST_RESULT_CLR``
+ Operation was aborted by a Controller Level Reset.
+
+``NVME_ST_RESULT_NS_REMOVED``
+ Operation was aborted due to a removal of
+ a namespace from the namespace inventory.
+
+``NVME_ST_RESULT_ABORTED_FORMAT``
+ Operation was aborted due to the processing
+ of a Format NVM command.
+
+``NVME_ST_RESULT_FATAL_ERR``
+ A fatal error or unknown test error occurred
+ while the controller was executing the device
+ self-test operation and the operation did
+ not complete.
+
+``NVME_ST_RESULT_UNKNOWN_SEG_FAIL``
+ Operation completed with a segment that failed
+ and the segment that failed is not known.
+
+``NVME_ST_RESULT_KNOWN_SEG_FAIL``
+ Operation completed with one or more failed
+ segments and the first segment that failed
+ is indicated in the Segment Number field.
+
+``NVME_ST_RESULT_ABORTED_UNKNOWN``
+ Operation was aborted for unknown reason.
+
+``NVME_ST_RESULT_ABORTED_SANITIZE``
+ Operation was aborted due to a sanitize operation.
+
+``NVME_ST_RESULT_NOT_USED``
+ Entry not used (does not contain a test result).
+
+``NVME_ST_RESULT_MASK``
+ Mask to get the status result value from
+ the :c:type:`struct nvme_st_result <nvme_st_result>`.dsts field.
+
+
+
+
+.. c:enum:: nvme_st_code
+
+ Self-test Code value
+
+**Constants**
+
+``NVME_ST_CODE_RESERVED``
+ Reserved.
+
+``NVME_ST_CODE_SHORT``
+ Short device self-test operation.
+
+``NVME_ST_CODE_EXTENDED``
+ Extended device self-test operation.
+
+``NVME_ST_CODE_VS``
+ Vendor specific.
+
+``NVME_ST_CODE_ABORT``
+ Abort device self-test operation.
+
+``NVME_ST_CODE_SHIFT``
+ Shift amount to get the code value from the
+ :c:type:`struct nvme_st_result <nvme_st_result>`.dsts field.
+
+
+
+
+.. c:enum:: nvme_st_curr_op
+
+ Current Device Self-Test Operation
+
+**Constants**
+
+``NVME_ST_CURR_OP_NOT_RUNNING``
+ No device self-test operation in progress.
+
+``NVME_ST_CURR_OP_SHORT``
+ Short device self-test operation in progress.
+
+``NVME_ST_CURR_OP_EXTENDED``
+ Extended device self-test operation in progress.
+
+``NVME_ST_CURR_OP_VS``
+ Vendor specific.
+
+``NVME_ST_CURR_OP_RESERVED``
+ Reserved.
+
+``NVME_ST_CURR_OP_MASK``
+ Mask to get the current operation value from the
+ :c:type:`struct nvme_self_test_log <nvme_self_test_log>`.current_operation field.
+
+``NVME_ST_CURR_OP_CMPL_MASK``
+ Mask to get the current operation completion value
+ from the :c:type:`struct nvme_self_test_log <nvme_self_test_log>`.completion field.
+
+
+
+
+.. c:enum:: nvme_st_valid_diag_info
+
+ Valid Diagnostic Information
+
+**Constants**
+
+``NVME_ST_VALID_DIAG_INFO_NSID``
+ NSID Valid: if set, then the contents of
+ the Namespace Identifier field are valid.
+
+``NVME_ST_VALID_DIAG_INFO_FLBA``
+ FLBA Valid: if set, then the contents of
+ the Failing LBA field are valid.
+
+``NVME_ST_VALID_DIAG_INFO_SCT``
+ SCT Valid: if set, then the contents of
+ the Status Code Type field are valid.
+
+``NVME_ST_VALID_DIAG_INFO_SC``
+ SC Valid: if set, then the contents of
+ the Status Code field are valid.
+
+
+
+
+.. c:struct:: nvme_self_test_log
+
+ Device Self-test (Log Identifier 06h)
+
+**Definition**
+
+::
+
+ struct nvme_self_test_log {
+ __u8 current_operation;
+ __u8 completion;
+ __u8 rsvd[2];
+ struct nvme_st_result result[NVME_LOG_ST_MAX_RESULTS];
+ };
+
+**Members**
+
+``current_operation``
+ Current Device Self-Test Operation: indicates the status
+ of the current device self-test operation. If a device
+ self-test operation is in process (i.e., this field is set
+ to #NVME_ST_CURR_OP_SHORT or #NVME_ST_CURR_OP_EXTENDED),
+ then the controller shall not set this field to
+ #NVME_ST_CURR_OP_NOT_RUNNING until a new Self-test Result
+ Data Structure is created (i.e., if a device self-test
+ operation completes or is aborted, then the controller
+ shall create a Self-test Result Data Structure prior to
+ setting this field to #NVME_ST_CURR_OP_NOT_RUNNING).
+ See :c:type:`enum nvme_st_curr_op <nvme_st_curr_op>`.
+
+``completion``
+ Current Device Self-Test Completion: indicates the percentage
+ of the device self-test operation that is complete (e.g.,
+ a value of 25 indicates that 25% of the device self-test
+ operation is complete and 75% remains to be tested).
+ If the **current_operation** field is cleared to
+ #NVME_ST_CURR_OP_NOT_RUNNING (indicating there is no device
+ self-test operation in progress), then this field is ignored.
+
+``rsvd``
+ Reserved
+
+``result``
+ Self-test Result Data Structures, see :c:type:`struct nvme_st_result <nvme_st_result>`.
+
+
+
+
+
+.. c:enum:: nvme_cmd_get_log_telemetry_host_lsp
+
+ Telemetry Host-Initiated log specific field
+
+**Constants**
+
+``NVME_LOG_TELEM_HOST_LSP_RETAIN``
+ Get Telemetry Data Blocks
+
+``NVME_LOG_TELEM_HOST_LSP_CREATE``
+ Create Telemetry Data Blocks
+
+
+
+
+.. c:struct:: nvme_telemetry_log
+
+ Retrieve internal data specific to the manufacturer.
+
+**Definition**
+
+::
+
+ struct nvme_telemetry_log {
+ __u8 lpi;
+ __u8 rsvd1[4];
+ __u8 ieee[3];
+ __le16 dalb1;
+ __le16 dalb2;
+ __le16 dalb3;
+ __u8 rsvd14[2];
+ __le32 dalb4;
+ __u8 rsvd20[361];
+ __u8 hostdgn;
+ __u8 ctrlavail;
+ __u8 ctrldgn;
+ __u8 rsnident[128];
+ __u8 data_area[];
+ };
+
+**Members**
+
+``lpi``
+ Log Identifier, either ``NVME_LOG_LID_TELEMETRY_HOST`` or
+ ``NVME_LOG_LID_TELEMETRY_CTRL``
+
+``rsvd1``
+ Reserved
+
+``ieee``
+ IEEE OUI Identifier is the Organization Unique Identifier (OUI)
+ for the controller vendor that is able to interpret the data.
+
+``dalb1``
+ Telemetry Controller-Initiated Data Area 1 Last Block is
+ the value of the last block in this area.
+
+``dalb2``
+ Telemetry Controller-Initiated Data Area 1 Last Block is
+ the value of the last block in this area.
+
+``dalb3``
+ Telemetry Controller-Initiated Data Area 1 Last Block is
+ the value of the last block in this area.
+
+``rsvd14``
+ Reserved
+
+``dalb4``
+ Telemetry Controller-Initiated Data Area 4 Last Block is
+ the value of the last block in this area.
+
+``rsvd20``
+ Reserved
+
+``hostdgn``
+ Telemetry Host-Initiated Data Generation Number is a
+ value that is incremented each time the host initiates a
+ capture of its internal controller state in the controller .
+
+``ctrlavail``
+ Telemetry Controller-Initiated Data Available, if cleared,
+ then the controller telemetry log does not contain saved
+ internal controller state. If this field is set to 1h, the
+ controller log contains saved internal controller state. If
+ this field is set to 1h, the data will be latched until the
+ host releases it by reading the log with RAE cleared.
+
+``ctrldgn``
+ Telemetry Controller-Initiated Data Generation Number is
+ a value that is incremented each time the controller initiates a
+ capture of its internal controller state in the controller .
+
+``rsnident``
+ Reason Identifiers a vendor specific identifier that describes
+ the operating conditions of the controller at the time of
+ capture.
+
+``data_area``
+ Telemetry data blocks, vendor specific information data.
+
+
+**Description**
+
+This log consists of a header describing the log and zero or more Telemetry
+Data Blocks. All Telemetry Data Blocks are ``NVME_LOG_TELEM_BLOCK_SIZE``, 512
+bytes, in size. This log captures the controller’s internal state.
+
+
+
+
+.. c:struct:: nvme_endurance_group_log
+
+ Endurance Group Information Log
+
+**Definition**
+
+::
+
+ struct nvme_endurance_group_log {
+ __u8 critical_warning;
+ __u8 endurance_group_features;
+ __u8 rsvd2;
+ __u8 avl_spare;
+ __u8 avl_spare_threshold;
+ __u8 percent_used;
+ __le16 domain_identifier;
+ __u8 rsvd8[24];
+ __u8 endurance_estimate[16];
+ __u8 data_units_read[16];
+ __u8 data_units_written[16];
+ __u8 media_units_written[16];
+ __u8 host_read_cmds[16];
+ __u8 host_write_cmds[16];
+ __u8 media_data_integrity_err[16];
+ __u8 num_err_info_log_entries[16];
+ __u8 total_end_grp_cap[16];
+ __u8 unalloc_end_grp_cap[16];
+ __u8 rsvd192[320];
+ };
+
+**Members**
+
+``critical_warning``
+ Critical Warning
+
+``endurance_group_features``
+ Endurance Group Features
+
+``rsvd2``
+ Reserved
+
+``avl_spare``
+ Available Spare
+
+``avl_spare_threshold``
+ Available Spare Threshold
+
+``percent_used``
+ Percentage Used
+
+``domain_identifier``
+ Domain Identifier
+
+``rsvd8``
+ Reserved
+
+``endurance_estimate``
+ Endurance Estimate
+
+``data_units_read``
+ Data Units Read
+
+``data_units_written``
+ Data Units Written
+
+``media_units_written``
+ Media Units Written
+
+``host_read_cmds``
+ Host Read Commands
+
+``host_write_cmds``
+ Host Write Commands
+
+``media_data_integrity_err``
+ Media and Data Integrity Errors
+
+``num_err_info_log_entries``
+ Number of Error Information Log Entries
+
+``total_end_grp_cap``
+ Total Endurance Group Capacity
+
+``unalloc_end_grp_cap``
+ Unallocated Endurance Group Capacity
+
+``rsvd192``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_eg_critical_warning_flags
+
+ Endurance Group Information Log - Critical Warning
+
+**Constants**
+
+``NVME_EG_CRITICAL_WARNING_SPARE``
+ Available spare capacity of the Endurance Group
+ has fallen below the threshold
+
+``NVME_EG_CRITICAL_WARNING_DEGRADED``
+ Endurance Group reliability has been degraded
+
+``NVME_EG_CRITICAL_WARNING_READ_ONLY``
+ Endurance Group have been placed in read only
+ mode
+
+
+
+
+.. c:struct:: nvme_aggregate_endurance_group_event
+
+ Endurance Group Event Aggregate
+
+**Definition**
+
+::
+
+ struct nvme_aggregate_endurance_group_event {
+ __le64 num_entries;
+ __le16 entries[];
+ };
+
+**Members**
+
+``num_entries``
+ Number or entries
+
+``entries``
+ List of entries
+
+
+
+
+
+.. c:struct:: nvme_nvmset_predictable_lat_log
+
+ Predictable Latency Mode - Deterministic Threshold Configuration Data
+
+**Definition**
+
+::
+
+ struct nvme_nvmset_predictable_lat_log {
+ __u8 status;
+ __u8 rsvd1;
+ __le16 event_type;
+ __u8 rsvd4[28];
+ __le64 dtwin_rt;
+ __le64 dtwin_wt;
+ __le64 dtwin_tmax;
+ __le64 ndwin_tmin_hi;
+ __le64 ndwin_tmin_lo;
+ __u8 rsvd72[56];
+ __le64 dtwin_re;
+ __le64 dtwin_we;
+ __le64 dtwin_te;
+ __u8 rsvd152[360];
+ };
+
+**Members**
+
+``status``
+ Status
+
+``rsvd1``
+ Reserved
+
+``event_type``
+ Event Type
+
+``rsvd4``
+ Reserved
+
+``dtwin_rt``
+ DTWIN Reads Typical
+
+``dtwin_wt``
+ DTWIN Writes Typical
+
+``dtwin_tmax``
+ DTWIN Time Maximum
+
+``ndwin_tmin_hi``
+ NDWIN Time Minimum High
+
+``ndwin_tmin_lo``
+ NDWIN Time Minimum Low
+
+``rsvd72``
+ Reserved
+
+``dtwin_re``
+ DTWIN Reads Estimate
+
+``dtwin_we``
+ DTWIN Writes Estimate
+
+``dtwin_te``
+ DTWIN Time Estimate
+
+``rsvd152``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_nvmeset_pl_status
+
+ Predictable Latency Per NVM Set Log - Status
+
+**Constants**
+
+``NVME_NVMSET_PL_STATUS_DISABLED``
+ Not used (Predictable Latency Mode not enabled)
+
+``NVME_NVMSET_PL_STATUS_DTWIN``
+ Deterministic Window (DTWIN)
+
+``NVME_NVMSET_PL_STATUS_NDWIN``
+ Non-Deterministic Window (NDWIN)
+
+
+
+
+.. c:enum:: nvme_nvmset_pl_events
+
+ Predictable Latency Per NVM Set Log - Event Type
+
+**Constants**
+
+``NVME_NVMSET_PL_EVENT_DTWIN_READ_WARN``
+ DTWIN Reads Warning
+
+``NVME_NVMSET_PL_EVENT_DTWIN_WRITE_WARN``
+ DTWIN Writes Warning
+
+``NVME_NVMSET_PL_EVENT_DTWIN_TIME_WARN``
+ DTWIN Time Warning
+
+``NVME_NVMSET_PL_EVENT_DTWIN_EXCEEDED``
+ Autonomous transition from DTWIN
+ to NDWIN due to typical or
+ maximum value exceeded
+
+``NVME_NVMSET_PL_EVENT_DTWIN_EXCURSION``
+ Autonomous transition from DTWIN
+ to NDWIN due to Deterministic
+ Excursion
+
+
+
+
+.. c:struct:: nvme_aggregate_predictable_lat_event
+
+ Predictable Latency Event Aggregate Log Page
+
+**Definition**
+
+::
+
+ struct nvme_aggregate_predictable_lat_event {
+ __le64 num_entries;
+ __le16 entries[];
+ };
+
+**Members**
+
+``num_entries``
+ Number of entries
+
+``entries``
+ Entry list
+
+
+
+
+
+.. c:struct:: nvme_ana_group_desc
+
+ ANA Group Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_ana_group_desc {
+ __le32 grpid;
+ __le32 nnsids;
+ __le64 chgcnt;
+ __u8 state;
+ __u8 rsvd17[15];
+ __le32 nsids[];
+ };
+
+**Members**
+
+``grpid``
+ ANA group id
+
+``nnsids``
+ Number of namespaces in **nsids**
+
+``chgcnt``
+ Change counter
+
+``state``
+ ANA state
+
+``rsvd17``
+ Reserved
+
+``nsids``
+ List of namespaces
+
+
+
+
+
+.. c:enum:: nvme_ana_state
+
+ ANA Group Descriptor - Asymmetric Namespace Access State
+
+**Constants**
+
+``NVME_ANA_STATE_OPTIMIZED``
+ ANA Optimized state
+
+``NVME_ANA_STATE_NONOPTIMIZED``
+ ANA Non-Optimized state
+
+``NVME_ANA_STATE_INACCESSIBLE``
+ ANA Inaccessible state
+
+``NVME_ANA_STATE_PERSISTENT_LOSS``
+ ANA Persistent Loss state
+
+``NVME_ANA_STATE_CHANGE``
+ ANA Change state
+
+
+
+
+.. c:struct:: nvme_ana_log
+
+ Asymmetric Namespace Access Log
+
+**Definition**
+
+::
+
+ struct nvme_ana_log {
+ __le64 chgcnt;
+ __le16 ngrps;
+ __u8 rsvd10[6];
+ struct nvme_ana_group_desc descs[];
+ };
+
+**Members**
+
+``chgcnt``
+ Change Count
+
+``ngrps``
+ Number of ANA Group Descriptors
+
+``rsvd10``
+ Reserved
+
+``descs``
+ ANA Group Descriptor
+
+
+
+
+
+.. c:struct:: nvme_persistent_event_log
+
+ Persistent Event Log
+
+**Definition**
+
+::
+
+ struct nvme_persistent_event_log {
+ __u8 lid;
+ __u8 rsvd1[3];
+ __le32 tnev;
+ __le64 tll;
+ __u8 rv;
+ __u8 rsvd17;
+ __le16 lhl;
+ __le64 ts;
+ __u8 poh[16];
+ __le64 pcc;
+ __le16 vid;
+ __le16 ssvid;
+ char sn[20];
+ char mn[40];
+ char subnqn[NVME_NQN_LENGTH];
+ __le16 gen_number;
+ __le32 rci;
+ __u8 rsvd378[102];
+ __u8 seb[32];
+ };
+
+**Members**
+
+``lid``
+ Log Identifier
+
+``rsvd1``
+ Reserved
+
+``tnev``
+ Total Number of Events
+
+``tll``
+ Total Log Length
+
+``rv``
+ Log Revision
+
+``rsvd17``
+ Reserved
+
+``lhl``
+ Log Header Length
+
+``ts``
+ Timestamp
+
+``poh``
+ Power on Hours
+
+``pcc``
+ Power Cycle Count
+
+``vid``
+ PCI Vendor ID
+
+``ssvid``
+ PCI Subsystem Vendor ID
+
+``sn``
+ Serial Number
+
+``mn``
+ Model Number
+
+``subnqn``
+ NVM Subsystem NVMe Qualified Name
+
+``gen_number``
+ Generation Number
+
+``rci``
+ Reporting Context Information
+
+``rsvd378``
+ Reserved
+
+``seb``
+ Supported Events Bitmap
+
+
+
+
+
+.. c:struct:: nvme_persistent_event_entry
+
+ Persistent Event
+
+**Definition**
+
+::
+
+ struct nvme_persistent_event_entry {
+ __u8 etype;
+ __u8 etype_rev;
+ __u8 ehl;
+ __u8 ehai;
+ __le16 cntlid;
+ __le64 ets;
+ __le16 pelpid;
+ __u8 rsvd16[4];
+ __le16 vsil;
+ __le16 el;
+ };
+
+**Members**
+
+``etype``
+ Event Type
+
+``etype_rev``
+ Event Type Revision
+
+``ehl``
+ Event Header Length
+
+``ehai``
+ Event Header Additional Info
+
+``cntlid``
+ Controller Identifier
+
+``ets``
+ Event Timestamp
+
+``pelpid``
+ Port Identifier
+
+``rsvd16``
+ Reserved
+
+``vsil``
+ Vendor Specific Information Length
+
+``el``
+ Event Length
+
+
+
+
+
+.. c:enum:: nvme_persistent_event_types
+
+ Persistent event log events
+
+**Constants**
+
+``NVME_PEL_SMART_HEALTH_EVENT``
+ SMART / Health Log Snapshot Event
+
+``NVME_PEL_FW_COMMIT_EVENT``
+ Firmware Commit Event
+
+``NVME_PEL_TIMESTAMP_EVENT``
+ Timestamp Change Event
+
+``NVME_PEL_POWER_ON_RESET_EVENT``
+ Power-on or Reset Event
+
+``NVME_PEL_NSS_HW_ERROR_EVENT``
+ NVM Subsystem Hardware Error Event
+
+``NVME_PEL_CHANGE_NS_EVENT``
+ Change Namespace Event
+
+``NVME_PEL_FORMAT_START_EVENT``
+ Format NVM Start Event
+
+``NVME_PEL_FORMAT_COMPLETION_EVENT``
+ Format NVM Completion Event
+
+``NVME_PEL_SANITIZE_START_EVENT``
+ Sanitize Start Event
+
+``NVME_PEL_SANITIZE_COMPLETION_EVENT``
+ Sanitize Completion Event
+
+``NVME_PEL_SET_FEATURE_EVENT``
+ Set Feature Event
+
+``NVME_PEL_TELEMETRY_CRT``
+ Telemetry Log Create Event
+
+``NVME_PEL_THERMAL_EXCURSION_EVENT``
+ Thermal Excursion Event
+
+
+
+
+.. c:struct:: nvme_fw_commit_event
+
+ Firmware Commit Event Data
+
+**Definition**
+
+::
+
+ struct nvme_fw_commit_event {
+ __le64 old_fw_rev;
+ __le64 new_fw_rev;
+ __u8 fw_commit_action;
+ __u8 fw_slot;
+ __u8 sct_fw;
+ __u8 sc_fw;
+ __le16 vndr_assign_fw_commit_rc;
+ };
+
+**Members**
+
+``old_fw_rev``
+ Old Firmware Revision
+
+``new_fw_rev``
+ New Firmware Revision
+
+``fw_commit_action``
+ Firmware Commit Action
+
+``fw_slot``
+ Firmware Slot
+
+``sct_fw``
+ Status Code Type for Firmware Commit Command
+
+``sc_fw``
+ Status Returned for Firmware Commit Command
+
+``vndr_assign_fw_commit_rc``
+ Vendor Assigned Firmware Commit Result Code
+
+
+
+
+
+.. c:struct:: nvme_timestamp
+
+ Timestamp - Data Structure for Get Features
+
+**Definition**
+
+::
+
+ struct nvme_timestamp {
+ __u8 timestamp[6];
+ __u8 attr;
+ __u8 rsvd;
+ };
+
+**Members**
+
+``timestamp``
+ Timestamp value based on origin and synch field
+
+``attr``
+ Attribute
+
+``rsvd``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_time_stamp_change_event
+
+ Timestamp Change Event
+
+**Definition**
+
+::
+
+ struct nvme_time_stamp_change_event {
+ __le64 previous_timestamp;
+ __le64 ml_secs_since_reset;
+ };
+
+**Members**
+
+``previous_timestamp``
+ Previous Timestamp
+
+``ml_secs_since_reset``
+ Milliseconds Since Reset
+
+
+
+
+
+.. c:struct:: nvme_power_on_reset_info_list
+
+ Controller Reset Information
+
+**Definition**
+
+::
+
+ struct nvme_power_on_reset_info_list {
+ __le16 cid;
+ __u8 fw_act;
+ __u8 op_in_prog;
+ __u8 rsvd4[12];
+ __le32 ctrl_power_cycle;
+ __le64 power_on_ml_seconds;
+ __le64 ctrl_time_stamp;
+ };
+
+**Members**
+
+``cid``
+ Controller ID
+
+``fw_act``
+ Firmware Activation
+
+``op_in_prog``
+ Operation in Progress
+
+``rsvd4``
+ Reserved
+
+``ctrl_power_cycle``
+ Controller Power Cycle
+
+``power_on_ml_seconds``
+ Power on milliseconds
+
+``ctrl_time_stamp``
+ Controller Timestamp
+
+
+
+
+
+.. c:struct:: nvme_nss_hw_err_event
+
+ NVM Subsystem Hardware Error Event
+
+**Definition**
+
+::
+
+ struct nvme_nss_hw_err_event {
+ __le16 nss_hw_err_event_code;
+ __u8 rsvd2[2];
+ __u8 *add_hw_err_info;
+ };
+
+**Members**
+
+``nss_hw_err_event_code``
+ NVM Subsystem Hardware Error Event Code
+
+``rsvd2``
+ Reserved
+
+``add_hw_err_info``
+ Additional Hardware Error Information
+
+
+
+
+
+.. c:struct:: nvme_change_ns_event
+
+ Change Namespace Event Data
+
+**Definition**
+
+::
+
+ struct nvme_change_ns_event {
+ __le32 nsmgt_cdw10;
+ __u8 rsvd4[4];
+ __le64 nsze;
+ __u8 rsvd16[8];
+ __le64 nscap;
+ __u8 flbas;
+ __u8 dps;
+ __u8 nmic;
+ __u8 rsvd35;
+ __le32 ana_grp_id;
+ __le16 nvmset_id;
+ __le16 rsvd42;
+ __le32 nsid;
+ };
+
+**Members**
+
+``nsmgt_cdw10``
+ Namespace Management CDW10
+
+``rsvd4``
+ Reserved
+
+``nsze``
+ Namespace Size
+
+``rsvd16``
+ Reserved
+
+``nscap``
+ Namespace Capacity
+
+``flbas``
+ Formatted LBA Size
+
+``dps``
+ End-to-end Data Protection Type Settings
+
+``nmic``
+ Namespace Multi-path I/O and Namespace Sharing Capabilities
+
+``rsvd35``
+ Reserved
+
+``ana_grp_id``
+ ANA Group Identifier
+
+``nvmset_id``
+ NVM Set Identifier
+
+``rsvd42``
+ Reserved
+
+``nsid``
+ Namespace ID
+
+
+
+
+
+.. c:struct:: nvme_format_nvm_start_event
+
+ Format NVM Start Event Data
+
+**Definition**
+
+::
+
+ struct nvme_format_nvm_start_event {
+ __le32 nsid;
+ __u8 fna;
+ __u8 rsvd5[3];
+ __le32 format_nvm_cdw10;
+ };
+
+**Members**
+
+``nsid``
+ Namespace Identifier
+
+``fna``
+ Format NVM Attributes
+
+``rsvd5``
+ Reserved
+
+``format_nvm_cdw10``
+ Format NVM CDW10
+
+
+
+
+
+.. c:struct:: nvme_format_nvm_compln_event
+
+ Format NVM Completion Event Data
+
+**Definition**
+
+::
+
+ struct nvme_format_nvm_compln_event {
+ __le32 nsid;
+ __u8 smallest_fpi;
+ __u8 format_nvm_status;
+ __le16 compln_info;
+ __le32 status_field;
+ };
+
+**Members**
+
+``nsid``
+ Namespace Identifier
+
+``smallest_fpi``
+ Smallest Format Progress Indicator
+
+``format_nvm_status``
+ Format NVM Status
+
+``compln_info``
+ Completion Information
+
+``status_field``
+ Status Field
+
+
+
+
+
+.. c:struct:: nvme_sanitize_start_event
+
+ Sanitize Start Event Data
+
+**Definition**
+
+::
+
+ struct nvme_sanitize_start_event {
+ __le32 sani_cap;
+ __le32 sani_cdw10;
+ __le32 sani_cdw11;
+ };
+
+**Members**
+
+``sani_cap``
+ SANICAP
+
+``sani_cdw10``
+ Sanitize CDW10
+
+``sani_cdw11``
+ Sanitize CDW11
+
+
+
+
+
+.. c:struct:: nvme_sanitize_compln_event
+
+ Sanitize Completion Event Data
+
+**Definition**
+
+::
+
+ struct nvme_sanitize_compln_event {
+ __le16 sani_prog;
+ __le16 sani_status;
+ __le16 cmpln_info;
+ __u8 rsvd6[2];
+ };
+
+**Members**
+
+``sani_prog``
+ Sanitize Progress
+
+``sani_status``
+ Sanitize Status
+
+``cmpln_info``
+ Completion Information
+
+``rsvd6``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_set_feature_event
+
+ Set Feature Event Data
+
+**Definition**
+
+::
+
+ struct nvme_set_feature_event {
+ __le32 layout;
+ __le32 cdw_mem[0];
+ };
+
+**Members**
+
+``layout``
+ Set Feature Event Layout
+
+``cdw_mem``
+ Command Dwords Memory buffer
+
+
+
+
+
+.. c:struct:: nvme_thermal_exc_event
+
+ Thermal Excursion Event Data
+
+**Definition**
+
+::
+
+ struct nvme_thermal_exc_event {
+ __u8 over_temp;
+ __u8 threshold;
+ };
+
+**Members**
+
+``over_temp``
+ Over Temperature
+
+``threshold``
+ temperature threshold
+
+
+
+
+
+.. c:struct:: nvme_lba_rd
+
+ LBA Range Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_lba_rd {
+ __le64 rslba;
+ __le32 rnlb;
+ __u8 rsvd12[4];
+ };
+
+**Members**
+
+``rslba``
+ Range Starting LBA
+
+``rnlb``
+ Range Number of Logical Blocks
+
+``rsvd12``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_lbas_ns_element
+
+ LBA Status Log Namespace Element
+
+**Definition**
+
+::
+
+ struct nvme_lbas_ns_element {
+ __le32 neid;
+ __le32 nlrd;
+ __u8 ratype;
+ __u8 rsvd8[7];
+ struct nvme_lba_rd lba_rd[];
+ };
+
+**Members**
+
+``neid``
+ Namespace Element Identifier
+
+``nlrd``
+ Number of LBA Range Descriptors
+
+``ratype``
+ Recommended Action Type. see **enum** nvme_lba_status_atype
+
+``rsvd8``
+ Reserved
+
+``lba_rd``
+ LBA Range Descriptor
+
+
+
+
+
+.. c:enum:: nvme_lba_status_atype
+
+ Potentially Unrecoverable LBAs
+
+**Constants**
+
+``NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED``
+ Potentially Unrecoverable LBAs
+
+``NVME_LBA_STATUS_ATYPE_SCAN_TRACKED``
+ Potentially Unrecoverable LBAs
+ associated with physical storage
+
+
+
+
+.. c:struct:: nvme_lba_status_log
+
+ LBA Status Information Log
+
+**Definition**
+
+::
+
+ struct nvme_lba_status_log {
+ __le32 lslplen;
+ __le32 nlslne;
+ __le32 estulb;
+ __u8 rsvd12[2];
+ __le16 lsgc;
+ struct nvme_lbas_ns_element elements[];
+ };
+
+**Members**
+
+``lslplen``
+ LBA Status Log Page Length
+
+``nlslne``
+ Number of LBA Status Log Namespace Elements
+
+``estulb``
+ Estimate of Unrecoverable Logical Blocks
+
+``rsvd12``
+ Reserved
+
+``lsgc``
+ LBA Status Generation Counter
+
+``elements``
+ LBA Status Log Namespace Element List
+
+
+
+
+
+.. c:struct:: nvme_eg_event_aggregate_log
+
+ Endurance Group Event Aggregate
+
+**Definition**
+
+::
+
+ struct nvme_eg_event_aggregate_log {
+ __le64 nr_entries;
+ __le16 egids[];
+ };
+
+**Members**
+
+``nr_entries``
+ Number of Entries
+
+``egids``
+ Endurance Group Identifier
+
+
+
+
+
+.. c:enum:: nvme_fid_supported_effects
+
+ FID Supported and Effects Data Structure definitions
+
+**Constants**
+
+``NVME_FID_SUPPORTED_EFFECTS_FSUPP``
+ FID Supported
+
+``NVME_FID_SUPPORTED_EFFECTS_UDCC``
+ User Data Content Change
+
+``NVME_FID_SUPPORTED_EFFECTS_NCC``
+ Namespace Capability Change
+
+``NVME_FID_SUPPORTED_EFFECTS_NIC``
+ Namespace Inventory Change
+
+``NVME_FID_SUPPORTED_EFFECTS_CCC``
+ Controller Capability Change
+
+``NVME_FID_SUPPORTED_EFFECTS_UUID_SEL``
+ UUID Selection Supported
+
+``NVME_FID_SUPPORTED_EFFECTS_SCOPE_SHIFT``
+ FID Scope Shift
+
+``NVME_FID_SUPPORTED_EFFECTS_SCOPE_MASK``
+ FID Scope Mask
+
+``NVME_FID_SUPPORTED_EFFECTS_SCOPE_NS``
+ Namespace Scope
+
+``NVME_FID_SUPPORTED_EFFECTS_SCOPE_CTRL``
+ Controller Scope
+
+``NVME_FID_SUPPORTED_EFFECTS_SCOPE_NVM_SET``
+ NVM Set Scope
+
+``NVME_FID_SUPPORTED_EFFECTS_SCOPE_ENDGRP``
+ Endurance Group Scope
+
+``NVME_FID_SUPPORTED_EFFECTS_SCOPE_DOMAIN``
+ Domain Scope
+
+``NVME_FID_SUPPORTED_EFFECTS_SCOPE_NSS``
+ NVM Subsystem Scope
+
+
+
+
+.. c:struct:: nvme_fid_supported_effects_log
+
+ Feature Identifiers Supported and Effects
+
+**Definition**
+
+::
+
+ struct nvme_fid_supported_effects_log {
+ __le32 fid_support[NVME_LOG_FID_SUPPORTED_EFFECTS_MAX];
+ };
+
+**Members**
+
+``fid_support``
+ Feature Identifier Supported
+
+
+
+
+
+.. c:enum:: nvme_mi_cmd_supported_effects
+
+ MI Command Supported and Effects Data Structure
+
+**Constants**
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_CSUPP``
+ Command Supported
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_UDCC``
+ User Data Content Change
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_NCC``
+ Namespace Capability Change
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_NIC``
+ Namespace Inventory Change
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_CCC``
+ Controller Capability Change
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_SHIFT``
+ 20 bit shift
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_MASK``
+ 12 bit mask - 0xfff
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NS``
+ Namespace Scope
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_CTRL``
+ Controller Scope
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NVM_SET``
+ NVM Set Scope
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_ENDGRP``
+ Endurance Group Scope
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_DOMAIN``
+ Domain Scope
+
+``NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NSS``
+ NVM Subsystem Scope
+
+
+
+
+.. c:struct:: nvme_mi_cmd_supported_effects_log
+
+ NVMe-MI Commands Supported and Effects Log
+
+**Definition**
+
+::
+
+ struct nvme_mi_cmd_supported_effects_log {
+ __le32 mi_cmd_support[NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX];
+ __le32 reserved1[NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED];
+ };
+
+**Members**
+
+``mi_cmd_support``
+ NVMe-MI Commands Supported
+
+``reserved1``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_boot_partition
+
+ Boot Partition Log
+
+**Definition**
+
+::
+
+ struct nvme_boot_partition {
+ __u8 lid;
+ __u8 rsvd1[3];
+ __le32 bpinfo;
+ __u8 rsvd8[8];
+ __u8 boot_partition_data[];
+ };
+
+**Members**
+
+``lid``
+ Boot Partition Identifier
+
+``rsvd1``
+ Reserved
+
+``bpinfo``
+ Boot Partition Information
+
+``rsvd8``
+ Reserved
+
+``boot_partition_data``
+ Contains the contents of the
+ specified Boot Partition
+
+
+
+
+
+.. c:struct:: nvme_eom_lane_desc
+
+ EOM Lane Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_eom_lane_desc {
+ __u8 rsvd0;
+ __u8 mstatus;
+ __u8 lane;
+ __u8 eye;
+ __le16 top;
+ __le16 bottom;
+ __le16 left;
+ __le16 right;
+ __le16 nrows;
+ __le16 ncols;
+ __le16 edlen;
+ __u8 rsvd18[14];
+ __u8 eye_desc[];
+ };
+
+**Members**
+
+``rsvd0``
+ Reserved
+
+``mstatus``
+ Measurement Status
+
+``lane``
+ Lane number
+
+``eye``
+ Eye number
+
+``top``
+ Absolute number of rows from center to top edge of eye
+
+``bottom``
+ Absolute number of rows from center to bottom edge of eye
+
+``left``
+ Absolute number of rows from center to left edge of eye
+
+``right``
+ Absolute number of rows from center to right edge of eye
+
+``nrows``
+ Number of Rows
+
+``ncols``
+ Number of Columns
+
+``edlen``
+ Eye Data Length
+
+``rsvd18``
+ Reserved
+
+``eye_desc``
+ Printable Eye, Eye Data, and any Padding
+
+
+
+
+
+.. c:struct:: nvme_phy_rx_eom_log
+
+ Physical Interface Receiver Eye Opening Measurement Log
+
+**Definition**
+
+::
+
+ struct nvme_phy_rx_eom_log {
+ __u8 lid;
+ __u8 eomip;
+ __le16 hsize;
+ __le32 rsize;
+ __u8 eomdgn;
+ __u8 lr;
+ __u8 odp;
+ __u8 lanes;
+ __u8 epl;
+ __u8 lspfc;
+ __u8 li;
+ __u8 rsvd15[3];
+ __le16 lsic;
+ __le32 dsize;
+ __le16 nd;
+ __le16 maxtb;
+ __le16 maxlr;
+ __le16 etgood;
+ __le16 etbetter;
+ __le16 etbest;
+ __u8 rsvd36[28];
+ struct nvme_eom_lane_desc descs[];
+ };
+
+**Members**
+
+``lid``
+ Log Identifier
+
+``eomip``
+ EOM In Progress
+
+``hsize``
+ Header Size
+
+``rsize``
+ Result Size
+
+``eomdgn``
+ EOM Data Generation Number
+
+``lr``
+ Log Revision
+
+``odp``
+ Optional Data Present
+
+``lanes``
+ Number of lanes configured for this port
+
+``epl``
+ Eyes Per Lane
+
+``lspfc``
+ Log Specific Parameter Field Copy
+
+``li``
+ Link Information
+
+``rsvd15``
+ Reserved
+
+``lsic``
+ Log Specific Identifier Copy
+
+``dsize``
+ Descriptor Size
+
+``nd``
+ Number of Descriptors
+
+``maxtb``
+ Maximum Top Bottom
+
+``maxlr``
+ Maximum Left Right
+
+``etgood``
+ Estimated Time for Good Quality
+
+``etbetter``
+ Estimated Time for Better Quality
+
+``etbest``
+ Estimated Time for Best Quality
+
+``rsvd36``
+ Reserved
+
+``descs``
+ EOM Lane Descriptors
+
+
+
+
+
+.. c:enum:: nvme_eom_optional_data
+
+ EOM Optional Data Present Fields
+
+**Constants**
+
+``NVME_EOM_EYE_DATA_PRESENT``
+ Eye Data Present
+
+``NVME_EOM_PRINTABLE_EYE_PRESENT``
+ Printable Eye Present
+
+
+
+
+.. c:enum:: nvme_phy_rx_eom_progress
+
+ EOM In Progress Values
+
+**Constants**
+
+``NVME_PHY_RX_EOM_NOT_STARTED``
+ EOM Not Started
+
+``NVME_PHY_RX_EOM_IN_PROGRESS``
+ EOM In Progress
+
+``NVME_PHY_RX_EOM_COMPLETED``
+ EOM Completed
+
+
+
+
+.. c:struct:: nvme_media_unit_stat_desc
+
+ Media Unit Status Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_media_unit_stat_desc {
+ __le16 muid;
+ __le16 domainid;
+ __le16 endgid;
+ __le16 nvmsetid;
+ __le16 cap_adj_fctr;
+ __u8 avl_spare;
+ __u8 percent_used;
+ __u8 mucs;
+ __u8 cio;
+ };
+
+**Members**
+
+``muid``
+ Media Unit Identifier
+
+``domainid``
+ Domain Identifier
+
+``endgid``
+ Endurance Group Identifier
+
+``nvmsetid``
+ NVM Set Identifier
+
+``cap_adj_fctr``
+ Capacity Adjustment Factor
+
+``avl_spare``
+ Available Spare
+
+``percent_used``
+ Percentage Used
+
+``mucs``
+ Number of Channels attached to media units
+
+``cio``
+ Channel Identifiers Offset
+
+
+
+
+
+.. c:struct:: nvme_media_unit_stat_log
+
+ Media Unit Status
+
+**Definition**
+
+::
+
+ struct nvme_media_unit_stat_log {
+ __le16 nmu;
+ __le16 cchans;
+ __le16 sel_config;
+ __u8 rsvd6[10];
+ struct nvme_media_unit_stat_desc mus_desc[];
+ };
+
+**Members**
+
+``nmu``
+ Number unit status descriptor
+
+``cchans``
+ Number of Channels
+
+``sel_config``
+ Selected Configuration
+
+``rsvd6``
+ Reserved
+
+``mus_desc``
+ Media unit statistic descriptors
+
+
+
+
+
+.. c:struct:: nvme_media_unit_config_desc
+
+ Media Unit Configuration Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_media_unit_config_desc {
+ __le16 muid;
+ __u8 rsvd2[4];
+ __le16 mudl;
+ };
+
+**Members**
+
+``muid``
+ Media Unit Identifier
+
+``rsvd2``
+ Reserved
+
+``mudl``
+ Media Unit Descriptor Length
+
+
+
+
+
+.. c:struct:: nvme_channel_config_desc
+
+ Channel Configuration Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_channel_config_desc {
+ __le16 chanid;
+ __le16 chmus;
+ struct nvme_media_unit_config_desc mu_config_desc[];
+ };
+
+**Members**
+
+``chanid``
+ Channel Identifier
+
+``chmus``
+ Number Channel Media Units
+
+``mu_config_desc``
+ Channel Unit config descriptors.
+ See **struct** nvme_media_unit_config_desc
+
+
+
+
+
+.. c:struct:: nvme_end_grp_chan_desc
+
+ Endurance Group Channel Configuration Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_end_grp_chan_desc {
+ __le16 egchans;
+ struct nvme_channel_config_desc chan_config_desc[];
+ };
+
+**Members**
+
+``egchans``
+ Number of Channels
+
+``chan_config_desc``
+ Channel config descriptors.
+ See **struct** nvme_channel_config_desc
+
+
+
+
+
+.. c:struct:: nvme_end_grp_config_desc
+
+ Endurance Group Configuration Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_end_grp_config_desc {
+ __le16 endgid;
+ __le16 cap_adj_factor;
+ __u8 rsvd4[12];
+ __u8 tegcap[16];
+ __u8 segcap[16];
+ __u8 end_est[16];
+ __u8 rsvd64[16];
+ __le16 egsets;
+ __le16 nvmsetid[];
+ };
+
+**Members**
+
+``endgid``
+ Endurance Group Identifier
+
+``cap_adj_factor``
+ Capacity Adjustment Factor
+
+``rsvd4``
+ Reserved
+
+``tegcap``
+ Total Endurance Group Capacity
+
+``segcap``
+ Spare Endurance Group Capacity
+
+``end_est``
+ Endurance Estimate
+
+``rsvd64``
+ Reserved
+
+``egsets``
+ Number of NVM Sets
+
+``nvmsetid``
+ NVM Set Identifier
+
+
+
+
+
+.. c:struct:: nvme_capacity_config_desc
+
+ Capacity Configuration structure definitions
+
+**Definition**
+
+::
+
+ struct nvme_capacity_config_desc {
+ __le16 cap_config_id;
+ __le16 domainid;
+ __le16 egcn;
+ __u8 rsvd6[26];
+ struct nvme_end_grp_config_desc egcd[];
+ };
+
+**Members**
+
+``cap_config_id``
+ Capacity Configuration Identifier
+
+``domainid``
+ Domain Identifier
+
+``egcn``
+ Number Endurance Group Configuration
+ Descriptors
+
+``rsvd6``
+ Reserved
+
+``egcd``
+ Endurance Group Config descriptors.
+ See **struct** nvme_end_grp_config_desc
+
+
+
+
+
+.. c:struct:: nvme_supported_cap_config_list_log
+
+ Supported Capacity Configuration list log page
+
+**Definition**
+
+::
+
+ struct nvme_supported_cap_config_list_log {
+ __u8 sccn;
+ __u8 rsvd1[15];
+ struct nvme_capacity_config_desc cap_config_desc[];
+ };
+
+**Members**
+
+``sccn``
+ Number of capacity configuration
+
+``rsvd1``
+ Reserved
+
+``cap_config_desc``
+ Capacity configuration descriptor.
+ See **struct** nvme_capacity_config_desc
+
+
+
+
+
+.. c:struct:: nvme_resv_notification_log
+
+ Reservation Notification Log
+
+**Definition**
+
+::
+
+ struct nvme_resv_notification_log {
+ __le64 lpc;
+ __u8 rnlpt;
+ __u8 nalp;
+ __u8 rsvd9[2];
+ __le32 nsid;
+ __u8 rsvd16[48];
+ };
+
+**Members**
+
+``lpc``
+ Log Page Count
+
+``rnlpt``
+ See :c:type:`enum nvme_resv_notify_rnlpt <nvme_resv_notify_rnlpt>`.
+
+``nalp``
+ Number of Available Log Pages
+
+``rsvd9``
+ Reserved
+
+``nsid``
+ Namespace ID
+
+``rsvd16``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_resv_notify_rnlpt
+
+ Reservation Notification Log - Reservation Notification Log Page Type
+
+**Constants**
+
+``NVME_RESV_NOTIFY_RNLPT_EMPTY``
+ Empty Log Page
+
+``NVME_RESV_NOTIFY_RNLPT_REGISTRATION_PREEMPTED``
+ Registration Preempted
+
+``NVME_RESV_NOTIFY_RNLPT_RESERVATION_RELEASED``
+ Reservation Released
+
+``NVME_RESV_NOTIFY_RNLPT_RESERVATION_PREEMPTED``
+ Reservation Preempted
+
+
+
+
+.. c:struct:: nvme_sanitize_log_page
+
+ Sanitize Status (Log Identifier 81h)
+
+**Definition**
+
+::
+
+ struct nvme_sanitize_log_page {
+ __le16 sprog;
+ __le16 sstat;
+ __le32 scdw10;
+ __le32 eto;
+ __le32 etbe;
+ __le32 etce;
+ __le32 etond;
+ __le32 etbend;
+ __le32 etcend;
+ __u8 rsvd32[480];
+ };
+
+**Members**
+
+``sprog``
+ Sanitize Progress (SPROG): indicates the fraction complete of the
+ sanitize operation. The value is a numerator of the fraction
+ complete that has 65,536 (10000h) as its denominator. This value
+ shall be set to FFFFh if the **sstat** field is not set to
+ ``NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS``.
+
+``sstat``
+ Sanitize Status (SSTAT): indicates the status associated with
+ the most recent sanitize operation. See :c:type:`enum nvme_sanitize_sstat <nvme_sanitize_sstat>`.
+
+``scdw10``
+ Sanitize Command Dword 10 Information (SCDW10): contains the value
+ of the Command Dword 10 field of the Sanitize command that started
+ the sanitize operation.
+
+``eto``
+ Estimated Time For Overwrite: indicates the number of seconds required
+ to complete an Overwrite sanitize operation with 16 passes in
+ the background when the No-Deallocate Modifies Media After Sanitize
+ field is not set to 10b. A value of 0h indicates that the sanitize
+ operation is expected to be completed in the background when the
+ Sanitize command that started that operation is completed. A value
+ of FFFFFFFFh indicates that no time period is reported.
+
+``etbe``
+ Estimated Time For Block Erase: indicates the number of seconds
+ required to complete a Block Erase sanitize operation in the
+ background when the No-Deallocate Modifies Media After Sanitize
+ field is not set to 10b. A value of 0h indicates that the sanitize
+ operation is expected to be completed in the background when the
+ Sanitize command that started that operation is completed.
+ A value of FFFFFFFFh indicates that no time period is reported.
+
+``etce``
+ Estimated Time For Crypto Erase: indicates the number of seconds
+ required to complete a Crypto Erase sanitize operation in the
+ background when the No-Deallocate Modifies Media After Sanitize
+ field is not set to 10b. A value of 0h indicates that the sanitize
+ operation is expected to be completed in the background when the
+ Sanitize command that started that operation is completed.
+ A value of FFFFFFFFh indicates that no time period is reported.
+
+``etond``
+ Estimated Time For Overwrite With No-Deallocate Media Modification:
+ indicates the number of seconds required to complete an Overwrite
+ sanitize operation and the associated additional media modification
+ after the Overwrite sanitize operation in the background when
+ the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+ command that requested the Overwrite sanitize operation; and
+ the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+ A value of 0h indicates that the sanitize operation is expected
+ to be completed in the background when the Sanitize command that
+ started that operation is completed. A value of FFFFFFFFh indicates
+ that no time period is reported.
+
+``etbend``
+ Estimated Time For Block Erase With No-Deallocate Media Modification:
+ indicates the number of seconds required to complete a Block Erase
+ sanitize operation and the associated additional media modification
+ after the Block Erase sanitize operation in the background when
+ the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+ command that requested the Overwrite sanitize operation; and
+ the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+ A value of 0h indicates that the sanitize operation is expected
+ to be completed in the background when the Sanitize command that
+ started that operation is completed. A value of FFFFFFFFh indicates
+ that no time period is reported.
+
+``etcend``
+ Estimated Time For Crypto Erase With No-Deallocate Media Modification:
+ indicates the number of seconds required to complete a Crypto Erase
+ sanitize operation and the associated additional media modification
+ after the Crypto Erase sanitize operation in the background when
+ the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+ command that requested the Overwrite sanitize operation; and
+ the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+ A value of 0h indicates that the sanitize operation is expected
+ to be completed in the background when the Sanitize command that
+ started that operation is completed. A value of FFFFFFFFh indicates
+ that no time period is reported.
+
+``rsvd32``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_sanitize_sstat
+
+ Sanitize Status (SSTAT)
+
+**Constants**
+
+``NVME_SANITIZE_SSTAT_STATUS_SHIFT``
+ Shift amount to get the status value of
+ the most recent sanitize operation from
+ the :c:type:`struct nvme_sanitize_log_page <nvme_sanitize_log_page>`.sstat
+ field.
+
+``NVME_SANITIZE_SSTAT_STATUS_MASK``
+ Mask to get the status value of the most
+ recent sanitize operation.
+
+``NVME_SANITIZE_SSTAT_STATUS_NEVER_SANITIZED``
+ The NVM subsystem has never been
+ sanitized.
+
+``NVME_SANITIZE_SSTAT_STATUS_COMPLETE_SUCCESS``
+ The most recent sanitize operation
+ completed successfully including any
+ additional media modification.
+
+``NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS``
+ A sanitize operation is currently in progress.
+
+``NVME_SANITIZE_SSTAT_STATUS_COMPLETED_FAILED``
+ The most recent sanitize operation
+ failed.
+
+``NVME_SANITIZE_SSTAT_STATUS_ND_COMPLETE_SUCCESS``
+ The most recent sanitize operation
+ for which No-Deallocate After Sanitize was
+ requested has completed successfully with
+ deallocation of all user data.
+
+``NVME_SANITIZE_SSTAT_COMPLETED_PASSES_SHIFT``
+ Shift amount to get the number
+ of completed passes if the most recent
+ sanitize operation was an Overwrite. This
+ value shall be cleared to 0h if the most
+ recent sanitize operation was not
+ an Overwrite.
+
+``NVME_SANITIZE_SSTAT_COMPLETED_PASSES_MASK``
+ Mask to get the number of completed
+ passes.
+
+``NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT``
+ Shift amount to get the Global
+ Data Erased value from the
+ :c:type:`struct nvme_sanitize_log_page <nvme_sanitize_log_page>`.sstat field.
+
+``NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK``
+ Mask to get the Global Data Erased
+ value.
+
+``NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED``
+ Global Data Erased: if set, then no
+ namespace user data in the NVM subsystem
+ has been written to and no Persistent
+ Memory Region in the NVM subsystem has
+ been enabled since being manufactured and
+ the NVM subsystem has never been sanitized;
+ or since the most recent successful sanitize
+ operation.
+
+
+
+
+.. c:struct:: nvme_zns_changed_zone_log
+
+ ZNS Changed Zone List log
+
+**Definition**
+
+::
+
+ struct nvme_zns_changed_zone_log {
+ __le16 nrzid;
+ __u8 rsvd2[6];
+ __le64 zid[NVME_ZNS_CHANGED_ZONES_MAX];
+ };
+
+**Members**
+
+``nrzid``
+ Number of Zone Identifiers
+
+``rsvd2``
+ Reserved
+
+``zid``
+ Zone Identifier
+
+
+
+
+
+.. c:enum:: nvme_zns_zt
+
+ Zone Descriptor Data Structure - Zone Type
+
+**Constants**
+
+``NVME_ZONE_TYPE_SEQWRITE_REQ``
+ Sequential Write Required
+
+
+
+
+.. c:enum:: nvme_zns_za
+
+ Zone Descriptor Data Structure
+
+**Constants**
+
+``NVME_ZNS_ZA_ZFC``
+ Zone Finished by Controller
+
+``NVME_ZNS_ZA_FZR``
+ Finish Zone Recommended
+
+``NVME_ZNS_ZA_RZR``
+ Reset Zone Recommended
+
+``NVME_ZNS_ZA_ZRWAV``
+
+``NVME_ZNS_ZA_ZDEV``
+ Zone Descriptor Extension Valid
+
+
+
+
+.. c:enum:: nvme_zns_zs
+
+ Zone Descriptor Data Structure - Zone State
+
+**Constants**
+
+``NVME_ZNS_ZS_EMPTY``
+ Empty state
+
+``NVME_ZNS_ZS_IMPL_OPEN``
+ Implicitly open state
+
+``NVME_ZNS_ZS_EXPL_OPEN``
+ Explicitly open state
+
+``NVME_ZNS_ZS_CLOSED``
+ Closed state
+
+``NVME_ZNS_ZS_READ_ONLY``
+ Read only state
+
+``NVME_ZNS_ZS_FULL``
+ Full state
+
+``NVME_ZNS_ZS_OFFLINE``
+ Offline state
+
+
+
+
+.. c:struct:: nvme_zns_desc
+
+ Zone Descriptor Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_zns_desc {
+ __u8 zt;
+ __u8 zs;
+ __u8 za;
+ __u8 zai;
+ __u8 rsvd4[4];
+ __le64 zcap;
+ __le64 zslba;
+ __le64 wp;
+ __u8 rsvd32[32];
+ };
+
+**Members**
+
+``zt``
+ Zone Type
+
+``zs``
+ Zone State
+
+``za``
+ Zone Attributes
+
+``zai``
+ Zone Attributes Information
+
+``rsvd4``
+ Reserved
+
+``zcap``
+ Zone Capacity
+
+``zslba``
+ Zone Start Logical Block Address
+
+``wp``
+ Write Pointer
+
+``rsvd32``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_zone_report
+
+ Report Zones Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_zone_report {
+ __le64 nr_zones;
+ __u8 rsvd8[56];
+ struct nvme_zns_desc entries[];
+ };
+
+**Members**
+
+``nr_zones``
+ Number of descriptors in **entries**
+
+``rsvd8``
+ Reserved
+
+``entries``
+ Zoned namespace descriptors
+
+
+
+
+
+.. c:enum:: nvme_fdp_ruh_type
+
+ Reclaim Unit Handle Type
+
+**Constants**
+
+``NVME_FDP_RUHT_INITIALLY_ISOLATED``
+ Initially Isolated
+
+``NVME_FDP_RUHT_PERSISTENTLY_ISOLATED``
+ Persistently Isolated
+
+
+
+
+.. c:struct:: nvme_fdp_ruh_desc
+
+ Reclaim Unit Handle Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_fdp_ruh_desc {
+ __u8 ruht;
+ __u8 rsvd1[3];
+ };
+
+**Members**
+
+``ruht``
+ Reclaim Unit Handle Type
+
+``rsvd1``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_fdp_config_fdpa
+
+ FDP Attributes
+
+**Constants**
+
+``NVME_FDP_CONFIG_FDPA_RGIF_SHIFT``
+ Reclaim Group Identifier Format Shift
+
+``NVME_FDP_CONFIG_FDPA_RGIF_MASK``
+ Reclaim Group Identifier Format Mask
+
+``NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT``
+ FDP Volatile Write Cache Shift
+
+``NVME_FDP_CONFIG_FDPA_FDPVWC_MASK``
+ FDP Volatile Write Cache Mask
+
+``NVME_FDP_CONFIG_FDPA_VALID_SHIFT``
+ FDP Configuration Valid Shift
+
+``NVME_FDP_CONFIG_FDPA_VALID_MASK``
+ FDP Configuration Valid Mask
+
+
+
+
+.. c:struct:: nvme_fdp_config_desc
+
+ FDP Configuration Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_fdp_config_desc {
+ __le16 size;
+ __u8 fdpa;
+ __u8 vss;
+ __le32 nrg;
+ __le16 nruh;
+ __le16 maxpids;
+ __le32 nnss;
+ __le64 runs;
+ __le32 erutl;
+ __u8 rsvd28[36];
+ struct nvme_fdp_ruh_desc ruhs[];
+ };
+
+**Members**
+
+``size``
+ Descriptor size
+
+``fdpa``
+ FDP Attributes (:c:type:`enum nvme_fdp_config_fdpa <nvme_fdp_config_fdpa>`)
+
+``vss``
+ Vendor Specific Size
+
+``nrg``
+ Number of Reclaim Groups
+
+``nruh``
+ Number of Reclaim Unit Handles
+
+``maxpids``
+ Max Placement Identifiers
+
+``nnss``
+ Number of Namespaces Supported
+
+``runs``
+ Reclaim Unit Nominal Size
+
+``erutl``
+ Estimated Reclaim Unit Time Limit
+
+``rsvd28``
+ Reserved
+
+``ruhs``
+ Reclaim Unit Handle descriptors (:c:type:`struct nvme_fdp_ruh_desc <nvme_fdp_ruh_desc>`)
+
+
+
+
+
+.. c:struct:: nvme_fdp_config_log
+
+ FDP Configurations Log Page
+
+**Definition**
+
+::
+
+ struct nvme_fdp_config_log {
+ __le16 n;
+ __u8 version;
+ __u8 rsvd3;
+ __le32 size;
+ __u8 rsvd8[8];
+ struct nvme_fdp_config_desc configs[];
+ };
+
+**Members**
+
+``n``
+ Number of FDP Configurations
+
+``version``
+ Log page version
+
+``rsvd3``
+ Reserved
+
+``size``
+ Log page size in bytes
+
+``rsvd8``
+ Reserved
+
+``configs``
+ FDP Configuration descriptors (:c:type:`struct nvme_fdp_config_desc <nvme_fdp_config_desc>`)
+
+
+
+
+
+.. c:enum:: nvme_fdp_ruha
+
+ Reclaim Unit Handle Attributes
+
+**Constants**
+
+``NVME_FDP_RUHA_HOST_SHIFT``
+ Host Specified Reclaim Unit Handle Shift
+
+``NVME_FDP_RUHA_HOST_MASK``
+ Host Specified Reclaim Unit Handle Mask
+
+``NVME_FDP_RUHA_CTRL_SHIFT``
+ Controller Specified Reclaim Unit Handle Shift
+
+``NVME_FDP_RUHA_CTRL_MASK``
+ Controller Specified Reclaim Unit Handle Mask
+
+
+
+
+.. c:struct:: nvme_fdp_ruhu_desc
+
+ Reclaim Unit Handle Usage Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_fdp_ruhu_desc {
+ __u8 ruha;
+ __u8 rsvd1[7];
+ };
+
+**Members**
+
+``ruha``
+ Reclaim Unit Handle Attributes (:c:type:`enum nvme_fdp_ruha <nvme_fdp_ruha>`)
+
+``rsvd1``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_fdp_ruhu_log
+
+ Reclaim Unit Handle Usage Log Page
+
+**Definition**
+
+::
+
+ struct nvme_fdp_ruhu_log {
+ __le16 nruh;
+ __u8 rsvd2[6];
+ struct nvme_fdp_ruhu_desc ruhus[];
+ };
+
+**Members**
+
+``nruh``
+ Number of Reclaim Unit Handles
+
+``rsvd2``
+ Reserved
+
+``ruhus``
+ Reclaim Unit Handle Usage descriptors
+
+
+
+
+
+.. c:struct:: nvme_fdp_stats_log
+
+ FDP Statistics Log Page
+
+**Definition**
+
+::
+
+ struct nvme_fdp_stats_log {
+ __u8 hbmw[16];
+ __u8 mbmw[16];
+ __u8 mbe[16];
+ __u8 rsvd48[16];
+ };
+
+**Members**
+
+``hbmw``
+ Host Bytes with Metadata Written
+
+``mbmw``
+ Media Bytes with Metadata Written
+
+``mbe``
+ Media Bytes Erased
+
+``rsvd48``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_fdp_event_type
+
+ FDP Event Types
+
+**Constants**
+
+``NVME_FDP_EVENT_RUNFW``
+ Reclaim Unit Not Fully Written
+
+``NVME_FDP_EVENT_RUTLE``
+ Reclaim Unit Time Limit Exceeded
+
+``NVME_FDP_EVENT_RESET``
+ Controller Level Reset Modified Reclaim Unit Handles
+
+``NVME_FDP_EVENT_PID``
+ Invalid Placement Identifier
+
+``NVME_FDP_EVENT_REALLOC``
+ Media Reallocated
+
+``NVME_FDP_EVENT_MODIFY``
+ Implicitly Modified Reclaim Unit Handle
+
+
+
+
+.. c:enum:: nvme_fdp_event_realloc_flags
+
+ Media Reallocated Event Type Specific Flags
+
+**Constants**
+
+``NVME_FDP_EVENT_REALLOC_F_LBAV``
+ LBA Valid
+
+
+
+
+.. c:struct:: nvme_fdp_event_realloc
+
+ Media Reallocated Event Type Specific Information
+
+**Definition**
+
+::
+
+ struct nvme_fdp_event_realloc {
+ __u8 flags;
+ __u8 rsvd1;
+ __le16 nlbam;
+ __le64 lba;
+ __u8 rsvd12[4];
+ };
+
+**Members**
+
+``flags``
+ Event Type Specific flags (:c:type:`enum nvme_fdp_event_realloc_flags <nvme_fdp_event_realloc_flags>`)
+
+``rsvd1``
+ Reserved
+
+``nlbam``
+ Number of LBAs Moved
+
+``lba``
+ Logical Block Address
+
+``rsvd12``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_fdp_event_flags
+
+ FDP Event Flags
+
+**Constants**
+
+``NVME_FDP_EVENT_F_PIV``
+ Placement Identifier Valid
+
+``NVME_FDP_EVENT_F_NSIDV``
+ Namespace Identifier Valid
+
+``NVME_FDP_EVENT_F_LV``
+ Location Valid
+
+
+
+
+.. c:struct:: nvme_fdp_event
+
+ FDP Event
+
+**Definition**
+
+::
+
+ struct nvme_fdp_event {
+ __u8 type;
+ __u8 flags;
+ __le16 pid;
+ struct nvme_timestamp ts;
+ __le32 nsid;
+ __u8 type_specific[16];
+ __le16 rgid;
+ __u8 ruhid;
+ __u8 rsvd35[5];
+ __u8 vs[24];
+ };
+
+**Members**
+
+``type``
+ Event Type (:c:type:`enum nvme_fdp_event_type <nvme_fdp_event_type>`)
+
+``flags``
+ Event Flags (:c:type:`enum nvme_fdp_event_flags <nvme_fdp_event_flags>`)
+
+``pid``
+ Placement Identifier
+
+``ts``
+ Timestamp
+
+``nsid``
+ Namespace Identifier
+
+``type_specific``
+ Event Type Specific Information
+
+``rgid``
+ Reclaim Group Identifier
+
+``ruhid``
+ Reclaim Unit Handle Identifier
+
+``rsvd35``
+ Reserved
+
+``vs``
+ Vendor Specific
+
+
+
+
+
+.. c:struct:: nvme_fdp_events_log
+
+ FDP Events Log Page
+
+**Definition**
+
+::
+
+ struct nvme_fdp_events_log {
+ __le32 n;
+ __u8 rsvd4[60];
+ struct nvme_fdp_event events[63];
+ };
+
+**Members**
+
+``n``
+ Number of FDP Events
+
+``rsvd4``
+ Reserved
+
+``events``
+ FDP Events (:c:type:`struct nvme_fdp_event <nvme_fdp_event>`)
+
+
+
+
+
+.. c:struct:: nvme_feat_fdp_events_cdw11
+
+ FDP Events Feature Command Dword 11
+
+**Definition**
+
+::
+
+ struct nvme_feat_fdp_events_cdw11 {
+ __le16 phndl;
+ __u8 noet;
+ __u8 rsvd24;
+ };
+
+**Members**
+
+``phndl``
+ Placement Handle
+
+``noet``
+ Number of FDP Event Types
+
+``rsvd24``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_fdp_supported_event_attributes
+
+ Supported FDP Event Attributes
+
+**Constants**
+
+``NVME_FDP_SUPP_EVENT_ENABLED_SHIFT``
+ FDP Event Enable Shift
+
+``NVME_FDP_SUPP_EVENT_ENABLED_MASK``
+ FDP Event Enable Mask
+
+
+
+
+.. c:struct:: nvme_fdp_supported_event_desc
+
+ Supported FDP Event Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_fdp_supported_event_desc {
+ __u8 evt;
+ __u8 evta;
+ };
+
+**Members**
+
+``evt``
+ FDP Event Type
+
+``evta``
+ FDP Event Type Attributes (:c:type:`enum nvme_fdp_supported_event_attributes <nvme_fdp_supported_event_attributes>`)
+
+
+
+
+
+.. c:struct:: nvme_fdp_ruh_status_desc
+
+ Reclaim Unit Handle Status Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_fdp_ruh_status_desc {
+ __le16 pid;
+ __le16 ruhid;
+ __le32 earutr;
+ __le64 ruamw;
+ __u8 rsvd16[16];
+ };
+
+**Members**
+
+``pid``
+ Placement Identifier
+
+``ruhid``
+ Reclaim Unit Handle Identifier
+
+``earutr``
+ Estimated Active Reclaim Unit Time Remaining
+
+``ruamw``
+ Reclaim Unit Available Media Writes
+
+``rsvd16``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_fdp_ruh_status
+
+ Reclaim Unit Handle Status
+
+**Definition**
+
+::
+
+ struct nvme_fdp_ruh_status {
+ __u8 rsvd0[14];
+ __le16 nruhsd;
+ struct nvme_fdp_ruh_status_desc ruhss[];
+ };
+
+**Members**
+
+``rsvd0``
+ Reserved
+
+``nruhsd``
+ Number of Reclaim Unit Handle Status Descriptors
+
+``ruhss``
+ Reclaim Unit Handle Status descriptors
+
+
+
+
+
+.. c:struct:: nvme_lba_status_desc
+
+ LBA Status Descriptor Entry
+
+**Definition**
+
+::
+
+ struct nvme_lba_status_desc {
+ __le64 dslba;
+ __le32 nlb;
+ __u8 rsvd12;
+ __u8 status;
+ __u8 rsvd14[2];
+ };
+
+**Members**
+
+``dslba``
+ Descriptor Starting LBA
+
+``nlb``
+ Number of Logical Blocks
+
+``rsvd12``
+ Reserved
+
+``status``
+ Additional status about this LBA range
+
+``rsvd14``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_lba_status
+
+ LBA Status Descriptor List
+
+**Definition**
+
+::
+
+ struct nvme_lba_status {
+ __le32 nlsd;
+ __u8 cmpc;
+ __u8 rsvd5[3];
+ struct nvme_lba_status_desc descs[];
+ };
+
+**Members**
+
+``nlsd``
+ Number of LBA Status Descriptors
+
+``cmpc``
+ Completion Condition
+
+``rsvd5``
+ Reserved
+
+``descs``
+ LBA status descriptor Entry
+
+
+
+
+
+.. c:struct:: nvme_feat_auto_pst
+
+ Autonomous Power State Transition
+
+**Definition**
+
+::
+
+ struct nvme_feat_auto_pst {
+ __le64 apst_entry[32];
+ };
+
+**Members**
+
+``apst_entry``
+ See :c:type:`enum nvme_apst_entry <nvme_apst_entry>`
+
+
+
+
+
+.. c:enum:: nvme_apst_entry
+
+ Autonomous Power State Transition
+
+**Constants**
+
+``NVME_APST_ENTRY_ITPS_SHIFT``
+ Idle Transition Power State Shift
+
+``NVME_APST_ENTRY_ITPT_SHIFT``
+ Idle Time Prior to Transition Shift
+
+``NVME_APST_ENTRY_ITPS_MASK``
+ Idle Transition Power State Mask
+
+``NVME_APST_ENTRY_ITPT_MASK``
+ Idle Time Prior to Transition Mask
+
+
+
+
+.. c:struct:: nvme_metadata_element_desc
+
+ Metadata Element Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_metadata_element_desc {
+ __u8 type;
+ __u8 rev;
+ __le16 len;
+ __u8 val[0];
+ };
+
+**Members**
+
+``type``
+ Element Type (ET)
+
+``rev``
+ Element Revision (ER)
+
+``len``
+ Element Length (ELEN)
+
+``val``
+ Element Value (EVAL), UTF-8 string
+
+
+
+
+
+.. c:struct:: nvme_host_metadata
+
+ Host Metadata Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_host_metadata {
+ __u8 ndesc;
+ __u8 rsvd1;
+ union {
+ struct nvme_metadata_element_desc descs[0];
+ __u8 descs_buf[4094];
+ };
+ };
+
+**Members**
+
+``ndesc``
+ Number of metadata element descriptors
+
+``rsvd1``
+ Reserved
+
+``{unnamed_union}``
+ anonymous
+
+``descs``
+ Metadata element descriptors
+
+``descs_buf``
+ Metadata element descriptor buffer
+
+
+
+
+
+.. c:enum:: nvme_ctrl_metadata_type
+
+ Controller Metadata Element Types
+
+**Constants**
+
+``NVME_CTRL_METADATA_OS_CTRL_NAME``
+ Name of the controller in
+ the operating system.
+
+``NVME_CTRL_METADATA_OS_DRIVER_NAME``
+ Name of the driver in the
+ operating system.
+
+``NVME_CTRL_METADATA_OS_DRIVER_VER``
+ Version of the driver in
+ the operating system.
+
+``NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME``
+ Name of the controller in
+ the pre-boot environment.
+
+``NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME``
+ Name of the driver in the
+ pre-boot environment.
+
+``NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER``
+ Version of the driver in the
+ pre-boot environment.
+
+``NVME_CTRL_METADATA_SYS_PROC_MODEL``
+ Model of the processor.
+
+``NVME_CTRL_METADATA_CHIPSET_DRV_NAME``
+ Chipset driver name.
+
+``NVME_CTRL_METADATA_CHIPSET_DRV_VERSION``
+ Chipset driver version.
+
+``NVME_CTRL_METADATA_OS_NAME_AND_BUILD``
+ Operating system name and build.
+
+``NVME_CTRL_METADATA_SYS_PROD_NAME``
+ System product name.
+
+``NVME_CTRL_METADATA_FIRMWARE_VERSION``
+ Host firmware (e.g UEFI) version.
+
+``NVME_CTRL_METADATA_OS_DRIVER_FILENAME``
+ Operating system driver filename.
+
+``NVME_CTRL_METADATA_DISPLAY_DRV_NAME``
+ Display driver name.
+
+``NVME_CTRL_METADATA_DISPLAY_DRV_VERSION``
+ Display driver version.
+
+``NVME_CTRL_METADATA_HOST_DET_FAIL_REC``
+ Failure record.
+
+
+
+
+.. c:enum:: nvme_ns_metadata_type
+
+ Namespace Metadata Element Types
+
+**Constants**
+
+``NVME_NS_METADATA_OS_NS_NAME``
+ Name of the namespace in the
+ operating system
+
+``NVME_NS_METADATA_PRE_BOOT_NS_NAME``
+ Name of the namespace in the pre-boot
+ environment.
+
+``NVME_NS_METADATA_OS_NS_QUAL_1``
+ First qualifier of the Operating System
+ Namespace Name.
+
+``NVME_NS_METADATA_OS_NS_QUAL_2``
+ Second qualifier of the Operating System
+ Namespace Name.
+
+
+
+
+.. c:struct:: nvme_lba_range_type_entry
+
+ LBA Range Type - Data Structure Entry
+
+**Definition**
+
+::
+
+ struct nvme_lba_range_type_entry {
+ __u8 type;
+ __u8 attributes;
+ __u8 rsvd2[14];
+ __le64 slba;
+ __le64 nlb;
+ __u8 guid[16];
+ __u8 rsvd48[16];
+ };
+
+**Members**
+
+``type``
+ Specifies the Type of the LBA range
+
+``attributes``
+ Specifies attributes of the LBA range
+
+``rsvd2``
+ Reserved
+
+``slba``
+ Starting LBA
+
+``nlb``
+ Number of Logical Blocks
+
+``guid``
+ Unique Identifier
+
+``rsvd48``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_lbart
+
+ LBA Range Type - Data Structure Entry
+
+**Constants**
+
+``NVME_LBART_TYPE_GP``
+ General Purpose
+
+``NVME_LBART_TYPE_FS``
+ Filesystem
+
+``NVME_LBART_TYPE_RAID``
+ RAID
+
+``NVME_LBART_TYPE_CACHE``
+ Cache
+
+``NVME_LBART_TYPE_SWAP``
+ Page / swap file
+
+``NVME_LBART_ATTRIB_TEMP``
+ Temp
+
+``NVME_LBART_ATTRIB_HIDE``
+ Hidden
+
+
+
+
+.. c:struct:: nvme_lba_range_type
+
+ LBA Range Type
+
+**Definition**
+
+::
+
+ struct nvme_lba_range_type {
+ struct nvme_lba_range_type_entry entry[NVME_FEAT_LBA_RANGE_MAX];
+ };
+
+**Members**
+
+``entry``
+ LBA range type entry. See **struct** nvme_lba_range_type_entry
+
+
+
+
+
+.. c:struct:: nvme_plm_config
+
+ Predictable Latency Mode - Deterministic Threshold Configuration Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_plm_config {
+ __le16 ee;
+ __u8 rsvd2[30];
+ __le64 dtwinrt;
+ __le64 dtwinwt;
+ __le64 dtwintt;
+ __u8 rsvd56[456];
+ };
+
+**Members**
+
+``ee``
+ Enable Event
+
+``rsvd2``
+ Reserved
+
+``dtwinrt``
+ DTWIN Reads Threshold
+
+``dtwinwt``
+ DTWIN Writes Threshold
+
+``dtwintt``
+ DTWIN Time Threshold
+
+``rsvd56``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_feat_host_behavior
+
+ Host Behavior Support - Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_feat_host_behavior {
+ __u8 acre;
+ __u8 etdas;
+ __u8 lbafee;
+ __u8 rsvd3;
+ __u16 cdfe;
+ __u8 rsvd6[506];
+ };
+
+**Members**
+
+``acre``
+ Advanced Command Retry Enable
+
+``etdas``
+ Extended Telemetry Data Area 4 Supported
+
+``lbafee``
+ LBA Format Extension Enable
+
+``rsvd3``
+ Reserved
+
+``cdfe``
+ Copy Descriptor Formats Enable
+
+``rsvd6``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_host_behavior_support
+
+ Enable Advanced Command
+
+**Constants**
+
+``NVME_ENABLE_ACRE``
+ Enable Advanced Command Retry Enable
+
+
+
+
+.. c:struct:: nvme_dsm_range
+
+ Dataset Management - Range Definition
+
+**Definition**
+
+::
+
+ struct nvme_dsm_range {
+ __le32 cattr;
+ __le32 nlb;
+ __le64 slba;
+ };
+
+**Members**
+
+``cattr``
+ Context Attributes
+
+``nlb``
+ Length in logical blocks
+
+``slba``
+ Starting LBA
+
+
+
+
+
+.. c:struct:: nvme_copy_range
+
+ Copy - Source Range Entries Descriptor Format
+
+**Definition**
+
+::
+
+ struct nvme_copy_range {
+ __u8 rsvd0[8];
+ __le64 slba;
+ __le16 nlb;
+ __u8 rsvd18[6];
+ __le32 eilbrt;
+ __le16 elbat;
+ __le16 elbatm;
+ };
+
+**Members**
+
+``rsvd0``
+ Reserved
+
+``slba``
+ Starting LBA
+
+``nlb``
+ Number of Logical Blocks
+
+``rsvd18``
+ Reserved
+
+``eilbrt``
+ Expected Initial Logical Block Reference Tag /
+ Expected Logical Block Storage Tag
+
+``elbat``
+ Expected Logical Block Application Tag
+
+``elbatm``
+ Expected Logical Block Application Tag Mask
+
+
+
+
+
+.. c:struct:: nvme_copy_range_f1
+
+ Copy - Source Range Entries Descriptor Format 1h
+
+**Definition**
+
+::
+
+ struct nvme_copy_range_f1 {
+ __u8 rsvd0[8];
+ __le64 slba;
+ __le16 nlb;
+ __u8 rsvd18[8];
+ __u8 elbt[10];
+ __le16 elbat;
+ __le16 elbatm;
+ };
+
+**Members**
+
+``rsvd0``
+ Reserved
+
+``slba``
+ Starting LBA
+
+``nlb``
+ Number of Logical Blocks
+
+``rsvd18``
+ Reserved
+
+``elbt``
+ Expected Initial Logical Block Reference Tag /
+ Expected Logical Block Storage Tag
+
+``elbat``
+ Expected Logical Block Application Tag
+
+``elbatm``
+ Expected Logical Block Application Tag Mask
+
+
+
+
+
+.. c:enum:: nvme_copy_range_sopt
+
+ NVMe Copy Range Source Options
+
+**Constants**
+
+``NVME_COPY_SOPT_FCO``
+ NVMe Copy Source Option Fast Copy Only
+
+
+
+
+.. c:struct:: nvme_copy_range_f2
+
+ Copy - Source Range Entries Descriptor Format 2h
+
+**Definition**
+
+::
+
+ struct nvme_copy_range_f2 {
+ __le32 snsid;
+ __u8 rsvd4[4];
+ __le64 slba;
+ __le16 nlb;
+ __u8 rsvd18[4];
+ __le16 sopt;
+ __le32 eilbrt;
+ __le16 elbat;
+ __le16 elbatm;
+ };
+
+**Members**
+
+``snsid``
+ Source Namespace Identifier
+
+``rsvd4``
+ Reserved
+
+``slba``
+ Starting LBA
+
+``nlb``
+ Number of Logical Blocks
+
+``rsvd18``
+ Reserved
+
+``sopt``
+ Source Options
+
+``eilbrt``
+ Expected Initial Logical Block Reference Tag /
+ Expected Logical Block Storage Tag
+
+``elbat``
+ Expected Logical Block Application Tag
+
+``elbatm``
+ Expected Logical Block Application Tag Mask
+
+
+
+
+
+.. c:struct:: nvme_copy_range_f3
+
+ Copy - Source Range Entries Descriptor Format 3h
+
+**Definition**
+
+::
+
+ struct nvme_copy_range_f3 {
+ __le32 snsid;
+ __u8 rsvd4[4];
+ __le64 slba;
+ __le16 nlb;
+ __u8 rsvd18[4];
+ __le16 sopt;
+ __u8 rsvd24[2];
+ __u8 elbt[10];
+ __le16 elbat;
+ __le16 elbatm;
+ };
+
+**Members**
+
+``snsid``
+ Source Namespace Identifier
+
+``rsvd4``
+ Reserved
+
+``slba``
+ Starting LBA
+
+``nlb``
+ Number of Logical Blocks
+
+``rsvd18``
+ Reserved
+
+``sopt``
+ Source Options
+
+``rsvd24``
+ Reserved
+
+``elbt``
+ Expected Initial Logical Block Reference Tag /
+ Expected Logical Block Storage Tag
+
+``elbat``
+ Expected Logical Block Application Tag
+
+``elbatm``
+ Expected Logical Block Application Tag Mask
+
+
+
+
+
+.. c:struct:: nvme_registered_ctrl
+
+ Registered Controller Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_registered_ctrl {
+ __le16 cntlid;
+ __u8 rcsts;
+ __u8 rsvd3[5];
+ __le64 hostid;
+ __le64 rkey;
+ };
+
+**Members**
+
+``cntlid``
+ Controller ID
+
+``rcsts``
+ Reservation Status
+
+``rsvd3``
+ Reserved
+
+``hostid``
+ Host Identifier
+
+``rkey``
+ Reservation Key
+
+
+
+
+
+.. c:struct:: nvme_registered_ctrl_ext
+
+ Registered Controller Extended Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_registered_ctrl_ext {
+ __le16 cntlid;
+ __u8 rcsts;
+ __u8 rsvd3[5];
+ __le64 rkey;
+ __u8 hostid[16];
+ __u8 rsvd32[32];
+ };
+
+**Members**
+
+``cntlid``
+ Controller ID
+
+``rcsts``
+ Reservation Status
+
+``rsvd3``
+ Reserved
+
+``rkey``
+ Reservation Key
+
+``hostid``
+ Host Identifier
+
+``rsvd32``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_resv_status
+
+ Reservation Status Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_resv_status {
+ __le32 gen;
+ __u8 rtype;
+ __u8 regctl[2];
+ __u8 rsvd7[2];
+ __u8 ptpls;
+ __u8 rsvd10[14];
+ union {
+ struct {
+ __u8 rsvd24[40];
+ struct nvme_registered_ctrl_ext regctl_eds[0];
+ };
+ struct nvme_registered_ctrl regctl_ds[0];
+ };
+ };
+
+**Members**
+
+``gen``
+ Generation
+
+``rtype``
+ Reservation Type
+
+``regctl``
+ Number of Registered Controllers
+
+``rsvd7``
+ Reserved
+
+``ptpls``
+ Persist Through Power Loss State
+
+``rsvd10``
+ Reserved
+
+``{unnamed_union}``
+ anonymous
+
+``{unnamed_struct}``
+ anonymous
+
+``rsvd24``
+ Reserved
+
+``regctl_eds``
+ Registered Controller Extended Data Structure
+
+``regctl_ds``
+ Registered Controller Data Structure
+
+
+
+
+
+.. c:struct:: nvme_streams_directive_params
+
+ Streams Directive - Return Parameters Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_streams_directive_params {
+ __le16 msl;
+ __le16 nssa;
+ __le16 nsso;
+ __u8 nssc;
+ __u8 rsvd[9];
+ __le32 sws;
+ __le16 sgs;
+ __le16 nsa;
+ __le16 nso;
+ __u8 rsvd2[6];
+ };
+
+**Members**
+
+``msl``
+ Max Streams Limit
+
+``nssa``
+ NVM Subsystem Streams Available
+
+``nsso``
+ NVM Subsystem Streams Open
+
+``nssc``
+ NVM Subsystem Stream Capability
+
+``rsvd``
+ Reserved
+
+``sws``
+ Stream Write Size
+
+``sgs``
+ Stream Granularity Size
+
+``nsa``
+ Namespace Streams Allocated
+
+``nso``
+ Namespace Streams Open
+
+``rsvd2``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_streams_directive_status
+
+ Streams Directive - Get Status Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_streams_directive_status {
+ __le16 osc;
+ __le16 sid[];
+ };
+
+**Members**
+
+``osc``
+ Open Stream Count
+
+``sid``
+ Stream Identifier
+
+
+
+
+
+.. c:struct:: nvme_id_directives
+
+ Identify Directive - Return Parameters Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_id_directives {
+ __u8 supported[32];
+ __u8 enabled[32];
+ __u8 rsvd64[4032];
+ };
+
+**Members**
+
+``supported``
+ Identify directive is supported
+
+``enabled``
+ Identify directive is Enabled
+
+``rsvd64``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_directive_types
+
+ Directives Supported or Enabled
+
+**Constants**
+
+``NVME_ID_DIR_ID_BIT``
+ Identify directive is supported
+
+``NVME_ID_DIR_SD_BIT``
+ Streams directive is supported
+
+``NVME_ID_DIR_DP_BIT``
+ Direct Placement directive is supported
+
+
+
+
+.. c:struct:: nvme_host_mem_buf_attrs
+
+ Host Memory Buffer - Attributes Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_host_mem_buf_attrs {
+ __le32 hsize;
+ __le32 hmdlal;
+ __le32 hmdlau;
+ __le32 hmdlec;
+ __u8 rsvd16[4080];
+ };
+
+**Members**
+
+``hsize``
+ Host Memory Buffer Size
+
+``hmdlal``
+ Host Memory Descriptor List Lower Address
+
+``hmdlau``
+ Host Memory Descriptor List Upper Address
+
+``hmdlec``
+ Host Memory Descriptor List Entry Count
+
+``rsvd16``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_ae_type
+
+ Asynchronous Event Type
+
+**Constants**
+
+``NVME_AER_ERROR``
+ Error event
+
+``NVME_AER_SMART``
+ SMART / Health Status event
+
+``NVME_AER_NOTICE``
+ Notice event
+
+``NVME_AER_CSS``
+ NVM Command Set Specific events
+
+``NVME_AER_VS``
+ Vendor Specific event
+
+
+
+
+.. c:enum:: nvme_ae_info_error
+
+ Asynchronous Event Information - Error Status
+
+**Constants**
+
+``NVME_AER_ERROR_INVALID_DB_REG``
+ Write to Invalid Doorbell Register
+
+``NVME_AER_ERROR_INVALID_DB_VAL``
+ Invalid Doorbell Write Value
+
+``NVME_AER_ERROR_DIAG_FAILURE``
+ Diagnostic Failure
+
+``NVME_AER_ERROR_PERSISTENT_INTERNAL_ERROR``
+ Persistent Internal Error
+
+``NVME_AER_ERROR_TRANSIENT_INTERNAL_ERROR``
+ Transient Internal Error
+
+``NVME_AER_ERROR_FW_IMAGE_LOAD_ERROR``
+ Firmware Image Load Error
+
+
+
+
+.. c:enum:: nvme_ae_info_smart
+
+ Asynchronous Event Information - SMART / Health Status
+
+**Constants**
+
+``NVME_AER_SMART_SUBSYSTEM_RELIABILITY``
+ NVM subsystem Reliability
+
+``NVME_AER_SMART_TEMPERATURE_THRESHOLD``
+ Temperature Threshold
+
+``NVME_AER_SMART_SPARE_THRESHOLD``
+ Spare Below Threshold
+
+
+
+
+.. c:enum:: nvme_ae_info_css_nvm
+
+ Asynchronous Event Information - I/O Command Specific Status
+
+**Constants**
+
+``NVME_AER_CSS_NVM_RESERVATION``
+ Reservation Log Page Available
+
+``NVME_AER_CSS_NVM_SANITIZE_COMPLETED``
+ Sanitize Operation Completed
+
+``NVME_AER_CSS_NVM_UNEXPECTED_SANITIZE_DEALLOC``
+ Sanitize Operation Completed
+ With Unexpected Deallocation
+
+
+
+
+.. c:enum:: nvme_ae_info_notice
+
+ Asynchronous Event Information - Notice
+
+**Constants**
+
+``NVME_AER_NOTICE_NS_CHANGED``
+ Namespace Attribute Changed
+
+``NVME_AER_NOTICE_FW_ACT_STARTING``
+ Firmware Activation Starting
+
+``NVME_AER_NOTICE_TELEMETRY``
+ Telemetry Log Changed
+
+``NVME_AER_NOTICE_ANA``
+ Asymmetric Namespace Access Change
+
+``NVME_AER_NOTICE_PL_EVENT``
+ Predictable Latency Event Aggregate Log Change
+
+``NVME_AER_NOTICE_LBA_STATUS_ALERT``
+ LBA Status Information Alert
+
+``NVME_AER_NOTICE_EG_EVENT``
+ Endurance Group Event Aggregate Log Page Change
+
+``NVME_AER_NOTICE_DISC_CHANGED``
+ Discovery Log Page Change
+
+
+
+
+.. c:enum:: nvme_subsys_type
+
+ Type of the NVM subsystem.
+
+**Constants**
+
+``NVME_NQN_DISC``
+ Discovery type target subsystem. Describes a referral to another
+ Discovery Service composed of Discovery controllers that provide
+ additional discovery records. Multiple Referral entries may
+ be reported for each Discovery Service (if that Discovery Service
+ has multiple NVM subsystem ports or supports multiple protocols).
+
+``NVME_NQN_NVME``
+ NVME type target subsystem. Describes an NVM subsystem whose
+ controllers may have attached namespaces (an NVM subsystem
+ that is not composed of Discovery controllers). Multiple NVM
+ Subsystem entries may be reported for each NVM subsystem if
+ that NVM subsystem has multiple NVM subsystem ports.
+
+``NVME_NQN_CURR``
+ Current Discovery type target subsystem. Describes this Discovery
+ subsystem (the Discovery Service that contains the controller
+ processing the Get Log Page command). Multiple Current Discovery
+ Subsystem entries may be reported for this Discovery subsystem
+ if the current Discovery subsystem has multiple NVM subsystem
+ ports.
+
+
+
+
+.. c:enum:: nvmf_disc_eflags
+
+ Discovery Log Page entry flags.
+
+**Constants**
+
+``NVMF_DISC_EFLAGS_NONE``
+ Indicates that none of the DUPRETINFO or EPCSD
+ features are supported.
+
+``NVMF_DISC_EFLAGS_DUPRETINFO``
+ Duplicate Returned Information (DUPRETINFO):
+ Indicates that using the content of this entry
+ to access this Discovery Service returns the same
+ information that is returned by using the content
+ of other entries in this log page that also have
+ this flag set.
+
+``NVMF_DISC_EFLAGS_EPCSD``
+ Explicit Persistent Connection Support for Discovery (EPCSD):
+ Indicates that Explicit Persistent Connections are
+ supported for the Discovery controller.
+
+``NVMF_DISC_EFLAGS_NCC``
+ No CDC Connectivity (NCC): If set to
+ '1', then no DDC that describes this entry
+ is currently connected to the CDC. If
+ cleared to '0', then at least one DDC that
+ describes this entry is currently
+ connected to the CDC. If the Discovery
+ controller returning this log page is not
+ a CDC, then this bit shall be cleared to
+ '0' and should be ignored by the host.
+
+
+
+
+.. c:union:: nvmf_tsas
+
+ Transport Specific Address Subtype
+
+**Definition**
+
+::
+
+ union nvmf_tsas {
+ char common[NVMF_TSAS_SIZE];
+ struct rdma {
+ __u8 qptype;
+ __u8 prtype;
+ __u8 cms;
+ __u8 rsvd3[5];
+ __le16 pkey;
+ __u8 rsvd10[246];
+ } rdma;
+ struct tcp {
+ __u8 sectype;
+ } tcp;
+ };
+
+**Members**
+
+``common``
+ Common transport specific attributes
+
+``rdma``
+ RDMA transport specific attribute settings
+
+``tcp``
+ TCP transport specific attribute settings
+
+
+
+
+
+.. c:struct:: nvmf_disc_log_entry
+
+ Discovery Log Page entry
+
+**Definition**
+
+::
+
+ struct nvmf_disc_log_entry {
+ __u8 trtype;
+ __u8 adrfam;
+ __u8 subtype;
+ __u8 treq;
+ __le16 portid;
+ __le16 cntlid;
+ __le16 asqsz;
+ __le16 eflags;
+ __u8 rsvd12[20];
+ char trsvcid[NVMF_TRSVCID_SIZE];
+ __u8 rsvd64[192];
+ char subnqn[NVME_NQN_LENGTH];
+ char traddr[NVMF_TRADDR_SIZE];
+ union nvmf_tsas tsas;
+ };
+
+**Members**
+
+``trtype``
+ Transport Type (TRTYPE): Specifies the NVMe Transport type.
+ See :c:type:`enum nvmf_trtype <nvmf_trtype>`.
+
+``adrfam``
+ Address Family (ADRFAM): Specifies the address family.
+ See :c:type:`enum nvmf_addr_family <nvmf_addr_family>`.
+
+``subtype``
+ Subsystem Type (SUBTYPE): Specifies the type of the NVM subsystem
+ that is indicated in this entry. See :c:type:`enum nvme_subsys_type <nvme_subsys_type>`.
+
+``treq``
+ Transport Requirements (TREQ): Indicates requirements for the NVMe
+ Transport. See :c:type:`enum nvmf_treq <nvmf_treq>`.
+
+``portid``
+ Port ID (PORTID): Specifies a particular NVM subsystem port.
+ Different NVMe Transports or address families may utilize the same
+ Port ID value (e.g. a Port ID may support both iWARP and RoCE).
+
+``cntlid``
+ Controller ID (CNTLID): Specifies the controller ID. If the NVM
+ subsystem uses a dynamic controller model, then this field shall
+ be set to FFFFh. If the NVM subsystem uses a static controller model,
+ then this field may be set to a specific controller ID (values 0h
+ to FFEFh are valid). If the NVM subsystem uses a static controller
+ model and the value indicated is FFFEh, then the host should remember
+ the Controller ID returned as part of the Fabrics Connect command
+ in order to re-establish an association in the future with the same
+ controller.
+
+``asqsz``
+ Admin Max SQ Size (ASQSZ): Specifies the maximum size of an Admin
+ Submission Queue. This applies to all controllers in the NVM
+ subsystem. The value shall be a minimum of 32 entries.
+
+``eflags``
+ Entry Flags (EFLAGS): Indicates additional information related to
+ the current entry. See :c:type:`enum nvmf_disc_eflags <nvmf_disc_eflags>`.
+
+``rsvd12``
+ Reserved
+
+``trsvcid``
+ Transport Service Identifier (TRSVCID): Specifies the NVMe Transport
+ service identifier as an ASCII string. The NVMe Transport service
+ identifier is specified by the associated NVMe Transport binding
+ specification.
+
+``rsvd64``
+ Reserved
+
+``subnqn``
+ NVM Subsystem Qualified Name (SUBNQN): NVMe Qualified Name (NQN)
+ that uniquely identifies the NVM subsystem. For a subsystem, if that
+ Discovery subsystem has a unique NQN (i.e., the NVM Subsystem NVMe
+ Qualified Name (SUBNQN) field in that Discovery subsystem's Identify
+ Controller data structure contains a unique NQN value), then the
+ value returned shall be that unique NQN. If the Discovery subsystem
+ does not have a unique NQN, then the value returned shall be the
+ well-known Discovery Service NQN (nqn.2014-08.org.nvmexpress.discovery).
+
+``traddr``
+ Transport Address (TRADDR): Specifies the address of the NVM subsystem
+ that may be used for a Connect command as an ASCII string. The
+ Address Family field describes the reference for parsing this field.
+
+``tsas``
+ Transport specific attribute settings
+
+
+
+
+
+.. c:enum:: nvmf_trtype
+
+ Transport Type codes for Discovery Log Page entry TRTYPE field
+
+**Constants**
+
+``NVMF_TRTYPE_UNSPECIFIED``
+ Not indicated
+
+``NVMF_TRTYPE_RDMA``
+ RDMA
+
+``NVMF_TRTYPE_FC``
+ Fibre Channel
+
+``NVMF_TRTYPE_TCP``
+ TCP
+
+``NVMF_TRTYPE_LOOP``
+ Intra-host Transport (i.e., loopback), reserved
+ for host usage.
+
+``NVMF_TRTYPE_MAX``
+ Maximum value for :c:type:`enum nvmf_trtype <nvmf_trtype>`
+
+
+
+
+.. c:enum:: nvmf_addr_family
+
+ Address Family codes for Discovery Log Page entry ADRFAM field
+
+**Constants**
+
+``NVMF_ADDR_FAMILY_PCI``
+ PCIe
+
+``NVMF_ADDR_FAMILY_IP4``
+ AF_INET: IPv4 address family.
+
+``NVMF_ADDR_FAMILY_IP6``
+ AF_INET6: IPv6 address family.
+
+``NVMF_ADDR_FAMILY_IB``
+ AF_IB: InfiniBand address family.
+
+``NVMF_ADDR_FAMILY_FC``
+ Fibre Channel address family.
+
+``NVMF_ADDR_FAMILY_LOOP``
+ Intra-host Transport (i.e., loopback), reserved
+ for host usage.
+
+
+
+
+.. c:enum:: nvmf_treq
+
+ Transport Requirements codes for Discovery Log Page entry TREQ field
+
+**Constants**
+
+``NVMF_TREQ_NOT_SPECIFIED``
+ Not specified
+
+``NVMF_TREQ_REQUIRED``
+ Required
+
+``NVMF_TREQ_NOT_REQUIRED``
+ Not Required
+
+``NVMF_TREQ_DISABLE_SQFLOW``
+ SQ flow control disable supported
+
+
+
+
+.. c:enum:: nvmf_rdma_qptype
+
+ RDMA QP Service Type codes for Discovery Log Page entry TSAS RDMA_QPTYPE field
+
+**Constants**
+
+``NVMF_RDMA_QPTYPE_CONNECTED``
+ Reliable Connected
+
+``NVMF_RDMA_QPTYPE_DATAGRAM``
+ Reliable Datagram
+
+
+
+
+.. c:enum:: nvmf_rdma_prtype
+
+ RDMA Provider Type codes for Discovery Log Page entry TSAS RDMA_PRTYPE field
+
+**Constants**
+
+``NVMF_RDMA_PRTYPE_NOT_SPECIFIED``
+ No Provider Specified
+
+``NVMF_RDMA_PRTYPE_IB``
+ InfiniBand
+
+``NVMF_RDMA_PRTYPE_ROCE``
+ InfiniBand RoCE
+
+``NVMF_RDMA_PRTYPE_ROCEV2``
+ InfiniBand RoCEV2
+
+``NVMF_RDMA_PRTYPE_IWARP``
+ iWARP
+
+
+
+
+.. c:enum:: nvmf_rdma_cms
+
+ RDMA Connection Management Service Type codes for Discovery Log Page entry TSAS RDMA_CMS field
+
+**Constants**
+
+``NVMF_RDMA_CMS_RDMA_CM``
+ Sockets based endpoint addressing
+
+
+
+
+.. c:enum:: nvmf_tcp_sectype
+
+ Transport Specific Address Subtype Definition for NVMe/TCP Transport
+
+**Constants**
+
+``NVMF_TCP_SECTYPE_NONE``
+ No Security
+
+``NVMF_TCP_SECTYPE_TLS``
+ Transport Layer Security version 1.2
+
+``NVMF_TCP_SECTYPE_TLS13``
+ Transport Layer Security version 1.3 or a subsequent
+ version. The TLS protocol negotiates the version and
+ cipher suite for each TCP connection.
+
+
+
+
+.. c:enum:: nvmf_log_discovery_lid_support
+
+ Discovery log specific support
+
+**Constants**
+
+``NVMF_LOG_DISC_LID_NONE``
+ None
+
+``NVMF_LOG_DISC_LID_EXTDLPES``
+ Extended Discovery Log Page Entries Supported
+
+``NVMF_LOG_DISC_LID_PLEOS``
+ Port Local Entries Only Supported
+
+``NVMF_LOG_DISC_LID_ALLSUBES``
+ All NVM Subsystem Entries Supported
+
+
+
+
+.. c:enum:: nvmf_log_discovery_lsp
+
+ Discovery log specific field
+
+**Constants**
+
+``NVMF_LOG_DISC_LSP_NONE``
+ None
+
+``NVMF_LOG_DISC_LSP_EXTDLPE``
+ Extended Discovery Log Page Entries
+
+``NVMF_LOG_DISC_LSP_PLEO``
+ Port Local Entries Only
+
+``NVMF_LOG_DISC_LSP_ALLSUBE``
+ All NVM Subsystem Entries
+
+
+
+
+.. c:struct:: nvmf_discovery_log
+
+ Discovery Log Page (Log Identifier 70h)
+
+**Definition**
+
+::
+
+ struct nvmf_discovery_log {
+ __le64 genctr;
+ __le64 numrec;
+ __le16 recfmt;
+ __u8 rsvd14[1006];
+ struct nvmf_disc_log_entry entries[];
+ };
+
+**Members**
+
+``genctr``
+ Generation Counter (GENCTR): Indicates the version of the discovery
+ information, starting at a value of 0h. For each change in the
+ Discovery Log Page, this counter is incremented by one. If the value
+ of this field is FFFFFFFF_FFFFFFFFh, then the field shall be cleared
+ to 0h when incremented (i.e., rolls over to 0h).
+
+``numrec``
+ Number of Records (NUMREC): Indicates the number of records
+ contained in the log.
+
+``recfmt``
+ Record Format (RECFMT): Specifies the format of the Discovery Log
+ Page. If a new format is defined, this value is incremented by one.
+ The format of the record specified in this definition shall be 0h.
+
+``rsvd14``
+ Reserved
+
+``entries``
+ Discovery Log Page Entries - see :c:type:`struct nvmf_disc_log_entry <nvmf_disc_log_entry>`.
+
+
+
+
+
+.. c:enum:: nvmf_dim_tas
+
+ Discovery Information Management Task
+
+**Constants**
+
+``NVMF_DIM_TAS_REGISTER``
+ Register
+
+``NVMF_DIM_TAS_DEREGISTER``
+ Deregister
+
+``NVMF_DIM_TAS_UPDATE``
+ Update
+
+
+
+
+.. c:enum:: nvmf_dim_entfmt
+
+ Discovery Information Management Entry Format
+
+**Constants**
+
+``NVMF_DIM_ENTFMT_BASIC``
+ Basic discovery information entry
+
+``NVMF_DIM_ENTFMT_EXTENDED``
+ Extended discovery information entry
+
+
+
+
+.. c:enum:: nvmf_dim_etype
+
+ Discovery Information Management Entity Type
+
+**Constants**
+
+``NVMF_DIM_ETYPE_HOST``
+ Host
+
+``NVMF_DIM_ETYPE_DDC``
+ Direct Discovery controller
+
+``NVMF_DIM_ETYPE_CDC``
+ Centralized Discovery controller
+
+
+
+
+.. c:enum:: nvmf_exattype
+
+ Extended Attribute Type
+
+**Constants**
+
+``NVMF_EXATTYPE_HOSTID``
+ Host Identifier
+
+``NVMF_EXATTYPE_SYMNAME``
+ Symblic Name
+
+
+
+
+.. c:struct:: nvmf_ext_attr
+
+ Extended Attribute (EXAT)
+
+**Definition**
+
+::
+
+ struct nvmf_ext_attr {
+ __le16 exattype;
+ __le16 exatlen;
+ __u8 exatval[];
+ };
+
+**Members**
+
+``exattype``
+ Extended Attribute Type (EXATTYPE) - see **enum** nvmf_exattype
+
+``exatlen``
+ Extended Attribute Length (EXATLEN)
+
+``exatval``
+ Extended Attribute Value (EXATVAL) - size allocated for array
+ must be a multiple of 4 bytes
+
+
+
+
+
+.. c:struct:: nvmf_ext_die
+
+ Extended Discovery Information Entry (DIE)
+
+**Definition**
+
+::
+
+ struct nvmf_ext_die {
+ __u8 trtype;
+ __u8 adrfam;
+ __u8 subtype;
+ __u8 treq;
+ __le16 portid;
+ __le16 cntlid;
+ __le16 asqsz;
+ __u8 rsvd10[22];
+ char trsvcid[NVMF_TRSVCID_SIZE];
+ __u8 resv64[192];
+ char nqn[NVME_NQN_LENGTH];
+ char traddr[NVMF_TRADDR_SIZE];
+ union nvmf_tsas tsas;
+ __le32 tel;
+ __le16 numexat;
+ __u8 resv1030[2];
+ struct nvmf_ext_attr exat[];
+ };
+
+**Members**
+
+``trtype``
+ Transport Type (:c:type:`enum nvmf_trtype <nvmf_trtype>`)
+
+``adrfam``
+ Address Family (:c:type:`enum nvmf_addr_family <nvmf_addr_family>`)
+
+``subtype``
+ Subsystem Type (:c:type:`enum nvme_subsys_type <nvme_subsys_type>`)
+
+``treq``
+ Transport Requirements (:c:type:`enum nvmf_treq <nvmf_treq>`)
+
+``portid``
+ Port ID
+
+``cntlid``
+ Controller ID
+
+``asqsz``
+ Admin Max SQ Size
+
+``rsvd10``
+ Reserved
+
+``trsvcid``
+ Transport Service Identifier
+
+``resv64``
+ Reserved
+
+``nqn``
+ NVM Qualified Name
+
+``traddr``
+ Transport Address
+
+``tsas``
+ Transport Specific Address Subtype (:c:type:`union nvmf_tsas <nvmf_tsas>`)
+
+``tel``
+ Total Entry Length
+
+``numexat``
+ Number of Extended Attributes
+
+``resv1030``
+ Reserved
+
+``exat``
+ Extended Attributes 0 (:c:type:`struct nvmf_ext_attr <nvmf_ext_attr>`)
+
+
+
+
+
+.. c:union:: nvmf_die
+
+ Discovery Information Entry (DIE)
+
+**Definition**
+
+::
+
+ union nvmf_die {
+ struct nvmf_disc_log_entry basic[0];
+ struct nvmf_ext_die extended;
+ };
+
+**Members**
+
+``basic``
+ Basic format (:c:type:`struct nvmf_disc_log_entry <nvmf_disc_log_entry>`)
+
+``extended``
+ Extended format (:c:type:`struct nvmf_ext_die <nvmf_ext_die>`)
+
+
+**Description**
+
+Depending on the ENTFMT specified in the DIM, DIEs can be entered
+with the Basic or Extended formats. For Basic format, each entry
+has a fixed length. Therefore, the "basic" field defined below can
+be accessed as a C array. For the Extended format, however, each
+entry is of variable length (TEL). Therefore, the "extended" field
+defined below cannot be accessed as a C array. Instead, the
+"extended" field is akin to a linked-list, where one can "walk"
+through the list. To move to the next entry, one simply adds the
+current entry's length (TEL) to the "walk" pointer. The number of
+entries in the list is specified by NUMENT. Although extended
+entries are of a variable lengths (TEL), TEL is always a multiple of
+4 bytes.
+
+
+
+
+.. c:struct:: nvmf_dim_data
+
+ Discovery Information Management (DIM) - Data
+
+**Definition**
+
+::
+
+ struct nvmf_dim_data {
+ __le32 tdl;
+ __u8 rsvd4[4];
+ __le64 nument;
+ __le16 entfmt;
+ __le16 etype;
+ __u8 portlcl;
+ __u8 rsvd21;
+ __le16 ektype;
+ char eid[NVME_NQN_LENGTH];
+ char ename[NVMF_ENAME_LEN];
+ char ever[NVMF_EVER_LEN];
+ __u8 rsvd600[424];
+ union nvmf_die die[];
+ };
+
+**Members**
+
+``tdl``
+ Total Data Length
+
+``rsvd4``
+ Reserved
+
+``nument``
+ Number of entries
+
+``entfmt``
+ Entry Format (:c:type:`enum nvmf_dim_entfmt <nvmf_dim_entfmt>`)
+
+``etype``
+ Entity Type (:c:type:`enum nvmf_dim_etype <nvmf_dim_etype>`)
+
+``portlcl``
+ Port Local
+
+``rsvd21``
+ Reserved
+
+``ektype``
+ Entry Key Type
+
+``eid``
+ Entity Identifier (e.g. Host NQN)
+
+``ename``
+ Entity Name (e.g. hostname)
+
+``ever``
+ Entity Version (e.g. OS Name/Version)
+
+``rsvd600``
+ Reserved
+
+``die``
+ Discovery Information Entry (see **nument** above)
+
+
+
+
+
+.. c:struct:: nvmf_connect_data
+
+ Data payload for the 'connect' command
+
+**Definition**
+
+::
+
+ struct nvmf_connect_data {
+ __u8 hostid[16];
+ __le16 cntlid;
+ char rsvd4[238];
+ char subsysnqn[NVME_NQN_LENGTH];
+ char hostnqn[NVME_NQN_LENGTH];
+ char rsvd5[256];
+ };
+
+**Members**
+
+``hostid``
+ Host ID of the connecting host
+
+``cntlid``
+ Requested controller ID
+
+``rsvd4``
+ Reserved
+
+``subsysnqn``
+ Subsystem NQN to connect to
+
+``hostnqn``
+ Host NQN of the connecting host
+
+``rsvd5``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_mi_read_nvm_ss_info
+
+ NVM Subsystem Information Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_mi_read_nvm_ss_info {
+ __u8 nump;
+ __u8 mjr;
+ __u8 mnr;
+ __u8 rsvd3[29];
+ };
+
+**Members**
+
+``nump``
+ Number of Ports
+
+``mjr``
+ NVMe-MI Major Version Number
+
+``mnr``
+ NVMe-MI Minor Version Number
+
+``rsvd3``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_mi_port_pcie
+
+ PCIe Port Specific Data
+
+**Definition**
+
+::
+
+ struct nvme_mi_port_pcie {
+ __u8 mps;
+ __u8 sls;
+ __u8 cls;
+ __u8 mlw;
+ __u8 nlw;
+ __u8 pn;
+ __u8 rsvd14[18];
+ };
+
+**Members**
+
+``mps``
+ PCIe Maximum Payload Size
+
+``sls``
+ PCIe Supported Link Speeds Vector
+
+``cls``
+ PCIe Current Link Speed
+
+``mlw``
+ PCIe Maximum Link Width
+
+``nlw``
+ PCIe Negotiated Link Width
+
+``pn``
+ PCIe Port Number
+
+``rsvd14``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_mi_port_smb
+
+ SMBus Port Specific Data
+
+**Definition**
+
+::
+
+ struct nvme_mi_port_smb {
+ __u8 vpd_addr;
+ __u8 mvpd_freq;
+ __u8 mme_addr;
+ __u8 mme_freq;
+ __u8 nvmebm;
+ __u8 rsvd13[19];
+ };
+
+**Members**
+
+``vpd_addr``
+ Current VPD SMBus/I2C Address
+
+``mvpd_freq``
+ Maximum VPD Access SMBus/I2C Frequency
+
+``mme_addr``
+ Current Management Endpoint SMBus/I2C Address
+
+``mme_freq``
+ Maximum Management Endpoint SMBus/I2C Frequency
+
+``nvmebm``
+ NVMe Basic Management
+
+``rsvd13``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_mi_read_port_info
+
+ Port Information Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_mi_read_port_info {
+ __u8 portt;
+ __u8 rsvd1;
+ __le16 mmctptus;
+ __le32 meb;
+ union {
+ struct nvme_mi_port_pcie pcie;
+ struct nvme_mi_port_smb smb;
+ };
+ };
+
+**Members**
+
+``portt``
+ Port Type
+
+``rsvd1``
+ Reserved
+
+``mmctptus``
+ Maximum MCTP Transmission Unit Size
+
+``meb``
+ Management Endpoint Buffer Size
+
+``{unnamed_union}``
+ anonymous
+
+``pcie``
+ PCIe Port Specific Data
+
+``smb``
+ SMBus Port Specific Data
+
+
+
+
+
+.. c:struct:: nvme_mi_read_ctrl_info
+
+ Controller Information Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_mi_read_ctrl_info {
+ __u8 portid;
+ __u8 rsvd1[4];
+ __u8 prii;
+ __le16 pri;
+ __le16 vid;
+ __le16 did;
+ __le16 ssvid;
+ __le16 ssid;
+ __u8 rsvd16[16];
+ };
+
+**Members**
+
+``portid``
+ Port Identifier
+
+``rsvd1``
+ Reserved
+
+``prii``
+ PCIe Routing ID Information
+
+``pri``
+ PCIe Routing ID
+
+``vid``
+ PCI Vendor ID
+
+``did``
+ PCI Device ID
+
+``ssvid``
+ PCI Subsystem Vendor ID
+
+``ssid``
+ PCI Subsystem Device ID
+
+``rsvd16``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_mi_osc
+
+ Optionally Supported Command Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_mi_osc {
+ __u8 type;
+ __u8 opc;
+ };
+
+**Members**
+
+``type``
+ Command Type
+
+``opc``
+ Opcode
+
+
+
+
+
+.. c:struct:: nvme_mi_read_sc_list
+
+ Management Endpoint Buffer Supported Command List Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_mi_read_sc_list {
+ __le16 numcmd;
+ struct nvme_mi_osc cmds[];
+ };
+
+**Members**
+
+``numcmd``
+ Number of Commands
+
+``cmds``
+ MEB supported Command Data Structure.
+ See **struct** nvme_mi_osc
+
+
+
+
+
+.. c:struct:: nvme_mi_nvm_ss_health_status
+
+ Subsystem Management Data Structure
+
+**Definition**
+
+::
+
+ struct nvme_mi_nvm_ss_health_status {
+ __u8 nss;
+ __u8 sw;
+ __u8 ctemp;
+ __u8 pdlu;
+ __le16 ccs;
+ __u8 rsvd8[2];
+ };
+
+**Members**
+
+``nss``
+ NVM Subsystem Status
+
+``sw``
+ Smart Warnings
+
+``ctemp``
+ Composite Temperature
+
+``pdlu``
+ Percentage Drive Life Used
+
+``ccs``
+ Composite Controller Status
+
+``rsvd8``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_mi_ccs
+
+ Get State Control Primitive Success Response Fields - Control Primitive Specific Response
+
+**Constants**
+
+``NVME_MI_CCS_RDY``
+ Ready
+
+``NVME_MI_CCS_CFS``
+ Controller Fatal Status
+
+``NVME_MI_CCS_SHST``
+ Shutdown Status
+
+``NVME_MI_CCS_NSSRO``
+ NVM Subsystem Reset Occurred
+
+``NVME_MI_CCS_CECO``
+ Controller Enable Change Occurred
+
+``NVME_MI_CCS_NAC``
+ Namespace Attribute Changed
+
+``NVME_MI_CCS_FA``
+ Firmware Activated
+
+``NVME_MI_CCS_CSTS``
+ Controller Status Change
+
+``NVME_MI_CCS_CTEMP``
+ Composite Temperature Change
+
+``NVME_MI_CCS_PDLU``
+ Percentage Used
+
+``NVME_MI_CCS_SPARE``
+ Available Spare
+
+``NVME_MI_CCS_CCWARN``
+ Critical Warning
+
+
+
+
+.. c:struct:: nvme_mi_ctrl_health_status
+
+ Controller Health Data Structure (CHDS)
+
+**Definition**
+
+::
+
+ struct nvme_mi_ctrl_health_status {
+ __le16 ctlid;
+ __le16 csts;
+ __le16 ctemp;
+ __u8 pdlu;
+ __u8 spare;
+ __u8 cwarn;
+ __u8 rsvd9[7];
+ };
+
+**Members**
+
+``ctlid``
+ Controller Identifier
+
+``csts``
+ Controller Status
+
+``ctemp``
+ Composite Temperature
+
+``pdlu``
+ Percentage Used
+
+``spare``
+ Available Spare
+
+``cwarn``
+ Critical Warning
+
+``rsvd9``
+ Reserved
+
+
+
+
+
+.. c:enum:: nvme_mi_csts
+
+ Controller Health Data Structure (CHDS) - Controller Status (CSTS)
+
+**Constants**
+
+``NVME_MI_CSTS_RDY``
+ Ready
+
+``NVME_MI_CSTS_CFS``
+ Controller Fatal Status
+
+``NVME_MI_CSTS_SHST``
+ Shutdown Status
+
+``NVME_MI_CSTS_NSSRO``
+ NVM Subsystem Reset Occurred
+
+``NVME_MI_CSTS_CECO``
+ Controller Enable Change Occurred
+
+``NVME_MI_CSTS_NAC``
+ Namespace Attribute Changed
+
+``NVME_MI_CSTS_FA``
+ Firmware Activated
+
+
+
+
+.. c:enum:: nvme_mi_cwarn
+
+ Controller Health Data Structure (CHDS) - Critical Warning (CWARN)
+
+**Constants**
+
+``NVME_MI_CWARN_ST``
+ Spare Threshold
+
+``NVME_MI_CWARN_TAUT``
+ Temperature Above or Under Threshold
+
+``NVME_MI_CWARN_RD``
+ Reliability Degraded
+
+``NVME_MI_CWARN_RO``
+ Read Only
+
+``NVME_MI_CWARN_VMBF``
+ Volatile Memory Backup Failed
+
+
+
+
+.. c:struct:: nvme_mi_vpd_mra
+
+ NVMe MultiRecord Area
+
+**Definition**
+
+::
+
+ struct nvme_mi_vpd_mra {
+ __u8 nmravn;
+ __u8 ff;
+ __u8 rsvd7[6];
+ __u8 i18vpwr;
+ __u8 m18vpwr;
+ __u8 i33vpwr;
+ __u8 m33vpwr;
+ __u8 rsvd17;
+ __u8 m33vapsr;
+ __u8 i5vapsr;
+ __u8 m5vapsr;
+ __u8 i12vapsr;
+ __u8 m12vapsr;
+ __u8 mtl;
+ __u8 tnvmcap[16];
+ __u8 rsvd37[27];
+ };
+
+**Members**
+
+``nmravn``
+ NVMe MultiRecord Area Version Number
+
+``ff``
+ Form Factor
+
+``rsvd7``
+ Reserved
+
+``i18vpwr``
+ Initial 1.8 V Power Supply Requirements
+
+``m18vpwr``
+ Maximum 1.8 V Power Supply Requirements
+
+``i33vpwr``
+ Initial 3.3 V Power Supply Requirements
+
+``m33vpwr``
+ Maximum 3.3 V Power Supply Requirements
+
+``rsvd17``
+ Reserved
+
+``m33vapsr``
+ Maximum 3.3 Vi aux Power Supply Requirements
+
+``i5vapsr``
+ Initial 5 V Power Supply Requirements
+
+``m5vapsr``
+ Maximum 5 V Power Supply Requirements
+
+``i12vapsr``
+ Initial 12 V Power Supply Requirements
+
+``m12vapsr``
+ Maximum 12 V Power Supply Requirements
+
+``mtl``
+ Maximum Thermal Load
+
+``tnvmcap``
+ Total NVM Capacity
+
+``rsvd37``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_mi_vpd_ppmra
+
+ NVMe PCIe Port MultiRecord Area
+
+**Definition**
+
+::
+
+ struct nvme_mi_vpd_ppmra {
+ __u8 nppmravn;
+ __u8 pn;
+ __u8 ppi;
+ __u8 ls;
+ __u8 mlw;
+ __u8 mctp;
+ __u8 refccap;
+ __u8 pi;
+ __u8 rsvd13[3];
+ };
+
+**Members**
+
+``nppmravn``
+ NVMe PCIe Port MultiRecord Area Version Number
+
+``pn``
+ PCIe Port Number
+
+``ppi``
+ Port Information
+
+``ls``
+ PCIe Link Speed
+
+``mlw``
+ PCIe Maximum Link Width
+
+``mctp``
+ MCTP Support
+
+``refccap``
+ Ref Clk Capability
+
+``pi``
+ Port Identifier
+
+``rsvd13``
+ Reserved
+
+
+
+
+
+.. c:struct:: nvme_mi_vpd_telem
+
+ Vital Product Data Element Descriptor
+
+**Definition**
+
+::
+
+ struct nvme_mi_vpd_telem {
+ __u8 type;
+ __u8 rev;
+ __u8 len;
+ __u8 data[0];
+ };
+
+**Members**
+
+``type``
+ Type of the Element Descriptor
+
+``rev``
+ Revision of the Element Descriptor
+
+``len``
+ Number of bytes in the Element Descriptor
+
+``data``
+ Type-specific information associated with
+ the Element Descriptor
+
+
+
+
+
+.. c:enum:: nvme_mi_elem
+
+ Element Descriptor Types
+
+**Constants**
+
+``NVME_MI_ELEM_EED``
+ Extended Element Descriptor
+
+``NVME_MI_ELEM_USCE``
+ Upstream Connector Element Descriptor
+
+``NVME_MI_ELEM_ECED``
+ Expansion Connector Element Descriptor
+
+``NVME_MI_ELEM_LED``
+ Label Element Descriptor
+
+``NVME_MI_ELEM_SMBMED``
+ SMBus/I2C Mux Element Descriptor
+
+``NVME_MI_ELEM_PCIESED``
+ PCIe Switch Element Descriptor
+
+``NVME_MI_ELEM_NVMED``
+ NVM Subsystem Element Descriptor
+
+
+
+
+.. c:struct:: nvme_mi_vpd_tra
+
+ Vital Product Data Topology MultiRecord
+
+**Definition**
+
+::
+
+ struct nvme_mi_vpd_tra {
+ __u8 vn;
+ __u8 rsvd6;
+ __u8 ec;
+ struct nvme_mi_vpd_telem elems[0];
+ };
+
+**Members**
+
+``vn``
+ Version Number
+
+``rsvd6``
+ Reserved
+
+``ec``
+ Element Count
+
+``elems``
+ Element Descriptor
+
+
+
+
+
+.. c:struct:: nvme_mi_vpd_mr_common
+
+ NVMe MultiRecord Area
+
+**Definition**
+
+::
+
+ struct nvme_mi_vpd_mr_common {
+ __u8 type;
+ __u8 rf;
+ __u8 rlen;
+ __u8 rchksum;
+ __u8 hchksum;
+ union {
+ struct nvme_mi_vpd_mra nmra;
+ struct nvme_mi_vpd_ppmra ppmra;
+ struct nvme_mi_vpd_tra tmra;
+ };
+ };
+
+**Members**
+
+``type``
+ NVMe Record Type ID
+
+``rf``
+ Record Format
+
+``rlen``
+ Record Length
+
+``rchksum``
+ Record Checksum
+
+``hchksum``
+ Header Checksum
+
+``{unnamed_union}``
+ anonymous
+
+``nmra``
+ NVMe MultiRecord Area
+
+``ppmra``
+ NVMe PCIe Port MultiRecord Area
+
+``tmra``
+ Topology MultiRecord Area
+
+
+
+
+
+.. c:struct:: nvme_mi_vpd_hdr
+
+ Vital Product Data Common Header
+
+**Definition**
+
+::
+
+ struct nvme_mi_vpd_hdr {
+ __u8 ipmiver;
+ __u8 iuaoff;
+ __u8 ciaoff;
+ __u8 biaoff;
+ __u8 piaoff;
+ __u8 mrioff;
+ __u8 rsvd6;
+ __u8 chchk;
+ __u8 vpd[];
+ };
+
+**Members**
+
+``ipmiver``
+ IPMI Format Version Number
+
+``iuaoff``
+ Internal Use Area Starting Offset
+
+``ciaoff``
+ Chassis Info Area Starting Offset
+
+``biaoff``
+ Board Info Area Starting Offset
+
+``piaoff``
+ Product Info Area Starting Offset
+
+``mrioff``
+ MultiRecord Info Area Starting Offset
+
+``rsvd6``
+ Reserved
+
+``chchk``
+ Common Header Checksum
+
+``vpd``
+ Vital Product Data
+
+
+
+
+
+.. c:enum:: nvme_status_field
+
+ Defines all parts of the nvme status field: status code, status code type, and additional flags.
+
+**Constants**
+
+``NVME_SCT_GENERIC``
+ Generic errors applicable to multiple opcodes
+
+``NVME_SCT_CMD_SPECIFIC``
+ Errors associated to a specific opcode
+
+``NVME_SCT_MEDIA``
+ Errors associated with media and data integrity
+
+``NVME_SCT_PATH``
+ Errors associated with the paths connection
+
+``NVME_SCT_VS``
+ Vendor specific errors
+
+``NVME_SCT_MASK``
+ Mask to get the value of the Status Code Type
+
+``NVME_SCT_SHIFT``
+ Shift value to get the value of the Status
+ Code Type
+
+``NVME_SC_MASK``
+ Mask to get the value of the status code.
+
+``NVME_SC_SHIFT``
+ Shift value to get the value of the status
+ code.
+
+``NVME_SC_SUCCESS``
+ Successful Completion: The command
+ completed without error.
+
+``NVME_SC_INVALID_OPCODE``
+ Invalid Command Opcode: A reserved coded
+ value or an unsupported value in the
+ command opcode field.
+
+``NVME_SC_INVALID_FIELD``
+ Invalid Field in Command: A reserved
+ coded value or an unsupported value in a
+ defined field.
+
+``NVME_SC_CMDID_CONFLICT``
+ Command ID Conflict: The command
+ identifier is already in use.
+
+``NVME_SC_DATA_XFER_ERROR``
+ Data Transfer Error: Transferring the
+ data or metadata associated with a
+ command experienced an error.
+
+``NVME_SC_POWER_LOSS``
+ Commands Aborted due to Power Loss
+ Notification: Indicates that the command
+ was aborted due to a power loss
+ notification.
+
+``NVME_SC_INTERNAL``
+ Internal Error: The command was not
+ completed successfully due to an internal error.
+
+``NVME_SC_ABORT_REQ``
+ Command Abort Requested: The command was
+ aborted due to an Abort command being
+ received that specified the Submission
+ Queue Identifier and Command Identifier
+ of this command.
+
+``NVME_SC_ABORT_QUEUE``
+ Command Aborted due to SQ Deletion: The
+ command was aborted due to a Delete I/O
+ Submission Queue request received for the
+ Submission Queue to which the command was
+ submitted.
+
+``NVME_SC_FUSED_FAIL``
+ Command Aborted due to Failed Fused Command:
+ The command was aborted due to the other
+ command in a fused operation failing.
+
+``NVME_SC_FUSED_MISSING``
+ Aborted due to Missing Fused Command: The
+ fused command was aborted due to the
+ adjacent submission queue entry not
+ containing a fused command that is the
+ other command.
+
+``NVME_SC_INVALID_NS``
+ Invalid Namespace or Format: The
+ namespace or the format of that namespace
+ is invalid.
+
+``NVME_SC_CMD_SEQ_ERROR``
+ Command Sequence Error: The command was
+ aborted due to a protocol violation in a
+ multi-command sequence.
+
+``NVME_SC_SGL_INVALID_LAST``
+ Invalid SGL Segment Descriptor: The
+ command includes an invalid SGL Last
+ Segment or SGL Segment descriptor.
+
+``NVME_SC_SGL_INVALID_COUNT``
+ Invalid Number of SGL Descriptors: There
+ is an SGL Last Segment descriptor or an
+ SGL Segment descriptor in a location
+ other than the last descriptor of a
+ segment based on the length indicated.
+
+``NVME_SC_SGL_INVALID_DATA``
+ Data SGL Length Invalid: This may occur
+ if the length of a Data SGL is too short.
+ This may occur if the length of a Data
+ SGL is too long and the controller does
+ not support SGL transfers longer than the
+ amount of data to be transferred as
+ indicated in the SGL Support field of the
+ Identify Controller data structure.
+
+``NVME_SC_SGL_INVALID_METADATA``
+ Metadata SGL Length Invalid: This may
+ occur if the length of a Metadata SGL is
+ too short. This may occur if the length
+ of a Metadata SGL is too long and the
+ controller does not support SGL transfers
+ longer than the amount of data to be
+ transferred as indicated in the SGL
+ Support field of the Identify Controller
+ data structure.
+
+``NVME_SC_SGL_INVALID_TYPE``
+ SGL Descriptor Type Invalid: The type of
+ an SGL Descriptor is a type that is not
+ supported by the controller.
+
+``NVME_SC_CMB_INVALID_USE``
+ Invalid Use of Controller Memory Buffer:
+ The attempted use of the Controller
+ Memory Buffer is not supported by the
+ controller.
+
+``NVME_SC_PRP_INVALID_OFFSET``
+ PRP Offset Invalid: The Offset field for
+ a PRP entry is invalid.
+
+``NVME_SC_AWU_EXCEEDED``
+ Atomic Write Unit Exceeded: The length
+ specified exceeds the atomic write unit size.
+
+``NVME_SC_OP_DENIED``
+ Operation Denied: The command was denied
+ due to lack of access rights. Refer to
+ the appropriate security specification.
+
+``NVME_SC_SGL_INVALID_OFFSET``
+ SGL Offset Invalid: The offset specified
+ in a descriptor is invalid. This may
+ occur when using capsules for data
+ transfers in NVMe over Fabrics
+ implementations and an invalid offset in
+ the capsule is specified.
+
+``NVME_SC_HOSTID_FORMAT``
+ Host Identifier Inconsistent Format: The
+ NVM subsystem detected the simultaneous
+ use of 64- bit and 128-bit Host
+ Identifier values on different
+ controllers.
+
+``NVME_SC_KAT_EXPIRED``
+ Keep Alive Timer Expired: The Keep Alive
+ Timer expired.
+
+``NVME_SC_KAT_INVALID``
+ Keep Alive Timeout Invalid: The Keep
+ Alive Timeout value specified is invalid.
+
+``NVME_SC_CMD_ABORTED_PREMEPT``
+ Command Aborted due to Preempt and Abort:
+ The command was aborted due to a
+ Reservation Acquire command.
+
+``NVME_SC_SANITIZE_FAILED``
+ Sanitize Failed: The most recent sanitize
+ operation failed and no recovery action
+ has been successfully completed.
+
+``NVME_SC_SANITIZE_IN_PROGRESS``
+ Sanitize In Progress: The requested
+ function (e.g., command) is prohibited
+ while a sanitize operation is in
+ progress.
+
+``NVME_SC_SGL_INVALID_GRANULARITY``
+ SGL Data Block Granularity Invalid: The
+ Address alignment or Length granularity
+ for an SGL Data Block descriptor is
+ invalid.
+
+``NVME_SC_CMD_IN_CMBQ_NOT_SUPP``
+ Command Not Supported for Queue in CMB:
+ The implementation does not support
+ submission of the command to a Submission
+ Queue in the Controller Memory Buffer or
+ command completion to a Completion Queue
+ in the Controller Memory Buffer.
+
+``NVME_SC_NS_WRITE_PROTECTED``
+ Namespace is Write Protected: The command
+ is prohibited while the namespace is
+ write protected as a result of a change
+ in the namespace write protection state
+ as defined by the Namespace Write
+ Protection State Machine.
+
+``NVME_SC_CMD_INTERRUPTED``
+ Command Interrupted: Command processing
+ was interrupted and the controller is
+ unable to successfully complete the
+ command. The host should retry the
+ command.
+
+``NVME_SC_TRAN_TPORT_ERROR``
+ Transient Transport Error: A transient
+ transport error was detected. If the
+ command is retried on the same
+ controller, the command is likely to
+ succeed. A command that fails with a
+ transient transport error four or more
+ times should be treated as a persistent
+ transport error that is not likely to
+ succeed if retried on the same
+ controller.
+
+``NVME_SC_PROHIBITED_BY_CMD_AND_FEAT``
+ Command Prohibited by Command and Feature
+ Lockdown: The command was aborted due to
+ command execution being prohibited by
+ the Command and Feature Lockdown.
+
+``NVME_SC_ADMIN_CMD_MEDIA_NOT_READY``
+ Admin Command Media Not Ready: The Admin
+ command requires access to media and
+ the media is not ready.
+
+``NVME_SC_FDP_DISABLED``
+ Command is not allowed when
+ Flexible Data Placement is disabled.
+
+``NVME_SC_INVALID_PLACEMENT_HANDLE_LIST``
+ The Placement Handle List is invalid
+ due to invalid Reclaim Unit Handle Identifier or
+ valid Reclaim Unit Handle Identifier but restricted or
+ the Placement Handle List number of entries exceeded the
+ maximum number allowed.
+
+``NVME_SC_LBA_RANGE``
+ LBA Out of Range: The command references
+ an LBA that exceeds the size of the namespace.
+
+``NVME_SC_CAP_EXCEEDED``
+ Capacity Exceeded: Execution of the
+ command has caused the capacity of the
+ namespace to be exceeded.
+
+``NVME_SC_NS_NOT_READY``
+ Namespace Not Ready: The namespace is not
+ ready to be accessed as a result of a
+ condition other than a condition that is
+ reported as an Asymmetric Namespace
+ Access condition.
+
+``NVME_SC_RESERVATION_CONFLICT``
+ Reservation Conflict: The command was
+ aborted due to a conflict with a
+ reservation held on the accessed
+ namespace.
+
+``NVME_SC_FORMAT_IN_PROGRESS``
+ Format In Progress: A Format NVM command
+ is in progress on the namespace.
+
+``NVME_SC_CQ_INVALID``
+ Completion Queue Invalid: The Completion
+ Queue identifier specified in the command
+ does not exist.
+
+``NVME_SC_QID_INVALID``
+ Invalid Queue Identifier: The creation of
+ the I/O Completion Queue failed due to an
+ invalid queue identifier specified as
+ part of the command. An invalid queue
+ identifier is one that is currently in
+ use or one that is outside the range
+ supported by the controller.
+
+``NVME_SC_QUEUE_SIZE``
+ Invalid Queue Size: The host attempted to
+ create an I/O Completion Queue with an
+ invalid number of entries.
+
+``NVME_SC_ABORT_LIMIT``
+ Abort Command Limit Exceeded: The number
+ of concurrently outstanding Abort commands
+ has exceeded the limit indicated in the
+ Identify Controller data structure.
+
+``NVME_SC_ABORT_MISSING``
+ Abort Command is missing: The abort
+ command is missing.
+
+``NVME_SC_ASYNC_LIMIT``
+ Asynchronous Event Request Limit
+ Exceeded: The number of concurrently
+ outstanding Asynchronous Event Request
+ commands has been exceeded.
+
+``NVME_SC_FIRMWARE_SLOT``
+ Invalid Firmware Slot: The firmware slot
+ indicated is invalid or read only. This
+ error is indicated if the firmware slot
+ exceeds the number supported.
+
+``NVME_SC_FIRMWARE_IMAGE``
+ Invalid Firmware Image: The firmware
+ image specified for activation is invalid
+ and not loaded by the controller.
+
+``NVME_SC_INVALID_VECTOR``
+ Invalid Interrupt Vector: The creation of
+ the I/O Completion Queue failed due to an
+ invalid interrupt vector specified as
+ part of the command.
+
+``NVME_SC_INVALID_LOG_PAGE``
+ Invalid Log Page: The log page indicated
+ is invalid. This error condition is also
+ returned if a reserved log page is
+ requested.
+
+``NVME_SC_INVALID_FORMAT``
+ Invalid Format: The LBA Format specified
+ is not supported.
+
+``NVME_SC_FW_NEEDS_CONV_RESET``
+ Firmware Activation Requires Conventional Reset:
+ The firmware commit was successful,
+ however, activation of the firmware image
+ requires a conventional reset.
+
+``NVME_SC_INVALID_QUEUE``
+ Invalid Queue Deletion: Invalid I/O
+ Completion Queue specified to delete.
+
+``NVME_SC_FEATURE_NOT_SAVEABLE``
+ Feature Identifier Not Saveable: The
+ Feature Identifier specified does not
+ support a saveable value.
+
+``NVME_SC_FEATURE_NOT_CHANGEABLE``
+ Feature Not Changeable: The Feature
+ Identifier is not able to be changed.
+
+``NVME_SC_FEATURE_NOT_PER_NS``
+ Feature Not Namespace Specific: The
+ Feature Identifier specified is not
+ namespace specific. The Feature
+ Identifier settings apply across all
+ namespaces.
+
+``NVME_SC_FW_NEEDS_SUBSYS_RESET``
+ Firmware Activation Requires NVM
+ Subsystem Reset: The firmware commit was
+ successful, however, activation of the
+ firmware image requires an NVM Subsystem.
+
+``NVME_SC_FW_NEEDS_RESET``
+ Firmware Activation Requires Controller
+ Level Reset: The firmware commit was
+ successful; however, the image specified
+ does not support being activated without
+ a reset.
+
+``NVME_SC_FW_NEEDS_MAX_TIME``
+ Firmware Activation Requires Maximum Time
+ Violation: The image specified if
+ activated immediately would exceed the
+ Maximum Time for Firmware Activation
+ (MTFA) value reported in Identify
+ Controller.
+
+``NVME_SC_FW_ACTIVATE_PROHIBITED``
+ Firmware Activation Prohibited: The image
+ specified is being prohibited from
+ activation by the controller for vendor
+ specific reasons.
+
+``NVME_SC_OVERLAPPING_RANGE``
+ Overlapping Range: The downloaded
+ firmware image has overlapping ranges.
+
+``NVME_SC_NS_INSUFFICIENT_CAP``
+ Namespace Insufficient Capacity: Creating
+ the namespace requires more free space
+ than is currently available.
+
+``NVME_SC_NS_ID_UNAVAILABLE``
+ Namespace Identifier Unavailable: The
+ number of namespaces supported has been
+ exceeded.
+
+``NVME_SC_NS_ALREADY_ATTACHED``
+ Namespace Already Attached: The
+ controller is already attached to the
+ namespace specified.
+
+``NVME_SC_NS_IS_PRIVATE``
+ Namespace Is Private: The namespace is
+ private and is already attached to one
+ controller.
+
+``NVME_SC_NS_NOT_ATTACHED``
+ Namespace Not Attached: The request to
+ detach the controller could not be
+ completed because the controller is not
+ attached to the namespace.
+
+``NVME_SC_THIN_PROV_NOT_SUPP``
+ Thin Provisioning Not Supported: Thin
+ provisioning is not supported by the
+ controller.
+
+``NVME_SC_CTRL_LIST_INVALID``
+ Controller List Invalid: The controller
+ list provided contains invalid controller
+ ids.
+
+``NVME_SC_SELF_TEST_IN_PROGRESS``
+ Device Self-test In Progress: The controller
+ or NVM subsystem already has a device
+ self-test operation in process.
+
+``NVME_SC_BP_WRITE_PROHIBITED``
+ Boot Partition Write Prohibited: The
+ command is trying to modify a locked Boot
+ Partition.
+
+``NVME_SC_INVALID_CTRL_ID``
+ Invalid Controller Identifier:
+
+``NVME_SC_INVALID_SEC_CTRL_STATE``
+ Invalid Secondary Controller State
+
+``NVME_SC_INVALID_CTRL_RESOURCES``
+ Invalid Number of Controller Resources
+
+``NVME_SC_INVALID_RESOURCE_ID``
+ Invalid Resource Identifier
+
+``NVME_SC_PMR_SAN_PROHIBITED``
+ Sanitize Prohibited While Persistent
+ Memory Region is Enabled
+
+``NVME_SC_ANA_GROUP_ID_INVALID``
+ ANA Group Identifier Invalid: The specified
+ ANA Group Identifier (ANAGRPID) is not
+ supported in the submitted command.
+
+``NVME_SC_ANA_ATTACH_FAILED``
+ ANA Attach Failed: The controller is not
+ attached to the namespace as a result
+ of an ANA condition.
+
+``NVME_SC_INSUFFICIENT_CAP``
+ Insufficient Capacity: Requested operation
+ requires more free space than is currently
+ available.
+
+``NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED``
+ Namespace Attachment Limit Exceeded:
+ Attaching the ns to a controller causes
+ max number of ns attachments allowed
+ to be exceeded.
+
+``NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED``
+ Prohibition of Command Execution
+ Not Supported
+
+``NVME_SC_IOCS_NOT_SUPPORTED``
+ I/O Command Set Not Supported
+
+``NVME_SC_IOCS_NOT_ENABLED``
+ I/O Command Set Not Enabled
+
+``NVME_SC_IOCS_COMBINATION_REJECTED``
+ I/O Command Set Combination Rejected
+
+``NVME_SC_INVALID_IOCS``
+ Invalid I/O Command Set
+
+``NVME_SC_ID_UNAVAILABLE``
+ Identifier Unavailable
+
+``NVME_SC_INVALID_DISCOVERY_INFO``
+ The discovery information provided in
+ one or more extended discovery
+ information entries is not applicable
+ for the type of entity selected in
+ the Entity Type (ETYPE) field of the
+ Discovery Information Management
+ command data portion’s header.
+
+``NVME_SC_ZONING_DATA_STRUCT_LOCKED``
+ The requested Zoning data structure
+ is locked on the CDC.
+
+``NVME_SC_ZONING_DATA_STRUCT_NOTFND``
+ The requested Zoning data structure
+ does not exist on the CDC.
+
+``NVME_SC_INSUFFICIENT_DISC_RES``
+ The number of discover information
+ entries provided in the data portion
+ of the Discovery Information
+ Management command for a registration
+ task (i.e., TAS field cleared to 0h)
+ exceeds the available capacity for
+ new discovery information entries on
+ the CDC or DDC. This may be a
+ transient condition.
+
+``NVME_SC_REQSTD_FUNCTION_DISABLED``
+ Fabric Zoning is not enabled on the
+ CDC
+
+``NVME_SC_ZONEGRP_ORIGINATOR_INVLD``
+ The NQN contained in the ZoneGroup
+ Originator field does not match the
+ Host NQN used by the DDC to connect
+ to the CDC.
+
+``NVME_SC_BAD_ATTRIBUTES``
+ Conflicting Dataset Management Attributes
+
+``NVME_SC_INVALID_PI``
+ Invalid Protection Information
+
+``NVME_SC_READ_ONLY``
+ Attempted Write to Read Only Range
+
+``NVME_SC_CMD_SIZE_LIMIT_EXCEEDED``
+ Command Size Limit Exceeded
+
+``NVME_SC_INCOMPATIBLE_NS``
+ Incompatible Namespace or Format: At
+ least one source namespace and the
+ destination namespace have incompatible
+ formats.
+
+``NVME_SC_FAST_COPY_NOT_POSSIBLE``
+ Fast Copy Not Possible: The Fast Copy
+ Only (FCO) bit was set to ‘1’ in a Source
+ Range entry and the controller was not
+ able to use fast copy operations to copy
+ the specified data.
+
+``NVME_SC_OVERLAPPING_IO_RANGE``
+ Overlapping I/O Range: A source logical
+ block range overlaps the destination
+ logical block range.
+
+``NVME_SC_INSUFFICIENT_RESOURCES``
+ Insufficient Resources: A resource
+ shortage prevented the controller from
+ performing the requested copy.
+
+``NVME_SC_CONNECT_FORMAT``
+ Incompatible Format: The NVM subsystem
+ does not support the record format
+ specified by the host.
+
+``NVME_SC_CONNECT_CTRL_BUSY``
+ Controller Busy: The controller is
+ already associated with a host.
+
+``NVME_SC_CONNECT_INVALID_PARAM``
+ Connect Invalid Parameters: One or more
+ of the command parameters.
+
+``NVME_SC_CONNECT_RESTART_DISC``
+ Connect Restart Discovery: The NVM
+ subsystem requested is not available.
+
+``NVME_SC_CONNECT_INVALID_HOST``
+ Connect Invalid Host: The host is either
+ not allowed to establish an association
+ to any controller in the NVM subsystem or
+ the host is not allowed to establish an
+ association to the specified controller
+
+``NVME_SC_DISCONNECT_INVALID_QTYPE``
+ Invalid Queue Type: The command was sent
+ on the wrong queue type.
+
+``NVME_SC_DISCOVERY_RESTART``
+ Discover Restart: The snapshot of the
+ records is now invalid or out of date.
+
+``NVME_SC_AUTH_REQUIRED``
+ Authentication Required: NVMe in-band
+ authentication is required and the queue
+ has not yet been authenticated.
+
+``NVME_SC_ZNS_INVALID_OP_REQUEST``
+ Invalid Zone Operation Request:
+ The operation requested is invalid. This may be due to
+ various conditions, including: attempting to allocate a
+ ZRWA when a zone is not in the ZSE:Empty state; or
+ invalid Flush Explicit ZRWA Range Send Zone Action
+ operation.
+
+``NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE``
+ ZRWA Resources Unavailable:
+ No ZRWAs are available.
+
+``NVME_SC_ZNS_BOUNDARY_ERROR``
+ Zone Boundary Error: The command specifies
+ logical blocks in more than one zone.
+
+``NVME_SC_ZNS_FULL``
+ Zone Is Full: The accessed zone is in the
+ ZSF:Full state.
+
+``NVME_SC_ZNS_READ_ONLY``
+ Zone Is Read Only: The accessed zone is
+ in the ZSRO:Read Only state.
+
+``NVME_SC_ZNS_OFFLINE``
+ Zone Is Offline: The accessed zone is
+ in the ZSO:Offline state.
+
+``NVME_SC_ZNS_INVALID_WRITE``
+ Zone Invalid Write: The write to a zone
+ was not at the write pointer.
+
+``NVME_SC_ZNS_TOO_MANY_ACTIVE``
+ Too Many Active Zones: The controller
+ does not allow additional active zones.
+
+``NVME_SC_ZNS_TOO_MANY_OPENS``
+ Too Many Open Zones: The controller does
+ not allow additional open zones.
+
+``NVME_SC_ZNS_INVAL_TRANSITION``
+ Invalid Zone State Transition: The request
+ is not a valid zone state transition.
+
+``NVME_SC_WRITE_FAULT``
+ Write Fault: The write data could not be
+ committed to the media.
+
+``NVME_SC_READ_ERROR``
+ Unrecovered Read Error: The read data
+ could not be recovered from the media.
+
+``NVME_SC_GUARD_CHECK``
+ End-to-end Guard Check Error: The command
+ was aborted due to an end-to-end guard
+ check failure.
+
+``NVME_SC_APPTAG_CHECK``
+ End-to-end Application Tag Check Error:
+ The command was aborted due to an
+ end-to-end application tag check failure.
+
+``NVME_SC_REFTAG_CHECK``
+ End-to-end Reference Tag Check Error: The
+ command was aborted due to an end-to-end
+ reference tag check failure.
+
+``NVME_SC_COMPARE_FAILED``
+ Compare Failure: The command failed due
+ to a miscompare during a Compare command.
+
+``NVME_SC_ACCESS_DENIED``
+ Access Denied: Access to the namespace
+ and/or LBA range is denied due to lack of
+ access rights.
+
+``NVME_SC_UNWRITTEN_BLOCK``
+ Deallocated or Unwritten Logical Block:
+ The command failed due to an attempt to
+ read from or verify an LBA range
+ containing a deallocated or unwritten
+ logical block.
+
+``NVME_SC_STORAGE_TAG_CHECK``
+ End-to-End Storage Tag Check Error: The
+ command was aborted due to an end-to-end
+ storage tag check failure.
+
+``NVME_SC_ANA_INTERNAL_PATH_ERROR``
+ Internal Path Error: The command was not
+ completed as the result of a controller
+ internal error that is specific to the
+ controller processing the command.
+
+``NVME_SC_ANA_PERSISTENT_LOSS``
+ Asymmetric Access Persistent Loss: The
+ requested function (e.g., command) is not
+ able to be performed as a result of the
+ relationship between the controller and
+ the namespace being in the ANA Persistent
+ Loss state.
+
+``NVME_SC_ANA_INACCESSIBLE``
+ Asymmetric Access Inaccessible: The
+ requested function (e.g., command) is not
+ able to be performed as a result of the
+ relationship between the controller and
+ the namespace being in the ANA
+ Inaccessible state.
+
+``NVME_SC_ANA_TRANSITION``
+ Asymmetric Access Transition: The
+ requested function (e.g., command) is not
+ able to be performed as a result of the
+ relationship between the controller and
+ the namespace transitioning between
+ Asymmetric Namespace Access states.
+
+``NVME_SC_CTRL_PATH_ERROR``
+ Controller Pathing Error: A pathing error
+ was detected by the controller.
+
+``NVME_SC_HOST_PATH_ERROR``
+ Host Pathing Error: A pathing error was
+ detected by the host.
+
+``NVME_SC_CMD_ABORTED_BY_HOST``
+ Command Aborted By Host: The command was
+ aborted as a result of host action.
+
+``NVME_SC_CRD``
+ Mask to get value of Command Retry Delay
+ index
+
+``NVME_SC_MORE``
+ More bit. If set, more status information
+ for this command as part of the Error
+ Information log that may be retrieved with
+ the Get Log Page command.
+
+``NVME_SC_DNR``
+ Do Not Retry bit. If set, if the same
+ command is re-submitted to any controller
+ in the NVM subsystem, then that
+ re-submitted command is expected to fail.
+
+
+.. c:function:: __u16 nvme_status_code_type (__u16 status_field)
+
+ Returns the NVMe Status Code Type
+
+**Parameters**
+
+``__u16 status_field``
+ The NVMe Completion Queue Entry's Status Field
+ See :c:type:`enum nvme_status_field <nvme_status_field>`
+
+**Return**
+
+status code type
+
+
+.. c:function:: __u16 nvme_status_code (__u16 status_field)
+
+ Returns the NVMe Status Code
+
+**Parameters**
+
+``__u16 status_field``
+ The NVMe Completion Queue Entry's Status Field
+ See :c:type:`enum nvme_status_field <nvme_status_field>`
+
+**Return**
+
+status code
+
+
+
+
+.. c:enum:: nvme_status_type
+
+ type encoding for NVMe return values, when represented as an int.
+
+**Constants**
+
+``NVME_STATUS_TYPE_SHIFT``
+ shift value for status bits
+
+``NVME_STATUS_TYPE_MASK``
+ mask value for status bits
+
+``NVME_STATUS_TYPE_NVME``
+ NVMe command status value, typically from CDW3
+
+``NVME_STATUS_TYPE_MI``
+ NVMe-MI header status
+
+**Description**
+
+
+The nvme_* api returns an int, with negative values indicating an internal
+or syscall error, zero signifying success, positive values representing
+the NVMe status.
+
+That latter case (the NVMe status) may represent status values from
+different parts of the transport/controller/etc, and are at most 16 bits of
+data. So, we use the most-significant 3 bits of the signed int to indicate
+which type of status this is.
+
+
+.. c:function:: __u32 nvme_status_get_type (int status)
+
+ extract the type from a nvme_* return value
+
+**Parameters**
+
+``int status``
+ the (non-negative) return value from the NVMe API
+
+**Return**
+
+the type component of the status.
+
+
+.. c:function:: __u32 nvme_status_get_value (int status)
+
+ extract the status value from a nvme_* return value
+
+**Parameters**
+
+``int status``
+ the (non-negative) return value from the NVMe API
+
+**Return**
+
+the value component of the status; the set of values will depend
+on the status type.
+
+
+.. c:function:: __u32 nvme_status_equals (int status, enum nvme_status_type type, unsigned int value)
+
+ helper to check a status against a type and value
+
+**Parameters**
+
+``int status``
+ the (non-negative) return value from the NVMe API
+
+``enum nvme_status_type type``
+ the status type
+
+``unsigned int value``
+ the status value
+
+**Return**
+
+true if **status** is of the specified type and value
+
+
+
+
+.. c:enum:: nvme_admin_opcode
+
+ Known NVMe admin opcodes
+
+**Constants**
+
+``nvme_admin_delete_sq``
+ Delete I/O Submission Queue
+
+``nvme_admin_create_sq``
+ Create I/O Submission Queue
+
+``nvme_admin_get_log_page``
+ Get Log Page
+
+``nvme_admin_delete_cq``
+ Delete I/O Completion Queue
+
+``nvme_admin_create_cq``
+ Create I/O Completion Queue
+
+``nvme_admin_identify``
+ Identify
+
+``nvme_admin_abort_cmd``
+ Abort
+
+``nvme_admin_set_features``
+ Set Features
+
+``nvme_admin_get_features``
+ Get Features
+
+``nvme_admin_async_event``
+ Asynchronous Event Request
+
+``nvme_admin_ns_mgmt``
+ Namespace Management
+
+``nvme_admin_fw_commit``
+ Firmware Commit
+
+``nvme_admin_fw_activate``
+ Firmware Commit
+
+``nvme_admin_fw_download``
+ Firmware Image Download
+
+``nvme_admin_dev_self_test``
+ Device Self-test
+
+``nvme_admin_ns_attach``
+ Namespace Attachment
+
+``nvme_admin_keep_alive``
+ Keep Alive
+
+``nvme_admin_directive_send``
+ Directive Send
+
+``nvme_admin_directive_recv``
+ Directive Receive
+
+``nvme_admin_virtual_mgmt``
+ Virtualization Management
+
+``nvme_admin_nvme_mi_send``
+ NVMe-MI Send
+
+``nvme_admin_nvme_mi_recv``
+ NVMe-MI Receive
+
+``nvme_admin_capacity_mgmt``
+ Capacity Management
+
+``nvme_admin_discovery_info_mgmt``
+ Discovery Information Management (DIM)
+
+``nvme_admin_fabric_zoning_recv``
+ Fabric Zoning Receive
+
+``nvme_admin_lockdown``
+ Lockdown
+
+``nvme_admin_fabric_zoning_lookup``
+ Fabric Zoning Lookup
+
+``nvme_admin_fabric_zoning_send``
+ Fabric Zoning Send
+
+``nvme_admin_dbbuf``
+ Doorbell Buffer Config
+
+``nvme_admin_fabrics``
+ Fabrics Commands
+
+``nvme_admin_format_nvm``
+ Format NVM
+
+``nvme_admin_security_send``
+ Security Send
+
+``nvme_admin_security_recv``
+ Security Receive
+
+``nvme_admin_sanitize_nvm``
+ Sanitize
+
+``nvme_admin_get_lba_status``
+ Get LBA Status
+
+
+
+
+.. c:enum:: nvme_identify_cns
+
+ Identify - CNS Values
+
+**Constants**
+
+``NVME_IDENTIFY_CNS_NS``
+ Identify Namespace data structure
+
+``NVME_IDENTIFY_CNS_CTRL``
+ Identify Controller data structure
+
+``NVME_IDENTIFY_CNS_NS_ACTIVE_LIST``
+ Active Namespace ID list
+
+``NVME_IDENTIFY_CNS_NS_DESC_LIST``
+ Namespace Identification Descriptor list
+
+``NVME_IDENTIFY_CNS_NVMSET_LIST``
+ NVM Set List
+
+``NVME_IDENTIFY_CNS_CSI_NS``
+ I/O Command Set specific Identify
+ Namespace data structure
+
+``NVME_IDENTIFY_CNS_CSI_CTRL``
+ I/O Command Set specific Identify
+ Controller data structure
+
+``NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST``
+ Active Namespace ID list associated
+ with the specified I/O Command Set
+
+``NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS``
+ I/O Command Set Independent Identify
+
+``NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT``
+ Namespace user data format
+
+``NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT``
+ I/O Command Set specific user data
+ format
+ Namespace data structure
+
+``NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST``
+ Allocated Namespace ID list
+
+``NVME_IDENTIFY_CNS_ALLOCATED_NS``
+ Identify Namespace data structure for
+ the specified allocated NSID
+
+``NVME_IDENTIFY_CNS_NS_CTRL_LIST``
+ Controller List of controllers attached
+ to the specified NSID
+
+``NVME_IDENTIFY_CNS_CTRL_LIST``
+ Controller List of controllers that exist
+ in the NVM subsystem
+
+``NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP``
+ Primary Controller Capabilities data
+ structure for the specified primary controller
+
+``NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST``
+ Secondary Controller list of controllers
+ associated with the primary controller
+ processing the command
+
+``NVME_IDENTIFY_CNS_NS_GRANULARITY``
+ A Namespace Granularity List
+
+``NVME_IDENTIFY_CNS_UUID_LIST``
+ A UUID List
+
+``NVME_IDENTIFY_CNS_DOMAIN_LIST``
+ Domain List
+
+``NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID``
+ Endurance Group List
+
+``NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST``
+ I/O Command Set specific Allocated Namespace
+ ID list
+
+``NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE``
+ I/O Command Set specific ID Namespace
+ Data Structure for Allocated Namespace ID
+
+``NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE``
+ Base Specification 2.0a section 5.17.2.21
+
+
+
+
+.. c:enum:: nvme_cmd_get_log_lid
+
+ Get Log Page -Log Page Identifiers
+
+**Constants**
+
+``NVME_LOG_LID_SUPPORTED_LOG_PAGES``
+ Supported Log Pages
+
+``NVME_LOG_LID_ERROR``
+ Error Information
+
+``NVME_LOG_LID_SMART``
+ SMART / Health Information
+
+``NVME_LOG_LID_FW_SLOT``
+ Firmware Slot Information
+
+``NVME_LOG_LID_CHANGED_NS``
+ Changed Namespace List
+
+``NVME_LOG_LID_CMD_EFFECTS``
+ Commands Supported and Effects
+
+``NVME_LOG_LID_DEVICE_SELF_TEST``
+ Device Self-test
+
+``NVME_LOG_LID_TELEMETRY_HOST``
+ Telemetry Host-Initiated
+
+``NVME_LOG_LID_TELEMETRY_CTRL``
+ Telemetry Controller-Initiated
+
+``NVME_LOG_LID_ENDURANCE_GROUP``
+ Endurance Group Information
+
+``NVME_LOG_LID_PREDICTABLE_LAT_NVMSET``
+ Predictable Latency Per NVM Set
+
+``NVME_LOG_LID_PREDICTABLE_LAT_AGG``
+ Predictable Latency Event Aggregate
+
+``NVME_LOG_LID_ANA``
+ Asymmetric Namespace Access
+
+``NVME_LOG_LID_PERSISTENT_EVENT``
+ Persistent Event Log
+
+``NVME_LOG_LID_LBA_STATUS``
+ LBA Status Information
+
+``NVME_LOG_LID_ENDURANCE_GRP_EVT``
+ Endurance Group Event Aggregate
+
+``NVME_LOG_LID_MEDIA_UNIT_STATUS``
+ Media Unit Status
+
+``NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST``
+ Supported Capacity Configuration Lis
+
+``NVME_LOG_LID_FID_SUPPORTED_EFFECTS``
+ Feature Identifiers Supported and Effects
+
+``NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS``
+ NVMe-MI Commands Supported and Effects
+
+``NVME_LOG_LID_BOOT_PARTITION``
+ Boot Partition
+
+``NVME_LOG_LID_PHY_RX_EOM``
+ Physical Interface Receiver Eye Opening Measurement
+
+``NVME_LOG_LID_FDP_CONFIGS``
+ FDP Configurations
+
+``NVME_LOG_LID_FDP_RUH_USAGE``
+ Reclaim Unit Handle Usage
+
+``NVME_LOG_LID_FDP_STATS``
+ FDP Statistics
+
+``NVME_LOG_LID_FDP_EVENTS``
+ FDP Events
+
+``NVME_LOG_LID_DISCOVER``
+ Discovery
+
+``NVME_LOG_LID_RESERVATION``
+ Reservation Notification
+
+``NVME_LOG_LID_SANITIZE``
+ Sanitize Status
+
+``NVME_LOG_LID_ZNS_CHANGED_ZONES``
+ Changed Zone List
+
+
+
+
+.. c:enum:: nvme_features_id
+
+ Features - Feature Identifiers
+
+**Constants**
+
+``NVME_FEAT_FID_ARBITRATION``
+ Arbitration
+
+``NVME_FEAT_FID_POWER_MGMT``
+ Power Management
+
+``NVME_FEAT_FID_LBA_RANGE``
+ LBA Range Type
+
+``NVME_FEAT_FID_TEMP_THRESH``
+ Temperature Threshold
+
+``NVME_FEAT_FID_ERR_RECOVERY``
+ Error Recovery
+
+``NVME_FEAT_FID_VOLATILE_WC``
+ Volatile Write Cache
+
+``NVME_FEAT_FID_NUM_QUEUES``
+ Number of Queues
+
+``NVME_FEAT_FID_IRQ_COALESCE``
+ Interrupt Coalescing
+
+``NVME_FEAT_FID_IRQ_CONFIG``
+ Interrupt Vector Configuration
+
+``NVME_FEAT_FID_WRITE_ATOMIC``
+ Write Atomicity Normal
+
+``NVME_FEAT_FID_ASYNC_EVENT``
+ Asynchronous Event Configuration
+
+``NVME_FEAT_FID_AUTO_PST``
+ Autonomous Power State Transition
+
+``NVME_FEAT_FID_HOST_MEM_BUF``
+ Host Memory Buffer
+
+``NVME_FEAT_FID_TIMESTAMP``
+ Timestamp
+
+``NVME_FEAT_FID_KATO``
+ Keep Alive Timer
+
+``NVME_FEAT_FID_HCTM``
+ Host Controlled Thermal Management
+
+``NVME_FEAT_FID_NOPSC``
+ Non-Operational Power State Config
+
+``NVME_FEAT_FID_RRL``
+ Read Recovery Level Config
+
+``NVME_FEAT_FID_PLM_CONFIG``
+ Predictable Latency Mode Config
+
+``NVME_FEAT_FID_PLM_WINDOW``
+ Predictable Latency Mode Window
+
+``NVME_FEAT_FID_LBA_STS_INTERVAL``
+ LBA Status Information Report Interval
+
+``NVME_FEAT_FID_HOST_BEHAVIOR``
+ Host Behavior Support
+
+``NVME_FEAT_FID_SANITIZE``
+ Endurance Group Event Configuration
+
+``NVME_FEAT_FID_ENDURANCE_EVT_CFG``
+ Endurance Group Event Configuration
+
+``NVME_FEAT_FID_IOCS_PROFILE``
+ I/O Command Set Profile
+
+``NVME_FEAT_FID_SPINUP_CONTROL``
+ Spinup Control
+
+``NVME_FEAT_FID_FDP``
+ Flexible Data Placement
+
+``NVME_FEAT_FID_FDP_EVENTS``
+ FDP Events
+
+``NVME_FEAT_FID_ENH_CTRL_METADATA``
+ Enhanced Controller Metadata
+
+``NVME_FEAT_FID_CTRL_METADATA``
+ Controller Metadata
+
+``NVME_FEAT_FID_NS_METADATA``
+ Namespace Metadata
+
+``NVME_FEAT_FID_SW_PROGRESS``
+ Software Progress Marker
+
+``NVME_FEAT_FID_HOST_ID``
+ Host Identifier
+
+``NVME_FEAT_FID_RESV_MASK``
+ Reservation Notification Mask
+
+``NVME_FEAT_FID_RESV_PERSIST``
+ Reservation Persistence
+
+``NVME_FEAT_FID_WRITE_PROTECT``
+ Namespace Write Protection Config
+
+
+
+
+.. c:enum:: nvme_feat
+
+ Features Access Shifts/Masks values
+
+**Constants**
+
+``NVME_FEAT_ARBITRATION_BURST_SHIFT``
+
+``NVME_FEAT_ARBITRATION_BURST_MASK``
+
+``NVME_FEAT_ARBITRATION_LPW_SHIFT``
+
+``NVME_FEAT_ARBITRATION_LPW_MASK``
+
+``NVME_FEAT_ARBITRATION_MPW_SHIFT``
+
+``NVME_FEAT_ARBITRATION_MPW_MASK``
+
+``NVME_FEAT_ARBITRATION_HPW_SHIFT``
+
+``NVME_FEAT_ARBITRATION_HPW_MASK``
+
+``NVME_FEAT_PWRMGMT_PS_SHIFT``
+
+``NVME_FEAT_PWRMGMT_PS_MASK``
+
+``NVME_FEAT_PWRMGMT_WH_SHIFT``
+
+``NVME_FEAT_PWRMGMT_WH_MASK``
+
+``NVME_FEAT_LBAR_NR_SHIFT``
+
+``NVME_FEAT_LBAR_NR_MASK``
+
+``NVME_FEAT_TT_TMPTH_SHIFT``
+
+``NVME_FEAT_TT_TMPTH_MASK``
+
+``NVME_FEAT_TT_TMPSEL_SHIFT``
+
+``NVME_FEAT_TT_TMPSEL_MASK``
+
+``NVME_FEAT_TT_THSEL_SHIFT``
+
+``NVME_FEAT_TT_THSEL_MASK``
+
+``NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT``
+
+``NVME_FEAT_ERROR_RECOVERY_TLER_MASK``
+
+``NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT``
+
+``NVME_FEAT_ERROR_RECOVERY_DULBE_MASK``
+
+``NVME_FEAT_VWC_WCE_SHIFT``
+
+``NVME_FEAT_VWC_WCE_MASK``
+
+``NVME_FEAT_NRQS_NSQR_SHIFT``
+
+``NVME_FEAT_NRQS_NSQR_MASK``
+
+``NVME_FEAT_NRQS_NCQR_SHIFT``
+
+``NVME_FEAT_NRQS_NCQR_MASK``
+
+``NVME_FEAT_IRQC_THR_SHIFT``
+
+``NVME_FEAT_IRQC_THR_MASK``
+
+``NVME_FEAT_IRQC_TIME_SHIFT``
+
+``NVME_FEAT_IRQC_TIME_MASK``
+
+``NVME_FEAT_ICFG_IV_SHIFT``
+
+``NVME_FEAT_ICFG_IV_MASK``
+
+``NVME_FEAT_ICFG_CD_SHIFT``
+
+``NVME_FEAT_ICFG_CD_MASK``
+
+``NVME_FEAT_WA_DN_SHIFT``
+
+``NVME_FEAT_WA_DN_MASK``
+
+``NVME_FEAT_AE_SMART_SHIFT``
+
+``NVME_FEAT_AE_SMART_MASK``
+
+``NVME_FEAT_AE_NAN_SHIFT``
+
+``NVME_FEAT_AE_NAN_MASK``
+
+``NVME_FEAT_AE_FW_SHIFT``
+
+``NVME_FEAT_AE_FW_MASK``
+
+``NVME_FEAT_AE_TELEM_SHIFT``
+
+``NVME_FEAT_AE_TELEM_MASK``
+
+``NVME_FEAT_AE_ANA_SHIFT``
+
+``NVME_FEAT_AE_ANA_MASK``
+
+``NVME_FEAT_AE_PLA_SHIFT``
+
+``NVME_FEAT_AE_PLA_MASK``
+
+``NVME_FEAT_AE_LBAS_SHIFT``
+
+``NVME_FEAT_AE_LBAS_MASK``
+
+``NVME_FEAT_AE_EGA_SHIFT``
+
+``NVME_FEAT_AE_EGA_MASK``
+
+``NVME_FEAT_APST_APSTE_SHIFT``
+
+``NVME_FEAT_APST_APSTE_MASK``
+
+``NVME_FEAT_HMEM_EHM_SHIFT``
+
+``NVME_FEAT_HMEM_EHM_MASK``
+
+``NVME_FEAT_HCTM_TMT2_SHIFT``
+
+``NVME_FEAT_HCTM_TMT2_MASK``
+
+``NVME_FEAT_HCTM_TMT1_SHIFT``
+
+``NVME_FEAT_HCTM_TMT1_MASK``
+
+``NVME_FEAT_NOPS_NOPPME_SHIFT``
+
+``NVME_FEAT_NOPS_NOPPME_MASK``
+
+``NVME_FEAT_RRL_RRL_SHIFT``
+
+``NVME_FEAT_RRL_RRL_MASK``
+
+``NVME_FEAT_PLM_PLME_SHIFT``
+
+``NVME_FEAT_PLM_PLME_MASK``
+
+``NVME_FEAT_PLMW_WS_SHIFT``
+
+``NVME_FEAT_PLMW_WS_MASK``
+
+``NVME_FEAT_LBAS_LSIRI_SHIFT``
+
+``NVME_FEAT_LBAS_LSIRI_MASK``
+
+``NVME_FEAT_LBAS_LSIPI_SHIFT``
+
+``NVME_FEAT_LBAS_LSIPI_MASK``
+
+``NVME_FEAT_SC_NODRM_SHIFT``
+
+``NVME_FEAT_SC_NODRM_MASK``
+
+``NVME_FEAT_EG_ENDGID_SHIFT``
+
+``NVME_FEAT_EG_ENDGID_MASK``
+
+``NVME_FEAT_EG_EGCW_SHIFT``
+
+``NVME_FEAT_EG_EGCW_MASK``
+
+``NVME_FEAT_SPM_PBSLC_SHIFT``
+
+``NVME_FEAT_SPM_PBSLC_MASK``
+
+``NVME_FEAT_HOSTID_EXHID_SHIFT``
+
+``NVME_FEAT_HOSTID_EXHID_MASK``
+
+``NVME_FEAT_RM_REGPRE_SHIFT``
+
+``NVME_FEAT_RM_REGPRE_MASK``
+
+``NVME_FEAT_RM_RESREL_SHIFT``
+
+``NVME_FEAT_RM_RESREL_MASK``
+
+``NVME_FEAT_RM_RESPRE_SHIFT``
+
+``NVME_FEAT_RM_RESPRE_MASK``
+
+``NVME_FEAT_RP_PTPL_SHIFT``
+
+``NVME_FEAT_RP_PTPL_MASK``
+
+``NVME_FEAT_WP_WPS_SHIFT``
+
+``NVME_FEAT_WP_WPS_MASK``
+
+``NVME_FEAT_IOCSP_IOCSCI_SHIFT``
+
+``NVME_FEAT_IOCSP_IOCSCI_MASK``
+
+``NVME_FEAT_FDP_ENABLED_SHIFT``
+
+``NVME_FEAT_FDP_ENABLED_MASK``
+
+``NVME_FEAT_FDP_INDEX_SHIFT``
+
+``NVME_FEAT_FDP_INDEX_MASK``
+
+``NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT``
+
+``NVME_FEAT_FDP_EVENTS_ENABLE_MASK``
+
+
+
+
+.. c:enum:: nvme_get_features_sel
+
+ Get Features - Select
+
+**Constants**
+
+``NVME_GET_FEATURES_SEL_CURRENT``
+ Current value
+
+``NVME_GET_FEATURES_SEL_DEFAULT``
+ Default value
+
+``NVME_GET_FEATURES_SEL_SAVED``
+ Saved value
+
+``NVME_GET_FEATURES_SEL_SUPPORTED``
+ Supported capabilities
+
+
+
+
+.. c:enum:: nvme_cmd_format_mset
+
+ Format NVM - Metadata Settings
+
+**Constants**
+
+``NVME_FORMAT_MSET_SEPARATE``
+ indicates that the metadata is transferred
+ as part of a separate buffer.
+
+``NVME_FORMAT_MSET_EXTENDED``
+ indicates that the metadata is transferred
+ as part of an extended data LBA.
+
+
+
+
+.. c:enum:: nvme_cmd_format_pi
+
+ Format NVM - Protection Information
+
+**Constants**
+
+``NVME_FORMAT_PI_DISABLE``
+ Protection information is not enabled.
+
+``NVME_FORMAT_PI_TYPE1``
+ Protection information is enabled, Type 1.
+
+``NVME_FORMAT_PI_TYPE2``
+ Protection information is enabled, Type 2.
+
+``NVME_FORMAT_PI_TYPE3``
+ Protection information is enabled, Type 3.
+
+
+
+
+.. c:enum:: nvme_cmd_format_pil
+
+ Format NVM - Protection Information Location
+
+**Constants**
+
+``NVME_FORMAT_PIL_LAST``
+ Protection information is transferred as the last
+ bytes of metadata.
+
+``NVME_FORMAT_PIL_FIRST``
+ Protection information is transferred as the first
+ bytes of metadata.
+
+
+
+
+.. c:enum:: nvme_cmd_format_ses
+
+ Format NVM - Secure Erase Settings
+
+**Constants**
+
+``NVME_FORMAT_SES_NONE``
+ No secure erase operation requested.
+
+``NVME_FORMAT_SES_USER_DATA_ERASE``
+ User Data Erase: All user data shall be erased,
+ contents of the user data after the erase is
+ indeterminate (e.g. the user data may be zero
+ filled, one filled, etc.). If a User Data Erase
+ is requested and all affected user data is
+ encrypted, then the controller is allowed
+ to use a cryptographic erase to perform
+ the requested User Data Erase.
+
+``NVME_FORMAT_SES_CRYPTO_ERASE``
+ Cryptographic Erase: All user data shall
+ be erased cryptographically. This is
+ accomplished by deleting the encryption key.
+
+
+
+
+.. c:enum:: nvme_ns_mgmt_sel
+
+ Namespace Management - Select
+
+**Constants**
+
+``NVME_NS_MGMT_SEL_CREATE``
+ Namespace Create selection
+
+``NVME_NS_MGMT_SEL_DELETE``
+ Namespace Delete selection
+
+
+
+
+.. c:enum:: nvme_ns_attach_sel
+
+ Namespace Attachment - Select
+
+**Constants**
+
+``NVME_NS_ATTACH_SEL_CTRL_ATTACH``
+ Namespace attach selection
+
+``NVME_NS_ATTACH_SEL_CTRL_DEATTACH``
+ Namespace detach selection
+
+
+
+
+.. c:enum:: nvme_fw_commit_ca
+
+ Firmware Commit - Commit Action
+
+**Constants**
+
+``NVME_FW_COMMIT_CA_REPLACE``
+ Downloaded image replaces the existing
+ image, if any, in the specified Firmware
+ Slot. The newly placed image is not
+ activated.
+
+``NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE``
+ Downloaded image replaces the existing
+ image, if any, in the specified Firmware
+ Slot. The newly placed image is activated
+ at the next Controller Level Reset.
+
+``NVME_FW_COMMIT_CA_SET_ACTIVE``
+ The existing image in the specified
+ Firmware Slot is activated at the
+ next Controller Level Reset.
+
+``NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE``
+ Downloaded image replaces the existing
+ image, if any, in the specified Firmware
+ Slot and is then activated immediately.
+ If there is not a newly downloaded image,
+ then the existing image in the specified
+ firmware slot is activated immediately.
+
+``NVME_FW_COMMIT_CA_REPLACE_BOOT_PARTITION``
+ Downloaded image replaces the Boot
+ Partition specified by the Boot
+ Partition ID field.
+
+``NVME_FW_COMMIT_CA_ACTIVATE_BOOT_PARTITION``
+ Mark the Boot Partition specified in
+ the BPID field as active and update
+ BPINFO.ABPID.
+
+
+
+
+.. c:enum:: nvme_directive_dtype
+
+ Directive Types
+
+**Constants**
+
+``NVME_DIRECTIVE_DTYPE_IDENTIFY``
+ Identify directive type
+
+``NVME_DIRECTIVE_DTYPE_STREAMS``
+ Streams directive type
+
+
+
+
+.. c:enum:: nvme_directive_receive_doper
+
+ Directive Receive Directive Operation
+
+**Constants**
+
+``NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM``
+
+``NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM``
+
+``NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS``
+
+``NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE``
+
+
+
+
+.. c:enum:: nvme_directive_send_doper
+
+ Directive Send Directive Operation
+
+**Constants**
+
+``NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR``
+
+``NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER``
+
+``NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE``
+
+
+
+
+.. c:enum:: nvme_directive_send_identify_endir
+
+ Enable Directive
+
+**Constants**
+
+``NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_DISABLE``
+
+``NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_ENABLE``
+
+
+
+
+.. c:enum:: nvme_sanitize_sanact
+
+ Sanitize Action
+
+**Constants**
+
+``NVME_SANITIZE_SANACT_EXIT_FAILURE``
+ Exit Failure Mode.
+
+``NVME_SANITIZE_SANACT_START_BLOCK_ERASE``
+ Start a Block Erase sanitize operation.
+
+``NVME_SANITIZE_SANACT_START_OVERWRITE``
+ Start an Overwrite sanitize operation.
+
+``NVME_SANITIZE_SANACT_START_CRYPTO_ERASE``
+ Start a Crypto Erase sanitize operation.
+
+
+
+
+.. c:enum:: nvme_dst_stc
+
+ Action taken by the Device Self-test command
+
+**Constants**
+
+``NVME_DST_STC_SHORT``
+ Start a short device self-test operation
+
+``NVME_DST_STC_LONG``
+ Start an extended device self-test operation
+
+``NVME_DST_STC_VS``
+ Start a vendor specific device self-test operation
+
+``NVME_DST_STC_ABORT``
+ Abort device self-test operation
+
+
+
+
+.. c:enum:: nvme_virt_mgmt_act
+
+ Virtualization Management - Action
+
+**Constants**
+
+``NVME_VIRT_MGMT_ACT_PRIM_CTRL_FLEX_ALLOC``
+ Primary Controller Flexible
+ Allocation
+
+``NVME_VIRT_MGMT_ACT_OFFLINE_SEC_CTRL``
+ Secondary Controller Offline
+
+``NVME_VIRT_MGMT_ACT_ASSIGN_SEC_CTRL``
+ Secondary Controller Assign
+
+``NVME_VIRT_MGMT_ACT_ONLINE_SEC_CTRL``
+ Secondary Controller Online
+
+
+
+
+.. c:enum:: nvme_virt_mgmt_rt
+
+ Virtualization Management - Resource Type
+
+**Constants**
+
+``NVME_VIRT_MGMT_RT_VQ_RESOURCE``
+ VQ Resources
+
+``NVME_VIRT_MGMT_RT_VI_RESOURCE``
+ VI Resources
+
+
+
+
+.. c:enum:: nvme_ns_write_protect_cfg
+
+ Write Protection - Write Protection State
+
+**Constants**
+
+``NVME_NS_WP_CFG_NONE``
+ No Write Protect
+
+``NVME_NS_WP_CFG_PROTECT``
+ Write Protect
+
+``NVME_NS_WP_CFG_PROTECT_POWER_CYCLE``
+ Write Protect Until Power Cycle
+
+``NVME_NS_WP_CFG_PROTECT_PERMANENT``
+ Permanent Write Protect
+
+
+
+
+.. c:enum:: nvme_log_ana_lsp
+
+ Asymmetric Namespace Access - Return Groups Only
+
+**Constants**
+
+``NVME_LOG_ANA_LSP_RGO_NAMESPACES``
+
+``NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY``
+
+
+
+
+.. c:enum:: nvme_log_phy_rx_eom_action
+
+ Physical Interface Receiver Eye Opening Measurement Action
+
+**Constants**
+
+``NVME_LOG_PHY_RX_EOM_READ``
+ Read Log Data
+
+``NVME_LOG_PHY_RX_EOM_START_READ``
+ Start Measurement and Read Log Data
+
+``NVME_LOG_PHY_RX_EOM_ABORT_CLEAR``
+ Abort Measurement and Clear Log Data
+
+
+
+
+.. c:enum:: nvme_log_phy_rx_eom_quality
+
+ Physical Interface Receiver Eye Opening Measurement Quality
+
+**Constants**
+
+``NVME_LOG_PHY_RX_EOM_GOOD``
+ <= Better Quality
+
+``NVME_LOG_PHY_RX_EOM_BETTER``
+ <= Best Quality, >= Good Quality
+
+``NVME_LOG_PHY_RX_EOM_BEST``
+ >= Better Quality
+
+
+
+
+.. c:enum:: nvme_pevent_log_action
+
+ Persistent Event Log - Action
+
+**Constants**
+
+``NVME_PEVENT_LOG_READ``
+ Read Log Data
+
+``NVME_PEVENT_LOG_EST_CTX_AND_READ``
+ Establish Context and Read Log Data
+
+``NVME_PEVENT_LOG_RELEASE_CTX``
+ Release Context
+
+
+
+
+.. c:enum:: nvme_feat_tmpthresh_thsel
+
+ Temperature Threshold - Threshold Type Select
+
+**Constants**
+
+``NVME_FEATURE_TEMPTHRESH_THSEL_OVER``
+ Over temperature threshold select
+
+``NVME_FEATURE_TEMPTHRESH_THSEL_UNDER``
+ Under temperature threshold select
+
+
+
+
+.. c:enum:: nvme_features_async_event_config_flags
+
+ Asynchronous Event Configuration configuration flags
+
+**Constants**
+
+``NVME_FEATURE_AENCFG_SMART_CRIT_SPARE``
+
+``NVME_FEATURE_AENCFG_SMART_CRIT_TEMPERATURE``
+
+``NVME_FEATURE_AENCFG_SMART_CRIT_DEGRADED``
+
+``NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY``
+
+``NVME_FEATURE_AENCFG_SMART_CRIT_VOLATILE_BACKUP``
+
+``NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY_PMR``
+
+``NVME_FEATURE_AENCFG_NOTICE_NAMESPACE_ATTRIBUTES``
+
+``NVME_FEATURE_AENCFG_NOTICE_FIRMWARE_ACTIVATION``
+
+``NVME_FEATURE_AENCFG_NOTICE_TELEMETRY_LOG``
+
+``NVME_FEATURE_AENCFG_NOTICE_ANA_CHANGE``
+
+``NVME_FEATURE_AENCFG_NOTICE_PL_EVENT``
+
+``NVME_FEATURE_AENCFG_NOTICE_LBA_STATUS``
+
+``NVME_FEATURE_AENCFG_NOTICE_EG_EVENT``
+
+``NVME_FEATURE_AENCFG_NOTICE_DISCOVERY_CHANGE``
+
+
+
+
+.. c:enum:: nvme_feat_plm_window_select
+
+ Predictable Latency Per NVM Set Log
+
+**Constants**
+
+``NVME_FEATURE_PLM_DTWIN``
+ Deterministic Window select
+
+``NVME_FEATURE_PLM_NDWIN``
+ Non-Deterministic Window select
+
+
+
+
+.. c:enum:: nvme_feat_resv_notify_flags
+
+ Reservation Notification Configuration
+
+**Constants**
+
+``NVME_FEAT_RESV_NOTIFY_REGPRE``
+ Mask Registration Preempted Notification
+
+``NVME_FEAT_RESV_NOTIFY_RESREL``
+ Mask Reservation Released Notification
+
+``NVME_FEAT_RESV_NOTIFY_RESPRE``
+ Mask Reservation Preempted Notification
+
+
+
+
+.. c:enum:: nvme_feat_nswpcfg_state
+
+ Write Protection - Write Protection State
+
+**Constants**
+
+``NVME_FEAT_NS_NO_WRITE_PROTECT``
+ No Write Protect
+
+``NVME_FEAT_NS_WRITE_PROTECT``
+ Write Protect
+
+``NVME_FEAT_NS_WRITE_PROTECT_PWR_CYCLE``
+ Write Protect Until Power Cycle
+
+``NVME_FEAT_NS_WRITE_PROTECT_PERMANENT``
+ Permanent Write Protect
+
+
+
+
+.. c:enum:: nvme_fctype
+
+ Fabrics Command Types
+
+**Constants**
+
+``nvme_fabrics_type_property_set``
+ Property set
+
+``nvme_fabrics_type_connect``
+ Connect
+
+``nvme_fabrics_type_property_get``
+ Property Get
+
+``nvme_fabrics_type_auth_send``
+ Authentication Send
+
+``nvme_fabrics_type_auth_receive``
+ Authentication Receive
+
+``nvme_fabrics_type_disconnect``
+ Disconnect
+
+
+
+
+.. c:enum:: nvme_data_tfr
+
+ Data transfer direction of the command
+
+**Constants**
+
+``NVME_DATA_TFR_NO_DATA_TFR``
+ No data transfer
+
+``NVME_DATA_TFR_HOST_TO_CTRL``
+ Host to controller
+
+``NVME_DATA_TFR_CTRL_TO_HOST``
+ Controller to host
+
+``NVME_DATA_TFR_BIDIRECTIONAL``
+ Bidirectional
+
+
+
+
+.. c:enum:: nvme_io_opcode
+
+ Opcodes for I/O Commands
+
+**Constants**
+
+``nvme_cmd_flush``
+ Flush
+
+``nvme_cmd_write``
+ Write
+
+``nvme_cmd_read``
+ Read
+
+``nvme_cmd_write_uncor``
+ Write Uncorrectable
+
+``nvme_cmd_compare``
+ Compare
+
+``nvme_cmd_write_zeroes``
+ write Zeros
+
+``nvme_cmd_dsm``
+ Dataset Management
+
+``nvme_cmd_verify``
+ Verify
+
+``nvme_cmd_resv_register``
+ Reservation Register
+
+``nvme_cmd_resv_report``
+ Reservation Report
+
+``nvme_cmd_resv_acquire``
+ Reservation Acquire
+
+``nvme_cmd_io_mgmt_recv``
+ I/O Management Receive
+
+``nvme_cmd_resv_release``
+ Reservation Release
+
+``nvme_cmd_copy``
+ Copy
+
+``nvme_cmd_io_mgmt_send``
+ I/O Management Send
+
+``nvme_zns_cmd_mgmt_send``
+ Zone Management Send
+
+``nvme_zns_cmd_mgmt_recv``
+ Zone Management Receive
+
+``nvme_zns_cmd_append``
+ Zone Append
+
+
+
+
+.. c:enum:: nvme_io_control_flags
+
+ I/O control flags
+
+**Constants**
+
+``NVME_IO_DTYPE_STREAMS``
+ Directive Type Streams
+
+``NVME_IO_STC``
+ Storage Tag Check
+
+``NVME_IO_DEAC``
+ Deallocate
+
+``NVME_IO_ZNS_APPEND_PIREMAP``
+ Protection Information Remap
+
+``NVME_IO_PRINFO_PRCHK_REF``
+ Protection Information Check Reference Tag
+
+``NVME_IO_PRINFO_PRCHK_APP``
+ Protection Information Check Application Tag
+
+``NVME_IO_PRINFO_PRCHK_GUARD``
+ Protection Information Check Guard field
+
+``NVME_IO_PRINFO_PRACT``
+ Protection Information Action
+
+``NVME_IO_FUA``
+ Force Unit Access
+
+``NVME_IO_LR``
+ Limited Retry
+
+
+
+
+.. c:enum:: nvme_io_dsm_flags
+
+ Dataset Management flags
+
+**Constants**
+
+``NVME_IO_DSM_FREQ_UNSPEC``
+ No frequency information provided
+
+``NVME_IO_DSM_FREQ_TYPICAL``
+ Typical number of reads and writes
+ expected for this LBA range
+
+``NVME_IO_DSM_FREQ_RARE``
+ Infrequent writes and infrequent
+ reads to the LBA range indicated
+
+``NVME_IO_DSM_FREQ_READS``
+ Infrequent writes and frequent
+ reads to the LBA range indicated
+
+``NVME_IO_DSM_FREQ_WRITES``
+ Frequent writes and infrequent
+ reads to the LBA range indicated
+
+``NVME_IO_DSM_FREQ_RW``
+ Frequent writes and frequent reads
+ to the LBA range indicated
+
+``NVME_IO_DSM_FREQ_ONCE``
+
+``NVME_IO_DSM_FREQ_PREFETCH``
+
+``NVME_IO_DSM_FREQ_TEMP``
+
+``NVME_IO_DSM_LATENCY_NONE``
+ No latency information provided
+
+``NVME_IO_DSM_LATENCY_IDLE``
+ Longer latency acceptable
+
+``NVME_IO_DSM_LATENCY_NORM``
+ Typical latency
+
+``NVME_IO_DSM_LATENCY_LOW``
+ Smallest possible latency
+
+``NVME_IO_DSM_SEQ_REQ``
+
+``NVME_IO_DSM_COMPRESSED``
+
+
+
+
+.. c:enum:: nvme_dsm_attributes
+
+ Dataset Management attributes
+
+**Constants**
+
+``NVME_DSMGMT_IDR``
+ Attribute -Integral Dataset for Read
+
+``NVME_DSMGMT_IDW``
+ Attribute - Integral Dataset for Write
+
+``NVME_DSMGMT_AD``
+ Attribute - Deallocate
+
+
+
+
+.. c:enum:: nvme_resv_rtype
+
+ Reservation Type Encoding
+
+**Constants**
+
+``NVME_RESERVATION_RTYPE_WE``
+ Write Exclusive Reservation
+
+``NVME_RESERVATION_RTYPE_EA``
+ Exclusive Access Reservation
+
+``NVME_RESERVATION_RTYPE_WERO``
+ Write Exclusive - Registrants Only Reservation
+
+``NVME_RESERVATION_RTYPE_EARO``
+ Exclusive Access - Registrants Only Reservation
+
+``NVME_RESERVATION_RTYPE_WEAR``
+ Write Exclusive - All Registrants Reservation
+
+``NVME_RESERVATION_RTYPE_EAAR``
+ Exclusive Access - All Registrants Reservation
+
+
+
+
+.. c:enum:: nvme_resv_racqa
+
+ Reservation Acquire - Reservation Acquire Action
+
+**Constants**
+
+``NVME_RESERVATION_RACQA_ACQUIRE``
+ Acquire
+
+``NVME_RESERVATION_RACQA_PREEMPT``
+ Preempt
+
+``NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT``
+ Preempt and Abort
+
+
+
+
+.. c:enum:: nvme_resv_rrega
+
+ Reservation Register - Reservation Register Action
+
+**Constants**
+
+``NVME_RESERVATION_RREGA_REGISTER_KEY``
+ Register Reservation Key
+
+``NVME_RESERVATION_RREGA_UNREGISTER_KEY``
+ Unregister Reservation Key
+
+``NVME_RESERVATION_RREGA_REPLACE_KEY``
+ Replace Reservation Key
+
+
+
+
+.. c:enum:: nvme_resv_cptpl
+
+ Reservation Register - Change Persist Through Power Loss State
+
+**Constants**
+
+``NVME_RESERVATION_CPTPL_NO_CHANGE``
+ No change to PTPL state
+
+``NVME_RESERVATION_CPTPL_CLEAR``
+ Reservations are released and
+ registrants are cleared on a power on
+
+``NVME_RESERVATION_CPTPL_PERSIST``
+ Reservations and registrants persist
+ across a power loss
+
+
+
+
+.. c:enum:: nvme_resv_rrela
+
+ Reservation Release - Reservation Release Action
+
+**Constants**
+
+``NVME_RESERVATION_RRELA_RELEASE``
+ Release
+
+``NVME_RESERVATION_RRELA_CLEAR``
+ Clear
+
+
+
+
+.. c:enum:: nvme_zns_send_action
+
+ Zone Management Send - Zone Send Action
+
+**Constants**
+
+``NVME_ZNS_ZSA_CLOSE``
+ Close Zone
+
+``NVME_ZNS_ZSA_FINISH``
+ Finish Zone
+
+``NVME_ZNS_ZSA_OPEN``
+ Open Zone
+
+``NVME_ZNS_ZSA_RESET``
+ Reset Zone
+
+``NVME_ZNS_ZSA_OFFLINE``
+ Offline Zone
+
+``NVME_ZNS_ZSA_SET_DESC_EXT``
+ Set Zone Descriptor Extension
+
+``NVME_ZNS_ZSA_ZRWA_FLUSH``
+ Flush
+
+
+
+
+.. c:enum:: nvme_zns_recv_action
+
+ Zone Management Receive - Zone Receive Action Specific Features
+
+**Constants**
+
+``NVME_ZNS_ZRA_REPORT_ZONES``
+ Report Zones
+
+``NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES``
+ Extended Report Zones
+
+
+
+
+.. c:enum:: nvme_zns_report_options
+
+ Zone Management Receive - Zone Receive Action Specific Field
+
+**Constants**
+
+``NVME_ZNS_ZRAS_REPORT_ALL``
+ List all zones
+
+``NVME_ZNS_ZRAS_REPORT_EMPTY``
+ List the zones in the ZSE:Empty state
+
+``NVME_ZNS_ZRAS_REPORT_IMPL_OPENED``
+ List the zones in the ZSIO:Implicitly Opened state
+
+``NVME_ZNS_ZRAS_REPORT_EXPL_OPENED``
+ List the zones in the ZSEO:Explicitly Opened state
+
+``NVME_ZNS_ZRAS_REPORT_CLOSED``
+ List the zones in the ZSC:Closed state
+
+``NVME_ZNS_ZRAS_REPORT_FULL``
+ List the zones in the ZSF:Full state
+
+``NVME_ZNS_ZRAS_REPORT_READ_ONLY``
+ List the zones in the ZSRO:Read Only state
+
+``NVME_ZNS_ZRAS_REPORT_OFFLINE``
+ List the zones in the ZSO:Offline state
+
+
+
+
+.. c:enum:: nvme_io_mgmt_recv_mo
+
+ I/O Management Receive - Management Operation
+
+**Constants**
+
+``NVME_IO_MGMT_RECV_RUH_STATUS``
+ Reclaim Unit Handle Status
+
+
+
+
+.. c:enum:: nvme_io_mgmt_send_mo
+
+ I/O Management Send - Management Operation
+
+**Constants**
+
+``NVME_IO_MGMT_SEND_RUH_UPDATE``
+ Reclaim Unit Handle Update
+
+
+
+
+.. c:struct:: nvme_ns_mgmt_host_sw_specified
+
+ Namespace management Host Software Specified Fields.
+
+**Definition**
+
+::
+
+ struct nvme_ns_mgmt_host_sw_specified {
+ __le64 nsze;
+ __le64 ncap;
+ __u8 rsvd16[10];
+ __u8 flbas;
+ __u8 rsvd27[2];
+ __u8 dps;
+ __u8 nmic;
+ __u8 rsvd31[61];
+ __le32 anagrpid;
+ __u8 rsvd96[4];
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 rsvd104[280];
+ __le64 lbstm;
+ __le16 nphndls;
+ __u8 rsvd394[105];
+ union {
+ __u8 rsvd499[13];
+ struct {
+ __u8 znsco;
+ __le32 rar;
+ __le32 ror;
+ __le32 rnumzrwa;
+ } zns;
+ };
+ __le16 phndl[128];
+ __u8 rsvd768[3328];
+ };
+
+**Members**
+
+``nsze``
+ Namespace Size indicates the total size of the namespace in
+ logical blocks. The number of logical blocks is based on the
+ formatted LBA size.
+
+``ncap``
+ Namespace Capacity indicates the maximum number of logical blocks
+ that may be allocated in the namespace at any point in time. The
+ number of logical blocks is based on the formatted LBA size.
+
+``rsvd16``
+ Reserved
+
+``flbas``
+ Formatted LBA Size, see :c:type:`enum nvme_id_ns_flbas <nvme_id_ns_flbas>`.
+
+``rsvd27``
+ Reserved
+
+``dps``
+ End-to-end Data Protection Type Settings, see
+ :c:type:`enum nvme_id_ns_dps <nvme_id_ns_dps>`.
+
+``nmic``
+ Namespace Multi-path I/O and Namespace Sharing Capabilities, see
+ :c:type:`enum nvme_id_ns_nmic <nvme_id_ns_nmic>`.
+
+``rsvd31``
+ Reserved
+
+``anagrpid``
+ ANA Group Identifier indicates the ANA Group Identifier of the
+ ANA group of which the namespace is a member.
+
+``rsvd96``
+ Reserved
+
+``nvmsetid``
+ NVM Set Identifier indicates the NVM Set with which this
+ namespace is associated.
+
+``endgid``
+ Endurance Group Identifier indicates the Endurance Group with
+ which this namespace is associated.
+
+``rsvd104``
+ Reserved
+
+``lbstm``
+ Logical Block Storage Tag Mask Identifies the mask for the
+ Storage Tag field for the protection information
+
+``nphndls``
+ Number of Placement Handles specifies the number of Placement
+ Handles included in the Placement Handle List
+
+``rsvd394``
+ Reserved
+
+``{unnamed_union}``
+ anonymous
+
+``rsvd499``
+ Reserved for I/O Command Sets that extend this specification.
+
+``zns``
+ rsvd499( Zoned Namespace Command Set specific field )
+
+``phndl``
+ Placement Handle Associated RUH : This field specifies the Reclaim
+ Unit Handle Identifier to be associated with the Placement Handle
+ value. If the Flexible Data Placement capability is not supported or
+ not enabled in specified Endurance Group, then the controller shall
+ ignore this field.
+
+``rsvd768``
+ Reserved
+
+
+
diff --git a/doc/rst/util.rst b/doc/rst/util.rst
new file mode 100644
index 0000000..56df0f6
--- /dev/null
+++ b/doc/rst/util.rst
@@ -0,0 +1,734 @@
+.. _util.h:
+
+**util.h**
+
+
+libnvme utility functions
+
+
+
+.. c:enum:: nvme_connect_err
+
+ nvme connect error codes
+
+**Constants**
+
+``ENVME_CONNECT_RESOLVE``
+ failed to resolve host
+
+``ENVME_CONNECT_ADDRFAM``
+ unrecognized address family
+
+``ENVME_CONNECT_TRADDR``
+ failed to get traddr
+
+``ENVME_CONNECT_TARG``
+ need a transport (-t) argument
+
+``ENVME_CONNECT_AARG``
+ need a address (-a) argument
+
+``ENVME_CONNECT_OPEN``
+ failed to open nvme-fabrics device
+
+``ENVME_CONNECT_WRITE``
+ failed to write to nvme-fabrics device
+
+``ENVME_CONNECT_READ``
+ failed to read from nvme-fabrics device
+
+``ENVME_CONNECT_PARSE``
+ failed to parse ctrl info
+
+``ENVME_CONNECT_INVAL_TR``
+ invalid transport type
+
+``ENVME_CONNECT_LOOKUP_SUBSYS_NAME``
+ failed to lookup subsystem name
+
+``ENVME_CONNECT_LOOKUP_SUBSYS``
+ failed to lookup subsystem
+
+``ENVME_CONNECT_ALREADY``
+ the connect attempt failed, already connected
+
+``ENVME_CONNECT_INVAL``
+ invalid arguments/configuration
+
+``ENVME_CONNECT_ADDRINUSE``
+ hostnqn already in use
+
+``ENVME_CONNECT_NODEV``
+ invalid interface
+
+``ENVME_CONNECT_OPNOTSUPP``
+ not supported
+
+``ENVME_CONNECT_CONNREFUSED``
+ connection refused
+
+``ENVME_CONNECT_ADDRNOTAVAIL``
+ cannot assign requested address
+
+``ENVME_CONNECT_IGNORED``
+ connect attempt is ignored due to configuration
+
+
+.. c:function:: __u8 nvme_status_to_errno (int status, bool fabrics)
+
+ Converts nvme return status to errno
+
+**Parameters**
+
+``int status``
+ Return status from an nvme passthrough command
+
+``bool fabrics``
+ Set to true if :c:type:`status` is to a fabrics target.
+
+**Return**
+
+An errno representing the nvme status if it is an nvme status field,
+or unchanged status is < 0 since errno is already set.
+
+
+.. c:function:: const char * nvme_status_to_string (int status, bool fabrics)
+
+ Returns string describing nvme return status.
+
+**Parameters**
+
+``int status``
+ Return status from an nvme passthrough command
+
+``bool fabrics``
+ Set to true if :c:type:`status` is to a fabrics target.
+
+**Return**
+
+String representation of the nvme status if it is an nvme status field,
+or a standard errno string if status is < 0.
+
+
+.. c:function:: const char * nvme_errno_to_string (int err)
+
+ Returns string describing nvme connect failures
+
+**Parameters**
+
+``int err``
+ Returned error code from nvme_add_ctrl()
+
+**Return**
+
+String representation of the nvme connect error codes
+
+
+.. c:function:: void nvme_init_ctrl_list (struct nvme_ctrl_list *cntlist, __u16 num_ctrls, __u16 *ctrlist)
+
+ Initialize an nvme_ctrl_list structure from an array.
+
+**Parameters**
+
+``struct nvme_ctrl_list *cntlist``
+ The controller list structure to initialize
+
+``__u16 num_ctrls``
+ The number of controllers in the array, :c:type:`ctrlist`.
+
+``__u16 *ctrlist``
+ An array of controller identifiers in CPU native endian.
+
+**Description**
+
+This is intended to be used with any command that takes a controller list
+argument. See nvme_ns_attach_ctrls() and nvme_ns_detach().
+
+
+.. c:function:: void nvme_init_dsm_range (struct nvme_dsm_range *dsm, __u32 *ctx_attrs, __u32 *llbas, __u64 *slbas, __u16 nr_ranges)
+
+ Constructs a data set range structure
+
+**Parameters**
+
+``struct nvme_dsm_range *dsm``
+ DSM range array
+
+``__u32 *ctx_attrs``
+ Array of context attributes
+
+``__u32 *llbas``
+ Array of length in logical blocks
+
+``__u64 *slbas``
+ Array of starting logical blocks
+
+``__u16 nr_ranges``
+ The size of the dsm arrays
+
+**Description**
+
+Each array must be the same size of size 'nr_ranges'. This is intended to be
+used with constructing a payload for nvme_dsm().
+
+**Return**
+
+The nvme command status if a response was received or -errno
+otherwise.
+
+
+.. c:function:: void nvme_init_copy_range (struct nvme_copy_range *copy, __u16 *nlbs, __u64 *slbas, __u32 *eilbrts, __u32 *elbatms, __u32 *elbats, __u16 nr)
+
+ Constructs a copy range structure
+
+**Parameters**
+
+``struct nvme_copy_range *copy``
+ Copy range array
+
+``__u16 *nlbs``
+ Number of logical blocks
+
+``__u64 *slbas``
+ Starting LBA
+
+``__u32 *eilbrts``
+ Expected initial logical block reference tag
+
+``__u32 *elbatms``
+ Expected logical block application tag mask
+
+``__u32 *elbats``
+ Expected logical block application tag
+
+``__u16 nr``
+ Number of descriptors to construct
+
+
+.. c:function:: void nvme_init_copy_range_f1 (struct nvme_copy_range_f1 *copy, __u16 *nlbs, __u64 *slbas, __u64 *eilbrts, __u32 *elbatms, __u32 *elbats, __u16 nr)
+
+ Constructs a copy range f1 structure
+
+**Parameters**
+
+``struct nvme_copy_range_f1 *copy``
+ Copy range array
+
+``__u16 *nlbs``
+ Number of logical blocks
+
+``__u64 *slbas``
+ Starting LBA
+
+``__u64 *eilbrts``
+ Expected initial logical block reference tag
+
+``__u32 *elbatms``
+ Expected logical block application tag mask
+
+``__u32 *elbats``
+ Expected logical block application tag
+
+``__u16 nr``
+ Number of descriptors to construct
+
+
+.. c:function:: void nvme_init_copy_range_f2 (struct nvme_copy_range_f2 *copy, __u32 *snsids, __u16 *nlbs, __u64 *slbas, __u16 *sopts, __u32 *eilbrts, __u32 *elbatms, __u32 *elbats, __u16 nr)
+
+ Constructs a copy range f2 structure
+
+**Parameters**
+
+``struct nvme_copy_range_f2 *copy``
+ Copy range array
+
+``__u32 *snsids``
+ Source namespace identifier
+
+``__u16 *nlbs``
+ Number of logical blocks
+
+``__u64 *slbas``
+ Starting LBA
+
+``__u16 *sopts``
+ Source options
+
+``__u32 *eilbrts``
+ Expected initial logical block reference tag
+
+``__u32 *elbatms``
+ Expected logical block application tag mask
+
+``__u32 *elbats``
+ Expected logical block application tag
+
+``__u16 nr``
+ Number of descriptors to construct
+
+
+.. c:function:: void nvme_init_copy_range_f3 (struct nvme_copy_range_f3 *copy, __u32 *snsids, __u16 *nlbs, __u64 *slbas, __u16 *sopts, __u64 *eilbrts, __u32 *elbatms, __u32 *elbats, __u16 nr)
+
+ Constructs a copy range f3 structure
+
+**Parameters**
+
+``struct nvme_copy_range_f3 *copy``
+ Copy range array
+
+``__u32 *snsids``
+ Source namespace identifier
+
+``__u16 *nlbs``
+ Number of logical blocks
+
+``__u64 *slbas``
+ Starting LBA
+
+``__u16 *sopts``
+ Source options
+
+``__u64 *eilbrts``
+ Expected initial logical block reference tag
+
+``__u32 *elbatms``
+ Expected logical block application tag mask
+
+``__u32 *elbats``
+ Expected logical block application tag
+
+``__u16 nr``
+ Number of descriptors to construct
+
+
+.. c:function:: int nvme_get_feature_length (int fid, __u32 cdw11, __u32 *len)
+
+ Retreive the command payload length for a specific feature identifier
+
+**Parameters**
+
+``int fid``
+ Feature identifier, see :c:type:`enum nvme_features_id <nvme_features_id>`.
+
+``__u32 cdw11``
+ The cdw11 value may affect the transfer (only known fid is
+ ``NVME_FEAT_FID_HOST_ID``)
+
+``__u32 *len``
+ On success, set to this features payload length in bytes.
+
+**Return**
+
+0 on success, -1 with errno set to EINVAL if the function did not
+recognize :c:type:`fid`.
+
+
+.. c:function:: int nvme_get_feature_length2 (int fid, __u32 cdw11, enum nvme_data_tfr dir, __u32 *len)
+
+ Retreive the command payload length for a specific feature identifier
+
+**Parameters**
+
+``int fid``
+ Feature identifier, see :c:type:`enum nvme_features_id <nvme_features_id>`.
+
+``__u32 cdw11``
+ The cdw11 value may affect the transfer (only known fid is
+ ``NVME_FEAT_FID_HOST_ID``)
+
+``enum nvme_data_tfr dir``
+ Data transfer direction: false - host to controller, true -
+ controller to host may affect the transfer (only known fid is
+ ``NVME_FEAT_FID_HOST_MEM_BUF``).
+
+``__u32 *len``
+ On success, set to this features payload length in bytes.
+
+**Return**
+
+0 on success, -1 with errno set to EINVAL if the function did not
+recognize :c:type:`fid`.
+
+
+.. c:function:: int nvme_get_directive_receive_length (enum nvme_directive_dtype dtype, enum nvme_directive_receive_doper doper, __u32 *len)
+
+ Get directive receive length
+
+**Parameters**
+
+``enum nvme_directive_dtype dtype``
+ Directive type, see :c:type:`enum nvme_directive_dtype <nvme_directive_dtype>`
+
+``enum nvme_directive_receive_doper doper``
+ Directive receive operation, see :c:type:`enum nvme_directive_receive_doper <nvme_directive_receive_doper>`
+
+``__u32 *len``
+ On success, set to this directives payload length in bytes.
+
+**Return**
+
+0 on success, -1 with errno set to EINVAL if the function did not
+recognize :c:type:`dtype` or :c:type:`doper`.
+
+
+.. c:function:: size_t get_entity_name (char *buffer, size_t bufsz)
+
+ Get Entity Name (ENAME).
+
+**Parameters**
+
+``char *buffer``
+ The buffer where the ENAME will be saved as an ASCII string.
+
+``size_t bufsz``
+ The size of **buffer**.
+
+**Description**
+
+Per TP8010, ENAME is defined as the name associated with the host (i.e.
+hostname).
+
+**Return**
+
+Number of characters copied to **buffer**.
+
+
+.. c:function:: size_t get_entity_version (char *buffer, size_t bufsz)
+
+ Get Entity Version (EVER).
+
+**Parameters**
+
+``char *buffer``
+ The buffer where the EVER will be saved as an ASCII string.
+
+``size_t bufsz``
+ The size of **buffer**.
+
+**Description**
+
+EVER is defined as the operating system name and version as an ASCII
+string. This function reads different files from the file system and
+builds a string as follows: [os type] [os release] [distro release]
+
+ E.g. "Linux 5.17.0-rc1 SLES 15.4"
+
+**Return**
+
+Number of characters copied to **buffer**.
+
+
+.. c:function:: char * kv_strip (char *kv)
+
+ Strip blanks from key value string
+
+**Parameters**
+
+``char *kv``
+ The key-value string to strip
+
+**Description**
+
+Strip leading/trailing blanks as well as trailing comments from the
+Key=Value string pointed to by **kv**.
+
+**Return**
+
+A pointer to the stripped string. Note that the original string,
+**kv**, gets modified.
+
+
+.. c:function:: char * kv_keymatch (const char *kv, const char *key)
+
+ Look for key in key value string
+
+**Parameters**
+
+``const char *kv``
+ The key=value string to search for the presence of **key**
+
+``const char *key``
+ The key to look for
+
+**Description**
+
+Look for **key** in the Key=Value pair pointed to by **k** and return a
+pointer to the Value if **key** is found.
+
+Check if **kv** starts with **key**. If it does then make sure that we
+have a whole-word match on the **key**, and if we do, return a pointer
+to the first character of value (i.e. skip leading spaces, tabs,
+and equal sign)
+
+**Return**
+
+A pointer to the first character of "value" if a match is found.
+NULL otherwise.
+
+
+.. c:function:: char * startswith (const char *s, const char *prefix)
+
+ Checks that a string starts with a given prefix.
+
+**Parameters**
+
+``const char *s``
+ The string to check
+
+``const char *prefix``
+ A string that **s** could be starting with
+
+**Return**
+
+If **s** starts with **prefix**, then return a pointer within **s** at
+the first character after the matched **prefix**. NULL otherwise.
+
+
+.. c:macro:: round_up
+
+``round_up (val, mult)``
+
+ Round a value **val** to the next multiple specified by **mult**.
+
+**Parameters**
+
+``val``
+ Value to round
+
+``mult``
+ Multiple to round to.
+
+**Description**
+
+usage: int x = round_up(13, sizeof(__u32)); // 13 -> 16
+
+
+.. c:function:: __u16 nvmf_exat_len (size_t val_len)
+
+ Return length rounded up by 4
+
+**Parameters**
+
+``size_t val_len``
+ Value length
+
+**Description**
+
+Return the size in bytes, rounded to a multiple of 4 (e.g., size of
+__u32), of the buffer needed to hold the exat value of size
+**val_len**.
+
+**Return**
+
+Length rounded up by 4
+
+
+.. c:function:: __u16 nvmf_exat_size (size_t val_len)
+
+ Return min aligned size to hold value
+
+**Parameters**
+
+``size_t val_len``
+ This is the length of the data to be copied to the "exatval"
+ field of a "struct nvmf_ext_attr".
+
+**Description**
+
+Return the size of the "struct nvmf_ext_attr" needed to hold
+a value of size **val_len**.
+
+**Return**
+
+The size in bytes, rounded to a multiple of 4 (i.e. size of
+__u32), of the "struct nvmf_ext_attr" required to hold a string of
+length **val_len**.
+
+
+.. c:function:: struct nvmf_ext_attr * nvmf_exat_ptr_next (struct nvmf_ext_attr *p)
+
+ Increment **p** to the next element in the array.
+
+**Parameters**
+
+``struct nvmf_ext_attr *p``
+ Pointer to an element of an array of "struct nvmf_ext_attr".
+
+**Description**
+
+Extended attributes are saved to an array of "struct nvmf_ext_attr"
+where each element of the array is of variable size. In order to
+move to the next element in the array one must increment the
+pointer to the current element (**p**) by the size of the current
+element.
+
+**Return**
+
+Pointer to the next element in the array.
+
+
+
+
+.. c:enum:: nvme_version
+
+ Selector for version to be returned by **nvme_get_version**
+
+**Constants**
+
+``NVME_VERSION_PROJECT``
+ Project release version
+
+``NVME_VERSION_GIT``
+ Git reference
+
+
+.. c:function:: const char * nvme_get_version (enum nvme_version type)
+
+ Return version libnvme string
+
+**Parameters**
+
+``enum nvme_version type``
+ Selects which version type (see **struct** nvme_version)
+
+**Return**
+
+Returns version string for known types or else "n/a"
+
+
+.. c:function:: int nvme_uuid_to_string (unsigned char uuid[NVME_UUID_LEN], char *str)
+
+ Return string represenation of encoded UUID
+
+**Parameters**
+
+``unsigned char uuid[NVME_UUID_LEN]``
+ Binary encoded input UUID
+
+``char *str``
+ Output string represenation of UUID
+
+**Return**
+
+Returns error code if type conversion fails.
+
+
+.. c:function:: int nvme_uuid_from_string (const char *str, unsigned char uuid[NVME_UUID_LEN])
+
+ Return encoded UUID represenation of string UUID
+
+**Parameters**
+
+``const char *str``
+ Output string represenation of UUID
+
+``unsigned char uuid[NVME_UUID_LEN]``
+ Binary encoded input UUID
+
+**Return**
+
+Returns error code if type conversion fails.
+
+
+.. c:function:: int nvme_uuid_random (unsigned char uuid[NVME_UUID_LEN])
+
+ Generate random UUID
+
+**Parameters**
+
+``unsigned char uuid[NVME_UUID_LEN]``
+ Generated random UUID
+
+**Description**
+
+Generate random number according
+https://www.rfc-editor.org/rfc/rfc4122#section-4.4
+
+**Return**
+
+Returns error code if generating of random number fails.
+
+
+.. c:function:: int nvme_uuid_find (struct nvme_id_uuid_list *uuid_list, const unsigned char uuid[NVME_UUID_LEN])
+
+ Find UUID position on UUID list
+
+**Parameters**
+
+``struct nvme_id_uuid_list *uuid_list``
+ UUID list returned by identify UUID
+
+``const unsigned char uuid[NVME_UUID_LEN]``
+ Binary encoded input UUID
+
+**Return**
+
+The array position where given UUID is present, or -1 on failure with errno set.
+
+
+.. c:function:: bool nvme_ipaddrs_eq (const char *addr1, const char *addr2)
+
+ Check if 2 IP addresses are equal.
+
+**Parameters**
+
+``const char *addr1``
+ IP address (can be IPv4 or IPv6)
+
+``const char *addr2``
+ IP address (can be IPv4 or IPv6)
+
+**Return**
+
+true if addr1 == addr2. false otherwise.
+
+
+.. c:function:: const char * nvme_iface_matching_addr (const struct ifaddrs *iface_list, const char *addr)
+
+ Get interface matching **addr**
+
+**Parameters**
+
+``const struct ifaddrs *iface_list``
+ Interface list returned by getifaddrs()
+
+``const char *addr``
+ Address to match
+
+**Description**
+
+Parse the interface list pointed to by **iface_list** looking
+for the interface that has **addr** as one of its assigned
+addresses.
+
+**Return**
+
+The name of the interface that owns **addr** or NULL.
+
+
+.. c:function:: bool nvme_iface_primary_addr_matches (const struct ifaddrs *iface_list, const char *iface, const char *addr)
+
+ Check that interface's primary address matches
+
+**Parameters**
+
+``const struct ifaddrs *iface_list``
+ Interface list returned by getifaddrs()
+
+``const char *iface``
+ Interface to match
+
+``const char *addr``
+ Address to match
+
+**Description**
+
+Parse the interface list pointed to by **iface_list** and looking for
+interface **iface**. The get its primary address and check if it matches
+**addr**.
+
+**Return**
+
+true if a match is found, false otherwise.
+
+
diff --git a/examples/discover-loop.c b/examples/discover-loop.c
new file mode 100644
index 0000000..7528067
--- /dev/null
+++ b/examples/discover-loop.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+/**
+ * discover-loop: Use fabrics commands to discover any loop targets and print
+ * those records. You must have at least one configured nvme loop target on the
+ * system (no existing connection required). The output will look more
+ * interesting with more targets.
+ */
+#define __SANE_USERSPACE_TYPES__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libnvme.h>
+
+#include <ccan/endian/endian.h>
+
+static void print_discover_log(struct nvmf_discovery_log *log)
+{
+ int i, numrec = le64_to_cpu(log->numrec);
+
+ printf(".\n");
+ printf("|-- genctr:%llx\n", log->genctr);
+ printf("|-- numrec:%x\n", numrec);
+ printf("`-- recfmt:%x\n", log->recfmt);
+
+ for (i = 0; i < numrec; i++) {
+ struct nvmf_disc_log_entry *e = &log->entries[i];
+
+ printf(" %c-- Entry:%d\n", (i < numrec - 1) ? '|' : '`', i);
+ printf(" %c |-- trtype:%x\n", (i < numrec - 1) ? '|' : ' ', e->trtype);
+ printf(" %c |-- adrfam:%x\n", (i < numrec - 1) ? '|' : ' ', e->adrfam);
+ printf(" %c |-- subtype:%x\n", (i < numrec - 1) ? '|' : ' ', e->subtype);
+ printf(" %c |-- treq:%x\n", (i < numrec - 1) ? '|' : ' ', e->treq);
+ printf(" %c |-- portid:%x\n", (i < numrec - 1) ? '|' : ' ', e->portid);
+ printf(" %c |-- cntlid:%x\n", (i < numrec - 1) ? '|' : ' ', e->cntlid);
+ printf(" %c |-- asqsz:%x\n", (i < numrec - 1) ? '|' : ' ', e->asqsz);
+ printf(" %c |-- trsvcid:%s\n", (i < numrec - 1) ? '|' : ' ', e->trsvcid);
+ printf(" %c |-- subnqn:%s\n", (i < numrec - 1) ? '|' : ' ', e->subnqn);
+ printf(" %c `-- traddr:%s\n", (i < numrec - 1) ? '|' : ' ', e->traddr);
+ }
+}
+
+int main()
+{
+ struct nvmf_discovery_log *log = NULL;
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_ctrl_t c;
+ int ret;
+ struct nvme_fabrics_config cfg;
+
+ nvmf_default_config(&cfg);
+
+ r = nvme_scan(NULL);
+ h = nvme_default_host(r);
+ if (!h) {
+ fprintf(stderr, "Failed to allocated memory\n");
+ return ENOMEM;
+ }
+ c = nvme_create_ctrl(r, NVME_DISC_SUBSYS_NAME, "loop",
+ NULL, NULL, NULL, NULL);
+ if (!c) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ return ENOMEM;
+ }
+ ret = nvmf_add_ctrl(h, c, &cfg);
+ if (ret < 0) {
+ fprintf(stderr, "no controller found\n");
+ return errno;
+ }
+
+ ret = nvmf_get_discovery_log(c, &log, 4);
+ nvme_disconnect_ctrl(c);
+ nvme_free_ctrl(c);
+
+ if (ret)
+ fprintf(stderr, "nvmf-discover-log:%x\n", ret);
+ else
+ print_discover_log(log);
+
+ nvme_free_tree(r);
+ free(log);
+ return 0;
+}
diff --git a/examples/discover-loop.py b/examples/discover-loop.py
new file mode 100644
index 0000000..8481e82
--- /dev/null
+++ b/examples/discover-loop.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: Apache-2.0
+'''
+Example script for nvme discovery
+
+Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
+Licensed under the Apache License, Version 2.0 (the "License"); you may
+not use this file except in compliance with the License. You may obtain
+a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations
+under the License.
+'''
+
+import sys
+import pprint
+from libnvme import nvme
+
+def disc_supp_str(dlp_supp_opts):
+ d = {
+ nvme.NVMF_LOG_DISC_LID_EXTDLPES: "Extended Discovery Log Page Entry Supported (EXTDLPES)",
+ nvme.NVMF_LOG_DISC_LID_PLEOS: "Port Local Entries Only Supported (PLEOS)",
+ nvme.NVMF_LOG_DISC_LID_ALLSUBES: "All NVM Subsystem Entries Supported (ALLSUBES)",
+ }
+ return [txt for msk, txt in d.items() if dlp_supp_opts & msk]
+
+r = nvme.root()
+h = nvme.host(r)
+c = nvme.ctrl(r, nvme.NVME_DISC_SUBSYS_NAME, 'loop')
+try:
+ c.connect(h)
+except Exception as e:
+ sys.exit(f'Failed to connect: {e}')
+
+print("connected to %s subsys %s" % (c.name, c.subsystem.name))
+
+slp = c.supported_log_pages()
+
+try:
+ dlp_supp_opts = slp[nvme.NVME_LOG_LID_DISCOVER] >> 16
+except (TypeError, IndexError):
+ dlp_supp_opts = 0
+
+print(f"LID {nvme.NVME_LOG_LID_DISCOVER}h (Discovery), supports: {disc_supp_str(dlp_supp_opts)}")
+
+try:
+ lsp = nvme.NVMF_LOG_DISC_LSP_PLEO if dlp_supp_opts & nvme.NVMF_LOG_DISC_LID_PLEOS else 0
+ d = c.discover(lsp=lsp)
+ print(pprint.pformat(d))
+except Exception as e:
+ sys.exit(f'Failed to discover: {e}')
+
+try:
+ c.disconnect()
+except Exception as e:
+ sys.exit(f'Failed to disconnect: {e}')
+
+c = None
+h = None
+r = None
diff --git a/examples/display-columnar.c b/examples/display-columnar.c
new file mode 100644
index 0000000..db98bdf
--- /dev/null
+++ b/examples/display-columnar.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+/**
+ * display-columnar: Scans the nvme topology, prints each record type in a
+ * column format for easy visual scanning.
+ */
+#include <stdio.h>
+#include <inttypes.h>
+#include <libnvme.h>
+
+static const char dash[101] = {[0 ... 99] = '-'};
+int main()
+{
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ nvme_path_t p;
+ nvme_ns_t n;
+
+ r = nvme_scan(NULL);
+ if (!r)
+ return -1;
+
+
+ printf("%-16s %-96s %-.16s\n", "Subsystem", "Subsystem-NQN", "Controllers");
+ printf("%-.16s %-.96s %-.16s\n", dash, dash, dash);
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ bool first = true;
+ printf("%-16s %-96s ", nvme_subsystem_get_name(s),
+ nvme_subsystem_get_nqn(s));
+
+ nvme_subsystem_for_each_ctrl(s, c) {
+ printf("%s%s", first ? "": ", ",
+ nvme_ctrl_get_name(c));
+ first = false;
+ }
+ printf("\n");
+ }
+ }
+ printf("\n");
+
+ printf("%-8s %-20s %-40s %-8s %-6s %-14s %-12s %-16s\n", "Device",
+ "SN", "MN", "FR", "TxPort", "Address", "Subsystem", "Namespaces");
+ printf("%-.8s %-.20s %-.40s %-.8s %-.6s %-.14s %-.12s %-.16s\n", dash, dash,
+ dash, dash, dash, dash, dash, dash);
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ nvme_subsystem_for_each_ctrl(s, c) {
+ bool first = true;
+
+ printf("%-8s %-20s %-40s %-8s %-6s %-14s %-12s ",
+ nvme_ctrl_get_name(c),
+ nvme_ctrl_get_serial(c),
+ nvme_ctrl_get_model(c),
+ nvme_ctrl_get_firmware(c),
+ nvme_ctrl_get_transport(c),
+ nvme_ctrl_get_address(c),
+ nvme_subsystem_get_name(s));
+
+ nvme_ctrl_for_each_ns(c, n) {
+ printf("%s%s", first ? "": ", ",
+ nvme_ns_get_name(n));
+ first = false;
+ }
+
+ nvme_ctrl_for_each_path(c, p) {
+ printf("%s%s", first ? "": ", ",
+ nvme_ns_get_name(nvme_path_get_ns(p)));
+ first = false;
+ }
+ printf("\n");
+ }
+ }
+ }
+ printf("\n");
+
+ printf("%-12s %-8s %-16s %-8s %-16s\n", "Device", "NSID", "Sectors", "Format", "Controllers");
+ printf("%-.12s %-.8s %-.16s %-.8s %-.16s\n", dash, dash, dash, dash, dash);
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ nvme_subsystem_for_each_ctrl(s, c) {
+ nvme_ctrl_for_each_ns(c, n)
+ printf("%-12s %-8d %-16" PRIu64 " %-8d %s\n",
+ nvme_ns_get_name(n),
+ nvme_ns_get_nsid(n),
+ nvme_ns_get_lba_count(n),
+ nvme_ns_get_lba_size(n),
+ nvme_ctrl_get_name(c));
+ }
+
+ nvme_subsystem_for_each_ns(s, n) {
+ bool first = true;
+
+ printf("%-12s %-8d %-16" PRIu64 " %-8d ",
+ nvme_ns_get_name(n),
+ nvme_ns_get_nsid(n),
+ nvme_ns_get_lba_count(n),
+ nvme_ns_get_lba_size(n));
+ nvme_subsystem_for_each_ctrl(s, c) {
+ printf("%s%s", first ? "" : ", ",
+ nvme_ctrl_get_name(c));
+ first = false;
+ }
+ printf("\n");
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/examples/display-tree.c b/examples/display-tree.c
new file mode 100644
index 0000000..b9ea75f
--- /dev/null
+++ b/examples/display-tree.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+/**
+ * display-tree: Scans the nvme topology, prints as an ascii tree with some
+ * selected attributes for each component.
+ */
+#include <stdio.h>
+#include <inttypes.h>
+#include <libnvme.h>
+
+int main()
+{
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_subsystem_t s, _s;
+ nvme_ctrl_t c, _c;
+ nvme_path_t p, _p;
+ nvme_ns_t n, _n;
+
+ r = nvme_scan(NULL);
+ if (!r)
+ return -1;
+
+ printf(".\n");
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem_safe(h, s, _s) {
+ printf("%c-- %s - NQN=%s\n", _s ? '|' : '`',
+ nvme_subsystem_get_name(s),
+ nvme_subsystem_get_nqn(s));
+
+ nvme_subsystem_for_each_ns_safe(s, n, _n) {
+ printf("%c |-- %s lba size:%d lba max:%" PRIu64 "\n",
+ _s ? '|' : ' ',
+ nvme_ns_get_name(n),
+ nvme_ns_get_lba_size(n),
+ nvme_ns_get_lba_count(n));
+ }
+
+ nvme_subsystem_for_each_ctrl_safe(s, c, _c) {
+ printf("%c %c-- %s %s %s %s\n",
+ _s ? '|' : ' ', _c ? '|' : '`',
+ nvme_ctrl_get_name(c),
+ nvme_ctrl_get_transport(c),
+ nvme_ctrl_get_address(c),
+ nvme_ctrl_get_state(c));
+
+ nvme_ctrl_for_each_ns_safe(c, n, _n)
+ printf("%c %c %c-- %s lba size:%d lba max:%" PRIu64 "\n",
+ _s ? '|' : ' ', _c ? '|' : ' ',
+ _n ? '|' : '`',
+ nvme_ns_get_name(n),
+ nvme_ns_get_lba_size(n),
+ nvme_ns_get_lba_count(n));
+
+ nvme_ctrl_for_each_path_safe(c, p, _p)
+ printf("%c %c %c-- %s %s\n",
+ _s ? '|' : ' ', _c ? '|' : ' ',
+ _p ? '|' : '`',
+ nvme_path_get_name(p),
+ nvme_path_get_ana_state(p));
+ }
+ }
+ }
+ nvme_free_tree(r);
+ return 0;
+}
diff --git a/examples/meson.build b/examples/meson.build
new file mode 100644
index 0000000..3139311
--- /dev/null
+++ b/examples/meson.build
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2021 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+#
+executable(
+ 'telemetry-listen',
+ ['telemetry-listen.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+executable(
+ 'display-columnar',
+ ['display-columnar.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+executable(
+ 'display-tree',
+ ['display-tree.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+executable(
+ 'discover-loop',
+ ['discover-loop.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+executable(
+ 'mi-mctp',
+ ['mi-mctp.c'],
+ dependencies: libnvme_mi_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+if libdbus_dep.found()
+ executable(
+ 'mi-conf',
+ ['mi-conf.c'],
+ dependencies: [libnvme_mi_dep, libdbus_dep],
+ include_directories: [incdir, internal_incdir]
+ )
+endif
diff --git a/examples/mi-conf.c b/examples/mi-conf.c
new file mode 100644
index 0000000..4fdd405
--- /dev/null
+++ b/examples/mi-conf.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2022 Code Construct Pty Ltd.
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+
+/**
+ * mi-conf: query a device for optimal MTU and set for both the local MCTP
+ * route (through dbus to mctpd) and the device itself (through NVMe-MI
+ * configuration commands)
+ */
+
+#include <err.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libnvme-mi.h>
+
+#include <ccan/array_size/array_size.h>
+#include <ccan/endian/endian.h>
+
+#include <dbus/dbus.h>
+
+#define MCTP_DBUS_NAME "xyz.openbmc_project.MCTP"
+#define MCTP_DBUS_PATH "/xyz/openbmc_project/mctp"
+#define MCTP_DBUS_EP_IFACE "au.com.CodeConstruct.MCTP.Endpoint"
+
+static int parse_mctp(const char *devstr, unsigned int *net, uint8_t *eid)
+{
+ int rc;
+
+ rc = sscanf(devstr, "mctp:%u,%hhu", net, eid);
+ if (rc != 2)
+ return -1;
+
+ return 0;
+}
+
+int find_port(nvme_mi_ep_t ep, uint8_t *portp, uint16_t *mtup)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ struct nvme_mi_read_port_info port_info;
+ uint8_t port;
+ bool found;
+ int rc;
+
+ /* query number of ports */
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ if (rc) {
+ warn("Failed reading subsystem info");
+ return -1;
+ }
+
+ found = false;
+ for (port = 0; port <= ss_info.nump; port++) {
+ rc = nvme_mi_mi_read_mi_data_port(ep, port, &port_info);
+ if (rc) {
+ warn("Failed reading port info for port %ud", port);
+ return -1;
+ }
+
+ /* skip non-SMBus ports */
+ if (port_info.portt != 0x2)
+ continue;
+
+ if (found) {
+ warn("Mutliple SMBus ports; skipping duplicate");
+ } else {
+ *portp = port;
+ *mtup = port_info.mmctptus;
+ found = true;
+ }
+ }
+
+ return found ? 0 : 1;
+}
+
+int set_local_mtu(DBusConnection *bus, unsigned int net, uint8_t eid,
+ uint32_t mtu)
+{
+ DBusMessage *msg, *resp;
+ DBusError berr;
+ char *ep_path;
+ int rc;
+
+ rc = asprintf(&ep_path, "%s/%u/%hhu", MCTP_DBUS_PATH, net, eid);
+ if (rc < 0) {
+ warn("Failed to create dbus path");
+ return -1;
+ }
+
+ /* The NVMe-MI interfaces refer to their MTU as *not* including the
+ * 4-byte MCTP header, whereas the MCTP specs *do* include it. When
+ * we're setting the route MTU, we're using to the MCTP-style MTU,
+ * which needs the extra four bytes included
+ */
+ mtu += 4;
+
+ rc = -1;
+ dbus_error_init(&berr);
+ msg = dbus_message_new_method_call(MCTP_DBUS_NAME, ep_path,
+ MCTP_DBUS_EP_IFACE, "SetMTU");
+ if (!msg) {
+ warnx("Can't create D-Bus message");
+ goto out;
+ }
+
+ rc = dbus_message_append_args(msg,
+ DBUS_TYPE_UINT32, &mtu,
+ DBUS_TYPE_INVALID);
+ if (!rc) {
+ warnx("Can't construct D-Bus message arguments");
+ goto out_free_msg;
+ }
+
+ resp = dbus_connection_send_with_reply_and_block(bus, msg,
+ 2000, &berr);
+ if (!resp) {
+ warnx("Failed to set local MTU: %s (%s)", berr.message,
+ berr.name);
+ } else {
+ dbus_message_unref(resp);
+ rc = 0;
+ }
+
+out_free_msg:
+ dbus_message_unref(msg);
+out:
+ dbus_error_free(&berr);
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ uint16_t cur_mtu, mtu;
+ DBusConnection *bus;
+ const char *devstr;
+ uint8_t eid, port;
+ nvme_root_t root;
+ unsigned int net;
+ nvme_mi_ep_t ep;
+ DBusError berr;
+ int rc;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s mctp:<net>,<eid>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ devstr = argv[1];
+ rc = parse_mctp(devstr, &net, &eid);
+ if (rc)
+ errx(EXIT_FAILURE, "can't parse MI device string '%s'", devstr);
+
+ root = nvme_mi_create_root(stderr, DEFAULT_LOGLEVEL);
+ if (!root)
+ err(EXIT_FAILURE, "can't create NVMe root");
+
+ ep = nvme_mi_open_mctp(root, net, eid);
+ if (!ep) {
+ warnx("can't open MCTP endpoint %d:%d", net, eid);
+ goto out_free_root;
+ }
+
+ dbus_error_init(&berr);
+ bus = dbus_bus_get(DBUS_BUS_SYSTEM, &berr);
+ if (!bus) {
+ warnx("Failed opening D-Bus: %s (%s)\n",
+ berr.message, berr.name);
+ rc = -1;
+ goto out_close_ep;
+ }
+
+ rc = find_port(ep, &port, &mtu);
+ if (rc) {
+ warnx("Can't find SMBus port information");
+ goto out_close_bus;
+ }
+
+ rc = nvme_mi_mi_config_get_mctp_mtu(ep, port, &cur_mtu);
+ if (rc) {
+ cur_mtu = 0;
+ warn("Can't query current MTU; no way to revert on failure");
+ }
+
+ if (mtu == cur_mtu) {
+ printf("Current MTU (%d) is already at max\n", cur_mtu);
+ goto out_close_bus;
+ }
+
+ rc = nvme_mi_mi_config_set_mctp_mtu(ep, port, mtu);
+ if (rc) {
+ warn("Can't set MCTP MTU");
+ goto out_close_bus;
+ }
+
+ rc = set_local_mtu(bus, net, eid, mtu);
+ if (rc) {
+ /* revert if we have an old setting */
+ if (cur_mtu) {
+ rc = nvme_mi_mi_config_set_mctp_mtu(ep, port, cur_mtu);
+ if (rc)
+ warn("Failed to restore previous MTU!");
+ rc = -1;
+ }
+ } else {
+ printf("MTU for port %u set to %d (was %d)\n",
+ port, mtu, cur_mtu);
+ }
+
+out_close_bus:
+ dbus_connection_unref(bus);
+out_close_ep:
+ dbus_error_free(&berr);
+ nvme_mi_close(ep);
+out_free_root:
+ nvme_mi_free_root(root);
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/examples/mi-mctp.c b/examples/mi-mctp.c
new file mode 100644
index 0000000..e0b7644
--- /dev/null
+++ b/examples/mi-mctp.c
@@ -0,0 +1,778 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2021 Code Construct Pty Ltd.
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+
+/**
+ * mi-mctp: open a MI connection over MCTP, and query controller info
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <libnvme-mi.h>
+
+#include <ccan/array_size/array_size.h>
+#include <ccan/endian/endian.h>
+
+static void show_port_pcie(struct nvme_mi_read_port_info *port)
+{
+ printf(" PCIe max payload: 0x%x\n", 0x80 << port->pcie.mps);
+ printf(" PCIe link speeds: 0x%02x\n", port->pcie.sls);
+ printf(" PCIe current speed: 0x%02x\n", port->pcie.cls);
+ printf(" PCIe max link width: 0x%02x\n", port->pcie.mlw);
+ printf(" PCIe neg link width: 0x%02x\n", port->pcie.nlw);
+ printf(" PCIe port: 0x%02x\n", port->pcie.pn);
+}
+
+static void show_port_smbus(struct nvme_mi_read_port_info *port)
+{
+ printf(" SMBus address: 0x%02x\n", port->smb.vpd_addr);
+ printf(" VPD access freq: 0x%02x\n", port->smb.mvpd_freq);
+ printf(" MCTP address: 0x%02x\n", port->smb.mme_addr);
+ printf(" MCTP access freq: 0x%02x\n", port->smb.mme_freq);
+ printf(" NVMe basic management: %s\n",
+ (port->smb.nvmebm & 0x1) ? "enabled" : "disabled");
+}
+
+static struct {
+ int typeid;
+ const char *name;
+ void (*fn)(struct nvme_mi_read_port_info *);
+} port_types[] = {
+ { 0x00, "inactive", NULL },
+ { 0x01, "PCIe", show_port_pcie },
+ { 0x02, "SMBus", show_port_smbus },
+};
+
+static int show_port(nvme_mi_ep_t ep, int portid)
+{
+ void (*show_fn)(struct nvme_mi_read_port_info *);
+ struct nvme_mi_read_port_info port;
+ const char *typestr;
+ int rc;
+
+ rc = nvme_mi_mi_read_mi_data_port(ep, portid, &port);
+ if (rc)
+ return rc;
+
+ if (port.portt < ARRAY_SIZE(port_types)) {
+ show_fn = port_types[port.portt].fn;
+ typestr = port_types[port.portt].name;
+ } else {
+ show_fn = NULL;
+ typestr = "INVALID";
+ }
+
+ printf(" port %d\n", portid);
+ printf(" type %s[%d]\n", typestr, port.portt);
+ printf(" MCTP MTU: %d\n", port.mmctptus);
+ printf(" MEB size: %d\n", port.meb);
+
+ if (show_fn)
+ show_fn(&port);
+
+ return 0;
+}
+
+int do_info(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_nvm_ss_health_status ss_health;
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int i, rc;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ if (rc) {
+ warn("can't perform Read MI Data operation");
+ return -1;
+ }
+
+ printf("NVMe MI subsys info:\n");
+ printf(" num ports: %d\n", ss_info.nump + 1);
+ printf(" major ver: %d\n", ss_info.mjr);
+ printf(" minor ver: %d\n", ss_info.mnr);
+
+ printf("NVMe MI port info:\n");
+ for (i = 0; i <= ss_info.nump; i++)
+ show_port(ep, i);
+
+ rc = nvme_mi_mi_subsystem_health_status_poll(ep, true, &ss_health);
+ if (rc)
+ err(EXIT_FAILURE, "can't perform Health Status Poll operation");
+
+ printf("NVMe MI subsys health:\n");
+ printf(" subsystem status: 0x%x\n", ss_health.nss);
+ printf(" smart warnings: 0x%x\n", ss_health.sw);
+ printf(" composite temp: %d\n", ss_health.ctemp);
+ printf(" drive life used: %d%%\n", ss_health.pdlu);
+ printf(" controller status: 0x%04x\n", le16_to_cpu(ss_health.ccs));
+
+ return 0;
+}
+
+static int show_ctrl(nvme_mi_ep_t ep, uint16_t ctrl_id)
+{
+ struct nvme_mi_read_ctrl_info ctrl;
+ int rc;
+
+ rc = nvme_mi_mi_read_mi_data_ctrl(ep, ctrl_id, &ctrl);
+ if (rc)
+ return rc;
+
+ printf(" Controller id: %d\n", ctrl_id);
+ printf(" port id: %d\n", ctrl.portid);
+ if (ctrl.prii & 0x1) {
+ uint16_t bdfn = le16_to_cpu(ctrl.pri);
+ printf(" PCIe routing valid\n");
+ printf(" PCIe bus: 0x%02x\n", bdfn >> 8);
+ printf(" PCIe dev: 0x%02x\n", bdfn >> 3 & 0x1f);
+ printf(" PCIe fn : 0x%02x\n", bdfn & 0x7);
+ } else {
+ printf(" PCIe routing invalid\n");
+ }
+ printf(" PCI vendor: %04x\n", le16_to_cpu(ctrl.vid));
+ printf(" PCI device: %04x\n", le16_to_cpu(ctrl.did));
+ printf(" PCI subsys vendor: %04x\n", le16_to_cpu(ctrl.ssvid));
+ printf(" PCI subsys device: %04x\n", le16_to_cpu(ctrl.ssvid));
+
+ return 0;
+}
+
+static int do_controllers(nvme_mi_ep_t ep)
+{
+ struct nvme_ctrl_list ctrl_list;
+ int rc, i;
+
+ rc = nvme_mi_mi_read_mi_data_ctrl_list(ep, 0, &ctrl_list);
+ if (rc) {
+ warnx("Can't perform Controller List operation");
+ return rc;
+ }
+
+ printf("NVMe controller list:\n");
+ for (i = 0; i < le16_to_cpu(ctrl_list.num); i++) {
+ uint16_t id = le16_to_cpu(ctrl_list.identifier[i]);
+ show_ctrl(ep, id);
+ }
+ return 0;
+}
+
+static const char *__copy_id_str(const void *field, size_t size,
+ char *buf, size_t buf_size)
+{
+ assert(size < buf_size);
+ strncpy(buf, field, size);
+ buf[size] = '\0';
+ return buf;
+}
+
+#define copy_id_str(f,b) __copy_id_str(f, sizeof(f), b, sizeof(b))
+
+int do_identify(nvme_mi_ep_t ep, int argc, char **argv)
+{
+ struct nvme_identify_args id_args = { 0 };
+ struct nvme_mi_ctrl *ctrl;
+ struct nvme_id_ctrl id;
+ uint16_t ctrl_id;
+ char buf[41];
+ bool partial;
+ int rc, tmp;
+
+ if (argc < 2) {
+ fprintf(stderr, "no controller ID specified\n");
+ return -1;
+ }
+
+ tmp = atoi(argv[1]);
+ if (tmp < 0 || tmp > 0xffff) {
+ fprintf(stderr, "invalid controller ID\n");
+ return -1;
+ }
+
+ ctrl_id = tmp & 0xffff;
+
+ partial = argc > 2 && !strcmp(argv[2], "--partial");
+
+ ctrl = nvme_mi_init_ctrl(ep, ctrl_id);
+ if (!ctrl) {
+ warn("can't create controller");
+ return -1;
+ }
+
+ id_args.data = &id;
+ id_args.args_size = sizeof(id_args);
+ id_args.cns = NVME_IDENTIFY_CNS_CTRL;
+ id_args.nsid = NVME_NSID_NONE;
+ id_args.cntid = 0;
+ id_args.csi = NVME_CSI_NVM;
+
+ /* for this example code, we can either do a full or partial identify;
+ * since we're only printing the fields before the 'rab' member,
+ * these will be equivalent, aside from the size of the MI
+ * response.
+ */
+ if (partial) {
+ rc = nvme_mi_admin_identify_partial(ctrl, &id_args, 0,
+ offsetof(struct nvme_id_ctrl, rab));
+ } else {
+ rc = nvme_mi_admin_identify(ctrl, &id_args);
+ }
+
+ if (rc) {
+ warn("can't perform Admin Identify command");
+ return -1;
+ }
+
+ printf("NVMe Controller %d identify\n", ctrl_id);
+ printf(" PCI vendor: %04x\n", le16_to_cpu(id.vid));
+ printf(" PCI subsys vendor: %04x\n", le16_to_cpu(id.ssvid));
+ printf(" Serial number: %s\n", copy_id_str(id.sn, buf));
+ printf(" Model number: %s\n", copy_id_str(id.mn, buf));
+ printf(" Firmware rev: %s\n", copy_id_str(id.fr, buf));
+
+ return 0;
+}
+
+void fhexdump(FILE *fp, const unsigned char *buf, int len)
+{
+ const int row_len = 16;
+ int i, j;
+
+ for (i = 0; i < len; i += row_len) {
+ char hbuf[row_len * strlen("00 ") + 1];
+ char cbuf[row_len + strlen("|") + 1];
+
+ for (j = 0; (j < row_len) && ((i+j) < len); j++) {
+ unsigned char c = buf[i + j];
+
+ sprintf(hbuf + j * 3, "%02x ", c);
+
+ if (!isprint(c))
+ c = '.';
+
+ sprintf(cbuf + j, "%c", c);
+ }
+
+ strcat(cbuf, "|");
+
+ fprintf(fp, "%08x %*s |%s\n", i,
+ 0 - (int)sizeof(hbuf) + 1, hbuf, cbuf);
+ }
+}
+
+void hexdump(const unsigned char *buf, int len)
+{
+ fhexdump(stdout, buf, len);
+}
+
+int do_get_log_page(nvme_mi_ep_t ep, int argc, char **argv)
+{
+ struct nvme_get_log_args args = { 0 };
+ struct nvme_mi_ctrl *ctrl;
+ uint8_t buf[512];
+ uint16_t ctrl_id;
+ int rc, tmp;
+
+ if (argc < 2) {
+ fprintf(stderr, "no controller ID specified\n");
+ return -1;
+ }
+
+ tmp = atoi(argv[1]);
+ if (tmp < 0 || tmp > 0xffff) {
+ fprintf(stderr, "invalid controller ID\n");
+ return -1;
+ }
+
+ ctrl_id = tmp & 0xffff;
+
+ args.args_size = sizeof(args);
+ args.log = buf;
+ args.len = sizeof(buf);
+
+ if (argc > 2) {
+ tmp = atoi(argv[2]);
+ args.lid = tmp & 0xff;
+ } else {
+ args.lid = 0x1;
+ }
+
+ ctrl = nvme_mi_init_ctrl(ep, ctrl_id);
+ if (!ctrl) {
+ warn("can't create controller");
+ return -1;
+ }
+
+ rc = nvme_mi_admin_get_log(ctrl, &args);
+ if (rc) {
+ warn("can't perform Get Log page command");
+ return -1;
+ }
+
+ printf("Get log page (log id = 0x%02x) data:\n", args.lid);
+ hexdump(buf, args.len);
+
+ return 0;
+}
+
+int do_admin_raw(nvme_mi_ep_t ep, int argc, char **argv)
+{
+ struct nvme_mi_admin_req_hdr req;
+ struct nvme_mi_admin_resp_hdr *resp;
+ struct nvme_mi_ctrl *ctrl;
+ size_t resp_data_len;
+ unsigned long tmp;
+ uint8_t buf[512];
+ uint16_t ctrl_id;
+ uint8_t opcode;
+ __le32 *cdw;
+ int i, rc;
+
+ if (argc < 2) {
+ fprintf(stderr, "no controller ID specified\n");
+ return -1;
+ }
+
+ if (argc < 3) {
+ fprintf(stderr, "no opcode specified\n");
+ return -1;
+ }
+
+ tmp = atoi(argv[1]);
+ if (tmp > 0xffff) {
+ fprintf(stderr, "invalid controller ID\n");
+ return -1;
+ }
+ ctrl_id = tmp & 0xffff;
+
+ tmp = atoi(argv[2]);
+ if (tmp > 0xff) {
+ fprintf(stderr, "invalid opcode\n");
+ return -1;
+ }
+ opcode = tmp & 0xff;
+
+ memset(&req, 0, sizeof(req));
+ req.opcode = opcode;
+ req.ctrl_id = cpu_to_le16(ctrl_id);
+
+ /* The cdw10 - cdw16 fields are contiguous in req; set from argv. */
+ cdw = (void *)&req + offsetof(typeof(req), cdw10);
+ for (i = 0; i < 6; i++) {
+ if (argc >= 4 + i)
+ tmp = strtoul(argv[3 + i], NULL, 0);
+ else
+ tmp = 0;
+ *cdw = cpu_to_le32(tmp & 0xffffffff);
+ cdw++;
+ }
+
+ printf("Admin request:\n");
+ printf(" opcode: 0x%02x\n", req.opcode);
+ printf(" ctrl: 0x%04x\n", le16_to_cpu(req.ctrl_id));
+ printf(" cdw10: 0x%08x\n", le32_to_cpu(req.cdw10));
+ printf(" cdw11: 0x%08x\n", le32_to_cpu(req.cdw11));
+ printf(" cdw12: 0x%08x\n", le32_to_cpu(req.cdw12));
+ printf(" cdw13: 0x%08x\n", le32_to_cpu(req.cdw13));
+ printf(" cdw14: 0x%08x\n", le32_to_cpu(req.cdw14));
+ printf(" cdw15: 0x%08x\n", le32_to_cpu(req.cdw15));
+ printf(" raw:\n");
+ hexdump((void *)&req, sizeof(req));
+
+ memset(buf, 0, sizeof(buf));
+ resp = (void *)buf;
+
+ ctrl = nvme_mi_init_ctrl(ep, ctrl_id);
+ if (!ctrl) {
+ warn("can't create controller");
+ return -1;
+ }
+
+ resp_data_len = sizeof(buf) - sizeof(*resp);
+
+ rc = nvme_mi_admin_xfer(ctrl, &req, 0, resp, 0, &resp_data_len);
+ if (rc) {
+ warn("nvme_admin_xfer failed: %d", rc);
+ return -1;
+ }
+
+ printf("Admin response:\n");
+ printf(" Status: 0x%02x\n", resp->status);
+ printf(" cdw0: 0x%08x\n", le32_to_cpu(resp->cdw0));
+ printf(" cdw1: 0x%08x\n", le32_to_cpu(resp->cdw1));
+ printf(" cdw3: 0x%08x\n", le32_to_cpu(resp->cdw3));
+ printf(" data [%zd bytes]\n", resp_data_len);
+
+ hexdump(buf + sizeof(*resp), resp_data_len);
+ return 0;
+}
+
+static struct {
+ uint8_t id;
+ const char *name;
+} sec_protos[] = {
+ { 0x00, "Security protocol information" },
+ { 0xea, "NVMe" },
+ { 0xec, "JEDEC Universal Flash Storage" },
+ { 0xed, "SDCard TrustedFlash Security" },
+ { 0xee, "IEEE 1667" },
+ { 0xef, "ATA Device Server Password Security" },
+};
+
+static const char *sec_proto_description(uint8_t id)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sec_protos); i++) {
+ if (sec_protos[i].id == id)
+ return sec_protos[i].name;
+ }
+
+ if (id >= 0xf0)
+ return "Vendor specific";
+
+ return "unknown";
+}
+
+int do_security_info(nvme_mi_ep_t ep, int argc, char **argv)
+{
+ struct nvme_security_receive_args args = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ int i, rc, n_proto;
+ unsigned long tmp;
+ uint16_t ctrl_id;
+ struct {
+ uint8_t rsvd[6];
+ uint16_t len;
+ uint8_t protocols[256];
+ } proto_info;
+
+ if (argc != 2) {
+ fprintf(stderr, "no controller ID specified\n");
+ return -1;
+ }
+
+ tmp = atoi(argv[1]);
+ if (tmp > 0xffff) {
+ fprintf(stderr, "invalid controller ID\n");
+ return -1;
+ }
+
+ ctrl_id = tmp & 0xffff;
+
+ ctrl = nvme_mi_init_ctrl(ep, ctrl_id);
+ if (!ctrl) {
+ warn("can't create controller");
+ return -1;
+ }
+
+ /* protocol 0x00, spsp 0x0000: retrieve supported protocols */
+ args.args_size = sizeof(args);
+ args.data = &proto_info;
+ args.data_len = sizeof(proto_info);
+
+ rc = nvme_mi_admin_security_recv(ctrl, &args);
+ if (rc) {
+ warnx("can't perform Security Receive command: rc %d", rc);
+ return -1;
+ }
+
+ if (args.data_len < 6) {
+ warnx("Short response in security receive command (%d bytes)",
+ args.data_len);
+ return -1;
+ }
+
+ n_proto = be16_to_cpu(proto_info.len);
+ if (args.data_len < 6 + n_proto) {
+ warnx("Short response in security receive command (%d bytes), "
+ "for %d protocols", args.data_len, n_proto);
+ return -1;
+ }
+
+ printf("Supported protocols:\n");
+ for (i = 0; i < n_proto; i++) {
+ uint8_t id = proto_info.protocols[i];
+ printf(" 0x%02x: %s\n", id, sec_proto_description(id));
+ }
+
+ return 0;
+}
+
+struct {
+ enum nvme_mi_config_smbus_freq id;
+ const char *str;
+} smbus_freqs[] = {
+ { NVME_MI_CONFIG_SMBUS_FREQ_100kHz, "100k" },
+ { NVME_MI_CONFIG_SMBUS_FREQ_400kHz, "400k" },
+ { NVME_MI_CONFIG_SMBUS_FREQ_1MHz, "1M" },
+};
+
+static const char *smbus_freq_str(enum nvme_mi_config_smbus_freq freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(smbus_freqs); i++) {
+ if (smbus_freqs[i].id == freq)
+ return smbus_freqs[i].str;
+ }
+
+ return NULL;
+}
+
+static int smbus_freq_val(const char *str, enum nvme_mi_config_smbus_freq *freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(smbus_freqs); i++) {
+ if (!strcmp(smbus_freqs[i].str, str)) {
+ *freq = smbus_freqs[i].id;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int do_config_get(nvme_mi_ep_t ep, int argc, char **argv)
+{
+ enum nvme_mi_config_smbus_freq freq;
+ uint16_t mtu;
+ uint8_t port;
+ int rc;
+
+ if (argc > 1)
+ port = atoi(argv[1]) & 0xff;
+ else
+ port = 0;
+
+ rc = nvme_mi_mi_config_get_smbus_freq(ep, port, &freq);
+ if (rc) {
+ warn("can't query SMBus freq for port %d\n", port);
+ } else {
+ const char *fstr = smbus_freq_str(freq);
+ printf("SMBus access frequency (port %d): %s [0x%x]\n", port,
+ fstr ?: "unknown", freq);
+ }
+
+ rc = nvme_mi_mi_config_get_mctp_mtu(ep, port, &mtu);
+ if (rc)
+ warn("can't query MCTP MTU for port %d\n", port);
+ else
+ printf("MCTP MTU (port %d): %d\n", port, mtu);
+
+ return 0;
+}
+
+int do_config_set(nvme_mi_ep_t ep, int argc, char **argv)
+{
+ const char *name, *val;
+ uint8_t port;
+ int rc;
+
+ if (argc != 4) {
+ fprintf(stderr, "config set requires <port> <type> <val>\n");
+ return -1;
+ }
+
+ port = atoi(argv[1]) & 0xff;
+ name = argv[2];
+ val = argv[3];
+
+ if (!strcmp(name, "freq")) {
+ enum nvme_mi_config_smbus_freq freq;
+ rc = smbus_freq_val(val, &freq);
+ if (rc) {
+ fprintf(stderr, "unknown SMBus freq %s. "
+ "Try 100k, 400k or 1M\n", val);
+ return -1;
+ }
+ rc = nvme_mi_mi_config_set_smbus_freq(ep, port, freq);
+
+ } else if (!strcmp(name, "mtu")) {
+ uint16_t mtu;
+ mtu = atoi(val) & 0xffff;
+ /* controllers should reject this, but prevent the potential
+ * footgun of disabling futher comunication with the device
+ */
+ if (mtu < 64) {
+ fprintf(stderr, "MTU value too small\n");
+ return -1;
+ }
+ rc = nvme_mi_mi_config_set_mctp_mtu(ep, port, mtu);
+
+ } else {
+ fprintf(stderr, "Invalid configuration '%s', "
+ "try freq or mtu\n", name);
+ return -1;
+ }
+
+ if (rc)
+ fprintf(stderr, "config set failed with status %d\n", rc);
+
+ return rc;
+}
+
+enum action {
+ ACTION_INFO,
+ ACTION_CONTROLLERS,
+ ACTION_IDENTIFY,
+ ACTION_GET_LOG_PAGE,
+ ACTION_ADMIN_RAW,
+ ACTION_SECURITY_INFO,
+ ACTION_CONFIG_GET,
+ ACTION_CONFIG_SET,
+};
+
+static int do_action_endpoint(enum action action, nvme_mi_ep_t ep, int argc, char** argv)
+{
+ int rc;
+
+ switch (action) {
+ case ACTION_INFO:
+ rc = do_info(ep);
+ break;
+ case ACTION_CONTROLLERS:
+ rc = do_controllers(ep);
+ break;
+ case ACTION_IDENTIFY:
+ rc = do_identify(ep, argc, argv);
+ break;
+ case ACTION_GET_LOG_PAGE:
+ rc = do_get_log_page(ep, argc, argv);
+ break;
+ case ACTION_ADMIN_RAW:
+ rc = do_admin_raw(ep, argc, argv);
+ break;
+ case ACTION_SECURITY_INFO:
+ rc = do_security_info(ep, argc, argv);
+ break;
+ case ACTION_CONFIG_GET:
+ rc = do_config_get(ep, argc, argv);
+ break;
+ case ACTION_CONFIG_SET:
+ rc = do_config_set(ep, argc, argv);
+ break;
+ default:
+ /* This shouldn't be possible, as we should be covering all
+ * of the enum action options above. Hoever, keep the compilers
+ * happy and fail gracefully. */
+ fprintf(stderr, "invalid action %d?\n", action);
+ rc = -1;
+ }
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ enum action action;
+ nvme_root_t root;
+ nvme_mi_ep_t ep;
+ bool dbus = false, usage = true;
+ uint8_t eid;
+ int rc = 0, net;
+
+ if (argc >= 2 && strcmp(argv[1], "dbus") == 0) {
+ usage = false;
+ dbus= true;
+ argv += 1;
+ argc -= 1;
+ } else if (argc >= 3) {
+ usage = false;
+ net = atoi(argv[1]);
+ eid = atoi(argv[2]) & 0xff;
+ argv += 2;
+ argc -= 2;
+ }
+
+ if (usage) {
+ fprintf(stderr,
+ "usage: %s <net> <eid> [action] [action args]\n"
+ " %s 'dbus' [action] [action args]\n",
+ argv[0], argv[0]);
+ fprintf(stderr, "where action is:\n"
+ " info\n"
+ " controllers\n"
+ " identify <controller-id> [--partial]\n"
+ " get-log-page <controller-id> [<log-id>]\n"
+ " admin <controller-id> <opcode> [<cdw10>, <cdw11>, ...]\n"
+ " security-info <controller-id>\n"
+ " get-config [port]\n"
+ " set-config <port> <type> <val>\n"
+ "\n"
+ " 'dbus' target will query D-Bus for known MCTP endpoints\n"
+ );
+ return EXIT_FAILURE;
+ }
+
+ if (argc == 1) {
+ action = ACTION_INFO;
+ } else {
+ char *action_str = argv[1];
+ argc--;
+ argv++;
+
+ if (!strcmp(action_str, "info")) {
+ action = ACTION_INFO;
+ } else if (!strcmp(action_str, "controllers")) {
+ action = ACTION_CONTROLLERS;
+ } else if (!strcmp(action_str, "identify")) {
+ action = ACTION_IDENTIFY;
+ } else if (!strcmp(action_str, "get-log-page")) {
+ action = ACTION_GET_LOG_PAGE;
+ } else if (!strcmp(action_str, "admin")) {
+ action = ACTION_ADMIN_RAW;
+ } else if (!strcmp(action_str, "security-info")) {
+ action = ACTION_SECURITY_INFO;
+ } else if (!strcmp(action_str, "get-config")) {
+ action = ACTION_CONFIG_GET;
+ } else if (!strcmp(action_str, "set-config")) {
+ action = ACTION_CONFIG_SET;
+ } else {
+ fprintf(stderr, "invalid action '%s'\n", action_str);
+ return EXIT_FAILURE;
+ }
+ }
+ if (dbus) {
+ nvme_root_t root;
+ int i = 0;
+
+ root = nvme_mi_scan_mctp();
+ if (!root)
+ errx(EXIT_FAILURE, "can't scan D-Bus entries");
+
+ nvme_mi_for_each_endpoint(root, ep) i++;
+ printf("Found %d endpoints in D-Bus:\n", i);
+ nvme_mi_for_each_endpoint(root, ep) {
+ char *desc = nvme_mi_endpoint_desc(ep);
+ printf("%s\n", desc);
+ rc = do_action_endpoint(action, ep, argc, argv);
+ printf("---\n");
+ free(desc);
+ }
+ nvme_mi_free_root(root);
+ } else {
+ root = nvme_mi_create_root(stderr, DEFAULT_LOGLEVEL);
+ if (!root)
+ err(EXIT_FAILURE, "can't create NVMe root");
+
+ ep = nvme_mi_open_mctp(root, net, eid);
+ if (!ep)
+ errx(EXIT_FAILURE, "can't open MCTP endpoint %d:%d", net, eid);
+ rc = do_action_endpoint(action, ep, argc, argv);
+ nvme_mi_close(ep);
+ nvme_mi_free_root(root);
+ }
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+
diff --git a/examples/telemetry-listen.c b/examples/telemetry-listen.c
new file mode 100644
index 0000000..ec5edb3
--- /dev/null
+++ b/examples/telemetry-listen.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+/**
+ * Open all nvme controller's uevent and listen for changes. If NVME_AEN event
+ * is observed with controller telemetry data, read the log and save it to a
+ * file in /var/log/ with the device's unique name and epoch timestamp.
+ */
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <libnvme.h>
+
+#include <ccan/endian/endian.h>
+
+struct events {
+ nvme_ctrl_t c;
+ int uevent_fd;
+};
+
+static int open_uevent(nvme_ctrl_t c)
+{
+ char buf[0x1000];
+ if (snprintf(buf, sizeof(buf), "%s/uevent", nvme_ctrl_get_sysfs_dir(c)) < 0)
+ return -1;
+ return open(buf, O_RDONLY);
+}
+
+static void save_telemetry(nvme_ctrl_t c)
+{
+ char buf[0x1000];
+ size_t log_size;
+ int ret, fd;
+ struct nvme_telemetry_log *log;
+ time_t s;
+
+ /* Clear the log (rae == false) at the end to see new telemetry events later */
+ ret = nvme_get_ctrl_telemetry(nvme_ctrl_get_fd(c), false, &log, NVME_TELEMETRY_DA_3, &log_size);
+ if (ret)
+ return;
+
+ s = time(NULL);
+ ret = snprintf(buf, sizeof(buf), "/var/log/%s-telemetry-%ld",
+ nvme_ctrl_get_subsysnqn(c), s);
+ if (ret < 0) {
+ free(log);
+ return;
+ }
+
+ fd = open(buf, O_CREAT|O_WRONLY, S_IRUSR|S_IRGRP);
+ if (fd < 0) {
+ free(log);
+ return;
+ }
+
+ ret = write(fd, log, log_size);
+ if (ret < 0)
+ printf("failed to write telemetry log\n");
+ else
+ printf("telemetry log save as %s, wrote:%d size:%zd\n", buf,
+ ret, log_size);
+ close(fd);
+ free(log);
+}
+
+static void check_telemetry(nvme_ctrl_t c, int ufd)
+{
+ char buf[0x1000] = { 0 };
+ char *p, *ptr;
+ int len;
+
+ len = read(ufd, buf, sizeof(buf) - 1);
+ if (len < 0)
+ return;
+
+ ptr = buf;
+ while ((p = strsep(&ptr, "\n")) != NULL) {
+ __u32 aen, type, info, lid;
+
+ if (sscanf(p, "NVME_AEN=0x%08x", &aen) != 1)
+ continue;
+
+ type = aen & 0x07;
+ info = (aen >> 8) & 0xff;
+ lid = (aen >> 16) & 0xff;
+
+ printf("%s: aen type:%x info:%x lid:%d\n",
+ nvme_ctrl_get_name(c), type, info, lid);
+ if (type == NVME_AER_NOTICE &&
+ info == NVME_AER_NOTICE_TELEMETRY)
+ save_telemetry(c);
+ }
+}
+
+static void wait_events(fd_set *fds, struct events *e, int nr)
+{
+ int ret, i;
+
+ for (i = 0; i < nr; i++)
+ check_telemetry(e[i].c, e[i].uevent_fd);
+
+ while (1) {
+ ret = select(nr, fds, NULL, NULL, NULL);
+ if (ret < 0)
+ return;
+
+ for (i = 0; i < nr; i++) {
+ if (!FD_ISSET(e[i].uevent_fd, fds))
+ continue;
+ check_telemetry(e[i].c, e[i].uevent_fd);
+ }
+ }
+}
+
+int main()
+{
+ struct events *e;
+ fd_set fds;
+ int i = 0;
+
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ nvme_host_t h;
+ nvme_root_t r;
+
+ r = nvme_scan(NULL);
+ if (!r)
+ return EXIT_FAILURE;
+
+ nvme_for_each_host(r, h)
+ nvme_for_each_subsystem(h, s)
+ nvme_subsystem_for_each_ctrl(s, c)
+ i++;
+
+ e = calloc(i, sizeof(struct events));
+ FD_ZERO(&fds);
+ i = 0;
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ nvme_subsystem_for_each_ctrl(s, c) {
+ int fd = open_uevent(c);
+
+ if (fd < 0)
+ continue;
+ FD_SET(fd, &fds);
+ e[i].uevent_fd = fd;
+ e[i].c = c;
+ i++;
+ }
+ }
+ }
+
+ wait_events(&fds, e, i);
+ nvme_free_tree(r);
+ free(e);
+
+ return EXIT_SUCCESS;
+}
diff --git a/internal/meson.build b/internal/meson.build
new file mode 100644
index 0000000..744f83e
--- /dev/null
+++ b/internal/meson.build
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2022 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+#
+# Because libnvme is used as a subproject of nvme-cli,
+# the config.h file must be generated as a private file
+# hidden from nvme-cli.
+
+config_h = configure_file(
+ output: 'config.h',
+ configuration: conf
+)
+
+internal_incdir = include_directories('.')
+
+config_dep = declare_dependency(
+ include_directories : internal_incdir,
+ sources: config_h)
+
+config_h_path = meson.current_build_dir() / 'config.h'
+
+add_project_arguments(
+ [
+ '-include', config_h_path,
+ ],
+ language : 'c',
+)
diff --git a/libnvme.spec.in b/libnvme.spec.in
new file mode 100644
index 0000000..ea263ce
--- /dev/null
+++ b/libnvme.spec.in
@@ -0,0 +1,53 @@
+Name: @NAME@
+Version: @VERSION@
+Release: 0
+Summary: Linux-native nvme device management library
+
+License: @LICENSE@
+Source: libnvme.tar.gz
+BuildRoot: %{_tmppath}/%{name}-root
+URL: http://github.com/linux-nvme/libnvme
+
+%description
+Provides library functions for accessing and managing nvme devices on a Linux
+system.
+
+%package devel
+Summary: Development files for Linux-native nvme
+Requires: libnvme
+Provides: libnvme.so.1
+
+%description devel
+This package provides header files to include and libraries to link with
+for Linux-native nvme device maangement.
+
+%prep
+%autosetup -c
+
+%build
+meson .build -Ddocs=man -Ddocs-build=true -Ddefault_library=both
+
+%install
+cd .build
+meson install --destdir %{buildroot} --skip-subprojects
+
+%files
+%defattr(-,root,root)
+%attr(0755,root,root) %{_libdir}/libnvme*
+%doc COPYING
+
+%files devel
+%defattr(-,root,root)
+%attr(-,root,root) %{_includedir}/nvme/
+%attr(0644,root,root) %{_includedir}/libnvme*
+%attr(0755,root,root) %{_libdir}/libnvme*
+%attr(0644,root,root) %{_libdir}/pkgconfig/*
+%attr(0644,root,root) %{_mandir}/man2/*
+
+%changelog
+* Wed Jul 13 2022 Steven Seungcheol Lee <sc108.lee@samsung.com>
+- Enable building rpm
+- meson is needed higher version then what rpm repo offering (use pip install)
+
+* Thu Dec 12 2019 Keith Busch <kbusch@kernel.org> - 0.1
+- Initial version
diff --git a/libnvme/.gitignore b/libnvme/.gitignore
new file mode 100644
index 0000000..e943f50
--- /dev/null
+++ b/libnvme/.gitignore
@@ -0,0 +1,4 @@
+build/
+__pycache__/
+nvme.py
+nvme_wrap.c
diff --git a/libnvme/README.md b/libnvme/README.md
new file mode 100644
index 0000000..3195715
--- /dev/null
+++ b/libnvme/README.md
@@ -0,0 +1,66 @@
+# Python bindings for libnvme
+
+We use [SWIG](http://www.swig.org/) to generate Python bindings for libnvme.
+
+## How to use
+
+```python
+#!/usr/bin/env python3
+import sys
+import pprint
+from libnvme import nvme
+
+def disc_supp_str(dlp_supp_opts):
+ bitmap = {
+ nvme.NVMF_LOG_DISC_LID_EXTDLPES: "EXTDLPES",
+ nvme.NVMF_LOG_DISC_LID_PLEOS: "PLEOS",
+ nvme.NVMF_LOG_DISC_LID_ALLSUBES: "ALLSUBES",
+ }
+ return [txt for msk, txt in bitmap.items() if dlp_supp_opts & msk]
+
+root = nvme.root() # This is a singleton
+root.log_level('debug') # Optional: extra debug info
+
+host = nvme.host(root) # This "may be" a singleton.
+subsysnqn = [string] # e.g. nvme.NVME_DISC_SUBSYS_NAME, ...
+transport = [string] # One of: 'tcp', 'rdma', 'fc', 'loop'.
+traddr = [IPv4 or IPv6] # e.g. '192.168.10.10', 'fd2e:853b:3cad:e135:506a:65ee:29f2:1b18', ...
+trsvcid = [string] # e.g. '8009', '4420', ...
+host_iface = [interface] # e.g. 'eth1', ens256', ...
+ctrl = nvme.ctrl(root, subsysnqn=subsysnqn, transport=transport, traddr=traddr, trsvcid=trsvcid, host_iface=host_iface)
+
+try:
+ cfg = {
+ 'hdr_digest': True, # Enable header digests
+ 'data_digest': False, # Disable data digests
+ }
+ ctrl.connect(host, cfg)
+ print(f"connected to {ctrl.name} subsys {ctrl.subsystem.name}")
+except Exception as e:
+ sys.exit(f'Failed to connect: {e}')
+
+supported_log_pages = ctrl.supported_log_pages()
+try:
+ # Get the supported options for the Get Discovery Log Page command
+ dlp_supp_opts = supported_log_pages[nvme.NVME_LOG_LID_DISCOVER] >> 16
+except (TypeError, IndexError):
+ dlp_supp_opts = 0
+
+print(f"LID {nvme.NVME_LOG_LID_DISCOVER:02x}h (Discovery), supports: {disc_supp_str(dlp_supp_opts)}")
+try:
+ lsp = nvme.NVMF_LOG_DISC_LSP_PLEO if dlp_supp_opts & nvme.NVMF_LOG_DISC_LID_PLEOS else 0
+ log_pages = ctrl.discover(lsp=lsp)
+ print(pprint.pformat(log_pages))
+except Exception as e:
+ sys.exit(f'Failed to retrieve log pages: {e}')
+
+try:
+ ctrl.disconnect()
+except Exception as e:
+ sys.exit(f'Failed to disconnect: {e}')
+
+ctrl = None
+host = None
+root = None
+```
+
diff --git a/libnvme/__init__.py b/libnvme/__init__.py
new file mode 100644
index 0000000..b2fe8dd
--- /dev/null
+++ b/libnvme/__init__.py
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2022 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+
+__version__ = @PROJECT_VERSION@
+__git_version__ = @GIT_VERSION@
diff --git a/libnvme/meson.build b/libnvme/meson.build
new file mode 100644
index 0000000..b5b99fc
--- /dev/null
+++ b/libnvme/meson.build
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2021 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+#
+want_python = get_option('python')
+if want_python.disabled()
+ build_python_bindings = false
+ py3_dep = dependency('', required: false) # Needed for muon
+else
+ python3 = import('python').find_installation('python3')
+ py3_dep = python3.dependency(required: want_python)
+ swig = find_program('swig', required: want_python)
+ header_found = cc.has_header('Python.h', dependencies: py3_dep, required: want_python)
+ build_python_bindings = py3_dep.found() and swig.found() and header_found
+endif
+
+if build_python_bindings
+ r = run_command(swig, ['-version'], check: true) # Returns: "\nSWIG Version 4.1.1\n\nCompiled with ..."
+ swig_version = r.stdout().split('\n')[1].split()[2].strip()
+ if swig_version.version_compare('<4.1.0')
+ swig_cmd = [swig, '-python', '-py3', '-o', '@OUTPUT1@', '@INPUT0@']
+ else
+ swig_cmd = [swig, '-python', '-o', '@OUTPUT1@', '@INPUT0@']
+ endif
+
+ pymod_swig = custom_target(
+ 'nvme.py',
+ input: ['nvme.i'],
+ output: ['nvme.py', 'nvme_wrap.c'],
+ command: swig_cmd,
+ install: true,
+ install_dir: [python3.get_install_dir(pure: false, subdir: 'libnvme'), false],
+ )
+
+ pynvme_clib = python3.extension_module(
+ '_nvme',
+ pymod_swig[1],
+ dependencies : [libnvme_dep, py3_dep],
+ include_directories: [incdir, internal_incdir],
+ link_with: [libccan],
+ install: true,
+ subdir: 'libnvme',
+ )
+
+ # Little hack to copy file __init__.py to the build directory.
+ # This is needed to create the proper directory layout to run the tests.
+ # It's a hack because we don't really "configure" file __init__.py and we
+ # could simply install directly from the source tree with:
+ # python3.install_sources(['__init__.py', ], pure:false, subdir:'libnvme')
+ # However, since we need __init__.py in the build directory to run the tests
+ # we resort to this hack to copy it.
+ configure_file(
+ input: '__init__.py',
+ output: '__init__.py',
+ configuration: conf,
+ install_dir: python3.get_install_dir(pure: false, subdir: 'libnvme'),
+ )
+
+ # Set the PYTHONPATH so that we can run the
+ # tests directly from the build directory.
+ test_env = environment()
+ test_env.append('MALLOC_PERTURB_', '0')
+ test_env.append('PYTHONPATH', join_paths(meson.current_build_dir(), '..'))
+ test_env.append('PYTHONMALLOC', 'malloc')
+
+ # Test section
+ test('python-import-libnvme', python3, args: ['-c', 'from libnvme import nvme'], env: test_env, depends: pynvme_clib)
+
+ py_tests = [
+ [ 'create-ctrl-object', [ files('tests/create-ctrl-obj.py'), ] ],
+ [ 'sigsegv-during-gc', [ files('tests/gc.py'), ] ],
+ [ 'read-nbft-file', [ files('tests/test-nbft.py'), '--filename', join_paths(meson.current_source_dir(), 'tests', 'NBFT') ] ],
+ ]
+ foreach test: py_tests
+ description = test[0]
+ args = test[1]
+ test('python-' + description, python3, args: args, env: test_env, depends: pynvme_clib)
+ endforeach
+endif
diff --git a/libnvme/nvme.i b/libnvme/nvme.i
new file mode 100644
index 0000000..eb94cac
--- /dev/null
+++ b/libnvme/nvme.i
@@ -0,0 +1,1068 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021 SUSE Software Solutions
+ *
+ * Authors: Hannes Reinecke <hare@suse.de>
+ */
+
+%module(docstring="Python bindings for libnvme") nvme
+%feature("autodoc", "1");
+
+%include "exception.i"
+
+%allowexception;
+
+%rename(root) nvme_root;
+%rename(host) nvme_host;
+%rename(ctrl) nvme_ctrl;
+%rename(subsystem) nvme_subsystem;
+%rename(ns) nvme_ns;
+
+%{
+ #include <ccan/list/list.h>
+ #include <ccan/endian/endian.h>
+ #include "nvme/tree.h"
+ #include "nvme/fabrics.h"
+ #include "nvme/private.h"
+ #include "nvme/log.h"
+ #include "nvme/ioctl.h"
+ #include "nvme/types.h"
+ #include "nvme/nbft.h"
+
+ static int host_iter_err = 0;
+ static int subsys_iter_err = 0;
+ static int ctrl_iter_err = 0;
+ static int ns_iter_err = 0;
+ static int connect_err = 0;
+ static int discover_err = 0;
+
+ static void PyDict_SetItemStringDecRef(PyObject * p, const char *key, PyObject *val) {
+ PyDict_SetItemString(p, key, val); /* Does NOT steal reference to val .. */
+ Py_XDECREF(val); /* .. therefore decrement ref. count. */
+ }
+ PyObject *hostnqn_from_file() {
+ char * val = nvmf_hostnqn_from_file();
+ PyObject * obj = PyUnicode_FromString(val);
+ free(val);
+ return obj;
+ }
+ PyObject *hostid_from_file() {
+ char * val = nvmf_hostid_from_file();
+ PyObject * obj = PyUnicode_FromString(val);
+ free(val);
+ return obj;
+ }
+%}
+PyObject *hostnqn_from_file();
+PyObject *hostid_from_file();
+
+%inline %{
+ struct host_iter {
+ struct nvme_root *root;
+ struct nvme_host *pos;
+ };
+
+ struct subsystem_iter {
+ struct nvme_host *host;
+ struct nvme_subsystem *pos;
+ };
+
+ struct ctrl_iter {
+ struct nvme_subsystem *subsystem;
+ struct nvme_ctrl *pos;
+ };
+
+ struct ns_iter {
+ struct nvme_subsystem *subsystem;
+ struct nvme_ctrl *ctrl;
+ struct nvme_ns *pos;
+ };
+%}
+
+%exception host_iter::__next__ {
+ host_iter_err = 0;
+ $action /* $action sets host_iter_err to non-zero value on failure */
+ if (host_iter_err) {
+ PyErr_SetString(PyExc_StopIteration, "End of list");
+ return NULL;
+ }
+}
+
+%exception subsystem_iter::__next__ {
+ subsys_iter_err = 0;
+ $action /* $action sets subsys_iter_err to non-zero value on failure */
+ if (subsys_iter_err) {
+ PyErr_SetString(PyExc_StopIteration, "End of list");
+ return NULL;
+ }
+}
+
+%exception ctrl_iter::__next__ {
+ ctrl_iter_err = 0;
+ $action /* $action sets ctrl_iter_err to non-zero value on failure */
+ if (ctrl_iter_err) {
+ PyErr_SetString(PyExc_StopIteration, "End of list");
+ return NULL;
+ }
+}
+
+%exception ns_iter::__next__ {
+ ns_iter_err = 0;
+ $action /* $action sets ns_iter_err to non-zero value on failure */
+ if (ns_iter_err) {
+ PyErr_SetString(PyExc_StopIteration, "End of list");
+ return NULL;
+ }
+}
+
+%exception nvme_ctrl::connect {
+ connect_err = 0;
+ errno = 0;
+ $action /* $action sets connect_err to non-zero value on failure */
+ if (connect_err == 1) {
+ SWIG_exception(SWIG_AttributeError, "Existing controller connection");
+ } else if (connect_err) {
+ const char *errstr = nvme_errno_to_string(errno);
+ if (errstr) {
+ SWIG_exception(SWIG_RuntimeError, errstr);
+ } else {
+ SWIG_exception(SWIG_RuntimeError, "Connect failed");
+ }
+ }
+}
+
+%exception nvme_ctrl::discover {
+ discover_err = 0;
+ $action /* $action sets discover_err to non-zero value on failure */
+ if (discover_err) {
+ SWIG_exception(SWIG_RuntimeError, "Discover failed");
+ }
+}
+
+%typemap(in) struct nvme_fabrics_config *($*1_type temp){
+ Py_ssize_t pos = 0;
+ PyObject * key,*value;
+ memset(&temp, 0, sizeof(temp));
+ temp.tos = -1;
+ temp.ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO;
+ while (PyDict_Next($input, &pos, &key, &value)) {
+ if (!PyUnicode_CompareWithASCIIString(key, "host_traddr")) {
+ temp.host_traddr = PyBytes_AsString(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "host_iface")) {
+ temp.host_iface = PyBytes_AsString(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "nr_io_queues")) {
+ temp.nr_io_queues = PyLong_AsLong(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "reconnect_delay")) {
+ temp.reconnect_delay = PyLong_AsLong(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "ctrl_loss_tmo")) {
+ temp.ctrl_loss_tmo = PyLong_AsLong(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "keep_alive_tmo")) {
+ temp.keep_alive_tmo = PyLong_AsLong(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "nr_write_queues")) {
+ temp.nr_write_queues = PyLong_AsLong(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "nr_poll_queues")) {
+ temp.nr_poll_queues = PyLong_AsLong(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "tos")) {
+ temp.tos = PyLong_AsLong(value);
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "duplicate_connect")) {
+ temp.duplicate_connect = PyObject_IsTrue(value) ? true : false;
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "disable_sqflow")) {
+ temp.disable_sqflow = PyObject_IsTrue(value) ? true : false;
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "hdr_digest")) {
+ temp.hdr_digest = PyObject_IsTrue(value) ? true : false;
+ continue;
+ }
+ if (!PyUnicode_CompareWithASCIIString(key, "data_digest")) {
+ temp.data_digest = PyObject_IsTrue(value) ? true : false;
+ continue;
+ }
+ }
+ $1 = &temp;
+};
+
+%typemap(out) uint8_t [8] {
+ $result = PyBytes_FromStringAndSize((char *)$1, 8);
+};
+
+%typemap(out) uint8_t [16] {
+ $result = PyBytes_FromStringAndSize((char *)$1, 16);
+};
+
+%typemap(newfree) struct nvmf_discovery_log * {
+ if ($1) free($1);
+}
+
+%typemap(out) struct nvmf_discovery_log * {
+ struct nvmf_discovery_log *log = $1;
+ int numrec = log ? log->numrec : 0, i;
+ PyObject *obj = PyList_New(numrec);
+ if (!obj) return NULL;
+
+ for (i = 0; i < numrec; i++) {
+ struct nvmf_disc_log_entry *e = &log->entries[i];
+ PyObject *entry = PyDict_New(), *val;
+
+ switch (e->trtype) {
+ case NVMF_TRTYPE_UNSPECIFIED:
+ val = PyUnicode_FromString("unspecified");
+ break;
+ case NVMF_TRTYPE_RDMA:
+ val = PyUnicode_FromString("rdma");
+ break;
+ case NVMF_TRTYPE_FC:
+ val = PyUnicode_FromString("fc");
+ break;
+ case NVMF_TRTYPE_TCP:
+ val = PyUnicode_FromString("tcp");
+ break;
+ case NVMF_TRTYPE_LOOP:
+ val = PyUnicode_FromString("loop");
+ break;
+ default:
+ val = PyLong_FromLong(e->trtype);
+ }
+ PyDict_SetItemStringDecRef(entry, "trtype", val);
+
+ switch (e->adrfam) {
+ case NVMF_ADDR_FAMILY_PCI:
+ val = PyUnicode_FromString("pci");
+ break;
+ case NVMF_ADDR_FAMILY_IP4:
+ val = PyUnicode_FromString("ipv4");
+ break;
+ case NVMF_ADDR_FAMILY_IP6:
+ val = PyUnicode_FromString("ipv6");
+ break;
+ case NVMF_ADDR_FAMILY_IB:
+ val = PyUnicode_FromString("infiniband");
+ break;
+ case NVMF_ADDR_FAMILY_FC:
+ val = PyUnicode_FromString("fc");
+ break;
+ default:
+ val = PyLong_FromLong(e->adrfam);
+ }
+ PyDict_SetItemStringDecRef(entry, "adrfam", val);
+
+ val = PyUnicode_FromString(e->traddr);
+ PyDict_SetItemStringDecRef(entry, "traddr", val);
+ val = PyUnicode_FromString(e->trsvcid);
+ PyDict_SetItemStringDecRef(entry, "trsvcid", val);
+ val = PyUnicode_FromString(e->subnqn);
+ PyDict_SetItemStringDecRef(entry, "subnqn", val);
+
+ switch (e->subtype) {
+ case NVME_NQN_DISC:
+ val = PyUnicode_FromString("referral");
+ break;
+ case NVME_NQN_NVME:
+ val = PyUnicode_FromString("nvme");
+ break;
+ case NVME_NQN_CURR:
+ val = PyUnicode_FromString("discovery");
+ break;
+ default:
+ val = PyLong_FromLong(e->subtype);
+ }
+ PyDict_SetItemStringDecRef(entry, "subtype", val);
+
+ switch (e->treq) {
+ case NVMF_TREQ_NOT_SPECIFIED:
+ val = PyUnicode_FromString("not specified");
+ break;
+ case NVMF_TREQ_REQUIRED:
+ val = PyUnicode_FromString("required");
+ break;
+ case NVMF_TREQ_NOT_REQUIRED:
+ val = PyUnicode_FromString("not required");
+ break;
+ case NVMF_TREQ_DISABLE_SQFLOW:
+ val = PyUnicode_FromString("disable sqflow");
+ break;
+ default:
+ val = PyLong_FromLong(e->treq);
+ }
+ PyDict_SetItemStringDecRef(entry, "treq", val);
+
+ val = PyLong_FromLong(e->portid);
+ PyDict_SetItemStringDecRef(entry, "portid", val);
+ val = PyLong_FromLong(e->cntlid);
+ PyDict_SetItemStringDecRef(entry, "cntlid", val);
+ val = PyLong_FromLong(e->asqsz);
+ PyDict_SetItemStringDecRef(entry, "asqsz", val);
+ val = PyLong_FromLong(e->eflags);
+ PyDict_SetItemStringDecRef(entry, "eflags", val);
+ PyList_SetItem(obj, i, entry); /* steals ref. to object - no need to decref */
+ }
+ $result = obj;
+};
+
+#include "tree.h"
+#include "fabrics.h"
+#define STR_OR_NONE(str) (!(str) ? "None" : str)
+
+struct nvme_root {
+ %immutable config_file;
+ %immutable application;
+ char *config_file;
+ char *application;
+};
+
+struct nvme_host {
+ %immutable hostnqn;
+ %immutable hostid;
+ %immutable hostsymname;
+ char *hostnqn;
+ char *hostid;
+ char *hostsymname;
+ %extend {
+ char *dhchap_key;
+ }
+};
+
+struct nvme_subsystem {
+ %immutable subsysnqn;
+ %immutable model;
+ %immutable serial;
+ %immutable firmware;
+ %immutable application;
+ char *subsysnqn;
+ char *model;
+ char *serial;
+ char *firmware;
+ char *application;
+};
+
+struct nvme_ctrl {
+ %immutable sysfs_dir;
+ %immutable address;
+ %immutable firmware;
+ %immutable model;
+ %immutable numa_node;
+ %immutable queue_count;
+ %immutable serial;
+ %immutable sqsize;
+ %immutable transport;
+ %immutable subsysnqn;
+ %immutable traddr;
+ %immutable trsvcid;
+ %immutable dhchap_host_key;
+ %immutable dhchap_key;
+ %immutable cntrltype;
+ %immutable dctype;
+ %immutable discovery_ctrl;
+ %immutable discovered;
+ %immutable persistent;
+ char *sysfs_dir;
+ char *address;
+ char *firmware;
+ char *model;
+ char *numa_node;
+ char *queue_count;
+ char *serial;
+ char *sqsize;
+ char *transport;
+ char *subsysnqn;
+ char *traddr;
+ char *trsvcid;
+ %extend {
+ char *dhchap_host_key:
+ char *dhchap_key;
+ }
+ char *cntrltype;
+ char *dctype;
+ bool discovery_ctrl;
+ bool discovered;
+ bool persistent;
+};
+
+struct nvme_ns {
+ %immutable nsid;
+ %immutable eui64;
+ %immutable nguid;
+ %immutable uuid;
+ unsigned int nsid;
+ uint8_t eui64[8];
+ uint8_t nguid[16];
+ uint8_t uuid[16];
+};
+
+%extend nvme_root {
+ nvme_root(const char *config_file = NULL) {
+ return nvme_scan(config_file);
+ }
+ ~nvme_root() {
+ nvme_free_tree($self);
+ }
+ void log_level(const char *level) {
+ int log_level = DEFAULT_LOGLEVEL;
+ if (!strcmp(level, "debug")) log_level = LOG_DEBUG;
+ else if (!strcmp(level, "info")) log_level = LOG_INFO;
+ else if (!strcmp(level, "notice")) log_level = LOG_NOTICE;
+ else if (!strcmp(level, "warning")) log_level = LOG_WARNING;
+ else if (!strcmp(level, "err")) log_level = LOG_ERR;
+ else if (!strcmp(level, "crit")) log_level = LOG_CRIT;
+ else if (!strcmp(level, "alert")) log_level = LOG_ALERT;
+ else if (!strcmp(level, "emerg")) log_level = LOG_EMERG;
+ nvme_init_logging($self, log_level, false, false);
+ }
+ struct nvme_host *hosts() {
+ return nvme_first_host($self);
+ }
+ void refresh_topology() {
+ nvme_refresh_topology($self);
+ }
+ void update_config() {
+ nvme_update_config($self);
+ }
+ void dump_config() {
+ nvme_dump_config($self);
+ }
+}
+
+%extend host_iter {
+ struct host_iter *__iter__() {
+ return $self;
+ }
+ struct nvme_host *__next__() {
+ struct nvme_host *this = $self->pos;
+
+ if (!this) {
+ host_iter_err = 1;
+ return NULL;
+ }
+ $self->pos = nvme_next_host($self->root, this);
+ return this;
+ }
+}
+
+%define SET_SYMNAME_DOCSTRING
+"@brief Set or Clear Host's Symbolic Name
+
+@param hostsymname: A symbolic name, or None to clear the symbolic name.
+@type hostsymname: str|None
+
+@return: None"
+%enddef
+
+%pythonappend nvme_host::nvme_host(struct nvme_root *r,
+ const char *hostnqn,
+ const char *hostid,
+ const char *hostkey,
+ const char *hostsymname) {
+ self.__parent = r # Keep a reference to parent to ensure garbage collection happens in the right order}
+%extend nvme_host {
+ nvme_host(struct nvme_root *r,
+ const char *hostnqn = NULL,
+ const char *hostid = NULL,
+ const char *hostkey = NULL,
+ const char *hostsymname = NULL) {
+ nvme_host_t h = hostnqn ? nvme_lookup_host(r, hostnqn, hostid) : nvme_default_host(r);
+ if (hostsymname)
+ nvme_host_set_hostsymname(h, hostsymname);
+ if (hostkey)
+ nvme_host_set_dhchap_key(h, hostkey);
+ return h;
+ }
+ ~nvme_host() {
+ nvme_free_host($self);
+ }
+ %feature("autodoc", SET_SYMNAME_DOCSTRING) set_symname;
+ void set_symname(const char *hostsymname) {
+ nvme_host_set_hostsymname($self, hostsymname);
+ }
+
+ PyObject* __str__() {
+ return PyUnicode_FromFormat("nvme.host(%s,%s)", STR_OR_NONE($self->hostnqn), STR_OR_NONE($self->hostid));
+ }
+ struct host_iter __iter__() {
+ struct host_iter ret = {
+ .root = nvme_host_get_root($self),
+ .pos = $self
+ };
+ return ret;
+ }
+ struct nvme_subsystem* subsystems() {
+ return nvme_first_subsystem($self);
+ }
+}
+
+%{
+ const char *nvme_host_dhchap_key_get(struct nvme_host *h) {
+ return nvme_host_get_dhchap_key(h);
+ }
+ void nvme_host_dhchap_key_set(struct nvme_host *h, char *key) {
+ nvme_host_set_dhchap_key(h, key);
+ }
+%};
+
+%extend subsystem_iter {
+ struct subsystem_iter *__iter__() {
+ return $self;
+ }
+ struct nvme_subsystem *__next__() {
+ struct nvme_subsystem *this = $self->pos;
+
+ if (!this) {
+ subsys_iter_err = 1;
+ return NULL;
+ }
+ $self->pos = nvme_next_subsystem($self->host, this);
+ return this;
+ }
+}
+
+%extend ns_iter {
+ struct ns_iter *__iter__() {
+ return $self;
+ }
+ struct nvme_ns *__next__() {
+ struct nvme_ns *this = $self->pos;
+
+ if (!this) {
+ ns_iter_err = 1;
+ return NULL;
+ }
+ if ($self->ctrl)
+ $self->pos = nvme_ctrl_next_ns($self->ctrl, this);
+ else
+ $self->pos = nvme_subsystem_next_ns($self->subsystem, this);
+ return this;
+ }
+}
+
+%pythonappend nvme_subsystem::nvme_subsystem(struct nvme_host *host,
+ const char *subsysnqn,
+ const char *name) {
+ self.__parent = host # Keep a reference to parent to ensure garbage collection happens in the right order}
+%extend nvme_subsystem {
+ nvme_subsystem(struct nvme_host *host,
+ const char *subsysnqn,
+ const char *name = NULL) {
+ return nvme_lookup_subsystem(host, name, subsysnqn);
+ }
+ ~nvme_subsystem() {
+ nvme_free_subsystem($self);
+ }
+ PyObject *__str__() {
+ return PyUnicode_FromFormat("nvme.subsystem(%s,%s)", STR_OR_NONE($self->name), STR_OR_NONE($self->subsysnqn));
+ }
+ struct subsystem_iter __iter__() {
+ struct subsystem_iter ret = {
+ .host = nvme_subsystem_get_host($self),
+ .pos = $self
+ };
+ return ret;
+ }
+ struct nvme_ctrl *controllers() {
+ return nvme_subsystem_first_ctrl($self);
+ }
+ struct nvme_ns *namespaces() {
+ return nvme_subsystem_first_ns($self);
+ }
+ %immutable name;
+ const char *name;
+ %immutable host;
+ struct nvme_host *host;
+}
+
+%{
+ const char *nvme_subsystem_name_get(struct nvme_subsystem *s) {
+ return nvme_subsystem_get_name(s);
+ }
+ struct nvme_host *nvme_subsystem_host_get(struct nvme_subsystem *s) {
+ return nvme_subsystem_get_host(s);
+ }
+%};
+
+%extend ctrl_iter {
+ struct ctrl_iter *__iter__() {
+ return $self;
+ }
+ struct nvme_ctrl *__next__() {
+ struct nvme_ctrl *this = $self->pos;
+
+ if (!this) {
+ ctrl_iter_err = 1;
+ return NULL;
+ }
+ $self->pos = nvme_subsystem_next_ctrl($self->subsystem, this);
+ return this;
+ }
+}
+
+%pythonappend nvme_ctrl::connect(struct nvme_host *h,
+ struct nvme_fabrics_config *cfg) {
+ self.__host = h # Keep a reference to parent to ensure ctrl obj gets GCed before host}
+%pythonappend nvme_ctrl::init(struct nvme_host *h, int instance) {
+ self.__host = h # Keep a reference to parent to ensure ctrl obj gets GCed before host}
+%extend nvme_ctrl {
+ nvme_ctrl(struct nvme_root *r,
+ const char *subsysnqn,
+ const char *transport,
+ const char *traddr = NULL,
+ const char *host_traddr = NULL,
+ const char *host_iface = NULL,
+ const char *trsvcid = NULL) {
+ return nvme_create_ctrl(r, subsysnqn, transport, traddr,
+ host_traddr, host_iface, trsvcid);
+ }
+ ~nvme_ctrl() {
+ nvme_free_ctrl($self);
+ }
+
+ void discovery_ctrl_set(bool discovery) {
+ nvme_ctrl_set_discovery_ctrl($self, discovery);
+ }
+
+ bool init(struct nvme_host *h, int instance) {
+ return nvme_init_ctrl(h, $self, instance) == 0;
+ }
+
+ void connect(struct nvme_host *h,
+ struct nvme_fabrics_config *cfg = NULL) {
+ int ret;
+ const char *dev;
+
+ dev = nvme_ctrl_get_name($self);
+ if (dev && !cfg->duplicate_connect) {
+ connect_err = 1;
+ return;
+ }
+
+ Py_BEGIN_ALLOW_THREADS /* Release Python GIL */
+ ret = nvmf_add_ctrl(h, $self, cfg);
+ Py_END_ALLOW_THREADS /* Reacquire Python GIL */
+
+ if (ret < 0) {
+ connect_err = 2;
+ return;
+ }
+ }
+ bool connected() {
+ return nvme_ctrl_get_name($self) != NULL;
+ }
+ void persistent_set(bool persistent) {
+ nvme_ctrl_set_persistent($self, persistent);
+ }
+ void rescan() {
+ nvme_rescan_ctrl($self);
+ }
+ void disconnect() {
+ Py_BEGIN_ALLOW_THREADS /* Release Python GIL */
+ nvme_disconnect_ctrl($self);
+ Py_END_ALLOW_THREADS /* Reacquire Python GIL */
+ }
+
+ %feature("autodoc", "@return: True if controller supports explicit registration. False otherwise.") is_registration_supported;
+ bool is_registration_supported() {
+ return nvmf_is_registration_supported($self);
+ }
+
+ %feature("autodoc", "@return None on success or Error string on error.") registration_ctlr;
+ PyObject *registration_ctlr(enum nvmf_dim_tas tas) {
+ __u32 result;
+ int status;
+
+ Py_BEGIN_ALLOW_THREADS /* Release Python GIL */
+ status = nvmf_register_ctrl($self, NVMF_DIM_TAS_REGISTER, &result);
+ Py_END_ALLOW_THREADS /* Reacquire Python GIL */
+
+ if (status != NVME_SC_SUCCESS) {
+ /* On error, return an error message */
+ return (status < 0) ?
+ PyUnicode_FromFormat("Status:0x%04x - %s", status, nvme_status_to_string(status, false)) :
+ PyUnicode_FromFormat("Result:0x%04x, Status:0x%04x - %s", result, status, nvme_status_to_string(status, false));
+ }
+
+ /* On success, return None */
+ Py_RETURN_NONE;
+ }
+
+ %newobject discover;
+ struct nvmf_discovery_log *discover(int lsp = 0, int max_retries = 6) {
+ struct nvmf_discovery_log *logp;
+ struct nvme_get_discovery_args args = {
+ .c = $self,
+ .args_size = sizeof(args),
+ .max_retries = max_retries,
+ .result = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lsp = lsp,
+ };
+
+ Py_BEGIN_ALLOW_THREADS /* Release Python GIL */
+ logp = nvmf_get_discovery_wargs(&args);
+ Py_END_ALLOW_THREADS /* Reacquire Python GIL */
+
+ if (logp == NULL) discover_err = 1;
+ return logp;
+ }
+
+ %feature("autodoc", "@return: List of supported log pages") supported_log_pages;
+ PyObject *supported_log_pages(bool rae = true) {
+ struct nvme_supported_log_pages log;
+ PyObject *obj = NULL;
+ int ret = 0;
+
+ Py_BEGIN_ALLOW_THREADS /* Release Python GIL */
+ ret = nvme_get_log_supported_log_pages(nvme_ctrl_get_fd($self), rae, &log);
+ Py_END_ALLOW_THREADS /* Reacquire Python GIL */
+
+ if (ret < 0) {
+ Py_RETURN_NONE;
+ }
+
+ obj = PyList_New(NVME_LOG_SUPPORTED_LOG_PAGES_MAX);
+ if (!obj) Py_RETURN_NONE;
+
+ for (int i = 0; i < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; i++)
+ PyList_SetItem(obj, i, PyLong_FromLong(le32_to_cpu(log.lid_support[i]))); /* steals ref. to object - no need to decref */
+
+ return obj;
+ }
+
+ PyObject* __str__() {
+ return $self->address ?
+ PyUnicode_FromFormat("nvme_ctrl(transport=%s,%s)", STR_OR_NONE($self->transport), STR_OR_NONE($self->address)) :
+ PyUnicode_FromFormat("nvme_ctrl(transport=%s)", STR_OR_NONE($self->transport));
+ }
+ struct ctrl_iter __iter__() {
+ struct ctrl_iter ret = {
+ .subsystem = nvme_ctrl_get_subsystem($self),
+ .pos = $self
+ };
+ return ret;
+ }
+ struct nvme_ns* namespaces() {
+ return nvme_ctrl_first_ns($self);
+ }
+ %immutable name;
+ const char *name;
+ %immutable subsystem;
+ struct nvme_subsystem *subsystem;
+ %immutable state;
+ const char *state;
+}
+
+%{
+ const char *nvme_ctrl_name_get(struct nvme_ctrl *c) {
+ return nvme_ctrl_get_name(c);
+ }
+ struct nvme_subsystem *nvme_ctrl_subsystem_get(struct nvme_ctrl *c) {
+ return nvme_ctrl_get_subsystem(c);
+ }
+ const char *nvme_ctrl_state_get(struct nvme_ctrl *c) {
+ return nvme_ctrl_get_state(c);
+ }
+ const char *nvme_ctrl_dhchap_key_get(struct nvme_ctrl *c) {
+ return nvme_ctrl_get_dhchap_key(c);
+ }
+ void nvme_ctrl_dhchap_key_set(struct nvme_ctrl *c, const char *key) {
+ nvme_ctrl_set_dhchap_key(c, key);
+ }
+ const char *nvme_ctrl_dhchap_host_key_get(struct nvme_ctrl *c) {
+ return nvme_ctrl_get_dhchap_host_key(c);
+ }
+%};
+
+%pythonappend nvme_ns::nvme_ns(struct nvme_subsystem *s,
+ unsigned int nsid) {
+ self.__parent = s # Keep a reference to parent to ensure garbage collection happens in the right order}
+%extend nvme_ns {
+ nvme_ns(struct nvme_subsystem *s,
+ unsigned int nsid) {
+ return nvme_subsystem_lookup_namespace(s, nsid);
+ }
+ ~nvme_ns() {
+ nvme_free_ns($self);
+ }
+ PyObject *__str__() {
+ return PyUnicode_FromFormat("nvme.ns(%u)", $self->nsid);
+ }
+ struct ns_iter __iter__() {
+ struct ns_iter ret = { .ctrl = nvme_ns_get_ctrl($self),
+ .subsystem = nvme_ns_get_subsystem($self),
+ .pos = $self };
+ return ret;
+ }
+ %immutable name;
+ const char *name;
+}
+
+%{
+ const char *nvme_ns_name_get(struct nvme_ns *n) {
+ return nvme_ns_get_name(n);
+ }
+%};
+
+/******
+ NBFT
+ ******/
+%{
+ static PyObject *ssns_to_dict(struct nbft_info_subsystem_ns *ss)
+ {
+ unsigned int i;
+ PyObject *output = PyDict_New();
+ PyObject *hfis = PyList_New(ss->num_hfis);
+
+ for (i = 0; i < ss->num_hfis; i++)
+ PyList_SetItem(hfis, i, PyLong_FromLong(ss->hfis[i]->index - 1)); /* steals ref. to object - no need to decref */
+
+ PyDict_SetItemStringDecRef(output, "hfi_indexes", hfis);
+
+ PyDict_SetItemStringDecRef(output, "trtype", PyUnicode_FromString(ss->transport));
+ PyDict_SetItemStringDecRef(output, "traddr", PyUnicode_FromString(ss->traddr));
+ PyDict_SetItemStringDecRef(output, "trsvcid", PyUnicode_FromString(ss->trsvcid));
+ PyDict_SetItemStringDecRef(output, "subsys_port_id", PyLong_FromLong(ss->subsys_port_id));
+ PyDict_SetItemStringDecRef(output, "nsid", PyLong_FromLong(ss->nsid));
+
+ {
+ PyObject *nid;
+ switch (ss->nid_type) {
+ case NBFT_INFO_NID_TYPE_EUI64:
+ PyDict_SetItemStringDecRef(output, "nid_type", PyUnicode_FromString("eui64"));
+ nid = PyUnicode_FromFormat("%02x%02x%02x%02x%02x%02x%02x%02x",
+ ss->nid[0], ss->nid[1], ss->nid[2], ss->nid[3],
+ ss->nid[4], ss->nid[5], ss->nid[6], ss->nid[7]);
+ break;
+
+ case NBFT_INFO_NID_TYPE_NGUID:
+ PyDict_SetItemStringDecRef(output, "nid_type", PyUnicode_FromString("nguid"));
+ nid = PyUnicode_FromFormat("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ ss->nid[0], ss->nid[1], ss->nid[2], ss->nid[3],
+ ss->nid[4], ss->nid[5], ss->nid[6], ss->nid[7],
+ ss->nid[8], ss->nid[9], ss->nid[10], ss->nid[11],
+ ss->nid[12], ss->nid[13], ss->nid[14], ss->nid[15]);
+ break;
+
+ case NBFT_INFO_NID_TYPE_NS_UUID:
+ {
+ char uuid_str[NVME_UUID_LEN_STRING];
+ PyDict_SetItemStringDecRef(output, "nid_type", PyUnicode_FromString("uuid"));
+ nvme_uuid_to_string(ss->nid, uuid_str);
+ nid = PyUnicode_FromString(uuid_str);
+ break;
+ }
+
+ default:
+ nid = NULL;
+ break;
+ }
+ if (nid)
+ PyDict_SetItemStringDecRef(output, "nid", nid);
+ }
+
+ if (ss->subsys_nqn)
+ PyDict_SetItemStringDecRef(output, "subsys_nqn", PyUnicode_FromString(ss->subsys_nqn));
+
+ PyDict_SetItemStringDecRef(output, "controller_id", PyLong_FromLong(ss->controller_id));
+ PyDict_SetItemStringDecRef(output, "asqsz", PyLong_FromLong(ss->asqsz));
+
+ if (ss->dhcp_root_path_string)
+ PyDict_SetItemStringDecRef(output, "dhcp_root_path_string", PyUnicode_FromString(ss->dhcp_root_path_string));
+
+ PyDict_SetItemStringDecRef(output, "pdu_header_digest_required", PyBool_FromLong(ss->pdu_header_digest_required));
+ PyDict_SetItemStringDecRef(output, "data_digest_required", PyBool_FromLong(ss->data_digest_required));
+
+ return output;
+ }
+
+ static PyObject *hfi_to_dict(struct nbft_info_hfi *hfi)
+ {
+ PyObject *output = PyDict_New();
+
+ PyDict_SetItemStringDecRef(output, "trtype", PyUnicode_FromString(hfi->transport));
+
+ if (!strcmp(hfi->transport, "tcp")) {
+ PyDict_SetItemStringDecRef(output, "pcidev",
+ PyUnicode_FromFormat("%x:%x:%x.%x",
+ ((hfi->tcp_info.pci_sbdf & 0xffff0000) >> 16),
+ ((hfi->tcp_info.pci_sbdf & 0x0000ff00) >> 8),
+ ((hfi->tcp_info.pci_sbdf & 0x000000f8) >> 3),
+ ((hfi->tcp_info.pci_sbdf & 0x00000007) >> 0)));
+
+ PyDict_SetItemStringDecRef(output, "mac_addr",
+ PyUnicode_FromFormat("%02x:%02x:%02x:%02x:%02x:%02x",
+ hfi->tcp_info.mac_addr[0],
+ hfi->tcp_info.mac_addr[1],
+ hfi->tcp_info.mac_addr[2],
+ hfi->tcp_info.mac_addr[3],
+ hfi->tcp_info.mac_addr[4],
+ hfi->tcp_info.mac_addr[5]));
+
+ PyDict_SetItemStringDecRef(output, "vlan", PyLong_FromLong(hfi->tcp_info.vlan));
+ PyDict_SetItemStringDecRef(output, "ip_origin", PyLong_FromLong(hfi->tcp_info.ip_origin));
+ PyDict_SetItemStringDecRef(output, "ipaddr", PyUnicode_FromString(hfi->tcp_info.ipaddr));
+ PyDict_SetItemStringDecRef(output, "subnet_mask_prefix", PyLong_FromLong(hfi->tcp_info.subnet_mask_prefix));
+ PyDict_SetItemStringDecRef(output, "gateway_ipaddr", PyUnicode_FromString(hfi->tcp_info.gateway_ipaddr));
+ PyDict_SetItemStringDecRef(output, "route_metric", PyLong_FromLong(hfi->tcp_info.route_metric));
+ PyDict_SetItemStringDecRef(output, "primary_dns_ipaddr", PyUnicode_FromString(hfi->tcp_info.primary_dns_ipaddr));
+ PyDict_SetItemStringDecRef(output, "secondary_dns_ipaddr", PyUnicode_FromString(hfi->tcp_info.secondary_dns_ipaddr));
+ PyDict_SetItemStringDecRef(output, "dhcp_server_ipaddr", PyUnicode_FromString(hfi->tcp_info.dhcp_server_ipaddr));
+
+ if (hfi->tcp_info.host_name)
+ PyDict_SetItemStringDecRef(output, "host_name", PyUnicode_FromString(hfi->tcp_info.host_name));
+
+ PyDict_SetItemStringDecRef(output, "this_hfi_is_default_route", PyBool_FromLong(hfi->tcp_info.this_hfi_is_default_route));
+ PyDict_SetItemStringDecRef(output, "dhcp_override", PyBool_FromLong(hfi->tcp_info.dhcp_override));
+ }
+
+ return output;
+ }
+
+ static PyObject *discovery_to_dict(struct nbft_info_discovery *disc)
+ {
+ PyObject *output = PyDict_New();
+
+ if (disc->security)
+ PyDict_SetItemStringDecRef(output, "security_index", PyLong_FromLong(disc->security->index));
+ if (disc->hfi)
+ PyDict_SetItemStringDecRef(output, "hfi_index", PyLong_FromLong(disc->hfi->index - 1));
+ if (disc->uri)
+ PyDict_SetItemStringDecRef(output, "uri", PyUnicode_FromString(disc->uri));
+ if (disc->nqn)
+ PyDict_SetItemStringDecRef(output, "nqn", PyUnicode_FromString(disc->nqn));
+
+ return output;
+ }
+
+ static PyObject *nbft_to_pydict(struct nbft_info *nbft)
+ {
+ PyObject *val;
+ PyObject *output = PyDict_New();
+
+ {
+ PyObject *host = PyDict_New();
+
+ if (nbft->host.nqn)
+ PyDict_SetItemStringDecRef(host, "nqn", PyUnicode_FromString(nbft->host.nqn));
+ if (nbft->host.id) {
+ char uuid_str[NVME_UUID_LEN_STRING];
+ nvme_uuid_to_string((unsigned char *)nbft->host.id, uuid_str);
+ PyDict_SetItemStringDecRef(host, "id", PyUnicode_FromString(uuid_str));
+ }
+
+ PyDict_SetItemStringDecRef(host, "host_id_configured", PyBool_FromLong(nbft->host.host_id_configured));
+ PyDict_SetItemStringDecRef(host, "host_nqn_configured", PyBool_FromLong(nbft->host.host_nqn_configured));
+
+ val = PyUnicode_FromString(nbft->host.primary == NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED ? "not indicated" :
+ nbft->host.primary == NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED ? "unselected" :
+ nbft->host.primary == NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED ? "selected" : "reserved");
+ PyDict_SetItemStringDecRef(host, "primary_admin_host_flag", val);
+
+ PyDict_SetItemStringDecRef(output, "host", host);
+ }
+
+ {
+ size_t ss_num = 0;
+ struct nbft_info_subsystem_ns **ss;
+ PyObject *subsystem;
+
+ /* First, let's find how many entries there are */
+ for (ss = nbft->subsystem_ns_list; ss && *ss; ss++)
+ ss_num++;
+
+ /* Now, let's fill the list using "(*ss)->index - 1"
+ as the index for writing to the list */
+ subsystem = PyList_New(ss_num);
+ for (ss = nbft->subsystem_ns_list; ss && *ss; ss++)
+ PyList_SetItem(subsystem, (*ss)->index - 1, ssns_to_dict(*ss)); /* steals ref. to object - no need to decref */
+
+ PyDict_SetItemStringDecRef(output, "subsystem", subsystem);
+ }
+
+ {
+ size_t hfi_num = 0;
+ struct nbft_info_hfi **hfi;
+ PyObject *hfis;
+
+ /* First, let's find how many entries there are */
+ for (hfi = nbft->hfi_list; hfi && *hfi; hfi++)
+ hfi_num++;
+
+ /* Now, let's fill the list using "(*hfi)->index - 1"
+ as the index for writing to the list */
+ hfis = PyList_New(hfi_num);
+ for (hfi = nbft->hfi_list; hfi && *hfi; hfi++)
+ PyList_SetItem(hfis, (*hfi)->index-1, hfi_to_dict(*hfi)); /* steals ref. to object - no need to decref */
+
+ PyDict_SetItemStringDecRef(output, "hfi", hfis);
+ }
+
+ {
+ size_t disc_num = 0;
+ struct nbft_info_discovery **disc;
+ PyObject *discovery;
+
+ /* First, let's find how many entries there are */
+ for (disc = nbft->discovery_list; disc && *disc; disc++)
+ disc_num++;
+
+ /* Now, let's fill the list using "(*disc)->index - 1"
+ as the index for writing to the list */
+ discovery = PyList_New(disc_num);
+ for (disc = nbft->discovery_list; disc && *disc; disc++)
+ PyList_SetItem(discovery, (*disc)->index - 1, discovery_to_dict(*disc)); /* steals ref. to object - no need to decref */
+
+ PyDict_SetItemStringDecRef(output, "discovery", discovery);
+ }
+
+ /* Security profiles are currently not implemented. */
+
+ return output;
+ }
+
+ PyObject *nbft_get(const char * filename)
+ {
+ struct nbft_info *nbft;
+ PyObject *output;
+ int ret;
+
+ ret = nvme_nbft_read(&nbft, filename);
+ if (ret) {
+ Py_RETURN_NONE;
+ }
+
+ output = nbft_to_pydict(nbft);
+ nvme_nbft_free(nbft);
+ return output;
+ }
+%};
+
+%feature("autodoc", "@return an NBFT table as a dict on success, None otherwise.\n"
+ "@param filename: file to read") nbft_get;
+PyObject *nbft_get(const char * filename);
+
+// We want to swig all the #define and enum from types.h, but none of the structs.
+#pragma SWIG nowarn=503 // Supress warnings about unnamed struct
+#define __attribute__(x)
+%rename($ignore, %$isclass) ""; // ignore all classes/structs
+%rename($ignore, %$isfunction) ""; // ignore all functions
+%rename($ignore, %$isunion) ""; // ignore all unions
+%rename($ignore, %$isvariable) ""; // ignore all variables
+
+%include "../src/nvme/types.h"
diff --git a/libnvme/tests/NBFT b/libnvme/tests/NBFT
new file mode 100644
index 0000000..2dea936
--- /dev/null
+++ b/libnvme/tests/NBFT
Binary files differ
diff --git a/libnvme/tests/create-ctrl-obj.py b/libnvme/tests/create-ctrl-obj.py
new file mode 100755
index 0000000..f7b5f41
--- /dev/null
+++ b/libnvme/tests/create-ctrl-obj.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+import sys
+import pprint
+from libnvme import nvme
+
+root = nvme.root()
+root.log_level('debug')
+
+host = nvme.host(root)
+subsysnqn = nvme.NVME_DISC_SUBSYS_NAME
+transport = 'loop'
+traddr = '127.0.0.1'
+trsvcid = '8009'
+ctrl = nvme.ctrl(root, subsysnqn=subsysnqn, transport=transport, traddr=traddr, trsvcid=trsvcid)
diff --git a/libnvme/tests/gc.py b/libnvme/tests/gc.py
new file mode 100755
index 0000000..842a76d
--- /dev/null
+++ b/libnvme/tests/gc.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+import gc
+import sys
+import pprint
+from libnvme import nvme
+
+root = nvme.root()
+root.log_level('debug')
+print(f'root: {root}')
+
+host = nvme.host(root)
+print(f'host: {host}')
+
+subsystem = host.subsystems()
+print(f'subsystem: {subsystem}')
+
+ctrls = []
+for i in range(10):
+ ctrl = nvme.ctrl(
+ root,
+ subsysnqn=nvme.NVME_DISC_SUBSYS_NAME,
+ transport='loop',
+ )
+ ctrls.append(ctrl)
+ print(f'ctrl {i}: {ctrl}')
+
+ns = subsystem.namespaces() if subsystem is not None else None
+print(f'ns: {ns}')
+
+# Deleting objects in the following order would create a segmentation
+# fault if it weren't for the %pythonappend in nvme.i. This test is to
+# make sure garbage collection is not impacted by object deletion order.
+root = None
+host = None
+
+gc.collect() # Force garbage collection before controller/subsystem objects get deleted
+
+ctrls = None
+subsystem= None
+ns = None
diff --git a/libnvme/tests/test-nbft.py b/libnvme/tests/test-nbft.py
new file mode 100755
index 0000000..3aeeba4
--- /dev/null
+++ b/libnvme/tests/test-nbft.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+import os
+import unittest
+from libnvme import nvme
+from argparse import ArgumentParser
+
+
+class Testclass(unittest.TestCase):
+ def setUp(self):
+ self.expected_nbft = {
+ "discovery": [
+ {
+ "hfi_index": 0,
+ "nqn": "nqn.2014-08.org.nvmexpress.discovery",
+ "uri": "nvme+tcp://100.71.103.50:8009/",
+ }
+ ],
+ "hfi": [
+ {
+ "dhcp_override": True,
+ "dhcp_server_ipaddr": "100.71.245.254",
+ "gateway_ipaddr": "100.71.245.254",
+ "ip_origin": 82,
+ "ipaddr": "100.71.245.232",
+ "mac_addr": "b0:26:28:e8:7c:0e",
+ "pcidev": "0:40:0.0",
+ "primary_dns_ipaddr": "100.64.0.5",
+ "route_metric": 500,
+ "secondary_dns_ipaddr": "100.64.0.6",
+ "subnet_mask_prefix": 24,
+ "this_hfi_is_default_route": 1,
+ "trtype": "tcp",
+ "vlan": 0,
+ }
+ ],
+ "host": {
+ "host_id_configured": True,
+ "host_nqn_configured": True,
+ "id": "44454c4c-3400-1036-8038-b2c04f313233",
+ "nqn": "nqn.1988-11.com.dell:PowerEdge.R760.1234567",
+ "primary_admin_host_flag": "not indicated",
+ },
+ "subsystem": [
+ {
+ "asqsz": 0,
+ "controller_id": 5,
+ "data_digest_required": False,
+ "hfi_indexes": [0],
+ "nid": "c82404ed9c15f53b8ccf0968002e0fca",
+ "nid_type": "nguid",
+ "nsid": 148,
+ "pdu_header_digest_required": False,
+ "subsys_nqn": "nqn.1988-11.com.dell:powerstore:00:2a64abf1c5b81F6C4549",
+ "subsys_port_id": 0,
+ "traddr": "100.71.103.48",
+ "trsvcid": "4420",
+ "trtype": "tcp",
+ },
+ {
+ "asqsz": 0,
+ "controller_id": 4166,
+ "data_digest_required": False,
+ "hfi_indexes": [0],
+ "nid": "c82404ed9c15f53b8ccf0968002e0fca",
+ "nid_type": "nguid",
+ "nsid": 148,
+ "pdu_header_digest_required": False,
+ "subsys_nqn": "nqn.1988-11.com.dell:powerstore:00:2a64abf1c5b81F6C4549",
+ "subsys_port_id": 0,
+ "traddr": "100.71.103.49",
+ "trsvcid": "4420",
+ "trtype": "tcp",
+ },
+ ],
+ }
+
+ def test_read_nbft_file(self):
+ """Make sure we get expected data when reading from binary NBFT file"""
+ actual_nbft = nvme.nbft_get(args.filename)
+ self.assertEqual(actual_nbft, self.expected_nbft)
+
+
+if __name__ == "__main__":
+ import sys
+
+ parser = ArgumentParser(description="Test NBFT")
+ parser.add_argument("--filename", default=None, help="NBFT binary file to read")
+ parser.add_argument("unittest_args", nargs="*") # Grab everything else
+ args = parser.parse_args()
+ sys.argv[1:] = args.unittest_args
+
+ unittest.main()
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..5d48278
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,309 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2021 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+#
+project(
+ 'libnvme', ['c'],
+ meson_version: '>= 0.50.0',
+ version: '1.8',
+ license: 'LGPL-2.1-or-later',
+ default_options: [
+ 'c_std=gnu99',
+ 'warning_level=1',
+ 'buildtype=debug',
+ 'prefix=/usr/local',
+ 'sysconfdir=etc',
+ 'wrap_mode=nofallback'
+ ]
+)
+
+vstr = meson.project_version().split('-rc')[0]
+vid = vstr.split('.')
+library_version = '.'.join([vid[0], vid[1]])
+if vid.length() == 3
+ library_version = '.'.join([library_version, vid[2]])
+else
+ library_version = library_version + '.0'
+endif
+
+################################################################################
+cc = meson.get_compiler('c')
+cxx_available = add_languages('cpp', required: false)
+
+prefixdir = get_option('prefix')
+libdir = join_paths(prefixdir, get_option('libdir'))
+includedir = join_paths(prefixdir, get_option('includedir'))
+datadir = join_paths(prefixdir, get_option('datadir'))
+mandir = join_paths(prefixdir, get_option('mandir'))
+bindir = join_paths(prefixdir, get_option('bindir'))
+sysconfdir = join_paths(prefixdir, get_option('sysconfdir'))
+
+################################################################################
+conf = configuration_data()
+
+version_tag = get_option('version-tag')
+if version_tag != ''
+ conf.set('GIT_VERSION', '"@0@"'.format(version_tag))
+else
+ r = run_command('scripts/meson-vcs-tag.sh',
+ meson.current_source_dir(),
+ meson.project_version(),
+ check: true)
+ conf.set('GIT_VERSION', '"@0@"'.format(r.stdout().strip()))
+endif
+conf.set('PROJECT_VERSION', '"@0@"'.format(meson.project_version()))
+
+conf.set('SYSCONFDIR', '"@0@"'.format(sysconfdir))
+
+if get_option('json-c').disabled()
+ json_c_dep = dependency('', required: false)
+else
+ json_c_dep = dependency('json-c',
+ version: '>=0.13',
+ required: get_option('json-c'),
+ fallback : ['json-c', 'json_c_dep'])
+endif
+conf.set('CONFIG_JSONC', json_c_dep.found(), description: 'Is json-c required?')
+
+if get_option('openssl').disabled()
+ openssl_dep = dependency('', required: false)
+else
+ openssl_dep = dependency('openssl',
+ version: '>=1.1.0',
+ required: get_option('openssl'),
+ fallback : ['openssl', 'libssl_dep'])
+
+ if openssl_dep.found()
+ if openssl_dep.version().version_compare('<2.0.0')
+ api_version = 1
+ endif
+
+ if openssl_dep.version().version_compare('>=3.0.0')
+ api_version = 3
+ endif
+
+ # Test for LibreSSL v3.x with incomplete OpenSSL v3 APIs
+ if openssl_dep.type_name() != 'internal'
+ is_libressl = cc.has_header_symbol('openssl/opensslv.h',
+ 'LIBRESSL_VERSION_NUMBER',
+ dependencies: openssl_dep)
+ has_header = cc.has_header('openssl/core_names.h',
+ dependencies: openssl_dep)
+ if is_libressl and not has_header
+ api_version = 1
+ endif
+ endif
+
+ conf.set('CONFIG_OPENSSL_@0@'.format(api_version), true,
+ description: 'OpenSSL/LibreSSL API version @0@'.format(api_version))
+ endif
+endif
+
+conf.set('CONFIG_OPENSSL', openssl_dep.found(),
+ description: 'Is OpenSSL/LibreSSL available?')
+
+if get_option('keyutils').disabled()
+ keyutils_dep = dependency('', required: false)
+else
+ keyutils_dep = dependency('libkeyutils',
+ required : get_option('keyutils'))
+endif
+conf.set('CONFIG_KEYUTILS', keyutils_dep.found(),
+ description: 'Is libkeyutils available?')
+
+if get_option('libdbus').disabled()
+ libdbus_dep = dependency('', required: false)
+else
+ # Check for libdus availability. Optional, only required for MCTP dbus scan
+ libdbus_dep = dependency(
+ 'dbus-1',
+ required: true,
+ fallback: ['dbus', 'libdbus_dep'],
+ default_options: [
+ 'default_library=static',
+ 'embedded_tests=false',
+ 'message_bus=false',
+ 'modular_tests=disabled',
+ 'tools=false',
+ ],
+ )
+endif
+
+conf.set('CONFIG_DBUS', libdbus_dep.found(), description: 'Enable dbus support?')
+
+# local (cross-compilable) implementations of ccan configure steps
+conf.set10(
+ 'HAVE_BUILTIN_TYPES_COMPATIBLE_P',
+ cc.compiles(
+ '''int main(void) {
+ return __builtin_types_compatible_p(int, long);
+ }
+ ''',
+ name: '__builtin_type_compatible_p'
+ ),
+ description: 'Is __builtin_types_compatible_p available?'
+)
+conf.set10(
+ 'HAVE_TYPEOF',
+ cc.compiles(
+ '''int main(void) {
+ int a = 1;
+ typeof(a) b;
+ b = a;
+ }
+ ''',
+ name: 'typeof'
+ ),
+ description: 'Is typeof available?'
+)
+conf.set10(
+ 'HAVE_BYTESWAP_H',
+ cc.compiles(
+ '''#include <byteswap.h>''',
+ name: 'byteswap.h'
+ ),
+ description: 'Is byteswap.h include-able?'
+)
+conf.set10(
+ 'HAVE_BSWAP_64',
+ cc.links(
+ '''#include <byteswap.h>
+ int main(void) {
+ return bswap_64(0);
+ }
+ ''',
+ name: 'bswap64'
+ ),
+ description: 'Is bswap_64 available?'
+)
+conf.set10(
+ 'HAVE_LITTLE_ENDIAN',
+ host_machine.endian() == 'little',
+ description: 'Building for little-endian'
+)
+conf.set10(
+ 'HAVE_BIG_ENDIAN',
+ host_machine.endian() == 'big',
+ description: 'Building for big-endian'
+)
+conf.set10(
+ 'HAVE_STATEMENT_EXPR',
+ cc.compiles(
+ '''int main(int argc, char **argv) {
+ return ({ int x = argc; x == 1; });
+ }
+ ''',
+ name: 'statement-expr'
+ ),
+ description: 'Can we use a statement as an expression?'
+)
+conf.set10(
+ 'HAVE_ISBLANK',
+ cc.links(
+ '''#include <ctype.h>
+ int main(int argc, char **argv) {
+ return isblank(argv[0][0]);
+ }
+ ''',
+ name: 'isblank'
+ ),
+ description: 'Is isblank() available?'
+)
+conf.set10(
+ 'HAVE_LINUX_MCTP_H',
+ cc.compiles(
+ '''#include <linux/mctp.h>''',
+ name: 'linux/mctp.h'
+ ),
+ description: 'Is linux/mctp.h include-able?'
+)
+
+conf.set(
+ 'HAVE_NETDB',
+ cc.links(
+ '''#include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netdb.h>
+ int main(int argc, char **argv) {
+ struct addrinfo hints, *result;
+ return getaddrinfo(argv[1], argv[2], &hints, &result);
+ }
+ ''',
+ name: 'netdb',
+ ),
+ description: 'Is network address and service translation available'
+)
+conf.set(
+ 'HAVE_GLIBC_IOCTL',
+ cc.compiles(
+ '''#include <sys/ioctl.h>
+ int ioctl(int fd, unsigned long request, ...);
+ ''',
+ name: 'ioctl has glibc-style prototype'
+ ),
+ description: 'Is ioctl the glibc interface (rather than POSIX)'
+)
+
+if cc.has_function_attribute('fallthrough')
+ conf.set('fallthrough', '__attribute__((__fallthrough__))')
+else
+ conf.set('fallthrough', 'do {} while (0) /* fallthrough */')
+endif
+
+################################################################################
+substs = configuration_data()
+substs.set('NAME', meson.project_name())
+substs.set('VERSION', meson.project_version())
+substs.set('LICENSE', meson.project_license()[0])
+configure_file(
+ input: 'libnvme.spec.in',
+ output: 'libnvme.spec',
+ configuration: substs,
+)
+
+################################################################################
+add_project_arguments(
+ [
+ '-fomit-frame-pointer',
+ '-D_GNU_SOURCE',
+ ],
+ language : 'c',
+)
+incdir = include_directories(['.', 'ccan', 'src'])
+
+################################################################################
+subdir('internal')
+subdir('ccan')
+subdir('src')
+subdir('libnvme')
+if get_option('tests')
+ subdir('test')
+endif
+subdir('examples')
+subdir('doc')
+
+################################################################################
+if meson.version().version_compare('>=0.53.0')
+ path_dict = {
+ 'prefixdir': prefixdir,
+ 'sysconfdir': sysconfdir,
+ 'bindir': bindir,
+ 'includedir': includedir,
+ 'datadir': datadir,
+ 'mandir': mandir,
+ 'libdir': libdir,
+ 'build location': meson.current_build_dir(),
+ }
+ summary(path_dict, section: 'Paths')
+ dep_dict = {
+ 'json-c': json_c_dep.found(),
+ 'OpenSSL': openssl_dep.found(),
+ 'keyutitls': keyutils_dep.found(),
+ 'libdbus': libdbus_dep.found(),
+ 'Python 3': py3_dep.found(),
+ }
+ summary(dep_dict, section: 'Dependencies')
+endif
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..251ae11
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,15 @@
+# -*- mode: meson -*-
+# SPDX-License-Identifier: LGPL-2.1-or-later
+option('version-tag', type : 'string', description : 'override the git version string')
+option('htmldir', type : 'string', value : '', description : 'directory for HTML documentation')
+option('rstdir', type : 'string', value : '', description : 'directory for ReST documentation')
+
+option('docs', type : 'combo', choices : ['false', 'html', 'man', 'rst', 'all'], description : 'install documentation')
+option('docs-build', type : 'boolean', value : false, description : 'build documentation')
+option('tests', type : 'boolean', value : true, description : 'build tests')
+
+option('python', type : 'feature', value: 'auto', description : 'Generate libnvme python bindings')
+option('openssl', type : 'feature', value: 'auto', description : 'OpenSSL support')
+option('libdbus', type : 'feature', value: 'disabled', description : 'libdbus support')
+option('json-c', type : 'feature', value: 'auto', description : 'JSON support')
+option('keyutils', type: 'feature', value: 'auto', description: 'keyutils support')
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..5416f3d
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[build-system]
+requires = ["mesonpep517", "wheel", "meson==0.61.2", "ninja"] # PEP 508 specifications.
+build-backend = "mesonpep517.buildapi"
+
+[tool.mesonpep517.metadata]
+author="Hannes Reinecke"
+author-email="hare@suse.de"
+classifiers = [
+ "Intended Audience :: Developers",
+ "Development Status :: 5 - Production/Stable",
+ "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+]
+description-file="README.md"
+home-page = "https://github.com/linux-nvme/libnvme"
+license="LGPL-2.1-or-later"
+requires-python=">=3.6"
+summary="python bindings for libnvme"
+
diff --git a/scripts/build.sh b/scripts/build.sh
new file mode 100755
index 0000000..5a615ae
--- /dev/null
+++ b/scripts/build.sh
@@ -0,0 +1,214 @@
+#!/bin/bash
+set -e
+
+usage() {
+ echo "Usage: build.sh [-b [release|debug]] "
+ echo " [-c [gcc|clang]]"
+ echo " [-m [meson|muon]"
+ echo " [config]"
+ echo ""
+ echo "CI build script."
+ echo ""
+ echo " -b [release]|debug build type"
+ echo " -c [gcc]|clang compiler to use"
+ echo " -m [meson]|muon use meson or muon"
+ echo " -t [armhf]|ppc64le|s390x cross compile target"
+ echo ""
+ echo "configs with meson:"
+ echo " [default] default settings"
+ echo " libdbus build with libdbus"
+ echo " fallback download all dependencies"
+ echo " and build them as shared libaries"
+ echo " cross use cross toolchain to build"
+ echo " coverage build coverage report"
+ echo ""
+ echo "configs with muon:"
+ echo " [default] minimal static build"
+}
+
+BUILDTOOL=meson
+MESON=meson
+BUILDTYPE=release
+CROSS_TARGET=armhf
+CC=${CC:-"gcc"}
+
+while getopts "b:c:m:t:" o; do
+ case "${o}" in
+ b)
+ BUILDTYPE="${OPTARG}"
+ ;;
+ c)
+ CC="${OPTARG}"
+ ;;
+ m)
+ BUILDTOOL="${OPTARG}"
+ ;;
+ t)
+ CROSS_TARGET="${OPTARG}"
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+done
+shift $((OPTIND-1))
+
+CONFIG=${1:-"default"}
+
+cd "$(git rev-parse --show-toplevel)" || exit 1
+
+BUILDDIR="$(pwd)/.build-ci"
+TOOLDIR="$(pwd)/.build-tools"
+
+fn_exists() { declare -F "$1" > /dev/null; }
+
+config_meson_default() {
+ CC="${CC}" "${MESON}" setup \
+ --werror \
+ --buildtype="${BUILDTYPE}" \
+ "${BUILDDIR}"
+}
+
+config_meson_libdbus() {
+ CC="${CC}" "${MESON}" setup \
+ --werror \
+ --buildtype="${BUILDTYPE}" \
+ -Dlibdbus=enabled \
+ --prefix=/ \
+ "${BUILDDIR}"
+}
+
+config_meson_fallback() {
+ CC="${CC}" "${MESON}" setup \
+ --werror \
+ --buildtype="${BUILDTYPE}" \
+ --wrap-mode=forcefallback \
+ -Dlibdbus=enabled \
+ -Ddbus:werror=false \
+ -Dopenssl:werror=false \
+ "${BUILDDIR}"
+}
+
+config_meson_cross() {
+ CC="${CC}" "${MESON}" setup \
+ --werror \
+ --buildtype="${BUILDTYPE}" \
+ --cross-file=.github/cross/ubuntu-cross-"${CROSS_TARGET}".txt \
+ -Dpython=disabled \
+ -Dopenssl=disabled \
+ "${BUILDDIR}"
+}
+
+config_meson_coverage() {
+ CC="${CC}" "${MESON}" setup \
+ --werror \
+ --buildtype="${BUILDTYPE}" \
+ --wrap-mode=nofallback \
+ -Dlibdbus=enabled \
+ -Db_coverage=true \
+ "${BUILDDIR}"
+}
+
+build_meson() {
+ "${MESON}" compile \
+ -C "${BUILDDIR}"
+}
+
+test_meson() {
+ "${MESON}" test \
+ -C "${BUILDDIR}"
+}
+
+test_meson_coverage() {
+ "${MESON}" test \
+ -C "${BUILDDIR}"
+ ninja -C "${BUILDDIR}" coverage --verbose
+}
+
+tools_build_samurai() {
+ if [ ! -d "${TOOLDIR}"/samurai ]; then
+ git clone --depth 1 https://github.com/michaelforney/samurai.git \
+ "${TOOLDIR}/samurai"
+ fi
+
+ if [[ -f "${TOOLDIR}/samurai/samu" ]]; then
+ return
+ fi
+
+ pushd "${TOOLDIR}/samurai" || exit 1
+ CC="${CC}" make
+ popd || exit 1
+}
+
+tools_build_muon() {
+ if [ ! -d "${TOOLDIR}/muon" ]; then
+ git clone --depth 1 https://git.sr.ht/~lattis/muon \
+ "${TOOLDIR}/muon"
+ fi
+
+ if [[ -f "${TOOLDIR}/build-muon/muon" ]]; then
+ return
+ fi
+
+ pushd "${TOOLDIR}/muon" || exit 1
+
+ CC="${CC}" CFLAGS="${CFLAGS} -std=c99" ninja="${SAMU}" \
+ ./bootstrap.sh stage1
+
+ CC="${CC}" ninja="${SAMU}" stage1/muon setup \
+ -Dprefix="${TOOLDIR}" \
+ -Ddocs=disabled \
+ -Dsamurai=disabled \
+ "${TOOLDIR}/build-muon"
+ "${SAMU}" -C "${TOOLDIR}/build-muon"
+
+ # "${TOOLDIR}/build-muon/muon" \
+ # -C "${TOOLDIR}/build-muon" test
+
+ popd || exit 1
+}
+
+config_muon_default() {
+ CC="${CC}" CFLAGS="${CFLAGS}" ninja="${SAMU}" \
+ "${MUON}" setup \
+ -Ddefault_library=static \
+ -Dc_link_args="-static" \
+ -Djson-c=disabled \
+ -Dopenssl=disabled \
+ -Dkeyutils=disabled \
+ -Dpython=disabled \
+ -Dpython=disabled \
+ "${BUILDDIR}"
+}
+
+build_muon() {
+ "${SAMU}" -C "${BUILDDIR}"
+}
+
+test_muon() {
+ ninja="${SAMU}" "${MUON}" -C "${BUILDDIR}" test
+}
+
+if [[ "${BUILDTOOL}" == "muon" ]]; then
+ SAMU="$(which samu 2> /dev/null)" || true
+ if [[ -z "${SAMU}" ]]; then
+ tools_build_samurai
+ SAMU="${TOOLDIR}/samurai/samu"
+ fi
+
+ MUON="$(which muon 2> /dev/null)" || true
+ if [[ -z "${MUON}" ]]; then
+ tools_build_muon
+ MUON="${TOOLDIR}/build-muon/muon"
+ fi
+fi
+
+echo "samu: ${SAMU}"
+echo "muon: ${MUON}"
+
+rm -rf "${BUILDDIR}"
+
+config_"${BUILDTOOL}"_"${CONFIG}"
+fn_exists "build_${BUILDTOOL}_${CONFIG}" && "build_${BUILDTOOL}_${CONFIG}" || build_"${BUILDTOOL}"
+fn_exists "test_${BUILDTOOL}_${CONFIG}" && "test_${BUILDTOOL}_${CONFIG}" || test_"${BUILDTOOL}"
diff --git a/scripts/collect-sysfs.sh b/scripts/collect-sysfs.sh
new file mode 100755
index 0000000..b7ce017
--- /dev/null
+++ b/scripts/collect-sysfs.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+filename=nvme-sysfs-$(hostname)-$(uname -r).tar.xz
+
+declare -a dirs=(
+ "/sys/class/nvme"
+ "/sys/class/nvme-fabrics"
+ "/sys/class/nvme-generic"
+ "/sys/class/nvme-subsystem"
+ "/sys/bus/pci/slots"
+)
+
+files=""
+for d in "${dirs[@]}"; do
+ files+="${d} "
+ for l in "${d}"/*; do
+ files+="$(readlink -f $l) "
+ done
+done
+
+tar -c -J -p -f "${filename}" ${files} 2> /dev/null
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
new file mode 100755
index 0000000..4900c3a
--- /dev/null
+++ b/scripts/kernel-doc
@@ -0,0 +1,2523 @@
+#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
+
+use warnings;
+use strict;
+
+## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ##
+## Copyright (C) 2000, 1 Tim Waugh <twaugh@redhat.com> ##
+## Copyright (C) 2001 Simon Huggins ##
+## Copyright (C) 2005-2012 Randy Dunlap ##
+## Copyright (C) 2012 Dan Luedtke ##
+## ##
+## #define enhancements by Armin Kuster <akuster@mvista.com> ##
+## Copyright (c) 2000 MontaVista Software, Inc. ##
+## ##
+## This software falls under the GNU General Public License. ##
+## Please read the COPYING file for more information ##
+
+# 18/01/2001 - Cleanups
+# Functions prototyped as foo(void) same as foo()
+# Stop eval'ing where we don't need to.
+# -- huggie@earth.li
+
+# 27/06/2001 - Allowed whitespace after initial "/**" and
+# allowed comments before function declarations.
+# -- Christian Kreibich <ck@whoop.org>
+
+# Still to do:
+# - add perldoc documentation
+# - Look more closely at some of the scarier bits :)
+
+# 26/05/2001 - Support for separate source and object trees.
+# Return error code.
+# Keith Owens <kaos@ocs.com.au>
+
+# 23/09/2001 - Added support for typedefs, structs, enums and unions
+# Support for Context section; can be terminated using empty line
+# Small fixes (like spaces vs. \s in regex)
+# -- Tim Jansen <tim@tjansen.de>
+
+# 25/07/2012 - Added support for HTML5
+# -- Dan Luedtke <mail@danrl.de>
+
+sub usage {
+ my $message = <<"EOF";
+Usage: $0 [OPTION ...] FILE ...
+
+Read C language source or header FILEs, extract embedded documentation comments,
+and print formatted documentation to standard output.
+
+The documentation comments are identified by "/**" opening comment mark. See
+Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax.
+
+Output format selection (mutually exclusive):
+ -man Output troff manual page format. This is the default.
+ -rst Output reStructuredText format.
+ -none Do not output documentation, only warnings.
+
+Output format selection modifier (affects only ReST output):
+
+ -sphinx-version Use the ReST C domain dialect compatible with an
+ specific Sphinx Version.
+ If not specified, kernel-doc will auto-detect using
+ the sphinx-build version found on PATH.
+
+Output selection (mutually exclusive):
+ -export Only output documentation for symbols that have been
+ exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL()
+ in any input FILE or -export-file FILE.
+ -internal Only output documentation for symbols that have NOT been
+ exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL()
+ in any input FILE or -export-file FILE.
+ -function NAME Only output documentation for the given function(s)
+ or DOC: section title(s). All other functions and DOC:
+ sections are ignored. May be specified multiple times.
+ -nosymbol NAME Exclude the specified symbols from the output
+ documentation. May be specified multiple times.
+
+Output selection modifiers:
+ -no-doc-sections Do not output DOC: sections.
+ -enable-lineno Enable output of #define LINENO lines. Only works with
+ reStructuredText format.
+ -export-file FILE Specify an additional FILE in which to look for
+ EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with
+ -export or -internal. May be specified multiple times.
+
+Other parameters:
+ -v Verbose output, more warnings and other information.
+ -h Print this help.
+ -Werror Treat warnings as errors.
+
+EOF
+ print $message;
+ exit 1;
+}
+
+#
+# format of comments.
+# In the following table, (...)? signifies optional structure.
+# (...)* signifies 0 or more structure elements
+# /**
+# * function_name(:)? (- short description)?
+# (* @parameterx: (description of parameter x)?)*
+# (* a blank line)?
+# * (Description:)? (Description of function)?
+# * (section header: (section description)? )*
+# (*)?*/
+#
+# So .. the trivial example would be:
+#
+# /**
+# * my_function
+# */
+#
+# If the Description: header tag is omitted, then there must be a blank line
+# after the last parameter specification.
+# e.g.
+# /**
+# * my_function - does my stuff
+# * @my_arg: its mine damnit
+# *
+# * Does my stuff explained.
+# */
+#
+# or, could also use:
+# /**
+# * my_function - does my stuff
+# * @my_arg: its mine damnit
+# * Description: Does my stuff explained.
+# */
+# etc.
+#
+# Besides functions you can also write documentation for structs, unions,
+# enums and typedefs. Instead of the function name you must write the name
+# of the declaration; the struct/union/enum/typedef must always precede
+# the name. Nesting of declarations is not supported.
+# Use the argument mechanism to document members or constants.
+# e.g.
+# /**
+# * struct my_struct - short description
+# * @a: first member
+# * @b: second member
+# *
+# * Longer description
+# */
+# struct my_struct {
+# int a;
+# int b;
+# /* private: */
+# int c;
+# };
+#
+# All descriptions can be multiline, except the short function description.
+#
+# For really longs structs, you can also describe arguments inside the
+# body of the struct.
+# eg.
+# /**
+# * struct my_struct - short description
+# * @a: first member
+# * @b: second member
+# *
+# * Longer description
+# */
+# struct my_struct {
+# int a;
+# int b;
+# /**
+# * @c: This is longer description of C
+# *
+# * You can use paragraphs to describe arguments
+# * using this method.
+# */
+# int c;
+# };
+#
+# This should be use only for struct/enum members.
+#
+# You can also add additional sections. When documenting kernel functions you
+# should document the "Context:" of the function, e.g. whether the functions
+# can be called form interrupts. Unlike other sections you can end it with an
+# empty line.
+# A non-void function should have a "Return:" section describing the return
+# value(s).
+# Example-sections should contain the string EXAMPLE so that they are marked
+# appropriately in DocBook.
+#
+# Example:
+# /**
+# * user_function - function that can only be called in user context
+# * @a: some argument
+# * Context: !in_interrupt()
+# *
+# * Some description
+# * Example:
+# * user_function(22);
+# */
+# ...
+#
+#
+# All descriptive text is further processed, scanning for the following special
+# patterns, which are highlighted appropriately.
+#
+# 'funcname()' - function
+# '$ENVVAR' - environmental variable
+# '&struct_name' - name of a structure (up to two words including 'struct')
+# '&struct_name.member' - name of a structure member
+# '@parameter' - name of a parameter
+# '%CONST' - name of a constant.
+# '``LITERAL``' - literal string without any spaces on it.
+
+## init lots of data
+
+my $errors = 0;
+my $warnings = 0;
+my $anon_struct_union = 0;
+
+# match expressions used to find embedded type information
+my $type_constant = '\b``([^\`]+)``\b';
+my $type_constant2 = '\%([-_\w]+)';
+my $type_func = '(\w+)\(\)';
+my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
+my $type_param_ref = '([\!]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
+my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
+my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params
+my $type_env = '(\$\w+)';
+my $type_enum = '\&(enum\s*([_\w]+))';
+my $type_struct = '\&(struct\s*([_\w]+))';
+my $type_typedef = '\&(typedef\s*([_\w]+))';
+my $type_union = '\&(union\s*([_\w]+))';
+my $type_member = '\&([_\w]+)(\.|->)([_\w]+)';
+my $type_fallback = '\&([_\w]+)';
+my $type_member_func = $type_member . '\(\)';
+
+# Output conversion substitutions.
+# One for each output format
+
+# these are pretty rough
+my @highlights_man = (
+ [$type_constant, "\$1"],
+ [$type_constant2, "\$1"],
+ [$type_func, "\\\\fB\$1\\\\fP"],
+ [$type_enum, "\\\\fI\$1\\\\fP"],
+ [$type_struct, "\\\\fI\$1\\\\fP"],
+ [$type_typedef, "\\\\fI\$1\\\\fP"],
+ [$type_union, "\\\\fI\$1\\\\fP"],
+ [$type_param, "\\\\fI\$1\\\\fP"],
+ [$type_param_ref, "\\\\fI\$1\$2\\\\fP"],
+ [$type_member, "\\\\fI\$1\$2\$3\\\\fP"],
+ [$type_fallback, "\\\\fI\$1\\\\fP"]
+ );
+my $blankline_man = "";
+
+# rst-mode
+my @highlights_rst = (
+ [$type_constant, "``\$1``"],
+ [$type_constant2, "``\$1``"],
+ # Note: need to escape () to avoid func matching later
+ [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"],
+ [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"],
+ [$type_fp_param, "**\$1\\\\(\\\\)**"],
+ [$type_fp_param2, "**\$1\\\\(\\\\)**"],
+ [$type_func, "\$1()"],
+ [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"],
+ [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"],
+ [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"],
+ [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"],
+ # in rst this can refer to any type
+ [$type_fallback, "\\:c\\:type\\:`\$1`"],
+ [$type_param_ref, "**\$1\$2**"]
+ );
+my $blankline_rst = "\n";
+
+# read arguments
+if ($#ARGV == -1) {
+ usage();
+}
+
+my $kernelversion;
+my ($sphinx_major, $sphinx_minor, $sphinx_patch);
+
+my $dohighlight = "";
+
+my $verbose = 0;
+my $Werror = 0;
+my $output_mode = "rst";
+my $output_preformatted = 0;
+my $no_doc_sections = 0;
+my $enable_lineno = 0;
+my @highlights = @highlights_rst;
+my $blankline = $blankline_rst;
+my $modulename = "Kernel API";
+
+use constant {
+ OUTPUT_ALL => 0, # output all symbols and doc sections
+ OUTPUT_INCLUDE => 1, # output only specified symbols
+ OUTPUT_EXPORTED => 2, # output exported symbols
+ OUTPUT_INTERNAL => 3, # output non-exported symbols
+};
+my $output_selection = OUTPUT_ALL;
+my $show_not_found = 0; # No longer used
+
+my @export_file_list;
+
+my @build_time;
+if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) &&
+ (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') {
+ @build_time = gmtime($seconds);
+} else {
+ @build_time = localtime;
+}
+
+my $man_date = ('January', 'February', 'March', 'April', 'May', 'June',
+ 'July', 'August', 'September', 'October',
+ 'November', 'December')[$build_time[4]] .
+ " " . ($build_time[5]+1900);
+
+# Essentially these are globals.
+# They probably want to be tidied up, made more localised or something.
+# CAVEAT EMPTOR! Some of the others I localised may not want to be, which
+# could cause "use of undefined value" or other bugs.
+my ($function, %function_table, %parametertypes, $declaration_purpose);
+my %nosymbol_table = ();
+my $declaration_start_line;
+my ($type, $declaration_name, $return_type);
+my ($newsection, $newcontents, $prototype, $brcount, %source_map);
+
+if (defined($ENV{'KBUILD_VERBOSE'})) {
+ $verbose = "$ENV{'KBUILD_VERBOSE'}";
+}
+
+if (defined($ENV{'KCFLAGS'})) {
+ my $kcflags = "$ENV{'KCFLAGS'}";
+
+ if ($kcflags =~ /Werror/) {
+ $Werror = 1;
+ }
+}
+
+if (defined($ENV{'KDOC_WERROR'})) {
+ $Werror = "$ENV{'KDOC_WERROR'}";
+}
+
+# Generated docbook code is inserted in a template at a point where
+# docbook v3.1 requires a non-zero sequence of RefEntry's; see:
+# https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html
+# We keep track of number of generated entries and generate a dummy
+# if needs be to ensure the expanded template can be postprocessed
+# into html.
+my $section_counter = 0;
+
+my $lineprefix="";
+
+# Parser states
+use constant {
+ STATE_NORMAL => 0, # normal code
+ STATE_NAME => 1, # looking for function name
+ STATE_BODY_MAYBE => 2, # body - or maybe more description
+ STATE_BODY => 3, # the body of the comment
+ STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line
+ STATE_PROTO => 5, # scanning prototype
+ STATE_DOCBLOCK => 6, # documentation block
+ STATE_INLINE => 7, # gathering doc outside main block
+};
+my $state;
+my $in_doc_sect;
+my $leading_space;
+
+# Inline documentation state
+use constant {
+ STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE)
+ STATE_INLINE_NAME => 1, # looking for member name (@foo:)
+ STATE_INLINE_TEXT => 2, # looking for member documentation
+ STATE_INLINE_END => 3, # done
+ STATE_INLINE_ERROR => 4, # error - Comment without header was found.
+ # Spit a warning as it's not
+ # proper kernel-doc and ignore the rest.
+};
+my $inline_doc_state;
+
+#declaration types: can be
+# 'function', 'struct', 'union', 'enum', 'typedef'
+my $decl_type;
+
+# Name of the kernel-doc identifier for non-DOC markups
+my $identifier;
+
+my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start.
+my $doc_end = '\*/';
+my $doc_com = '\s*\*\s*';
+my $doc_com_body = '\s*\* ?';
+my $doc_decl = $doc_com . '(\w+)';
+# @params and a strictly limited set of supported section names
+# Specifically:
+# Match @word:
+# @...:
+# @{section-name}:
+# while trying to not match literal block starts like "example::"
+#
+my $doc_sect = $doc_com .
+ '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$';
+my $doc_content = $doc_com_body . '(.*)';
+my $doc_block = $doc_com . 'DOC:\s*(.*)?';
+my $doc_inline_start = '^\s*/\*\*\s*$';
+my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)';
+my $doc_inline_end = '^\s*\*/\s*$';
+my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$';
+my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;';
+my $function_pointer = qr{([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)};
+my $attribute = qr{__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)}i;
+
+my %parameterdescs;
+my %parameterdesc_start_lines;
+my @parameterlist;
+my %sections;
+my @sectionlist;
+my %section_start_lines;
+my $sectcheck;
+my $struct_actual;
+
+my $contents = "";
+my $new_start_line = 0;
+
+# the canonical section names. see also $doc_sect above.
+my $section_default = "Description"; # default section
+my $section_intro = "Introduction";
+my $section = $section_default;
+my $section_context = "Context";
+my $section_return = "Return";
+
+my $undescribed = "-- undescribed --";
+
+reset_state();
+
+while ($ARGV[0] =~ m/^--?(.*)/) {
+ my $cmd = $1;
+ shift @ARGV;
+ if ($cmd eq "man") {
+ $output_mode = "man";
+ @highlights = @highlights_man;
+ $blankline = $blankline_man;
+ } elsif ($cmd eq "rst") {
+ $output_mode = "rst";
+ @highlights = @highlights_rst;
+ $blankline = $blankline_rst;
+ } elsif ($cmd eq "none") {
+ $output_mode = "none";
+ } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document
+ $modulename = shift @ARGV;
+ } elsif ($cmd eq "function") { # to only output specific functions
+ $output_selection = OUTPUT_INCLUDE;
+ $function = shift @ARGV;
+ $function_table{$function} = 1;
+ } elsif ($cmd eq "nosymbol") { # Exclude specific symbols
+ my $symbol = shift @ARGV;
+ $nosymbol_table{$symbol} = 1;
+ } elsif ($cmd eq "export") { # only exported symbols
+ $output_selection = OUTPUT_EXPORTED;
+ %function_table = ();
+ } elsif ($cmd eq "internal") { # only non-exported symbols
+ $output_selection = OUTPUT_INTERNAL;
+ %function_table = ();
+ } elsif ($cmd eq "export-file") {
+ my $file = shift @ARGV;
+ push(@export_file_list, $file);
+ } elsif ($cmd eq "v") {
+ $verbose = 1;
+ } elsif ($cmd eq "Werror") {
+ $Werror = 1;
+ } elsif (($cmd eq "h") || ($cmd eq "help")) {
+ usage();
+ } elsif ($cmd eq 'no-doc-sections') {
+ $no_doc_sections = 1;
+ } elsif ($cmd eq 'enable-lineno') {
+ $enable_lineno = 1;
+ } elsif ($cmd eq 'show-not-found') {
+ $show_not_found = 1; # A no-op but don't fail
+ } elsif ($cmd eq "sphinx-version") {
+ my $ver_string = shift @ARGV;
+ if ($ver_string =~ m/^(\d+)(\.\d+)?(\.\d+)?/) {
+ $sphinx_major = $1;
+ if (defined($2)) {
+ $sphinx_minor = substr($2,1);
+ } else {
+ $sphinx_minor = 0;
+ }
+ if (defined($3)) {
+ $sphinx_patch = substr($3,1)
+ } else {
+ $sphinx_patch = 0;
+ }
+ } else {
+ die "Sphinx version should either major.minor or major.minor.patch format\n";
+ }
+ } else {
+ # Unknown argument
+ usage();
+ }
+}
+
+# continue execution near EOF;
+
+# The C domain dialect changed on Sphinx 3. So, we need to check the
+# version in order to produce the right tags.
+sub findprog($)
+{
+ foreach(split(/:/, $ENV{PATH})) {
+ return "$_/$_[0]" if(-x "$_/$_[0]");
+ }
+}
+
+sub get_sphinx_version()
+{
+ my $ver;
+
+ my $cmd = "sphinx-build";
+ if (!findprog($cmd)) {
+ my $cmd = "sphinx-build3";
+ if (!findprog($cmd)) {
+ $sphinx_major = 1;
+ $sphinx_minor = 2;
+ $sphinx_patch = 0;
+ printf STDERR "Warning: Sphinx version not found. Using default (Sphinx version %d.%d.%d)\n",
+ $sphinx_major, $sphinx_minor, $sphinx_patch;
+ return;
+ }
+ }
+
+ open IN, "$cmd --version 2>&1 |";
+ while (<IN>) {
+ if (m/^\s*sphinx-build\s+([\d]+)\.([\d\.]+)(\+\/[\da-f]+)?$/) {
+ $sphinx_major = $1;
+ $sphinx_minor = $2;
+ $sphinx_patch = $3;
+ last;
+ }
+ # Sphinx 1.2.x uses a different format
+ if (m/^\s*Sphinx.*\s+([\d]+)\.([\d\.]+)$/) {
+ $sphinx_major = $1;
+ $sphinx_minor = $2;
+ $sphinx_patch = $3;
+ last;
+ }
+ }
+ close IN;
+}
+
+# get kernel version from env
+sub get_kernel_version() {
+ my $version = 'unknown kernel version';
+
+ if (defined($ENV{'KERNELVERSION'})) {
+ $version = $ENV{'KERNELVERSION'};
+ }
+ return $version;
+}
+
+#
+sub print_lineno {
+ my $lineno = shift;
+ if ($enable_lineno && defined($lineno)) {
+ print "#define LINENO " . $lineno . "\n";
+ }
+}
+##
+# dumps section contents to arrays/hashes intended for that purpose.
+#
+sub dump_section {
+ my $file = shift;
+ my $name = shift;
+ my $contents = join "\n", @_;
+
+ if ($name =~ m/$type_param/) {
+ $name = $1;
+ $parameterdescs{$name} = $contents;
+ $sectcheck = $sectcheck . $name . " ";
+ $parameterdesc_start_lines{$name} = $new_start_line;
+ $new_start_line = 0;
+ } elsif ($name eq "@\.\.\.") {
+ $name = "...";
+ $parameterdescs{$name} = $contents;
+ $sectcheck = $sectcheck . $name . " ";
+ $parameterdesc_start_lines{$name} = $new_start_line;
+ $new_start_line = 0;
+ } else {
+ if (defined($sections{$name}) && ($sections{$name} ne "")) {
+ # Only warn on user specified duplicate section names.
+ if ($name ne $section_default) {
+ print STDERR "${file}:$.: warning: duplicate section name '$name'\n";
+ ++$warnings;
+ }
+ $sections{$name} .= $contents;
+ } else {
+ $sections{$name} = $contents;
+ push @sectionlist, $name;
+ $section_start_lines{$name} = $new_start_line;
+ $new_start_line = 0;
+ }
+ }
+}
+
+##
+# dump DOC: section after checking that it should go out
+#
+sub dump_doc_section {
+ my $file = shift;
+ my $name = shift;
+ my $contents = join "\n", @_;
+
+ if ($no_doc_sections) {
+ return;
+ }
+
+ return if (defined($nosymbol_table{$name}));
+
+ if (($output_selection == OUTPUT_ALL) ||
+ (($output_selection == OUTPUT_INCLUDE) &&
+ defined($function_table{$name})))
+ {
+ dump_section($file, $name, $contents);
+ output_blockhead({'sectionlist' => \@sectionlist,
+ 'sections' => \%sections,
+ 'module' => $modulename,
+ 'content-only' => ($output_selection != OUTPUT_ALL), });
+ }
+}
+
+##
+# output function
+#
+# parameterdescs, a hash.
+# function => "function name"
+# parameterlist => @list of parameters
+# parameterdescs => %parameter descriptions
+# sectionlist => @list of sections
+# sections => %section descriptions
+#
+
+sub output_highlight {
+ my $contents = join "\n",@_;
+ my $line;
+
+# DEBUG
+# if (!defined $contents) {
+# use Carp;
+# confess "output_highlight got called with no args?\n";
+# }
+
+# print STDERR "contents b4:$contents\n";
+ eval $dohighlight;
+ die $@ if $@;
+# print STDERR "contents af:$contents\n";
+
+ foreach $line (split "\n", $contents) {
+ if (! $output_preformatted) {
+ $line =~ s/^\s*//;
+ }
+ if ($line eq ""){
+ if (! $output_preformatted) {
+ print $lineprefix, $blankline;
+ }
+ } else {
+ if ($output_mode eq "man" && substr($line, 0, 1) eq ".") {
+ print "\\&$line";
+ } else {
+ print $lineprefix, $line;
+ }
+ }
+ print "\n";
+ }
+}
+
+##
+# output function in man
+sub output_function_man(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+
+ print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"libnvme API manual\" LINUX\n";
+
+ print ".SH NAME\n";
+ print $args{'function'} . " \\- " . $args{'purpose'} . "\n";
+
+ print ".SH SYNOPSIS\n";
+ if ($args{'functiontype'} ne "") {
+ print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n";
+ } else {
+ print ".B \"" . $args{'function'} . "\n";
+ }
+ $count = 0;
+ my $parenth = "(";
+ my $post = ",";
+ foreach my $parameter (@{$args{'parameterlist'}}) {
+ if ($count == $#{$args{'parameterlist'}}) {
+ $post = ");";
+ }
+ $type = $args{'parametertypes'}{$parameter};
+ if ($type =~ m/$function_pointer/) {
+ # pointer-to-function
+ print ".BI \"" . $parenth . $1 . "\" " . " \") (" . $2 . ")" . $post . "\"\n";
+ } else {
+ $type =~ s/([^\*])$/$1 /;
+ print ".BI \"" . $parenth . $type . "\" " . " \"" . $post . "\"\n";
+ }
+ $count++;
+ $parenth = "";
+ }
+
+ print ".SH ARGUMENTS\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ my $parameter_name = $parameter;
+ $parameter_name =~ s/\[.*//;
+
+ print ".IP \"" . $parameter . "\" 12\n";
+ output_highlight($args{'parameterdescs'}{$parameter_name});
+ }
+ foreach $section (@{$args{'sectionlist'}}) {
+ print ".SH \"", uc $section, "\"\n";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+##
+# output enum in man
+sub output_enum_man(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+
+ print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n";
+
+ print ".SH NAME\n";
+ print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n";
+
+ print ".SH SYNOPSIS\n";
+ print "enum " . $args{'enum'} . " {\n";
+ $count = 0;
+ foreach my $parameter (@{$args{'parameterlist'}}) {
+ print ".br\n.BI \" $parameter\"\n";
+ if ($count == $#{$args{'parameterlist'}}) {
+ print "\n};\n";
+ last;
+ }
+ else {
+ print ", \n.br\n";
+ }
+ $count++;
+ }
+
+ print ".SH Constants\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ my $parameter_name = $parameter;
+ $parameter_name =~ s/\[.*//;
+
+ print ".IP \"" . $parameter . "\" 12\n";
+ output_highlight($args{'parameterdescs'}{$parameter_name});
+ }
+ foreach $section (@{$args{'sectionlist'}}) {
+ print ".SH \"$section\"\n";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+##
+# output struct in man
+sub output_struct_man(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+
+ print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n";
+
+ print ".SH NAME\n";
+ print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n";
+
+ my $declaration = $args{'definition'};
+ $declaration =~ s/\t/ /g;
+ $declaration =~ s/\n/"\n.br\n.BI \"/g;
+ print ".SH SYNOPSIS\n";
+ print $args{'type'} . " " . $args{'struct'} . " {\n.br\n";
+ print ".BI \"$declaration\n};\n.br\n\n";
+
+ print ".SH Members\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ ($parameter =~ /^#/) && next;
+
+ my $parameter_name = $parameter;
+ $parameter_name =~ s/\[.*//;
+
+ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
+ print ".IP \"" . $parameter . "\" 12\n";
+ output_highlight($args{'parameterdescs'}{$parameter_name});
+ }
+ foreach $section (@{$args{'sectionlist'}}) {
+ print ".SH \"$section\"\n";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+##
+# output typedef in man
+sub output_typedef_man(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+
+ print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n";
+
+ print ".SH NAME\n";
+ print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n";
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ print ".SH \"$section\"\n";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+sub output_blockhead_man(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $count;
+
+ print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n";
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ print ".SH \"$section\"\n";
+ output_highlight($args{'sections'}{$section});
+ }
+}
+
+##
+# output in restructured text
+#
+
+#
+# This could use some work; it's used to output the DOC: sections, and
+# starts by putting out the name of the doc section itself, but that tends
+# to duplicate a header already in the template file.
+#
+sub output_blockhead_rst(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ next if (defined($nosymbol_table{$section}));
+
+ if ($output_selection != OUTPUT_INCLUDE) {
+ print ".. _$section:\n\n";
+ print "**$section**\n\n";
+ }
+ print_lineno($section_start_lines{$section});
+ output_highlight_rst($args{'sections'}{$section});
+ print "\n";
+ }
+}
+
+#
+# Apply the RST highlights to a sub-block of text.
+#
+sub highlight_block($) {
+ # The dohighlight kludge requires the text be called $contents
+ my $contents = shift;
+ eval $dohighlight;
+ die $@ if $@;
+ return $contents;
+}
+
+#
+# Regexes used only here.
+#
+my $sphinx_literal = '^[^.].*::$';
+my $sphinx_cblock = '^\.\.\ +code-block::';
+
+sub output_highlight_rst {
+ my $input = join "\n",@_;
+ my $output = "";
+ my $line;
+ my $in_literal = 0;
+ my $litprefix;
+ my $block = "";
+
+ foreach $line (split "\n",$input) {
+ #
+ # If we're in a literal block, see if we should drop out
+ # of it. Otherwise pass the line straight through unmunged.
+ #
+ if ($in_literal) {
+ if (! ($line =~ /^\s*$/)) {
+ #
+ # If this is the first non-blank line in a literal
+ # block we need to figure out what the proper indent is.
+ #
+ if ($litprefix eq "") {
+ $line =~ /^(\s*)/;
+ $litprefix = '^' . $1;
+ $output .= $line . "\n";
+ } elsif (! ($line =~ /$litprefix/)) {
+ $in_literal = 0;
+ } else {
+ $output .= $line . "\n";
+ }
+ } else {
+ $output .= $line . "\n";
+ }
+ }
+ #
+ # Not in a literal block (or just dropped out)
+ #
+ if (! $in_literal) {
+ $block .= $line . "\n";
+ if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) {
+ $in_literal = 1;
+ $litprefix = "";
+ $output .= highlight_block($block);
+ $block = ""
+ }
+ }
+ }
+
+ if ($block) {
+ $output .= highlight_block($block);
+ }
+ foreach $line (split "\n", $output) {
+ print $lineprefix . $line . "\n";
+ }
+}
+
+sub output_function_rst(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $oldprefix = $lineprefix;
+ my $start = "";
+ my $is_macro = 0;
+
+ if ($sphinx_major < 3) {
+ if ($args{'typedef'}) {
+ print ".. c:type:: ". $args{'function'} . "\n\n";
+ print_lineno($declaration_start_line);
+ print " **Typedef**: ";
+ $lineprefix = "";
+ output_highlight_rst($args{'purpose'});
+ $start = "\n\n**Syntax**\n\n ``";
+ $is_macro = 1;
+ } else {
+ print ".. c:function:: ";
+ }
+ } else {
+ if ($args{'typedef'} || $args{'functiontype'} eq "") {
+ $is_macro = 1;
+ print ".. c:macro:: ". $args{'function'} . "\n\n";
+ } else {
+ print ".. c:function:: ";
+ }
+
+ if ($args{'typedef'}) {
+ print_lineno($declaration_start_line);
+ print " **Typedef**: ";
+ $lineprefix = "";
+ output_highlight_rst($args{'purpose'});
+ $start = "\n\n**Syntax**\n\n ``";
+ } else {
+ print "``" if ($is_macro);
+ }
+ }
+ if ($args{'functiontype'} ne "") {
+ $start .= $args{'functiontype'} . " " . $args{'function'} . " (";
+ } else {
+ $start .= $args{'function'} . " (";
+ }
+ print $start;
+
+ my $count = 0;
+ foreach my $parameter (@{$args{'parameterlist'}}) {
+ if ($count ne 0) {
+ print ", ";
+ }
+ $count++;
+ $type = $args{'parametertypes'}{$parameter};
+
+ if ($type =~ m/$function_pointer/) {
+ # pointer-to-function
+ print $1 . $parameter . ") (" . $2 . ")";
+ } else {
+ print $type;
+ }
+ }
+ if ($is_macro) {
+ print ")``\n\n";
+ } else {
+ print ")\n\n";
+ }
+ if (!$args{'typedef'}) {
+ print_lineno($declaration_start_line);
+ $lineprefix = " ";
+ output_highlight_rst($args{'purpose'});
+ print "\n";
+ }
+
+ print "**Parameters**\n\n";
+ $lineprefix = " ";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ my $parameter_name = $parameter;
+ $parameter_name =~ s/\[.*//;
+ $type = $args{'parametertypes'}{$parameter};
+
+ if ($type ne "") {
+ print "``$type``\n";
+ } else {
+ print "``$parameter``\n";
+ }
+
+ print_lineno($parameterdesc_start_lines{$parameter_name});
+
+ if (defined($args{'parameterdescs'}{$parameter_name}) &&
+ $args{'parameterdescs'}{$parameter_name} ne $undescribed) {
+ output_highlight_rst($args{'parameterdescs'}{$parameter_name});
+ } else {
+ print " *undescribed*\n";
+ }
+ print "\n";
+ }
+
+ $lineprefix = $oldprefix;
+ output_section_rst(@_);
+}
+
+sub output_section_rst(%) {
+ my %args = %{$_[0]};
+ my $section;
+ my $oldprefix = $lineprefix;
+ $lineprefix = "";
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ print "**$section**\n\n";
+ print_lineno($section_start_lines{$section});
+ output_highlight_rst($args{'sections'}{$section});
+ print "\n";
+ }
+ print "\n";
+ $lineprefix = $oldprefix;
+}
+
+sub output_enum_rst(%) {
+ my %args = %{$_[0]};
+ my ($parameter);
+ my $oldprefix = $lineprefix;
+ my $count;
+
+ if ($sphinx_major < 3) {
+ my $name = "enum " . $args{'enum'};
+ print "\n\n.. c:type:: " . $name . "\n\n";
+ } else {
+ my $name = $args{'enum'};
+ print "\n\n.. c:enum:: " . $name . "\n\n";
+ }
+ print_lineno($declaration_start_line);
+ $lineprefix = " ";
+ output_highlight_rst($args{'purpose'});
+ print "\n";
+
+ print "**Constants**\n\n";
+ $lineprefix = " ";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print "``$parameter``\n";
+ if ($args{'parameterdescs'}{$parameter} ne $undescribed) {
+ output_highlight_rst($args{'parameterdescs'}{$parameter});
+ } else {
+ print " *undescribed*\n";
+ }
+ print "\n";
+ }
+
+ $lineprefix = $oldprefix;
+ output_section_rst(@_);
+}
+
+sub output_typedef_rst(%) {
+ my %args = %{$_[0]};
+ my ($parameter);
+ my $oldprefix = $lineprefix;
+ my $name;
+
+ if ($sphinx_major < 3) {
+ $name = "typedef " . $args{'typedef'};
+ } else {
+ $name = $args{'typedef'};
+ }
+ print "\n\n.. c:type:: " . $name . "\n\n";
+ print_lineno($declaration_start_line);
+ $lineprefix = " ";
+ output_highlight_rst($args{'purpose'});
+ print "\n";
+
+ $lineprefix = $oldprefix;
+ output_section_rst(@_);
+}
+
+sub output_struct_rst(%) {
+ my %args = %{$_[0]};
+ my ($parameter);
+ my $oldprefix = $lineprefix;
+
+ if ($sphinx_major < 3) {
+ my $name = $args{'type'} . " " . $args{'struct'};
+ print "\n\n.. c:type:: " . $name . "\n\n";
+ } else {
+ my $name = $args{'struct'};
+ if ($args{'type'} eq 'union') {
+ print "\n\n.. c:union:: " . $name . "\n\n";
+ } else {
+ print "\n\n.. c:struct:: " . $name . "\n\n";
+ }
+ }
+ print_lineno($declaration_start_line);
+ $lineprefix = " ";
+ output_highlight_rst($args{'purpose'});
+ print "\n";
+
+ print "**Definition**\n\n";
+ print "::\n\n";
+ my $declaration = $args{'definition'};
+ $declaration =~ s/\t/ /g;
+ print " " . $args{'type'} . " " . $args{'struct'} . " {\n$declaration };\n\n";
+
+ print "**Members**\n\n";
+ $lineprefix = " ";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ ($parameter =~ /^#/) && next;
+
+ my $parameter_name = $parameter;
+ $parameter_name =~ s/\[.*//;
+
+ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
+ $type = $args{'parametertypes'}{$parameter};
+ print_lineno($parameterdesc_start_lines{$parameter_name});
+ print "``" . $parameter . "``\n";
+ output_highlight_rst($args{'parameterdescs'}{$parameter_name});
+ print "\n";
+ }
+ print "\n";
+
+ $lineprefix = $oldprefix;
+ output_section_rst(@_);
+}
+
+## none mode output functions
+
+sub output_function_none(%) {
+}
+
+sub output_enum_none(%) {
+}
+
+sub output_typedef_none(%) {
+}
+
+sub output_struct_none(%) {
+}
+
+sub output_blockhead_none(%) {
+}
+
+##
+# generic output function for all types (function, struct/union, typedef, enum);
+# calls the generated, variable output_ function name based on
+# functype and output_mode
+sub output_declaration {
+ no strict 'refs';
+ my $name = shift;
+ my $functype = shift;
+ my $func = "output_${functype}_$output_mode";
+
+ return if (defined($nosymbol_table{$name}));
+
+ if (($output_selection == OUTPUT_ALL) ||
+ (($output_selection == OUTPUT_INCLUDE ||
+ $output_selection == OUTPUT_EXPORTED) &&
+ defined($function_table{$name})) ||
+ ($output_selection == OUTPUT_INTERNAL &&
+ !($functype eq "function" && defined($function_table{$name}))))
+ {
+ &$func(@_);
+ $section_counter++;
+ }
+}
+
+##
+# generic output function - calls the right one based on current output mode.
+sub output_blockhead {
+ no strict 'refs';
+ my $func = "output_blockhead_" . $output_mode;
+ &$func(@_);
+ $section_counter++;
+}
+
+##
+# takes a declaration (struct, union, enum, typedef) and
+# invokes the right handler. NOT called for functions.
+sub dump_declaration($$) {
+ no strict 'refs';
+ my ($prototype, $file) = @_;
+ my $func = "dump_" . $decl_type;
+ &$func(@_);
+}
+
+sub dump_union($$) {
+ dump_struct(@_);
+}
+
+sub dump_struct($$) {
+ my $x = shift;
+ my $file = shift;
+ my $decl_type;
+ my $members;
+ my $type = qr{struct|union};
+ # For capturing struct/union definition body, i.e. "{members*}qualifiers*"
+ my $qualifiers = qr{$attribute|__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned};
+ my $definition_body = qr{\{(.*)\}\s*$qualifiers*};
+ my $struct_members = qr{($type)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;};
+
+ if ($x =~ /($type)\s+(\w+)\s*$definition_body/) {
+ $decl_type = $1;
+ $declaration_name = $2;
+ $members = $3;
+ } elsif ($x =~ /typedef\s+($type)\s*$definition_body\s*(\w+)\s*;/) {
+ $decl_type = $1;
+ $declaration_name = $3;
+ $members = $2;
+ }
+
+ if ($members) {
+ if ($identifier ne $declaration_name) {
+ print STDERR "${file}:$.: warning: expecting prototype for $decl_type $identifier. Prototype was for $decl_type $declaration_name instead\n";
+ return;
+ }
+
+ # ignore members marked private:
+ $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi;
+ $members =~ s/\/\*\s*private:.*//gosi;
+ # strip comments:
+ $members =~ s/\/\*.*?\*\///gos;
+ # strip attributes
+ $members =~ s/\s*$attribute/ /gi;
+ $members =~ s/\s*__aligned\s*\([^;]*\)/ /gos;
+ $members =~ s/\s*__packed\s*/ /gos;
+ $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos;
+ $members =~ s/\s*____cacheline_aligned_in_smp/ /gos;
+ $members =~ s/\s*____cacheline_aligned/ /gos;
+ # unwrap struct_group():
+ # - first eat non-declaration parameters and rewrite for final match
+ # - then remove macro, outer parens, and trailing semicolon
+ $members =~ s/\bstruct_group\s*\(([^,]*,)/STRUCT_GROUP(/gos;
+ $members =~ s/\bstruct_group_(attr|tagged)\s*\(([^,]*,){2}/STRUCT_GROUP(/gos;
+ $members =~ s/\b__struct_group\s*\(([^,]*,){3}/STRUCT_GROUP(/gos;
+ $members =~ s/\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;/$2/gos;
+
+ my $args = qr{([^,)]+)};
+ # replace DECLARE_BITMAP
+ $members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos;
+ $members =~ s/DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, PHY_INTERFACE_MODE_MAX)/gos;
+ $members =~ s/DECLARE_BITMAP\s*\($args,\s*$args\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;
+ # replace DECLARE_HASHTABLE
+ $members =~ s/DECLARE_HASHTABLE\s*\($args,\s*$args\)/unsigned long $1\[1 << (($2) - 1)\]/gos;
+ # replace DECLARE_KFIFO
+ $members =~ s/DECLARE_KFIFO\s*\($args,\s*$args,\s*$args\)/$2 \*$1/gos;
+ # replace DECLARE_KFIFO_PTR
+ $members =~ s/DECLARE_KFIFO_PTR\s*\($args,\s*$args\)/$2 \*$1/gos;
+ # replace DECLARE_FLEX_ARRAY
+ $members =~ s/(?:__)?DECLARE_FLEX_ARRAY\s*\($args,\s*$args\)/$1 $2\[\]/gos;
+ my $declaration = $members;
+
+ # Split nested struct/union elements as newer ones
+ while ($members =~ m/$struct_members/) {
+ my $newmember;
+ my $maintype = $1;
+ my $ids = $4;
+ my $content = $3;
+ foreach my $id(split /,/, $ids) {
+ $newmember .= "$maintype $id; ";
+
+ $id =~ s/[:\[].*//;
+ $id =~ s/^\s*\**(\S+)\s*/$1/;
+ foreach my $arg (split /;/, $content) {
+ next if ($arg =~ m/^\s*$/);
+ if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) {
+ # pointer-to-function
+ my $type = $1;
+ my $name = $2;
+ my $extra = $3;
+ next if (!$name);
+ if ($id =~ m/^\s*$/) {
+ # anonymous struct/union
+ $newmember .= "$type$name$extra; ";
+ } else {
+ $newmember .= "$type$id.$name$extra; ";
+ }
+ } else {
+ my $type;
+ my $names;
+ $arg =~ s/^\s+//;
+ $arg =~ s/\s+$//;
+ # Handle bitmaps
+ $arg =~ s/:\s*\d+\s*//g;
+ # Handle arrays
+ $arg =~ s/\[.*\]//g;
+ # The type may have multiple words,
+ # and multiple IDs can be defined, like:
+ # const struct foo, *bar, foobar
+ # So, we remove spaces when parsing the
+ # names, in order to match just names
+ # and commas for the names
+ $arg =~ s/\s*,\s*/,/g;
+ if ($arg =~ m/(.*)\s+([\S+,]+)/) {
+ $type = $1;
+ $names = $2;
+ } else {
+ $newmember .= "$arg; ";
+ next;
+ }
+ foreach my $name (split /,/, $names) {
+ $name =~ s/^\s*\**(\S+)\s*/$1/;
+ next if (($name =~ m/^\s*$/));
+ if ($id =~ m/^\s*$/) {
+ # anonymous struct/union
+ $newmember .= "$type $name; ";
+ } else {
+ $newmember .= "$type $id.$name; ";
+ }
+ }
+ }
+ }
+ }
+ $members =~ s/$struct_members/$newmember/;
+ }
+
+ # Ignore other nested elements, like enums
+ $members =~ s/(\{[^\{\}]*\})//g;
+
+ create_parameterlist($members, ';', $file, $declaration_name);
+ check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual);
+
+ # Adjust declaration for better display
+ $declaration =~ s/([\{;])/$1\n/g;
+ $declaration =~ s/\}\s+;/};/g;
+ # Better handle inlined enums
+ do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/);
+
+ my @def_args = split /\n/, $declaration;
+ my $level = 1;
+ $declaration = "";
+ foreach my $clause (@def_args) {
+ $clause =~ s/^\s+//;
+ $clause =~ s/\s+$//;
+ $clause =~ s/\s+/ /;
+ next if (!$clause);
+ $level-- if ($clause =~ m/(\})/ && $level > 1);
+ if (!($clause =~ m/^\s*#/)) {
+ $declaration .= "\t" x $level;
+ }
+ $declaration .= "\t" . $clause . "\n";
+ $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/));
+ }
+ output_declaration($declaration_name,
+ 'struct',
+ {'struct' => $declaration_name,
+ 'module' => $modulename,
+ 'definition' => $declaration,
+ 'parameterlist' => \@parameterlist,
+ 'parameterdescs' => \%parameterdescs,
+ 'parametertypes' => \%parametertypes,
+ 'sectionlist' => \@sectionlist,
+ 'sections' => \%sections,
+ 'purpose' => $declaration_purpose,
+ 'type' => $decl_type
+ });
+ }
+ else {
+ print STDERR "${file}:$.: error: Cannot parse struct or union!\n";
+ ++$errors;
+ }
+}
+
+
+sub show_warnings($$) {
+ my $functype = shift;
+ my $name = shift;
+
+ return 0 if (defined($nosymbol_table{$name}));
+
+ return 1 if ($output_selection == OUTPUT_ALL);
+
+ if ($output_selection == OUTPUT_EXPORTED) {
+ if (defined($function_table{$name})) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ if ($output_selection == OUTPUT_INTERNAL) {
+ if (!($functype eq "function" && defined($function_table{$name}))) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ if ($output_selection == OUTPUT_INCLUDE) {
+ if (defined($function_table{$name})) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ die("Please add the new output type at show_warnings()");
+}
+
+sub dump_enum($$) {
+ my $x = shift;
+ my $file = shift;
+ my $members;
+
+
+ $x =~ s@/\*.*?\*/@@gos; # strip comments.
+ # strip #define macros inside enums
+ $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos;
+
+ if ($x =~ /typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;/) {
+ $declaration_name = $2;
+ $members = $1;
+ } elsif ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) {
+ $declaration_name = $1;
+ $members = $2;
+ }
+
+ if ($members) {
+ if ($identifier ne $declaration_name) {
+ if ($identifier eq "") {
+ print STDERR "${file}:$.: warning: wrong kernel-doc identifier on line:\n";
+ } else {
+ print STDERR "${file}:$.: warning: expecting prototype for enum $identifier. Prototype was for enum $declaration_name instead\n";
+ }
+ return;
+ }
+ $declaration_name = "(anonymous)" if ($declaration_name eq "");
+
+ my %_members;
+
+ $members =~ s/\s+$//;
+
+ foreach my $arg (split ',', $members) {
+ $arg =~ s/^\s*(\w+).*/$1/;
+ push @parameterlist, $arg;
+ if (!$parameterdescs{$arg}) {
+ $parameterdescs{$arg} = $undescribed;
+ if (show_warnings("enum", $declaration_name)) {
+ print STDERR "${file}:$.: warning: Enum value '$arg' not described in enum '$declaration_name'\n";
+ }
+ }
+ $_members{$arg} = 1;
+ }
+
+ while (my ($k, $v) = each %parameterdescs) {
+ if (!exists($_members{$k})) {
+ if (show_warnings("enum", $declaration_name)) {
+ print STDERR "${file}:$.: warning: Excess enum value '$k' description in '$declaration_name'\n";
+ }
+ }
+ }
+
+ output_declaration($declaration_name,
+ 'enum',
+ {'enum' => $declaration_name,
+ 'module' => $modulename,
+ 'parameterlist' => \@parameterlist,
+ 'parameterdescs' => \%parameterdescs,
+ 'sectionlist' => \@sectionlist,
+ 'sections' => \%sections,
+ 'purpose' => $declaration_purpose
+ });
+ } else {
+ print STDERR "${file}:$.: error: Cannot parse enum!\n";
+ ++$errors;
+ }
+}
+
+my $typedef_type = qr { ((?:\s+[\w\*]+\b){1,8})\s* }x;
+my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x;
+my $typedef_args = qr { \s*\((.*)\); }x;
+
+my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x;
+my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x;
+
+sub dump_typedef($$) {
+ my $x = shift;
+ my $file = shift;
+
+ $x =~ s@/\*.*?\*/@@gos; # strip comments.
+
+ # Parse function typedef prototypes
+ if ($x =~ $typedef1 || $x =~ $typedef2) {
+ $return_type = $1;
+ $declaration_name = $2;
+ my $args = $3;
+ $return_type =~ s/^\s+//;
+
+ if ($identifier ne $declaration_name) {
+ print STDERR "${file}:$.: warning: expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n";
+ return;
+ }
+
+ create_parameterlist($args, ',', $file, $declaration_name);
+
+ output_declaration($declaration_name,
+ 'function',
+ {'function' => $declaration_name,
+ 'typedef' => 1,
+ 'module' => $modulename,
+ 'functiontype' => $return_type,
+ 'parameterlist' => \@parameterlist,
+ 'parameterdescs' => \%parameterdescs,
+ 'parametertypes' => \%parametertypes,
+ 'sectionlist' => \@sectionlist,
+ 'sections' => \%sections,
+ 'purpose' => $declaration_purpose
+ });
+ return;
+ }
+
+ while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) {
+ $x =~ s/\(*.\)\s*;$/;/;
+ $x =~ s/\[*.\]\s*;$/;/;
+ }
+
+ if ($x =~ /typedef.*\s+(\w+)\s*;/) {
+ $declaration_name = $1;
+
+ if ($identifier ne $declaration_name) {
+ print STDERR "${file}:$.: warning: expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n";
+ return;
+ }
+
+ output_declaration($declaration_name,
+ 'typedef',
+ {'typedef' => $declaration_name,
+ 'module' => $modulename,
+ 'sectionlist' => \@sectionlist,
+ 'sections' => \%sections,
+ 'purpose' => $declaration_purpose
+ });
+ }
+ else {
+ print STDERR "${file}:$.: error: Cannot parse typedef!\n";
+ ++$errors;
+ }
+}
+
+sub save_struct_actual($) {
+ my $actual = shift;
+
+ # strip all spaces from the actual param so that it looks like one string item
+ $actual =~ s/\s*//g;
+ $struct_actual = $struct_actual . $actual . " ";
+}
+
+sub create_parameterlist($$$$) {
+ my $args = shift;
+ my $splitter = shift;
+ my $file = shift;
+ my $declaration_name = shift;
+ my $type;
+ my $param;
+
+ # temporarily replace commas inside function pointer definition
+ my $arg_expr = qr{\([^\),]+};
+ while ($args =~ /$arg_expr,/) {
+ $args =~ s/($arg_expr),/$1#/g;
+ }
+
+ foreach my $arg (split($splitter, $args)) {
+ # strip comments
+ $arg =~ s/\/\*.*\*\///;
+ # strip leading/trailing spaces
+ $arg =~ s/^\s*//;
+ $arg =~ s/\s*$//;
+ $arg =~ s/\s+/ /;
+
+ if ($arg =~ /^#/) {
+ # Treat preprocessor directive as a typeless variable just to fill
+ # corresponding data structures "correctly". Catch it later in
+ # output_* subs.
+ push_parameter($arg, "", "", $file);
+ } elsif ($arg =~ m/\(.+\)\s*\(/) {
+ # pointer-to-function
+ $arg =~ tr/#/,/;
+ $arg =~ m/[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)/;
+ $param = $1;
+ $type = $arg;
+ $type =~ s/([^\(]+\(\*?)\s*$param/$1/;
+ save_struct_actual($param);
+ push_parameter($param, $type, $arg, $file, $declaration_name);
+ } elsif ($arg) {
+ $arg =~ s/\s*:\s*/:/g;
+ $arg =~ s/\s*\[/\[/g;
+
+ my @args = split('\s*,\s*', $arg);
+ if ($args[0] =~ m/\*/) {
+ $args[0] =~ s/(\*+)\s*/ $1/;
+ }
+
+ my @first_arg;
+ if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) {
+ shift @args;
+ push(@first_arg, split('\s+', $1));
+ push(@first_arg, $2);
+ } else {
+ @first_arg = split('\s+', shift @args);
+ }
+
+ unshift(@args, pop @first_arg);
+ $type = join " ", @first_arg;
+
+ foreach $param (@args) {
+ if ($param =~ m/^(\*+)\s*(.*)/) {
+ save_struct_actual($2);
+
+ push_parameter($2, "$type $1", $arg, $file, $declaration_name);
+ }
+ elsif ($param =~ m/(.*?):(\d+)/) {
+ if ($type ne "") { # skip unnamed bit-fields
+ save_struct_actual($1);
+ push_parameter($1, "$type:$2", $arg, $file, $declaration_name)
+ }
+ }
+ else {
+ save_struct_actual($param);
+ push_parameter($param, $type, $arg, $file, $declaration_name);
+ }
+ }
+ }
+ }
+}
+
+sub push_parameter($$$$$) {
+ my $param = shift;
+ my $type = shift;
+ my $org_arg = shift;
+ my $file = shift;
+ my $declaration_name = shift;
+
+ if (($anon_struct_union == 1) && ($type eq "") &&
+ ($param eq "}")) {
+ return; # ignore the ending }; from anon. struct/union
+ }
+
+ $anon_struct_union = 0;
+ $param =~ s/[\[\)].*//;
+
+ if ($type eq "" && $param =~ /\.\.\.$/)
+ {
+ if (!$param =~ /\w\.\.\.$/) {
+ # handles unnamed variable parameters
+ $param = "...";
+ }
+ elsif ($param =~ /\w\.\.\.$/) {
+ # for named variable parameters of the form `x...`, remove the dots
+ $param =~ s/\.\.\.$//;
+ }
+ if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") {
+ $parameterdescs{$param} = "variable arguments";
+ }
+ }
+ elsif ($type eq "" && ($param eq "" or $param eq "void"))
+ {
+ $param="void";
+ $parameterdescs{void} = "no arguments";
+ }
+ elsif ($type eq "" && ($param eq "struct" or $param eq "union"))
+ # handle unnamed (anonymous) union or struct:
+ {
+ $type = $param;
+ $param = "{unnamed_" . $param . "}";
+ $parameterdescs{$param} = "anonymous\n";
+ $anon_struct_union = 1;
+ }
+
+ # warn if parameter has no description
+ # (but ignore ones starting with # as these are not parameters
+ # but inline preprocessor statements);
+ # Note: It will also ignore void params and unnamed structs/unions
+ if (!defined $parameterdescs{$param} && $param !~ /^#/) {
+ $parameterdescs{$param} = $undescribed;
+
+ if (show_warnings($type, $declaration_name) && $param !~ /\./) {
+ print STDERR
+ "${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n";
+ ++$warnings;
+ }
+ }
+
+ # strip spaces from $param so that it is one continuous string
+ # on @parameterlist;
+ # this fixes a problem where check_sections() cannot find
+ # a parameter like "addr[6 + 2]" because it actually appears
+ # as "addr[6", "+", "2]" on the parameter list;
+ # but it's better to maintain the param string unchanged for output,
+ # so just weaken the string compare in check_sections() to ignore
+ # "[blah" in a parameter string;
+ ###$param =~ s/\s*//g;
+ push @parameterlist, $param;
+ $org_arg =~ s/\s\s+/ /g;
+ $parametertypes{$param} = $org_arg;
+}
+
+sub check_sections($$$$$) {
+ my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_;
+ my @sects = split ' ', $sectcheck;
+ my @prms = split ' ', $prmscheck;
+ my $err;
+ my ($px, $sx);
+ my $prm_clean; # strip trailing "[array size]" and/or beginning "*"
+
+ foreach $sx (0 .. $#sects) {
+ $err = 1;
+ foreach $px (0 .. $#prms) {
+ $prm_clean = $prms[$px];
+ $prm_clean =~ s/\[.*\]//;
+ $prm_clean =~ s/$attribute//i;
+ # ignore array size in a parameter string;
+ # however, the original param string may contain
+ # spaces, e.g.: addr[6 + 2]
+ # and this appears in @prms as "addr[6" since the
+ # parameter list is split at spaces;
+ # hence just ignore "[..." for the sections check;
+ $prm_clean =~ s/\[.*//;
+
+ ##$prm_clean =~ s/^\**//;
+ if ($prm_clean eq $sects[$sx]) {
+ $err = 0;
+ last;
+ }
+ }
+ if ($err) {
+ if ($decl_type eq "function") {
+ print STDERR "${file}:$.: warning: " .
+ "Excess function parameter " .
+ "'$sects[$sx]' " .
+ "description in '$decl_name'\n";
+ ++$warnings;
+ }
+ }
+ }
+}
+
+##
+# Checks the section describing the return value of a function.
+sub check_return_section {
+ my $file = shift;
+ my $declaration_name = shift;
+ my $return_type = shift;
+
+ # Ignore an empty return type (It's a macro)
+ # Ignore functions with a "void" return type. (But don't ignore "void *")
+ if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) {
+ return;
+ }
+
+ if (!defined($sections{$section_return}) ||
+ $sections{$section_return} eq "") {
+ print STDERR "${file}:$.: warning: " .
+ "No description found for return value of " .
+ "'$declaration_name'\n";
+ ++$warnings;
+ }
+}
+
+##
+# takes a function prototype and the name of the current file being
+# processed and spits out all the details stored in the global
+# arrays/hashes.
+sub dump_function($$) {
+ my $prototype = shift;
+ my $file = shift;
+ my $noret = 0;
+
+ print_lineno($new_start_line);
+
+ $prototype =~ s/^static +//;
+ $prototype =~ s/^extern +//;
+ $prototype =~ s/^asmlinkage +//;
+ $prototype =~ s/^inline +//;
+ $prototype =~ s/^__inline__ +//;
+ $prototype =~ s/^__inline +//;
+ $prototype =~ s/^__always_inline +//;
+ $prototype =~ s/^noinline +//;
+ $prototype =~ s/__init +//;
+ $prototype =~ s/__init_or_module +//;
+ $prototype =~ s/__deprecated +//;
+ $prototype =~ s/__flatten +//;
+ $prototype =~ s/__meminit +//;
+ $prototype =~ s/__must_check +//;
+ $prototype =~ s/__weak +//;
+ $prototype =~ s/__sched +//;
+ $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//;
+ $prototype =~ s/__alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +//;
+ my $define = $prototype =~ s/^#\s*define\s+//; #ak added
+ $prototype =~ s/__attribute_const__ +//;
+ $prototype =~ s/__attribute__\s*\(\(
+ (?:
+ [\w\s]++ # attribute name
+ (?:\([^)]*+\))? # attribute arguments
+ \s*+,? # optional comma at the end
+ )+
+ \)\)\s+//x;
+
+ # Yes, this truly is vile. We are looking for:
+ # 1. Return type (may be nothing if we're looking at a macro)
+ # 2. Function name
+ # 3. Function parameters.
+ #
+ # All the while we have to watch out for function pointer parameters
+ # (which IIRC is what the two sections are for), C types (these
+ # regexps don't even start to express all the possibilities), and
+ # so on.
+ #
+ # If you mess with these regexps, it's a good idea to check that
+ # the following functions' documentation still comes out right:
+ # - parport_register_device (function pointer parameters)
+ # - atomic_set (macro)
+ # - pci_match_device, __copy_to_user (long return type)
+ my $name = qr{[a-zA-Z0-9_~:]+};
+ my $prototype_end1 = qr{[^\(]*};
+ my $prototype_end2 = qr{[^\{]*};
+ my $prototype_end = qr{\(($prototype_end1|$prototype_end2)\)};
+ my $type1 = qr{[\w\s]+};
+ my $type2 = qr{$type1\*+};
+
+ if ($define && $prototype =~ m/^()($name)\s+/) {
+ # This is an object-like macro, it has no return type and no parameter
+ # list.
+ # Function-like macros are not allowed to have spaces between
+ # declaration_name and opening parenthesis (notice the \s+).
+ $return_type = $1;
+ $declaration_name = $2;
+ $noret = 1;
+ } elsif ($prototype =~ m/^()($name)\s*$prototype_end/ ||
+ $prototype =~ m/^($type1)\s+($name)\s*$prototype_end/ ||
+ $prototype =~ m/^($type2+)\s*($name)\s*$prototype_end/) {
+ $return_type = $1;
+ $declaration_name = $2;
+ my $args = $3;
+
+ create_parameterlist($args, ',', $file, $declaration_name);
+ } else {
+ print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n";
+ return;
+ }
+
+ if ($identifier ne $declaration_name) {
+ print STDERR "${file}:$.: warning: expecting prototype for $identifier(). Prototype was for $declaration_name() instead\n";
+ return;
+ }
+
+ my $prms = join " ", @parameterlist;
+ check_sections($file, $declaration_name, "function", $sectcheck, $prms);
+
+ # This check emits a lot of warnings at the moment, because many
+ # functions don't have a 'Return' doc section. So until the number
+ # of warnings goes sufficiently down, the check is only performed in
+ # verbose mode.
+ # TODO: always perform the check.
+ if ($verbose && !$noret) {
+ check_return_section($file, $declaration_name, $return_type);
+ }
+
+ # The function parser can be called with a typedef parameter.
+ # Handle it.
+ if ($return_type =~ /typedef/) {
+ output_declaration($declaration_name,
+ 'function',
+ {'function' => $declaration_name,
+ 'typedef' => 1,
+ 'module' => $modulename,
+ 'functiontype' => $return_type,
+ 'parameterlist' => \@parameterlist,
+ 'parameterdescs' => \%parameterdescs,
+ 'parametertypes' => \%parametertypes,
+ 'sectionlist' => \@sectionlist,
+ 'sections' => \%sections,
+ 'purpose' => $declaration_purpose
+ });
+ } else {
+ output_declaration($declaration_name,
+ 'function',
+ {'function' => $declaration_name,
+ 'module' => $modulename,
+ 'functiontype' => $return_type,
+ 'parameterlist' => \@parameterlist,
+ 'parameterdescs' => \%parameterdescs,
+ 'parametertypes' => \%parametertypes,
+ 'sectionlist' => \@sectionlist,
+ 'sections' => \%sections,
+ 'purpose' => $declaration_purpose
+ });
+ }
+}
+
+sub reset_state {
+ $function = "";
+ %parameterdescs = ();
+ %parametertypes = ();
+ @parameterlist = ();
+ %sections = ();
+ @sectionlist = ();
+ $sectcheck = "";
+ $struct_actual = "";
+ $prototype = "";
+
+ $state = STATE_NORMAL;
+ $inline_doc_state = STATE_INLINE_NA;
+}
+
+sub tracepoint_munge($) {
+ my $file = shift;
+ my $tracepointname = 0;
+ my $tracepointargs = 0;
+
+ if ($prototype =~ m/TRACE_EVENT\((.*?),/) {
+ $tracepointname = $1;
+ }
+ if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) {
+ $tracepointname = $1;
+ }
+ if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) {
+ $tracepointname = $2;
+ }
+ $tracepointname =~ s/^\s+//; #strip leading whitespace
+ if ($prototype =~ m/TP_PROTO\((.*?)\)/) {
+ $tracepointargs = $1;
+ }
+ if (($tracepointname eq 0) || ($tracepointargs eq 0)) {
+ print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n".
+ "$prototype\n";
+ } else {
+ $prototype = "static inline void trace_$tracepointname($tracepointargs)";
+ $identifier = "trace_$identifier";
+ }
+}
+
+sub syscall_munge() {
+ my $void = 0;
+
+ $prototype =~ s@[\r\n]+@ @gos; # strip newlines/CR's
+## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) {
+ if ($prototype =~ m/SYSCALL_DEFINE0/) {
+ $void = 1;
+## $prototype = "long sys_$1(void)";
+ }
+
+ $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name
+ if ($prototype =~ m/long (sys_.*?),/) {
+ $prototype =~ s/,/\(/;
+ } elsif ($void) {
+ $prototype =~ s/\)/\(void\)/;
+ }
+
+ # now delete all of the odd-number commas in $prototype
+ # so that arg types & arg names don't have a comma between them
+ my $count = 0;
+ my $len = length($prototype);
+ if ($void) {
+ $len = 0; # skip the for-loop
+ }
+ for (my $ix = 0; $ix < $len; $ix++) {
+ if (substr($prototype, $ix, 1) eq ',') {
+ $count++;
+ if ($count % 2 == 1) {
+ substr($prototype, $ix, 1) = ' ';
+ }
+ }
+ }
+}
+
+sub process_proto_function($$) {
+ my $x = shift;
+ my $file = shift;
+
+ $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
+
+ if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) {
+ # do nothing
+ }
+ elsif ($x =~ /([^\{]*)/) {
+ $prototype .= $1;
+ }
+
+ if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) {
+ $prototype =~ s@/\*.*?\*/@@gos; # strip comments.
+ $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
+ $prototype =~ s@^\s+@@gos; # strip leading spaces
+
+ # Handle prototypes for function pointers like:
+ # int (*pcs_config)(struct foo)
+ $prototype =~ s@^(\S+\s+)\(\s*\*(\S+)\)@$1$2@gos;
+
+ if ($prototype =~ /SYSCALL_DEFINE/) {
+ syscall_munge();
+ }
+ if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ ||
+ $prototype =~ /DEFINE_SINGLE_EVENT/)
+ {
+ tracepoint_munge($file);
+ }
+ dump_function($prototype, $file);
+ reset_state();
+ }
+}
+
+sub process_proto_type($$) {
+ my $x = shift;
+ my $file = shift;
+
+ $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
+ $x =~ s@^\s+@@gos; # strip leading spaces
+ $x =~ s@\s+$@@gos; # strip trailing spaces
+ $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
+
+ if ($x =~ /^#/) {
+ # To distinguish preprocessor directive from regular declaration later.
+ $x .= ";";
+ }
+
+ while (1) {
+ if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) {
+ if( length $prototype ) {
+ $prototype .= " "
+ }
+ $prototype .= $1 . $2;
+ ($2 eq '{') && $brcount++;
+ ($2 eq '}') && $brcount--;
+ if (($2 eq ';') && ($brcount == 0)) {
+ dump_declaration($prototype, $file);
+ reset_state();
+ last;
+ }
+ $x = $3;
+ } else {
+ $prototype .= $x;
+ last;
+ }
+ }
+}
+
+
+sub map_filename($) {
+ my $file;
+ my ($orig_file) = @_;
+
+ if (defined($ENV{'SRCTREE'})) {
+ $file = "$ENV{'SRCTREE'}" . "/" . $orig_file;
+ } else {
+ $file = $orig_file;
+ }
+
+ if (defined($source_map{$file})) {
+ $file = $source_map{$file};
+ }
+
+ return $file;
+}
+
+sub process_export_file($) {
+ my ($orig_file) = @_;
+ my $file = map_filename($orig_file);
+
+ if (!open(IN,"<$file")) {
+ print STDERR "Error: Cannot open file $file\n";
+ ++$errors;
+ return;
+ }
+
+ while (<IN>) {
+ if (/$export_symbol/) {
+ next if (defined($nosymbol_table{$2}));
+ $function_table{$2} = 1;
+ }
+ }
+
+ close(IN);
+}
+
+#
+# Parsers for the various processing states.
+#
+# STATE_NORMAL: looking for the /** to begin everything.
+#
+sub process_normal() {
+ if (/$doc_start/o) {
+ $state = STATE_NAME; # next line is always the function name
+ $in_doc_sect = 0;
+ $declaration_start_line = $. + 1;
+ }
+}
+
+#
+# STATE_NAME: Looking for the "name - description" line
+#
+sub process_name($$) {
+ my $file = shift;
+ my $descr;
+
+ if (/$doc_block/o) {
+ $state = STATE_DOCBLOCK;
+ $contents = "";
+ $new_start_line = $.;
+
+ if ( $1 eq "" ) {
+ $section = $section_intro;
+ } else {
+ $section = $1;
+ }
+ } elsif (/$doc_decl/o) {
+ $identifier = $1;
+ my $is_kernel_comment = 0;
+ my $decl_start = qr{$doc_com};
+ # test for pointer declaration type, foo * bar() - desc
+ my $fn_type = qr{\w+\s*\*\s*};
+ my $parenthesis = qr{\(\w*\)};
+ my $decl_end = qr{[-:].*};
+ if (/^$decl_start([\w\s]+?)$parenthesis?\s*$decl_end?$/) {
+ $identifier = $1;
+ }
+ if ($identifier =~ m/^(struct|union|enum|typedef)\b\s*(\S*)/) {
+ $decl_type = $1;
+ $identifier = $2;
+ $is_kernel_comment = 1;
+ }
+ # Look for foo() or static void foo() - description; or misspelt
+ # identifier
+ elsif (/^$decl_start$fn_type?(\w+)\s*$parenthesis?\s*$decl_end?$/ ||
+ /^$decl_start$fn_type?(\w+.*)$parenthesis?\s*$decl_end$/) {
+ $identifier = $1;
+ $decl_type = 'function';
+ $identifier =~ s/^define\s+//;
+ $is_kernel_comment = 1;
+ }
+ $identifier =~ s/\s+$//;
+
+ $state = STATE_BODY;
+ # if there's no @param blocks need to set up default section
+ # here
+ $contents = "";
+ $section = $section_default;
+ $new_start_line = $. + 1;
+ if (/[-:](.*)/) {
+ # strip leading/trailing/multiple spaces
+ $descr= $1;
+ $descr =~ s/^\s*//;
+ $descr =~ s/\s*$//;
+ $descr =~ s/\s+/ /g;
+ $declaration_purpose = $descr;
+ $state = STATE_BODY_MAYBE;
+ } else {
+ $declaration_purpose = "";
+ }
+
+ if (!$is_kernel_comment) {
+ print STDERR "${file}:$.: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n";
+ print STDERR $_;
+ ++$warnings;
+ $state = STATE_NORMAL;
+ }
+
+ if (($declaration_purpose eq "") && $verbose) {
+ print STDERR "${file}:$.: warning: missing initial short description on line:\n";
+ print STDERR $_;
+ ++$warnings;
+ }
+
+ if ($identifier eq "" && $decl_type ne "enum") {
+ print STDERR "${file}:$.: warning: wrong kernel-doc identifier on line:\n";
+ print STDERR $_;
+ ++$warnings;
+ $state = STATE_NORMAL;
+ }
+
+ if ($verbose) {
+ print STDERR "${file}:$.: info: Scanning doc for $decl_type $identifier\n";
+ }
+ } else {
+ print STDERR "${file}:$.: warning: Cannot understand $_ on line $.",
+ " - I thought it was a doc line\n";
+ ++$warnings;
+ $state = STATE_NORMAL;
+ }
+}
+
+
+#
+# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment.
+#
+sub process_body($$) {
+ my $file = shift;
+
+ # Until all named variable macro parameters are
+ # documented using the bare name (`x`) rather than with
+ # dots (`x...`), strip the dots:
+ if ($section =~ /\w\.\.\.$/) {
+ $section =~ s/\.\.\.$//;
+
+ if ($verbose) {
+ print STDERR "${file}:$.: warning: Variable macro arguments should be documented without dots\n";
+ ++$warnings;
+ }
+ }
+
+ if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) {
+ dump_section($file, $section, $contents);
+ $section = $section_default;
+ $new_start_line = $.;
+ $contents = "";
+ }
+
+ if (/$doc_sect/i) { # case insensitive for supported section names
+ $newsection = $1;
+ $newcontents = $2;
+
+ # map the supported section names to the canonical names
+ if ($newsection =~ m/^description$/i) {
+ $newsection = $section_default;
+ } elsif ($newsection =~ m/^context$/i) {
+ $newsection = $section_context;
+ } elsif ($newsection =~ m/^returns?$/i) {
+ $newsection = $section_return;
+ } elsif ($newsection =~ m/^\@return$/) {
+ # special: @return is a section, not a param description
+ $newsection = $section_return;
+ }
+
+ if (($contents ne "") && ($contents ne "\n")) {
+ if (!$in_doc_sect && $verbose) {
+ print STDERR "${file}:$.: warning: contents before sections\n";
+ ++$warnings;
+ }
+ dump_section($file, $section, $contents);
+ $section = $section_default;
+ }
+
+ $in_doc_sect = 1;
+ $state = STATE_BODY;
+ $contents = $newcontents;
+ $new_start_line = $.;
+ while (substr($contents, 0, 1) eq " ") {
+ $contents = substr($contents, 1);
+ }
+ if ($contents ne "") {
+ $contents .= "\n";
+ }
+ $section = $newsection;
+ $leading_space = undef;
+ } elsif (/$doc_end/) {
+ if (($contents ne "") && ($contents ne "\n")) {
+ dump_section($file, $section, $contents);
+ $section = $section_default;
+ $contents = "";
+ }
+ # look for doc_com + <text> + doc_end:
+ if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') {
+ print STDERR "${file}:$.: warning: suspicious ending line: $_";
+ ++$warnings;
+ }
+
+ $prototype = "";
+ $state = STATE_PROTO;
+ $brcount = 0;
+ $new_start_line = $. + 1;
+ } elsif (/$doc_content/) {
+ if ($1 eq "") {
+ if ($section eq $section_context) {
+ dump_section($file, $section, $contents);
+ $section = $section_default;
+ $contents = "";
+ $new_start_line = $.;
+ $state = STATE_BODY;
+ } else {
+ if ($section ne $section_default) {
+ $state = STATE_BODY_WITH_BLANK_LINE;
+ } else {
+ $state = STATE_BODY;
+ }
+ $contents .= "\n";
+ }
+ } elsif ($state == STATE_BODY_MAYBE) {
+ # Continued declaration purpose
+ chomp($declaration_purpose);
+ $declaration_purpose .= " " . $1;
+ $declaration_purpose =~ s/\s+/ /g;
+ } else {
+ my $cont = $1;
+ if ($section =~ m/^@/ || $section eq $section_context) {
+ if (!defined $leading_space) {
+ if ($cont =~ m/^(\s+)/) {
+ $leading_space = $1;
+ } else {
+ $leading_space = "";
+ }
+ }
+ $cont =~ s/^$leading_space//;
+ }
+ $contents .= $cont . "\n";
+ }
+ } else {
+ # i dont know - bad line? ignore.
+ print STDERR "${file}:$.: warning: bad line: $_";
+ ++$warnings;
+ }
+}
+
+
+#
+# STATE_PROTO: reading a function/whatever prototype.
+#
+sub process_proto($$) {
+ my $file = shift;
+
+ if (/$doc_inline_oneline/) {
+ $section = $1;
+ $contents = $2;
+ if ($contents ne "") {
+ $contents .= "\n";
+ dump_section($file, $section, $contents);
+ $section = $section_default;
+ $contents = "";
+ }
+ } elsif (/$doc_inline_start/) {
+ $state = STATE_INLINE;
+ $inline_doc_state = STATE_INLINE_NAME;
+ } elsif ($decl_type eq 'function') {
+ process_proto_function($_, $file);
+ } else {
+ process_proto_type($_, $file);
+ }
+}
+
+#
+# STATE_DOCBLOCK: within a DOC: block.
+#
+sub process_docblock($$) {
+ my $file = shift;
+
+ if (/$doc_end/) {
+ dump_doc_section($file, $section, $contents);
+ $section = $section_default;
+ $contents = "";
+ $function = "";
+ %parameterdescs = ();
+ %parametertypes = ();
+ @parameterlist = ();
+ %sections = ();
+ @sectionlist = ();
+ $prototype = "";
+ $state = STATE_NORMAL;
+ } elsif (/$doc_content/) {
+ if ( $1 eq "" ) {
+ $contents .= $blankline;
+ } else {
+ $contents .= $1 . "\n";
+ }
+ }
+}
+
+#
+# STATE_INLINE: docbook comments within a prototype.
+#
+sub process_inline($$) {
+ my $file = shift;
+
+ # First line (state 1) needs to be a @parameter
+ if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) {
+ $section = $1;
+ $contents = $2;
+ $new_start_line = $.;
+ if ($contents ne "") {
+ while (substr($contents, 0, 1) eq " ") {
+ $contents = substr($contents, 1);
+ }
+ $contents .= "\n";
+ }
+ $inline_doc_state = STATE_INLINE_TEXT;
+ # Documentation block end */
+ } elsif (/$doc_inline_end/) {
+ if (($contents ne "") && ($contents ne "\n")) {
+ dump_section($file, $section, $contents);
+ $section = $section_default;
+ $contents = "";
+ }
+ $state = STATE_PROTO;
+ $inline_doc_state = STATE_INLINE_NA;
+ # Regular text
+ } elsif (/$doc_content/) {
+ if ($inline_doc_state == STATE_INLINE_TEXT) {
+ $contents .= $1 . "\n";
+ # nuke leading blank lines
+ if ($contents =~ /^\s*$/) {
+ $contents = "";
+ }
+ } elsif ($inline_doc_state == STATE_INLINE_NAME) {
+ $inline_doc_state = STATE_INLINE_ERROR;
+ print STDERR "${file}:$.: warning: ";
+ print STDERR "Incorrect use of kernel-doc format: $_";
+ ++$warnings;
+ }
+ }
+}
+
+
+sub process_file($) {
+ my $file;
+ my $initial_section_counter = $section_counter;
+ my ($orig_file) = @_;
+
+ $file = map_filename($orig_file);
+
+ if (!open(IN_FILE,"<$file")) {
+ print STDERR "Error: Cannot open file $file\n";
+ ++$errors;
+ return;
+ }
+
+ $. = 1;
+
+ $section_counter = 0;
+ while (<IN_FILE>) {
+ while (s/\\\s*$//) {
+ $_ .= <IN_FILE>;
+ }
+ # Replace tabs by spaces
+ while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {};
+ # Hand this line to the appropriate state handler
+ if ($state == STATE_NORMAL) {
+ process_normal();
+ } elsif ($state == STATE_NAME) {
+ process_name($file, $_);
+ } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE ||
+ $state == STATE_BODY_WITH_BLANK_LINE) {
+ process_body($file, $_);
+ } elsif ($state == STATE_INLINE) { # scanning for inline parameters
+ process_inline($file, $_);
+ } elsif ($state == STATE_PROTO) {
+ process_proto($file, $_);
+ } elsif ($state == STATE_DOCBLOCK) {
+ process_docblock($file, $_);
+ }
+ }
+
+ # Make sure we got something interesting.
+ if ($initial_section_counter == $section_counter && $
+ output_mode ne "none") {
+ if ($output_selection == OUTPUT_INCLUDE) {
+ print STDERR "${file}:1: warning: '$_' not found\n"
+ for keys %function_table;
+ }
+ else {
+ print STDERR "${file}:1: warning: no structured comments found\n";
+ }
+ }
+ close IN_FILE;
+}
+
+
+if ($output_mode eq "rst") {
+ get_sphinx_version() if (!$sphinx_major);
+}
+
+$kernelversion = get_kernel_version();
+
+# generate a sequence of code that will splice in highlighting information
+# using the s// operator.
+for (my $k = 0; $k < @highlights; $k++) {
+ my $pattern = $highlights[$k][0];
+ my $result = $highlights[$k][1];
+# print STDERR "scanning pattern:$pattern, highlight:($result)\n";
+ $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n";
+}
+
+# Read the file that maps relative names to absolute names for
+# separate source and object directories and for shadow trees.
+if (open(SOURCE_MAP, "<.tmp_filelist.txt")) {
+ my ($relname, $absname);
+ while(<SOURCE_MAP>) {
+ chop();
+ ($relname, $absname) = (split())[0..1];
+ $relname =~ s:^/+::;
+ $source_map{$relname} = $absname;
+ }
+ close(SOURCE_MAP);
+}
+
+if ($output_selection == OUTPUT_EXPORTED ||
+ $output_selection == OUTPUT_INTERNAL) {
+
+ push(@export_file_list, @ARGV);
+
+ foreach (@export_file_list) {
+ chomp;
+ process_export_file($_);
+ }
+}
+
+foreach (@ARGV) {
+ chomp;
+ process_file($_);
+}
+if ($verbose && $errors) {
+ print STDERR "$errors errors\n";
+}
+if ($verbose && $warnings) {
+ print STDERR "$warnings warnings\n";
+}
+
+if ($Werror && $warnings) {
+ print STDERR "$warnings warnings as Errors\n";
+ exit($warnings);
+} else {
+ exit($output_mode eq "none" ? 0 : $errors)
+}
diff --git a/scripts/kernel-doc-check b/scripts/kernel-doc-check
new file mode 100755
index 0000000..23887d0
--- /dev/null
+++ b/scripts/kernel-doc-check
@@ -0,0 +1,10 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+kernel_doc=$(dirname $0)/kernel-doc
+
+"$kernel_doc" -none "$@" 2>&1 |
+ grep '\(warning\|error\)'
+
+# check that kernel-doc succeeded, but the grep failed
+[ ${PIPESTATUS[0]} -eq 0 -a ${PIPESTATUS[1]} -eq 1 ]
diff --git a/scripts/list-man-pages.sh b/scripts/list-man-pages.sh
new file mode 100755
index 0000000..3acdf7a
--- /dev/null
+++ b/scripts/list-man-pages.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+file=$1
+
+for func in $(sed -n 's/ \* \([a-z_][a-z_0-9]*\)() -.*/\1/p' $file); do
+ echo ${func}
+done
+
+for struct in $(sed -n 's/ \* struct \([a-z_][a-z_0-9]*\) -.*/\1/p' $file); do
+ echo ${struct}
+done
+
+for enum in $(sed -n 's/ \* enum \([a-z_][a-z_0-9]*\) -.*/\1/p' $file); do
+ echo ${enum}
+done
diff --git a/scripts/list-pre-compiled.sh b/scripts/list-pre-compiled.sh
new file mode 100755
index 0000000..c31caf9
--- /dev/null
+++ b/scripts/list-pre-compiled.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+for i in man/*.2; do
+ echo $i
+done
diff --git a/scripts/meson-vcs-tag.sh b/scripts/meson-vcs-tag.sh
new file mode 100755
index 0000000..8ce6924
--- /dev/null
+++ b/scripts/meson-vcs-tag.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eu
+set -o pipefail
+
+dir="${1:?}"
+fallback="${2:?}"
+
+# Apparently git describe has a bug where it always considers the work-tree
+# dirty when invoked with --git-dir (even though 'git status' is happy). Work
+# around this issue by cd-ing to the source directory.
+cd "$dir"
+# Check that we have either .git/ (a normal clone) or a .git file (a work-tree)
+# and that we don't get confused if a tarball is extracted in a higher-level
+# git repository.
+[ -e .git ] && git describe --abbrev=7 --dirty=+ 2>/dev/null | sed 's/^v//' || echo "$fallback"
diff --git a/scripts/release.sh b/scripts/release.sh
new file mode 100755
index 0000000..dad3f25
--- /dev/null
+++ b/scripts/release.sh
@@ -0,0 +1,132 @@
+#!/bin/bash
+
+usage() {
+ echo "Usage: release.sh [-d] VERSION"
+ echo ""
+ echo "The script does all necessary steps to create a new release."
+ echo ""
+ echo " -d: no documentation update"
+ echo " -n: dry run"
+ echo ""
+ echo "Note: The version number needs to be exactly"
+ echo " '^v[\d]+.[\d]+(.[\d\]+(-rc[0-9]+)?$'"
+ echo ""
+ echo "example:"
+ echo " release.sh v2.1-rc0 # v2.1 release candidate 0"
+ echo " release.sh v2.1 # v2.1 release"
+}
+
+build_doc=true
+dry_run=false
+
+while getopts "dn" o; do
+ case "${o}" in
+ d)
+ build_doc=false
+ ;;
+ n)
+ dry_run=true
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+shift $((OPTIND-1))
+
+VERSION=${1:-}
+
+if [ -z "$VERSION" ] ; then
+ usage
+ exit 1
+fi
+
+# expected version regex
+re='^v([0-9]+\.[0-9]+(\.[0-9]+)?)(-rc[0-9]+)?$'
+
+# use the version string provided from the command line
+if [[ "$VERSION" =~ ${re} ]]; then
+ echo "valid version $VERSION string"
+
+ # remove the leading 'v'
+ ver="${VERSION#v}"
+else
+ echo "invalid version string $VERSION"
+ exit 1
+fi
+
+cd "$(git rev-parse --show-toplevel)" || exit 1
+
+if [[ -f subprojects/libnvme.wrap ]]; then
+ git -C subprojects/libnvme fetch --all
+
+ # extract the vesion string from libnvme by using the ref
+ # defined in libnvme.wrap.
+ libnvme_ref=$(sed -n "s/revision = \([0-9a-z]\+\)/\1/p" subprojects/libnvme.wrap)
+ libnvme_VERSION=$(git -C subprojects/libnvme describe "${libnvme_ref}")
+ if [[ "${libnvme_VERSION}" =~ ${re} ]]; then
+ echo "libnvme: valid version ${libnvme_VERSION} string"
+
+ # remove the leading 'v'
+ libnvme_ver="${libnvme_VERSION#v}"
+ else
+ echo "libnvme: invalid version string ${libnvme_VERSION}"
+ exit 1
+ fi
+fi
+
+if [[ -n $(git status -s) ]]; then
+ echo "tree is dirty."
+ if [[ "${dry_run}" = false ]]; then
+ exit 1
+ fi
+fi
+
+if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ] ; then
+ echo "currently not on master branch. abort."
+ exit 1
+fi
+
+# update all docs
+doc_dir=""
+if [ -d "Documentation" ]; then
+ doc_dir="Documentation"
+elif [ -d "doc" ]; then
+ doc_dir="doc"
+else
+ echo "documenation directory not found"
+ exit 1
+fi
+
+# update meson.build
+sed -i -e "0,/[ \t]version: /s/\([ \t]version: \).*/\1\'$ver\',/" meson.build
+if [[ -f subprojects/libnvme.wrap ]]; then
+ sed -i -e "s/\(dependency('libnvme', version: '>=\)\([\.1-9]\+\)/\1$libnvme_ver/" meson.build
+fi
+
+if [[ "${dry_run}" = false ]]; then
+ git add meson.build
+ git commit -s -m "build: Update version to $VERSION"
+fi
+
+if [ "$build_doc" = true ]; then
+ # update documentation
+ ./scripts/update-docs.sh
+ if [[ "${dry_run}" = false ]]; then
+ git add $doc_dir
+ git commit -s -m "doc: Regenerate all docs for $VERSION"
+ fi
+fi
+
+if [[ "${dry_run}" = true ]]; then
+ exit 0
+fi
+
+git tag -s -m "Release $VERSION" "$VERSION"
+git push --dry-run origin "$VERSION"^{}:master tag "$VERSION"
+
+read -p "All good? Ready to push changes to remote? [Yy]" -n 1 -r
+echo
+if [[ $REPLY =~ ^[Yy]$ ]]; then
+ git push origin "$VERSION"^{}:master tag "$VERSION"
+fi
diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh
new file mode 100755
index 0000000..34d181e
--- /dev/null
+++ b/scripts/update-docs.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+cd "$(git rev-parse --show-toplevel)" || exit 1
+
+# build man docs
+BUILDDIR="$(mktemp -d)"
+echo "${BUILDDIR}"
+trap 'rm -rf -- ${BUILDDIR}' EXIT
+
+meson setup \
+ -Ddocs=man \
+ -Ddocs-build=true \
+ "${BUILDDIR}"
+meson compile \
+ -C "${BUILDDIR}"
+
+rm -rf doc/man
+mkdir doc/man
+
+find "${BUILDDIR}/doc" -maxdepth 1 -name '*.2' -exec cp {} doc/man \;
+
+# build ReST docs
+rm -rf -- "${BUILDDIR}"
+BUILDDIR="$(mktemp -d)"
+echo "${BUILDDIR}"
+trap 'rm -rf -- ${BUILDDIR}' EXIT
+
+meson setup \
+ -Ddocs=rst \
+ -Ddocs-build=true \
+ "${BUILDDIR}"
+meson compile \
+ -C "${BUILDDIR}"
+
+rm -rf doc/rst/*.rst
+mkdir -p doc/rst
+
+find "${BUILDDIR}/doc/rst" -maxdepth 1 -name '*.rst' -exec cp {} doc/rst \;
+
+cp "${BUILDDIR}/doc/conf.py" doc
+cp "${BUILDDIR}/doc/index.rst" doc
+cp "${BUILDDIR}/doc/config-schema.json" doc
+
+# build html docs
+# The HTML doc is not ready yet
+# rm -rf $DESTDIR/doc/html
+# cp -R $BUILDDIR/doc/html $DESTDIR/doc/
+
diff --git a/src/libnvme-mi.h b/src/libnvme-mi.h
new file mode 100644
index 0000000..f0b1a91
--- /dev/null
+++ b/src/libnvme-mi.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021 Code Construct Pty Ltd
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+
+#ifndef _LIBNVME_MI_H
+#define _LIBNVME_MI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "nvme/types.h"
+#include "nvme/mi.h"
+#include "nvme/log.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBNVME_MI_H */
diff --git a/src/libnvme-mi.map b/src/libnvme-mi.map
new file mode 100644
index 0000000..41e8110
--- /dev/null
+++ b/src/libnvme-mi.map
@@ -0,0 +1,61 @@
+LIBNVME_MI_1_5 {
+ global:
+ nvme_mi_ctrl_id;
+};
+
+LIBNVME_MI_1_4 {
+ global:
+ nvme_mi_admin_get_log_page;
+};
+
+LIBNVME_MI_1_3 {
+ global:
+ nvme_mi_admin_admin_passthru;
+ nvme_mi_ep_get_timeout;
+ nvme_mi_ep_set_timeout;
+ nvme_mi_set_probe_enabled;
+};
+
+LIBNVME_MI_1_2 {
+ global:
+ nvme_mi_admin_get_features;
+ nvme_mi_admin_set_features;
+ nvme_mi_admin_ns_mgmt;
+ nvme_mi_admin_ns_attach;
+ nvme_mi_admin_format_nvm;
+ nvme_mi_admin_sanitize_nvm;
+ nvme_mi_admin_fw_download;
+ nvme_mi_admin_fw_commit;
+ nvme_mi_status_to_string;
+};
+
+LIBNVME_MI_1_1 {
+ global:
+ nvme_mi_create_root;
+ nvme_mi_free_root;
+ nvme_mi_init_ctrl;
+ nvme_mi_close_ctrl;
+ nvme_mi_close;
+ nvme_mi_mi_config_get;
+ nvme_mi_mi_config_set;
+ nvme_mi_mi_read_mi_data_subsys;
+ nvme_mi_mi_read_mi_data_port;
+ nvme_mi_mi_read_mi_data_ctrl_list;
+ nvme_mi_mi_read_mi_data_ctrl;
+ nvme_mi_mi_subsystem_health_status_poll;
+ nvme_mi_admin_identify_partial;
+ nvme_mi_admin_get_log;
+ nvme_mi_admin_xfer;
+ nvme_mi_admin_security_send;
+ nvme_mi_admin_security_recv;
+ nvme_mi_endpoint_desc;
+ nvme_mi_first_endpoint;
+ nvme_mi_next_endpoint;
+ nvme_mi_first_ctrl;
+ nvme_mi_next_ctrl;
+ nvme_mi_open_mctp;
+ nvme_mi_scan_mctp;
+ nvme_mi_scan_ep;
+ local:
+ *;
+};
diff --git a/src/libnvme.h b/src/libnvme.h
new file mode 100644
index 0000000..2c7fe3a
--- /dev/null
+++ b/src/libnvme.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+
+#ifndef _LIBNVME_H
+#define _LIBNVME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "nvme/types.h"
+#include "nvme/linux.h"
+#include "nvme/ioctl.h"
+#include "nvme/nbft.h"
+#include "nvme/fabrics.h"
+#include "nvme/filters.h"
+#include "nvme/tree.h"
+#include "nvme/util.h"
+#include "nvme/log.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBNVME_H */
diff --git a/src/libnvme.map b/src/libnvme.map
new file mode 100644
index 0000000..c8163cb
--- /dev/null
+++ b/src/libnvme.map
@@ -0,0 +1,356 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+LIBNVME_1_8 {
+ global:
+ nvme_uuid_find;
+};
+
+LIBNVME_1_7 {
+ global:
+ nvme_init_copy_range_f2;
+ nvme_init_copy_range_f3;
+ nvme_insert_tls_key_versioned;
+ nvme_generate_tls_key_identity;
+};
+
+LIBNVME_1_6 {
+ global:
+ nvme_ctrl_config_match;
+ nvme_ctrl_find;
+ nvme_ctrl_get_src_addr;
+ nvme_ctrl_release_fd;
+ nvme_get_debug;
+ nvme_get_features_err_recovery2;
+ nvme_get_features_host_mem_buf2;
+ nvme_get_features_iocs_profile;
+ nvme_get_features_lba_range2;
+ nvme_get_features_resv_mask2;
+ nvme_get_features_resv_persist2;
+ nvme_host_release_fds;
+ nvme_ns_release_fd;
+ nvme_root_release_fds;
+ nvme_set_debug;
+ nvme_set_features_iocs_profile;
+ nvme_set_features_resv_mask2;
+ nvme_set_features_resv_persist2;
+ nvme_set_features_write_protect2;
+ nvme_set_root;
+ nvme_subsystem_get_iopolicy;
+ nvme_subsystem_release_fds;
+};
+
+LIBNVME_1_5 {
+ global:
+ nvme_ctrl_get_phy_slot;
+ nvme_ipaddrs_eq;
+ nvme_nbft_free;
+ nvme_nbft_read;
+ nvme_root_get_application;
+ nvme_root_set_application;
+ nvme_subsystem_get_application;
+ nvme_subsystem_set_application;
+};
+
+LIBNVME_1_4 {
+ global:
+ nvme_lookup_keyring;
+ nvme_describe_key_serial;
+ nvme_lookup_key;
+ nvme_set_keyring;
+ nvme_insert_tls_key;
+};
+
+LIBNVME_1_3 {
+ global:
+ nvme_ctrl_is_unique_discovery_ctrl;
+ nvme_ctrl_set_unique_discovery_ctrl;
+ nvme_io_mgmt_recv;
+ nvme_io_mgmt_send;
+ nvme_host_is_pdc_enabled;
+ nvme_host_set_pdc_enabled;
+};
+
+LIBNVME_1_2 {
+ global:
+ nvme_ctrl_get_dhchap_host_key;
+ nvme_ctrl_set_dhchap_host_key;
+ nvmf_get_discovery_wargs;
+ nvme_get_feature_length2;
+ nvme_ctrl_is_persistent;
+ nvme_uuid_from_string;
+ nvme_uuid_to_string;
+ nvme_uuid_random;
+};
+
+LIBNVME_1_1 {
+ global:
+ nvme_get_version;
+ nvme_init_copy_range_f1;
+};
+
+LIBNVME_1_0 {
+ global:
+ nvme_admin_passthru64;
+ nvme_admin_passthru;
+ nvme_capacity_mgmt;
+ nvme_copy;
+ nvme_create_root;
+ nvme_create_ctrl;
+ nvme_ctrl_first_ns;
+ nvme_ctrl_first_path;
+ nvme_ctrl_get_address;
+ nvme_ctrl_get_config;
+ nvme_ctrl_get_dhchap_key;
+ nvme_ctrl_get_fd;
+ nvme_ctrl_get_firmware;
+ nvme_ctrl_get_host_iface;
+ nvme_ctrl_get_host_traddr;
+ nvme_ctrl_get_model;
+ nvme_ctrl_get_name;
+ nvme_ctrl_get_numa_node;
+ nvme_ctrl_get_queue_count;
+ nvme_ctrl_get_serial;
+ nvme_ctrl_get_sqsize;
+ nvme_ctrl_get_state;
+ nvme_ctrl_get_subsysnqn;
+ nvme_ctrl_get_subsystem;
+ nvme_ctrl_get_sysfs_dir;
+ nvme_ctrl_get_traddr;
+ nvme_ctrl_get_transport;
+ nvme_ctrl_get_trsvcid;
+ nvme_ctrl_identify;
+ nvme_ctrl_is_discovery_ctrl;
+ nvme_ctrl_next_ns;
+ nvme_ctrl_next_path;
+ nvme_ctrl_reset;
+ nvme_ctrl_set_dhchap_key;
+ nvme_ctrl_set_discovery_ctrl;
+ nvme_ctrl_set_persistent;
+ nvme_ctrls_filter;
+ nvme_default_host;
+ nvme_dev_self_test;
+ nvme_dim_send;
+ nvme_directive_recv;
+ nvme_directive_send;
+ nvme_directive_send_id_endir;
+ nvme_disconnect_ctrl;
+ nvme_dsm;
+ nvme_dump_config;
+ nvme_dump_tree;
+ nvme_errno_to_string;
+ nvme_first_host;
+ nvme_first_subsystem;
+ nvme_format_nvm;
+ nvme_free_ctrl;
+ nvme_free_host;
+ nvme_free_ns;
+ nvme_free_subsystem;
+ nvme_free_tree;
+ nvme_fw_commit;
+ nvme_fw_download;
+ nvme_fw_download_seq;
+ nvme_gen_dhchap_key;
+ nvme_get_ana_log_len;
+ nvme_get_attr;
+ nvme_get_ctrl_attr;
+ nvme_get_ctrl_telemetry;
+ nvme_get_directive_receive_length;
+ nvme_get_feature_length;
+ nvme_get_features;
+ nvme_get_features_arbitration;
+ nvme_get_features_async_event;
+ nvme_get_features_auto_pst;
+ nvme_get_features_endurance_event_cfg;
+ nvme_get_features_err_recovery;
+ nvme_get_features_hctm;
+ nvme_get_features_host_behavior;
+ nvme_get_features_host_id;
+ nvme_get_features_host_mem_buf;
+ nvme_get_features_irq_coalesce;
+ nvme_get_features_irq_config;
+ nvme_get_features_kato;
+ nvme_get_features_lba_range;
+ nvme_get_features_lba_sts_interval;
+ nvme_get_features_nopsc;
+ nvme_get_features_num_queues;
+ nvme_get_features_plm_config;
+ nvme_get_features_plm_window;
+ nvme_get_features_power_mgmt;
+ nvme_get_features_resv_mask;
+ nvme_get_features_resv_persist;
+ nvme_get_features_rrl;
+ nvme_get_features_sanitize;
+ nvme_get_features_sw_progress;
+ nvme_get_features_temp_thresh;
+ nvme_get_features_timestamp;
+ nvme_get_features_volatile_wc;
+ nvme_get_features_write_atomic;
+ nvme_get_features_write_protect;
+ nvme_get_host_telemetry;
+ nvme_get_lba_status;
+ nvme_get_log;
+ nvme_get_log_page;
+ nvme_get_logical_block_size;
+ nvme_get_new_host_telemetry;
+ nvme_get_ns_attr;
+ nvme_get_nsid;
+ nvme_get_path_attr;
+ nvme_get_property;
+ nvme_get_subsys_attr;
+ nvme_get_telemetry_log;
+ nvme_get_telemetry_max;
+ nvme_host_get_dhchap_key;
+ nvme_host_get_hostid;
+ nvme_host_get_hostnqn;
+ nvme_host_get_hostsymname;
+ nvme_host_get_root;
+ nvme_host_set_dhchap_key;
+ nvme_host_set_hostsymname;
+ nvme_identify;
+ nvme_init_copy_range;
+ nvme_init_ctrl;
+ nvme_init_ctrl_list;
+ nvme_init_dsm_range;
+ nvme_init_logging;
+ nvme_io;
+ nvme_io_passthru64;
+ nvme_io_passthru;
+ nvme_lockdown;
+ nvme_lookup_ctrl;
+ nvme_lookup_host;
+ nvme_lookup_subsystem;
+ nvme_namespace_attach_ctrls;
+ nvme_namespace_detach_ctrls;
+ nvme_namespace_filter;
+ nvme_namespace_first_path;
+ nvme_namespace_next_path;
+ nvme_next_host;
+ nvme_next_subsystem;
+ nvme_ns_attach;
+ nvme_ns_compare;
+ nvme_ns_flush;
+ nvme_ns_get_csi;
+ nvme_ns_get_ctrl;
+ nvme_ns_get_eui64;
+ nvme_ns_get_fd;
+ nvme_ns_get_firmware;
+ nvme_ns_get_generic_name;
+ nvme_ns_get_lba_count;
+ nvme_ns_get_lba_size;
+ nvme_ns_get_lba_util;
+ nvme_ns_get_meta_size;
+ nvme_ns_get_model;
+ nvme_ns_get_name;
+ nvme_ns_get_nguid;
+ nvme_ns_get_nsid;
+ nvme_ns_get_serial;
+ nvme_ns_get_subsystem;
+ nvme_ns_get_sysfs_dir;
+ nvme_ns_get_uuid;
+ nvme_ns_identify;
+ nvme_ns_mgmt;
+ nvme_ns_read;
+ nvme_ns_rescan;
+ nvme_ns_verify;
+ nvme_ns_write;
+ nvme_ns_write_uncorrectable;
+ nvme_ns_write_zeros;
+ nvme_open;
+ nvme_path_get_ana_state;
+ nvme_path_get_ctrl;
+ nvme_path_get_name;
+ nvme_path_get_ns;
+ nvme_path_get_sysfs_dir;
+ nvme_paths_filter;
+ nvme_read_config;
+ nvme_refresh_topology;
+ nvme_rescan_ctrl;
+ nvme_resv_acquire;
+ nvme_resv_register;
+ nvme_resv_release;
+ nvme_resv_report;
+ nvme_sanitize_nvm;
+ nvme_scan;
+ nvme_scan_ctrl;
+ nvme_scan_ctrls;
+ nvme_scan_ctrl_namespace_paths;
+ nvme_scan_ctrl_namespaces;
+ nvme_scan_topology;
+ nvme_scan_namespace;
+ nvme_scan_subsystem_namespaces;
+ nvme_scan_subsystems;
+ nvme_security_receive;
+ nvme_security_send;
+ nvme_set_features;
+ nvme_set_features_arbitration;
+ nvme_set_features_async_event;
+ nvme_set_features_auto_pst;
+ nvme_set_features_endurance_evt_cfg;
+ nvme_set_features_err_recovery;
+ nvme_set_features_hctm;
+ nvme_set_features_host_behavior;
+ nvme_set_features_host_id;
+ nvme_set_features_irq_coalesce;
+ nvme_set_features_irq_config;
+ nvme_set_features_lba_range;
+ nvme_set_features_lba_sts_interval;
+ nvme_set_features_nopsc;
+ nvme_set_features_plm_config;
+ nvme_set_features_plm_window;
+ nvme_set_features_power_mgmt;
+ nvme_set_features_resv_mask;
+ nvme_set_features_resv_persist;
+ nvme_set_features_rrl;
+ nvme_set_features_sanitize;
+ nvme_set_features_sw_progress;
+ nvme_set_features_temp_thresh;
+ nvme_set_features_timestamp;
+ nvme_set_features_volatile_wc;
+ nvme_set_features_write_atomic;
+ nvme_set_features_write_protect;
+ nvme_set_property;
+ nvme_status_to_errno;
+ nvme_status_to_string;
+ nvme_submit_admin_passthru64;
+ nvme_submit_admin_passthru;
+ nvme_submit_io_passthru64;
+ nvme_submit_io_passthru;
+ nvme_subsys_filter;
+ nvme_subsystem_first_ctrl;
+ nvme_subsystem_first_ns;
+ nvme_subsystem_get_host;
+ nvme_subsystem_get_name;
+ nvme_subsystem_get_nqn;
+ nvme_subsystem_get_sysfs_dir;
+ nvme_subsystem_get_type;
+ nvme_subsystem_lookup_namespace;
+ nvme_subsystem_next_ctrl;
+ nvme_subsystem_next_ns;
+ nvme_subsystem_reset;
+ nvme_unlink_ctrl;
+ nvme_update_config;
+ nvme_virtual_mgmt;
+ nvme_zns_append;
+ nvme_zns_mgmt_recv;
+ nvme_zns_mgmt_send;
+ nvmf_add_ctrl;
+ nvmf_adrfam_str;
+ nvmf_cms_str;
+ nvmf_connect_disc_entry;
+ nvmf_default_config;
+ nvmf_eflags_str;
+ nvmf_get_discovery_log;
+ nvmf_hostid_from_file;
+ nvmf_hostnqn_from_file;
+ nvmf_hostnqn_generate;
+ nvmf_is_registration_supported;
+ nvmf_prtype_str;
+ nvmf_qptype_str;
+ nvmf_register_ctrl;
+ nvmf_sectype_str;
+ nvmf_subtype_str;
+ nvmf_treq_str;
+ nvmf_trtype_str;
+ nvmf_update_config;
+ local:
+ *;
+};
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..811f0f8
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2021 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+#
+sources = [
+ 'nvme/nbft.c',
+ 'nvme/fabrics.c',
+ 'nvme/filters.c',
+ 'nvme/ioctl.c',
+ 'nvme/linux.c',
+ 'nvme/log.c',
+ 'nvme/tree.c',
+ 'nvme/util.c',
+ 'nvme/base64.c'
+]
+
+mi_sources = [
+ 'nvme/log.c',
+ 'nvme/mi.c',
+ 'nvme/mi-mctp.c',
+]
+
+if json_c_dep.found()
+ sources += 'nvme/json.c'
+else
+ sources += 'nvme/no-json.c'
+endif
+
+deps = [
+ json_c_dep,
+ openssl_dep,
+ keyutils_dep,
+]
+
+mi_deps = [
+ libdbus_dep,
+]
+
+source_dir = meson.current_source_dir()
+mapfile = 'libnvme.map'
+version_script_arg = join_paths(source_dir, mapfile)
+mi_mapfile = 'libnvme-mi.map'
+mi_version_script_arg = join_paths(source_dir, mi_mapfile)
+
+libnvme = library(
+ 'nvme', # produces libnvme.so
+ sources,
+ version: library_version,
+ link_args: ['-Wl,--version-script=' + version_script_arg],
+ dependencies: deps,
+ link_depends: mapfile,
+ include_directories: [incdir, internal_incdir],
+ install: true,
+ link_with: libccan,
+)
+
+pkg = import('pkgconfig')
+pkg.generate(libnvme,
+ filebase: meson.project_name(),
+ name: meson.project_name(),
+ version: meson.project_version(),
+ description: 'Manage "libnvme" subsystem devices (Non-volatile Memory Express)',
+ url: 'http://github.com/linux-nvme/libnvme/',
+)
+
+libnvme_dep = declare_dependency(
+ include_directories: ['.'],
+ dependencies: [
+ json_c_dep.partial_dependency(compile_args: true, includes: true),
+ ],
+ link_with: libnvme,
+)
+
+libnvme_mi = library(
+ 'nvme-mi', # produces libnvme-mi.so
+ mi_sources,
+ version: library_version,
+ link_args: ['-Wl,--version-script=' + mi_version_script_arg],
+ dependencies: mi_deps,
+ link_depends: mi_mapfile,
+ include_directories: [incdir, internal_incdir],
+ install: true,
+ link_with: libccan,
+)
+
+libnvme_mi_dep = declare_dependency(
+ include_directories: ['.'],
+ link_with: libnvme_mi,
+)
+
+# test library with all symbols visible, to use for MI unit tests. Should
+# match libnvme_mi above, but with no version script, and install: false.
+libnvme_mi_test = library(
+ 'nvme-mi-test', # produces libnvme-mi-test.so
+ mi_sources,
+ dependencies: mi_deps,
+ include_directories: [incdir, internal_incdir],
+ install: false,
+ link_with: libccan,
+)
+
+libnvme_mi_test_dep = declare_dependency(
+ include_directories: ['.'],
+ link_with: libnvme_mi_test,
+)
+
+pkg.generate(libnvme_mi,
+ filebase: 'libnvme-mi',
+ name: 'libnvme-mi',
+ version: meson.project_version(),
+ description: 'Manage "libnvme" subsystem devices (Non-volatile Memory Express) over Management Interface',
+ url: 'http://github.com/linux-nvme/libnvme/',
+)
+
+mode = ['rw-r--r--', 0, 0]
+install_headers('libnvme.h', install_mode: mode)
+install_headers('libnvme-mi.h', install_mode: mode)
+install_headers([
+ 'nvme/api-types.h',
+ 'nvme/fabrics.h',
+ 'nvme/filters.h',
+ 'nvme/ioctl.h',
+ 'nvme/linux.h',
+ 'nvme/log.h',
+ 'nvme/nbft.h',
+ 'nvme/tree.h',
+ 'nvme/types.h',
+ 'nvme/util.h',
+ 'nvme/mi.h',
+ ],
+ subdir: 'nvme',
+ install_mode: mode,
+)
diff --git a/src/nvme/api-types.h b/src/nvme/api-types.h
new file mode 100644
index 0000000..296a7b0
--- /dev/null
+++ b/src/nvme/api-types.h
@@ -0,0 +1,963 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Types used as part of the libnvme/libnvme-mi API, rather than specified
+ * by the NVM Express specification.
+ *
+ * These are shared across both libnvme and libnvme-mi interfaces.
+ *
+ * This file is part of libnvme.
+ * Copyright (c) 2022 Code Construct
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+#ifndef _LIBNVME_API_TYPES_H
+#define _LIBNVME_API_TYPES_H
+
+#include <stdbool.h>
+#include "types.h"
+
+/*
+ * _args struct definitions. These are used by both the ioctl-based and
+ * MI-based interfaces, as the call interface for (admin/io/etc) NVMe commands,
+ * passed to the nvme_*() and nvme_mi_*() functions.
+ *
+ * On MI-based interfaces, the fd and timeout members are unused, and should
+ * be set to zero.
+ */
+
+/**
+ * struct nvme_identify_args - Arguments for the NVMe Identify command
+ * @result: The command completion result from CQE dword0
+ * @data: User space destination address to transfer the data
+ * @args_size: Size of &struct nvme_identify_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms (0 for default timeout)
+ * @cns: The Controller or Namespace structure, see @enum nvme_identify_cns
+ * @csi: Command Set Identifier
+ * @nsid: Namespace identifier, if applicable
+ * @cntid: The Controller Identifier, if applicable
+ * @cns_specific_id: Identifier that is required for a particular CNS value
+ * @uuidx: UUID Index if controller supports this id selection method
+ */
+struct nvme_identify_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ enum nvme_identify_cns cns;
+ enum nvme_csi csi;
+ __u32 nsid;
+ __u16 cntid;
+ __u16 cns_specific_id;
+ __u8 uuidx;
+};
+
+/**
+ * struct nvme_get_log_args - Arguments for the NVMe Admin Get Log command
+ * @lpo: Log page offset for partial log transfers
+ * @result: The command completion result from CQE dword0
+ * @log: User space destination address to transfer the data
+ * @args_size: Length of the structure
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @lid: Log page identifier, see &enum nvme_cmd_get_log_lid for known
+ * values
+ * @len: Length of provided user buffer to hold the log data in bytes
+ * @nsid: Namespace identifier, if applicable
+ * @csi: Command set identifier, see &enum nvme_csi for known values
+ * @lsi: Log Specific Identifier
+ * @lsp: Log specific field
+ * @uuidx: UUID selection, if supported
+ * @rae: Retain asynchronous events
+ * @ot: Offset Type; if set @lpo specifies the index into the list
+ * of data structures, otherwise @lpo specifies the byte offset
+ * into the log page.
+ */
+struct nvme_get_log_args {
+ __u64 lpo;
+ __u32 *result;
+ void *log;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ enum nvme_cmd_get_log_lid lid;
+ __u32 len;
+ __u32 nsid;
+ enum nvme_csi csi;
+ __u16 lsi;
+ __u8 lsp;
+ __u8 uuidx;
+ bool rae;
+ bool ot;
+};
+
+/**
+ * struct nvme_set_features_args - Arguments for the NVMe Admin Set Feature command
+ * @result: The command completion result from CQE dword0
+ * @data: User address of feature data, if applicable
+ * @args_size: Size of &struct nvme_set_features_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID, if applicable
+ * @cdw11: Value to set the feature to
+ * @cdw12: Feature specific command dword12 field
+ * @cdw13: Feature specific command dword13 field
+ * @cdw15: Feature specific command dword15 field
+ * @data_len: Length of feature data, if applicable, in bytes
+ * @save: Save value across power states
+ * @uuidx: UUID Index for differentiating vendor specific encoding
+ * @fid: Feature identifier
+ */
+struct nvme_set_features_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw15;
+ __u32 data_len;
+ bool save;
+ __u8 uuidx;
+ __u8 fid;
+};
+
+/**
+ * struct nvme_get_features_args - Arguments for the NVMe Admin Get Feature command
+ * @args_size: Size of &struct nvme_get_features_args
+ * @fd: File descriptor of nvme device
+ * @result: The command completion result from CQE dword0
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID, if applicable
+ * @sel: Select which type of attribute to return,
+ * see &enum nvme_get_features_sel
+ * @cdw11: Feature specific command dword11 field
+ * @data_len: Length of feature data, if applicable, in bytes
+ * @data: User address of feature data, if applicable
+ * @fid: Feature identifier, see &enum nvme_features_id
+ * @uuidx: UUID Index for differentiating vendor specific encoding
+ */
+struct nvme_get_features_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_get_features_sel sel;
+ __u32 cdw11;
+ __u32 data_len;
+ __u8 fid;
+ __u8 uuidx;
+};
+
+/**
+ * struct nvme_format_nvm_args - Arguments for the Format Nvme Namespace command
+ * @result: The command completion result from CQE dword0
+ * @args_size: Size of &struct nvme_format_nvm_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Set to override default timeout to this value in milliseconds;
+ * useful for long running formats. 0 will use system default.
+ * @nsid: Namespace ID to format
+ * @mset: Metadata settings (extended or separated), true if extended
+ * @pi: Protection information type
+ * @pil: Protection information location (beginning or end), true if end
+ * @ses: Secure erase settings
+ * @lbaf: Logical block address format least significant 4 bits
+ * @rsvd1: Reserved
+ * @lbafu: Logical block address format most significant 2 bits
+ */
+struct nvme_format_nvm_args {
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_cmd_format_mset mset;
+ enum nvme_cmd_format_pi pi;
+ enum nvme_cmd_format_pil pil;
+ enum nvme_cmd_format_ses ses;
+ __u8 lbaf;
+ __u8 rsvd1[7];
+ __u8 lbafu;
+};
+
+/**
+ * struct nvme_ns_mgmt_args - Arguments for NVMe Namespace Management command
+ * @result: NVMe command result
+ * @ns: Namespace identification descriptors
+ * @args_size: Size of &struct nvme_ns_mgmt_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace identifier
+ * @sel: Type of management operation to perform
+ * @csi: Command Set Identifier
+ * @rsvd1: Reserved
+ * @rsvd2: Reserved
+ * @data: Host Software Specified Fields
+ */
+struct nvme_ns_mgmt_args {
+ __u32 *result;
+ struct nvme_id_ns *ns;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_ns_mgmt_sel sel;
+ __u8 csi;
+ __u8 rsvd1[3];
+ void *rsvd2;
+ struct nvme_ns_mgmt_host_sw_specified *data;
+};
+
+/**
+ * struct nvme_ns_attach_args - Arguments for Nvme Namespace Management command
+ * @result: NVMe command result
+ * @ctrlist: Controller list to modify attachment state of nsid
+ * @args_size: Size of &struct nvme_ns_attach_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID to execute attach selection
+ * @sel: Attachment selection, see &enum nvme_ns_attach_sel
+ */
+struct nvme_ns_attach_args {
+ __u32 *result;
+ struct nvme_ctrl_list *ctrlist;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_ns_attach_sel sel;
+};
+
+/**
+ * struct nvme_fw_download_args - Arguments for the NVMe Firmware Download command
+ * @args_size: Size of &struct nvme_fw_download_args
+ * @fd: File descriptor of nvme device
+ * @result: The command completion result from CQE dword0
+ * @timeout: Timeout in ms
+ * @offset: Offset in the firmware data
+ * @data: Userspace address of the firmware data
+ * @data_len: Length of data in this command in bytes
+ */
+struct nvme_fw_download_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 offset;
+ __u32 data_len;
+};
+
+/**
+ * struct nvme_fw_commit_args - Arguments for the NVMe Firmware Commit command
+ * @args_size: Size of &struct nvme_fw_commit_args
+ * @fd: File descriptor of nvme device
+ * @action: Action to use for the firmware image, see &enum nvme_fw_commit_ca
+ * @timeout: Timeout in ms
+ * @result: The command completion result from CQE dword0
+ * @slot: Firmware slot to commit the downloaded image
+ * @bpid: Set to true to select the boot partition id
+ */
+struct nvme_fw_commit_args {
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ enum nvme_fw_commit_ca action;
+ __u8 slot;
+ bool bpid;
+};
+
+/**
+ * struct nvme_security_send_args - Arguments for the NVMe Security Send command
+ * @result: The command completion result from CQE dword0
+ * @data: Security data payload to send
+ * @args_size: Size of &struct nvme_security_send_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID to issue security command on
+ * @tl: Protocol specific transfer length
+ * @data_len: Data length of the payload in bytes
+ * @nssf: NVMe Security Specific field
+ * @spsp0: Security Protocol Specific field
+ * @spsp1: Security Protocol Specific field
+ * @secp: Security Protocol
+ */
+struct nvme_security_send_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 tl;
+ __u32 data_len;
+ __u8 nssf;
+ __u8 spsp0;
+ __u8 spsp1;
+ __u8 secp;
+};
+
+/**
+ * struct nvme_security_receive_args - Arguments for the NVMe Security Receive command
+ * @result: The command completion result from CQE dword0
+ * @data: Security data payload to send
+ * @args_size: Size of &struct nvme_security_receive_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID to issue security command on
+ * @al: Protocol specific allocation length
+ * @data_len: Data length of the payload in bytes
+ * @nssf: NVMe Security Specific field
+ * @spsp0: Security Protocol Specific field
+ * @spsp1: Security Protocol Specific field
+ * @secp: Security Protocol
+ */
+struct nvme_security_receive_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 al;
+ __u32 data_len;
+ __u8 nssf;
+ __u8 spsp0;
+ __u8 spsp1;
+ __u8 secp;
+};
+
+/**
+ * struct nvme_get_lba_status_args - Arguments for the NVMe Get LBA Status command
+ * @lbas: Data payload to return status descriptors
+ * @result: The command completion result from CQE dword0
+ * @slba: Starting logical block address to check statuses
+ * @args_size: Size of &struct nvme_get_lba_status_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID to retrieve LBA status
+ * @mndw: Maximum number of dwords to return
+ * @atype: Action type mechanism to determine LBA status descriptors to
+ * return, see &enum nvme_lba_status_atype
+ * @rl: Range length from slba to perform the action
+ */
+struct nvme_get_lba_status_args {
+ __u64 slba;
+ __u32 *result;
+ struct nvme_lba_status *lbas;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 mndw;
+ enum nvme_lba_status_atype atype;
+ __u16 rl;
+};
+
+/**
+ * struct nvme_directive_send_args - Arguments for the NVMe Directive Send command
+ * @result: If successful, the CQE dword0 value
+ * @data: Data payload to be send
+ * @args_size: Size of &struct nvme_directive_send_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID, if applicable
+ * @doper: Directive send operation, see &enum nvme_directive_send_doper
+ * @dtype: Directive type, see &enum nvme_directive_dtype
+ * @cdw12: Directive specific command dword12
+ * @data_len: Length of data payload in bytes
+ * @dspec: Directive specific field
+ */
+struct nvme_directive_send_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_directive_send_doper doper;
+ enum nvme_directive_dtype dtype;
+ __u32 cdw12;
+ __u32 data_len;
+ __u16 dspec;
+};
+
+/**
+ * struct nvme_directive_recv_args - Arguments for the NVMe Directive Receive command
+ * @result: If successful, the CQE dword0 value
+ * @data: Userspace address of data payload
+ * @args_size: Size of &struct nvme_directive_recv_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID, if applicable
+ * @doper: Directive send operation, see &enum nvme_directive_send_doper
+ * @dtype: Directive type, see &enum nvme_directive_dtype
+ * @cdw12: Directive specific command dword12
+ * @data_len: Length of data payload in bytes
+ * @dspec: Directive specific field
+ */
+struct nvme_directive_recv_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_directive_receive_doper doper;
+ enum nvme_directive_dtype dtype;
+ __u32 cdw12;
+ __u32 data_len;
+ __u16 dspec;
+};
+
+/**
+ * struct nvme_capacity_mgmt_args - Arguments for the NVMe Capacity Management command
+ * @result: If successful, the CQE dword0 value
+ * @args_size: Size of &struct nvme_capacity_mgmt_args
+ * @fd: File descriptor of nvme device
+ * @cdw11: Least significant 32 bits of the capacity in bytes of the
+ * Endurance Group or NVM Set to be created
+ * @cdw12: Most significant 32 bits of the capacity in bytes of the
+ * Endurance Group or NVM Set to be created
+ * @timeout: Timeout in ms
+ * @element_id: Value specific to the value of the Operation field
+ * @op: Operation to be performed by the controller
+ */
+struct nvme_capacity_mgmt_args {
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u16 element_id;
+ __u8 op;
+};
+
+/**
+ * struct nvme_lockdown_args - Arguments for the NVME Lockdown command
+ * @args_size: Size of &struct nvme_lockdown_args
+ * @fd: File descriptor of nvme device
+ * @result: The command completion result from CQE dword0
+ * @timeout: Timeout in ms (0 for default timeout)
+ * @scp: Scope of the command
+ * @prhbt: Prohibit or allow the command opcode or Set Features command
+ * @ifc: Affected interface
+ * @ofi: Opcode or Feature Identifier
+ * @uuidx: UUID Index if controller supports this id selection method
+ */
+struct nvme_lockdown_args {
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u8 scp;
+ __u8 prhbt;
+ __u8 ifc;
+ __u8 ofi;
+ __u8 uuidx;
+};
+
+/**
+ * struct nvme_set_property_args - Arguments for NVMe Set Property command
+ * @args_size: Size of &struct nvme_set_property_args
+ * @fd: File descriptor of nvme device
+ * @result: The command completion result from CQE dword0
+ * @timeout: Timeout in ms
+ * @offset: Property offset from the base to set
+ * @value: The value to set the property
+ */
+struct nvme_set_property_args {
+ __u64 value;
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ int offset;
+};
+
+/**
+ * struct nvme_get_property_args - Arguments for NVMe Get Property command
+ * @value: Where the property's value will be stored on success
+ * @args_size: Size of &struct nvme_get_property_args
+ * @fd: File descriptor of nvme device
+ * @offset: Property offset from the base to retrieve
+ * @timeout: Timeout in ms
+ */
+struct nvme_get_property_args {
+ __u64 *value;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ int offset;
+};
+
+/**
+ * struct nvme_sanitize_nvm_args - Arguments for the NVMe Sanitize NVM command
+ * @result: The command completion result from CQE dword0
+ * @args_size: Size of &struct nvme_sanitize_nvm_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @ovrpat: Overwrite pattern
+ * @sanact: Sanitize action, see &enum nvme_sanitize_sanact
+ * @ause: Set to allow unrestricted sanitize exit
+ * @owpass: Overwrite pass count
+ * @oipbp: Set to overwrite invert pattern between passes
+ * @nodas: Set to not deallocate blocks after sanitizing
+ */
+struct nvme_sanitize_nvm_args {
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ enum nvme_sanitize_sanact sanact;
+ __u32 ovrpat;
+ bool ause;
+ __u8 owpass;
+ bool oipbp;
+ bool nodas;
+};
+
+/**
+ * struct nvme_dev_self_test_args - Arguments for the NVMe Device Self Test command
+ * @result: The command completion result from CQE dword0
+ * @args_size: Size of &struct nvme_dev_self_test_args
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID to test
+ * @stc: Self test code, see &enum nvme_dst_stc
+ * @timeout: Timeout in ms
+ */
+struct nvme_dev_self_test_args {
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_dst_stc stc;
+};
+
+/**
+ * struct nvme_virtual_mgmt_args - Arguments for the NVMe Virtualization
+ * resource management command
+ * @args_size: Size of &struct nvme_virtual_mgmt_args
+ * @fd: File descriptor of nvme device
+ * @result: If successful, the CQE dword0
+ * @timeout: Timeout in ms
+ * @act: Virtual resource action, see &enum nvme_virt_mgmt_act
+ * @rt: Resource type to modify, see &enum nvme_virt_mgmt_rt
+ * @cntlid: Controller id for which resources are bing modified
+ * @nr: Number of resources being allocated or assigned
+ */
+struct nvme_virtual_mgmt_args {
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ enum nvme_virt_mgmt_act act;
+ enum nvme_virt_mgmt_rt rt;
+ __u16 cntlid;
+ __u16 nr;
+};
+
+/**
+ * struct nvme_io_args - Arguments for NVMe I/O commands
+ * @slba: Starting logical block
+ * @storage_tag: This filed specifies Variable Sized Expected Logical Block
+ * Storage Tag (ELBST) or Logical Block Storage Tag (LBST)
+ * @result: The command completion result from CQE dword0
+ * @data: Pointer to user address of the data buffer
+ * @metadata: Pointer to user address of the metadata buffer
+ * @args_size: Size of &struct nvme_io_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID
+ * @data_len: Length of user buffer, @data, in bytes
+ * @metadata_len:Length of user buffer, @metadata, in bytes
+ * @nlb: Number of logical blocks to send (0's based value)
+ * @control: Command control flags, see &enum nvme_io_control_flags.
+ * @apptag: This field specifies the Application Tag Mask expected value.
+ * Used only if the namespace is formatted to use end-to-end
+ * protection information.
+ * @appmask: This field specifies the Application Tag expected value. Used
+ * only if the namespace is formatted to use end-to-end protection
+ * information.
+ * @reftag: This field specifies the variable sized Expected Initial
+ * Logical Block Reference Tag (EILBRT) or Initial Logical Block
+ * Reference Tag (ILBRT). Used only if the namespace is formatted
+ * to use end-to-end protection information.
+ * @dspec: Directive specific value
+ * @dsm: Data set management attributes, see &enum nvme_io_dsm_flags
+ * @rsvd1: Reserved
+ * @reftag_u64: This field specifies the variable sized Expected Initial
+ * Logical Block Reference Tag (EILBRT) or Initial Logical Block
+ * Reference Tag (ILBRT). It is the 8 byte version required for
+ * enhanced protection information. Used only if the namespace is
+ * formatted to use end-to-end protection information.
+ * @sts: Storage tag size in bits, set by namespace Extended LBA Format
+ * @pif: Protection information format, determines how variable sized
+ * storage_tag and reftag are put into dwords 2, 3, and 14. Set by
+ * namespace Extended LBA Format.
+ */
+struct nvme_io_args {
+ __u64 slba;
+ __u64 storage_tag;
+ __u32 *result;
+ void *data;
+ void *metadata;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 reftag;
+ __u32 data_len;
+ __u32 metadata_len;
+ __u16 nlb;
+ __u16 control;
+ __u16 apptag;
+ __u16 appmask;
+ __u16 dspec;
+ __u8 dsm;
+ __u8 rsvd1[1];
+ __u64 reftag_u64;
+ __u8 sts;
+ __u8 pif;
+};
+
+/**
+ * struct nvme_dsm_args - Arguments for the NVMe Dataset Management command
+ * @result: The command completion result from CQE dword0
+ * @dsm: The data set management attributes
+ * @args_size: Size of &struct nvme_dsm_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace identifier
+ * @attrs: DSM attributes, see &enum nvme_dsm_attributes
+ * @nr_ranges: Number of block ranges in the data set management attributes
+ */
+struct nvme_dsm_args {
+ __u32 *result;
+ struct nvme_dsm_range *dsm;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 attrs;
+ __u16 nr_ranges;
+};
+
+/**
+ * struct nvme_copy_args - Arguments for the NVMe Copy command
+ * @sdlba: Start destination LBA
+ * @result: The command completion result from CQE dword0
+ * @copy: Range description
+ * @args_size: Size of &struct nvme_copy_args
+ * @fd: File descriptor of the nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace identifier
+ * @ilbrt: Initial logical block reference tag
+ * @lr: Limited retry
+ * @fua: Force unit access
+ * @nr: Number of ranges
+ * @dspec: Directive specific value
+ * @lbatm: Logical block application tag mask
+ * @lbat: Logical block application tag
+ * @prinfor: Protection information field for read
+ * @prinfow: Protection information field for write
+ * @dtype: Directive type
+ * @format: Descriptor format
+ * @ilbrt_u64: Initial logical block reference tag - 8 byte
+ * version required for enhanced protection info
+ */
+struct nvme_copy_args {
+ __u64 sdlba;
+ __u32 *result;
+ struct nvme_copy_range *copy;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 ilbrt;
+ int lr;
+ int fua;
+ __u16 nr;
+ __u16 dspec;
+ __u16 lbatm;
+ __u16 lbat;
+ __u8 prinfor;
+ __u8 prinfow;
+ __u8 dtype;
+ __u8 format;
+ __u64 ilbrt_u64;
+};
+
+/**
+ * struct nvme_resv_acquire_args - Arguments for the NVMe Reservation Acquire Command
+ * @nrkey: The reservation key to be unregistered from the namespace if
+ * the action is preempt
+ * @iekey: Set to ignore the existing key
+ * @result: The command completion result from CQE dword0
+ * @args_size: Size of &struct nvme_resv_acquire_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace identifier
+ * @rtype: The type of reservation to be create, see &enum nvme_resv_rtype
+ * @racqa: The action that is performed by the command, see &enum nvme_resv_racqa
+ * @crkey: The current reservation key associated with the host
+ */
+struct nvme_resv_acquire_args {
+ __u64 crkey;
+ __u64 nrkey;
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_resv_rtype rtype;
+ enum nvme_resv_racqa racqa;
+ bool iekey;
+};
+
+/**
+ * struct nvme_resv_register_args - Arguments for the NVMe Reservation Register command
+ * @crkey: The current reservation key associated with the host
+ * @nrkey: The new reservation key to be register if action is register or
+ * replace
+ * @result: The command completion result from CQE dword0
+ * @args_size: Size of &struct nvme_resv_register_args
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace identifier
+ * @rrega: The registration action, see &enum nvme_resv_rrega
+ * @cptpl: Change persist through power loss, see &enum nvme_resv_cptpl
+ * @iekey: Set to ignore the existing key
+ * @timeout: Timeout in ms
+ */
+struct nvme_resv_register_args {
+ __u64 crkey;
+ __u64 nrkey;
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_resv_rrega rrega;
+ enum nvme_resv_cptpl cptpl;
+ bool iekey;
+};
+
+/**
+ * struct nvme_resv_release_args - Arguments for the NVMe Reservation Release Command
+ * @crkey: The current reservation key to release
+ * @result: The command completion result from CQE dword0
+ * @args_size: Size of &struct nvme_resv_release_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace identifier
+ * @rtype: The type of reservation to be create, see &enum nvme_resv_rtype
+ * @rrela: Reservation release action, see &enum nvme_resv_rrela
+ * @iekey: Set to ignore the existing key
+ */
+struct nvme_resv_release_args {
+ __u64 crkey;
+ __u32 *result;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_resv_rtype rtype;
+ enum nvme_resv_rrela rrela;
+ bool iekey;
+};
+
+/**
+ * struct nvme_resv_report_args - Arguments for the NVMe Reservation Report command
+ * @result: The command completion result from CQE dword0
+ * @report: The user space destination address to store the reservation
+ * report
+ * @args_size: Size of &struct nvme_resv_report_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace identifier
+ * @len: Number of bytes to request transferred with this command
+ * @eds: Request extended Data Structure
+ */
+struct nvme_resv_report_args {
+ __u32 *result;
+ struct nvme_resv_status *report;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 len;
+ bool eds;
+};
+
+/**
+ * struct nvme_io_mgmt_recv_args - Arguments for the NVMe I/O Management Receive command
+ * @data: Userspace address of the data
+ * @args_size: Size of &struct nvme_io_mgmt_recv_args
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace identifier
+ * @data_len: Length of @data
+ * @timeout: Timeout in ms
+ * @mos: Management Operation Specific
+ * @mo: Management Operation
+ */
+struct nvme_io_mgmt_recv_args {
+ void *data;
+ int args_size;
+ int fd;
+ __u32 nsid;
+ __u32 data_len;
+ __u32 timeout;
+ __u16 mos;
+ __u8 mo;
+};
+
+/**
+ * struct nvme_io_mgmt_send_args - Arguments for the NVMe I/O Management Send command
+ * @data: Userspace address of the data
+ * @args_size: Size of &struct nvme_io_mgmt_send_args
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace identifier
+ * @data_len: Length of @data
+ * @timeout: Timeout in ms
+ * @mos: Management Operation Specific
+ * @mo: Management Operation
+ */
+struct nvme_io_mgmt_send_args {
+ void *data;
+ int args_size;
+ int fd;
+ __u32 nsid;
+ __u32 data_len;
+ __u32 timeout;
+ __u16 mos;
+ __u8 mo;
+};
+
+/**
+ * struct nvme_zns_mgmt_send_args - Arguments for the NVMe ZNS Management Send command
+ * @slba: Starting logical block address
+ * @result: The command completion result from CQE dword0
+ * @data: Userspace address of the data
+ * @args_size: Size of &struct nvme_zns_mgmt_send_args
+ * @fd: File descriptor of nvme device
+ * @timeout: timeout in ms
+ * @nsid: Namespace ID
+ * @zsa: Zone send action
+ * @data_len: Length of @data
+ * @select_all: Select all flag
+ * @zsaso: Zone Send Action Specific Option
+ */
+struct nvme_zns_mgmt_send_args {
+ __u64 slba;
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_zns_send_action zsa;
+ __u32 data_len;
+ bool select_all;
+ __u8 zsaso;
+};
+
+/**
+ * struct nvme_zns_mgmt_recv_args - Arguments for the NVMe ZNS Management Receive command
+ * @slba: Starting logical block address
+ * @result: The command completion result from CQE dword0
+ * @data: Userspace address of the data
+ * @args_size: Size of &struct nvme_zns_mgmt_recv_args
+ * @fd: File descriptor of nvme device
+ * @timeout: timeout in ms
+ * @nsid: Namespace ID
+ * @zra: zone receive action
+ * @data_len: Length of @data
+ * @zrasf: Zone receive action specific field
+ * @zras_feat: Zone receive action specific features
+ */
+struct nvme_zns_mgmt_recv_args {
+ __u64 slba;
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ enum nvme_zns_recv_action zra;
+ __u32 data_len;
+ __u16 zrasf;
+ bool zras_feat;
+};
+
+/**
+ * struct nvme_zns_append_args - Arguments for the NVMe ZNS Append command
+ * @zslba: Zone start logical block address
+ * @result: The command completion result from CQE dword0
+ * @data: Userspace address of the data
+ * @metadata: Userspace address of the metadata
+ * @args_size: Size of &struct nvme_zns_append_args
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @nsid: Namespace ID
+ * @ilbrt: Initial logical block reference tag
+ * @data_len: Length of @data
+ * @metadata_len: Length of @metadata
+ * @nlb: Number of logical blocks
+ * @control:
+ * @lbat: Logical block application tag
+ * @lbatm: Logical block application tag mask
+ * @rsvd1: Reserved
+ * @ilbrt_u64: Initial logical block reference tag - 8 byte
+ * version required for enhanced protection info
+ *
+ */
+struct nvme_zns_append_args {
+ __u64 zslba;
+ __u64 *result;
+ void *data;
+ void *metadata;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 nsid;
+ __u32 ilbrt;
+ __u32 data_len;
+ __u32 metadata_len;
+ __u16 nlb;
+ __u16 control;
+ __u16 lbat;
+ __u16 lbatm;
+ __u8 rsvd1[4];
+ __u64 ilbrt_u64;
+};
+
+/**
+ * struct nvme_dim_args - Arguments for the Discovery Information Management (DIM) command
+ * @result: Set on completion to the command's CQE DWORD 0 controller response.
+ * @data: Pointer to the DIM data
+ * @args_size: Length of the structure
+ * @fd: File descriptor of nvme device
+ * @timeout: Timeout in ms
+ * @data_len: Length of @data
+ * @tas: Task field of the Command Dword 10 (cdw10)
+ */
+struct nvme_dim_args {
+ __u32 *result;
+ void *data;
+ int args_size;
+ int fd;
+ __u32 timeout;
+ __u32 data_len;
+ __u8 tas;
+};
+
+#endif /* _LIBNVME_API_TYPES_H */
diff --git a/src/nvme/base64.c b/src/nvme/base64.c
new file mode 100644
index 0000000..5fae829
--- /dev/null
+++ b/src/nvme/base64.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * base64.c - RFC4648-compliant base64 encoding
+ *
+ * Copyright (c) 2020 SUSE LLC
+ *
+ * Author: Hannes Reinecke <hare@suse.de>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+
+static const char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode() - base64-encode some bytes
+ * @src: the bytes to encode
+ * @srclen: number of bytes to encode
+ * @dst: (output) the base64-encoded string. Not NUL-terminated.
+ *
+ * Encodes the input string using characters from the set [A-Za-z0-9+,].
+ * The encoded string is roughly 4/3 times the size of the input string.
+ *
+ * Return: length of the encoded string
+ */
+int base64_encode(const unsigned char *src, int srclen, char *dst)
+{
+ int i, bits = 0;
+ u_int32_t ac = 0;
+ char *cp = dst;
+
+ for (i = 0; i < srclen; i++) {
+ ac = (ac << 8) | src[i];
+ bits += 8;
+ do {
+ bits -= 6;
+ *cp++ = base64_table[(ac >> bits) & 0x3f];
+ } while (bits >= 6);
+ }
+ if (bits) {
+ *cp++ = base64_table[(ac << (6 - bits)) & 0x3f];
+ bits -= 6;
+ }
+ while (bits < 0) {
+ *cp++ = '=';
+ bits += 2;
+ }
+
+ return cp - dst;
+}
+
+/**
+ * base64_decode() - base64-decode some bytes
+ * @src: the base64-encoded string to decode
+ * @len: number of bytes to decode
+ * @dst: (output) the decoded bytes.
+ *
+ * Decodes the base64-encoded bytes @src according to RFC 4648.
+ *
+ * Return: number of decoded bytes
+ */
+int base64_decode(const char *src, int srclen, unsigned char *dst)
+{
+ u_int32_t ac = 0;
+ int i, bits = 0;
+ unsigned char *bp = dst;
+
+ for (i = 0; i < srclen; i++) {
+ const char *p = strchr(base64_table, src[i]);
+
+ if (src[i] == '=') {
+ ac = (ac << 6);
+ bits += 6;
+ if (bits >= 8)
+ bits -= 8;
+ continue;
+ }
+ if (!p || !src[i])
+ return -EINVAL;
+ ac = (ac << 6) | (p - base64_table);
+ bits += 6;
+ if (bits >= 8) {
+ bits -= 8;
+ *bp++ = (unsigned char)(ac >> bits);
+ }
+ }
+ if (ac && ((1 << bits) - 1))
+ return -EAGAIN;
+
+ return bp - dst;
+}
diff --git a/src/nvme/base64.h b/src/nvme/base64.h
new file mode 100644
index 0000000..c0f62e2
--- /dev/null
+++ b/src/nvme/base64.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _BASE64_H
+#define _BASE64_H
+
+int base64_encode(const unsigned char *src, int len, char *dst);
+int base64_decode(const char *src, int len, unsigned char *dst);
+
+#endif /* _BASE64_H */
diff --git a/src/nvme/cleanup.h b/src/nvme/cleanup.h
new file mode 100644
index 0000000..4327600
--- /dev/null
+++ b/src/nvme/cleanup.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+#ifndef __CLEANUP_H
+#define __CLEANUP_H
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define __cleanup__(fn) __attribute__((cleanup(fn)))
+
+#define DECLARE_CLEANUP_FUNC(name, type) \
+ void name(type *__p)
+
+#define DEFINE_CLEANUP_FUNC(name, type, free_fn)\
+DECLARE_CLEANUP_FUNC(name, type) \
+{ \
+ if (*__p) \
+ free_fn(*__p); \
+}
+
+static inline void freep(void *p)
+{
+ free(*(void **)p);
+}
+#define _cleanup_free_ __cleanup__(freep)
+
+static inline DEFINE_CLEANUP_FUNC(cleanup_file, FILE *, fclose)
+#define _cleanup_file_ __cleanup__(cleanup_file)
+
+static inline DEFINE_CLEANUP_FUNC(cleanup_dir, DIR *, closedir)
+#define _cleanup_dir_ __cleanup__(cleanup_dir)
+
+static inline void cleanup_fd(int *fd)
+{
+ if (*fd >= 0)
+ close(*fd);
+}
+#define _cleanup_fd_ __cleanup__(cleanup_fd)
+
+#endif
diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c
new file mode 100644
index 0000000..1f50229
--- /dev/null
+++ b/src/nvme/fabrics.c
@@ -0,0 +1,1736 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <inttypes.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+
+#include <ccan/endian/endian.h>
+#include <ccan/list/list.h>
+#include <ccan/array_size/array_size.h>
+#include <ccan/str/str.h>
+
+#include "cleanup.h"
+#include "fabrics.h"
+#include "linux.h"
+#include "ioctl.h"
+#include "util.h"
+#include "log.h"
+#include "private.h"
+
+#define NVMF_HOSTID_SIZE 37
+
+#define NVMF_HOSTNQN_FILE SYSCONFDIR "/nvme/hostnqn"
+#define NVMF_HOSTID_FILE SYSCONFDIR "/nvme/hostid"
+
+const char *nvmf_dev = "/dev/nvme-fabrics";
+
+/**
+ * strchomp() - Strip trailing spaces
+ * @str: String to strip
+ * @max: Maximum length of string
+ */
+static void strchomp(char *str, int max)
+{
+ int i;
+
+ for (i = max - 1; i >= 0 && str[i] == ' '; i--) {
+ str[i] = '\0';
+ }
+}
+
+const char *arg_str(const char * const *strings,
+ size_t array_size, size_t idx)
+{
+ if (idx < array_size && strings[idx])
+ return strings[idx];
+ return "unrecognized";
+}
+
+const char * const trtypes[] = {
+ [NVMF_TRTYPE_RDMA] = "rdma",
+ [NVMF_TRTYPE_FC] = "fc",
+ [NVMF_TRTYPE_TCP] = "tcp",
+ [NVMF_TRTYPE_LOOP] = "loop",
+};
+
+const char *nvmf_trtype_str(__u8 trtype)
+{
+ return arg_str(trtypes, ARRAY_SIZE(trtypes), trtype);
+}
+
+static const char * const adrfams[] = {
+ [NVMF_ADDR_FAMILY_PCI] = "pci",
+ [NVMF_ADDR_FAMILY_IP4] = "ipv4",
+ [NVMF_ADDR_FAMILY_IP6] = "ipv6",
+ [NVMF_ADDR_FAMILY_IB] = "infiniband",
+ [NVMF_ADDR_FAMILY_FC] = "fibre-channel",
+};
+
+const char *nvmf_adrfam_str(__u8 adrfam)
+{
+ return arg_str(adrfams, ARRAY_SIZE(adrfams), adrfam);
+}
+
+static const char * const subtypes[] = {
+ [NVME_NQN_DISC] = "discovery subsystem referral",
+ [NVME_NQN_NVME] = "nvme subsystem",
+ [NVME_NQN_CURR] = "current discovery subsystem",
+};
+
+const char *nvmf_subtype_str(__u8 subtype)
+{
+ return arg_str(subtypes, ARRAY_SIZE(subtypes), subtype);
+}
+
+static const char * const treqs[] = {
+ [NVMF_TREQ_NOT_SPECIFIED] = "not specified",
+ [NVMF_TREQ_REQUIRED] = "required",
+ [NVMF_TREQ_NOT_REQUIRED] = "not required",
+ [NVMF_TREQ_NOT_SPECIFIED |
+ NVMF_TREQ_DISABLE_SQFLOW] = "not specified, "
+ "sq flow control disable supported",
+ [NVMF_TREQ_REQUIRED |
+ NVMF_TREQ_DISABLE_SQFLOW] = "required, "
+ "sq flow control disable supported",
+ [NVMF_TREQ_NOT_REQUIRED |
+ NVMF_TREQ_DISABLE_SQFLOW] = "not required, "
+ "sq flow control disable supported",
+};
+
+const char *nvmf_treq_str(__u8 treq)
+{
+ return arg_str(treqs, ARRAY_SIZE(treqs), treq);
+}
+
+static const char * const eflags_strings[] = {
+ [NVMF_DISC_EFLAGS_NONE] = "none",
+ [NVMF_DISC_EFLAGS_EPCSD] = "explicit discovery connections",
+ [NVMF_DISC_EFLAGS_DUPRETINFO] = "duplicate discovery information",
+ [NVMF_DISC_EFLAGS_EPCSD |
+ NVMF_DISC_EFLAGS_DUPRETINFO] = "explicit discovery connections, "
+ "duplicate discovery information",
+ [NVMF_DISC_EFLAGS_NCC] = "no cdc connectivity",
+ [NVMF_DISC_EFLAGS_EPCSD |
+ NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, "
+ "no cdc connectivity",
+ [NVMF_DISC_EFLAGS_DUPRETINFO |
+ NVMF_DISC_EFLAGS_NCC] = "duplicate discovery information, "
+ "no cdc connectivity",
+ [NVMF_DISC_EFLAGS_EPCSD |
+ NVMF_DISC_EFLAGS_DUPRETINFO |
+ NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, "
+ "duplicate discovery information, "
+ "no cdc connectivity",
+};
+
+const char *nvmf_eflags_str(__u16 eflags)
+{
+ return arg_str(eflags_strings, ARRAY_SIZE(eflags_strings), eflags);
+}
+
+static const char * const sectypes[] = {
+ [NVMF_TCP_SECTYPE_NONE] = "none",
+ [NVMF_TCP_SECTYPE_TLS] = "tls",
+ [NVMF_TCP_SECTYPE_TLS13] = "tls13",
+};
+
+const char *nvmf_sectype_str(__u8 sectype)
+{
+ return arg_str(sectypes, ARRAY_SIZE(sectypes), sectype);
+}
+
+static const char * const prtypes[] = {
+ [NVMF_RDMA_PRTYPE_NOT_SPECIFIED] = "not specified",
+ [NVMF_RDMA_PRTYPE_IB] = "infiniband",
+ [NVMF_RDMA_PRTYPE_ROCE] = "roce",
+ [NVMF_RDMA_PRTYPE_ROCEV2] = "roce-v2",
+ [NVMF_RDMA_PRTYPE_IWARP] = "iwarp",
+};
+
+const char *nvmf_prtype_str(__u8 prtype)
+{
+ return arg_str(prtypes, ARRAY_SIZE(prtypes), prtype);
+}
+
+static const char * const qptypes[] = {
+ [NVMF_RDMA_QPTYPE_CONNECTED] = "connected",
+ [NVMF_RDMA_QPTYPE_DATAGRAM] = "datagram",
+};
+
+const char *nvmf_qptype_str(__u8 qptype)
+{
+ return arg_str(qptypes, ARRAY_SIZE(qptypes), qptype);
+}
+
+static const char * const cms[] = {
+ [NVMF_RDMA_CMS_RDMA_CM] = "rdma-cm",
+};
+
+const char *nvmf_cms_str(__u8 cm)
+{
+ return arg_str(cms, ARRAY_SIZE(cms), cm);
+}
+
+/*
+ * Derived from Linux's supported options (the opt_tokens table)
+ * when the mechanism to report supported options was added (f18ee3d988157).
+ * Not all of these options may actually be supported,
+ * but we retain the old behavior of passing all that might be.
+ */
+static const struct nvme_fabric_options default_supported_options = {
+ .ctrl_loss_tmo = true,
+ .data_digest = true,
+ .disable_sqflow = true,
+ .discovery = true,
+ .duplicate_connect = true,
+ .fast_io_fail_tmo = true,
+ .hdr_digest = true,
+ .host_iface = true,
+ .host_traddr = true,
+ .hostid = true,
+ .hostnqn = true,
+ .keep_alive_tmo = true,
+ .nqn = true,
+ .nr_io_queues = true,
+ .nr_poll_queues = true,
+ .nr_write_queues = true,
+ .queue_size = true,
+ .reconnect_delay = true,
+ .tos = true,
+ .traddr = true,
+ .transport = true,
+ .trsvcid = true,
+};
+
+void nvmf_default_config(struct nvme_fabrics_config *cfg)
+{
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->tos = -1;
+ cfg->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO;
+}
+
+#define MERGE_CFG_OPTION(c, n, o, d) \
+ if ((c)->o == d) (c)->o = (n)->o
+#define MERGE_CFG_OPTION_STR(c, n, o, d) \
+ if ((c)->o == d && (n)->o) (c)->o = strdup((n)->o)
+static struct nvme_fabrics_config *merge_config(nvme_ctrl_t c,
+ const struct nvme_fabrics_config *cfg)
+{
+ struct nvme_fabrics_config *ctrl_cfg = nvme_ctrl_get_config(c);
+
+ MERGE_CFG_OPTION_STR(ctrl_cfg, cfg, host_traddr, NULL);
+ MERGE_CFG_OPTION_STR(ctrl_cfg, cfg, host_iface, NULL);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_io_queues, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_write_queues, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_poll_queues, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, queue_size, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, keep_alive_tmo, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, reconnect_delay, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, ctrl_loss_tmo,
+ NVMF_DEF_CTRL_LOSS_TMO);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, fast_io_fail_tmo, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, tos, -1);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, keyring, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, tls_key, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, duplicate_connect, false);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, disable_sqflow, false);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, hdr_digest, false);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, data_digest, false);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, tls, false);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, concat, false);
+
+ return ctrl_cfg;
+}
+
+#define UPDATE_CFG_OPTION(c, n, o, d) \
+ if ((n)->o != d) (c)->o = (n)->o
+void nvmf_update_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg)
+{
+ struct nvme_fabrics_config *ctrl_cfg = nvme_ctrl_get_config(c);
+
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, host_traddr, NULL);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, host_iface, NULL);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_io_queues, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_write_queues, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_poll_queues, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, queue_size, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, keep_alive_tmo, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, reconnect_delay, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, ctrl_loss_tmo,
+ NVMF_DEF_CTRL_LOSS_TMO);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, fast_io_fail_tmo, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, tos, -1);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, keyring, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, tls_key, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, duplicate_connect, false);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, disable_sqflow, false);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, hdr_digest, false);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, data_digest, false);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, tls, false);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, concat, false);
+}
+
+static int __add_bool_argument(char **argstr, char *tok, bool arg)
+{
+ char *nstr;
+
+ if (!arg)
+ return 0;
+ if (asprintf(&nstr, "%s,%s", *argstr, tok) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ free(*argstr);
+ *argstr = nstr;
+
+ return 0;
+}
+
+static int __add_int_argument(char **argstr, char *tok, int arg, bool allow_zero)
+{
+ char *nstr;
+
+ if (arg < 0 || (!arg && !allow_zero))
+ return 0;
+ if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ free(*argstr);
+ *argstr = nstr;
+
+ return 0;
+}
+
+static int __add_int_or_minus_one_argument(char **argstr, char *tok, int arg)
+{
+ char *nstr;
+
+ if (arg < -1)
+ return 0;
+ if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ free(*argstr);
+ *argstr = nstr;
+
+ return 0;
+}
+
+static int __add_argument(char **argstr, const char *tok, const char *arg)
+{
+ char *nstr;
+
+ if (!arg || arg[0] == '\0' || !strcmp(arg, "none"))
+ return 0;
+ if (asprintf(&nstr, "%s,%s=%s", *argstr, tok, arg) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ free(*argstr);
+ *argstr = nstr;
+
+ return 0;
+}
+
+static int __nvmf_supported_options(nvme_root_t r);
+#define nvmf_check_option(r, tok) \
+({ \
+ !__nvmf_supported_options(r) && (r)->options->tok; \
+})
+
+#define add_bool_argument(o, argstr, tok, arg) \
+({ \
+ int ret; \
+ if (nvmf_check_option(r, tok)) { \
+ ret = __add_bool_argument(argstr, \
+ stringify(tok), \
+ arg); \
+ } else { \
+ nvme_msg(r, LOG_DEBUG, \
+ "option \"%s\" ignored\n", \
+ stringify(tok)); \
+ ret = 0; \
+ } \
+ ret; \
+})
+
+#define add_int_argument(o, argstr, tok, arg, allow_zero) \
+({ \
+ int ret; \
+ if (nvmf_check_option(r, tok)) { \
+ ret = __add_int_argument(argstr, \
+ stringify(tok), \
+ arg, \
+ allow_zero); \
+ } else { \
+ nvme_msg(r, LOG_DEBUG, \
+ "option \"%s\" ignored\n", \
+ stringify(tok)); \
+ ret = 0; \
+ } \
+ ret; \
+})
+
+#define add_int_or_minus_one_argument(o, argstr, tok, arg) \
+({ \
+ int ret; \
+ if (nvmf_check_option(r, tok)) { \
+ ret = __add_int_or_minus_one_argument(argstr, \
+ stringify(tok), \
+ arg); \
+ } else { \
+ nvme_msg(r, LOG_DEBUG, \
+ "option \"%s\" ignored\n", \
+ stringify(tok)); \
+ ret = 0; \
+ } \
+ ret; \
+})
+
+#define add_argument(r, argstr, tok, arg) \
+({ \
+ int ret; \
+ if (nvmf_check_option(r, tok)) { \
+ ret = __add_argument(argstr, \
+ stringify(tok), \
+ arg); \
+ } else { \
+ nvme_msg(r, LOG_NOTICE, \
+ "option \"%s\" ignored\n", \
+ stringify(tok)); \
+ ret = 0; \
+ } \
+ ret; \
+})
+
+static int inet4_pton(const char *src, uint16_t port,
+ struct sockaddr_storage *addr)
+{
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
+
+ if (strlen(src) > INET_ADDRSTRLEN)
+ return -EINVAL;
+
+ if (inet_pton(AF_INET, src, &addr4->sin_addr.s_addr) <= 0)
+ return -EINVAL;
+
+ addr4->sin_family = AF_INET;
+ addr4->sin_port = htons(port);
+
+ return 0;
+}
+
+static int inet6_pton(nvme_root_t r, const char *src, uint16_t port,
+ struct sockaddr_storage *addr)
+{
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
+ const char *scope = NULL;
+ char *p;
+
+ if (strlen(src) > INET6_ADDRSTRLEN)
+ return -EINVAL;
+
+ _cleanup_free_ char *tmp = strdup(src);
+ if (!tmp) {
+ nvme_msg(r, LOG_ERR, "cannot copy: %s\n", src);
+ return -ENOMEM;
+ }
+
+ p = strchr(tmp, '%');
+ if (p) {
+ *p = '\0';
+ scope = src + (p - tmp) + 1;
+ }
+
+ if (inet_pton(AF_INET6, tmp, &addr6->sin6_addr) != 1)
+ return -EINVAL;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr) && scope) {
+ addr6->sin6_scope_id = if_nametoindex(scope);
+ if (addr6->sin6_scope_id == 0) {
+ nvme_msg(r, LOG_ERR,
+ "can't find iface index for: %s (%m)\n", scope);
+ return -EINVAL;
+ }
+ }
+
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = htons(port);
+ return 0;
+}
+
+/**
+ * inet_pton_with_scope - convert an IPv4/IPv6 to socket address
+ * @r: nvme_root_t object
+ * @af: address family, AF_INET, AF_INET6 or AF_UNSPEC for either
+ * @src: the start of the address string
+ * @trsvcid: transport service identifier
+ * @addr: output socket address
+ *
+ * Return 0 on success, errno otherwise.
+ */
+static int inet_pton_with_scope(nvme_root_t r, int af,
+ const char *src, const char * trsvcid,
+ struct sockaddr_storage *addr)
+{
+ int ret = -EINVAL;
+ uint16_t port = 0;
+
+ if (trsvcid) {
+ unsigned long long tmp = strtoull(trsvcid, NULL, 0);
+ port = (uint16_t)tmp;
+ if (tmp != port) {
+ nvme_msg(r, LOG_ERR, "trsvcid out of range: %s\n",
+ trsvcid);
+ return -ERANGE;
+ }
+ } else {
+ port = 0;
+ }
+
+ switch (af) {
+ case AF_INET:
+ ret = inet4_pton(src, port, addr);
+ break;
+ case AF_INET6:
+ ret = inet6_pton(r, src, port, addr);
+ break;
+ case AF_UNSPEC:
+ ret = inet4_pton(src, port, addr);
+ if (ret)
+ ret = inet6_pton(r, src, port, addr);
+ break;
+ default:
+ nvme_msg(r, LOG_ERR, "unexpected address family %d\n", af);
+ }
+
+ return ret;
+}
+
+static bool traddr_is_hostname(nvme_root_t r, nvme_ctrl_t c)
+{
+ struct sockaddr_storage addr;
+
+ if (!c->traddr)
+ return false;
+ if (strcmp(c->transport, "tcp") && strcmp(c->transport, "rdma"))
+ return false;
+ if (inet_pton_with_scope(r, AF_UNSPEC, c->traddr, c->trsvcid, &addr) == 0)
+ return false;
+ return true;
+}
+
+static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr)
+{
+ struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c);
+ const char *transport = nvme_ctrl_get_transport(c);
+ const char *hostnqn, *hostid, *hostkey, *ctrlkey;
+ bool discover = false, discovery_nqn = false;
+ nvme_root_t r = h->r;
+
+ if (!transport) {
+ nvme_msg(h->r, LOG_ERR, "need a transport (-t) argument\n");
+ errno = ENVME_CONNECT_TARG;
+ return -1;
+ }
+
+ if (strncmp(transport, "loop", 4)) {
+ if (!nvme_ctrl_get_traddr(c)) {
+ nvme_msg(h->r, LOG_ERR, "need a address (-a) argument\n");
+ errno = ENVME_CONNECT_AARG;
+ return -1;
+ }
+ }
+
+ /* always specify nqn as first arg - this will init the string */
+ if (asprintf(argstr, "nqn=%s",
+ nvme_ctrl_get_subsysnqn(c)) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (!strcmp(nvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME)) {
+ nvme_ctrl_set_discovery_ctrl(c, true);
+ nvme_ctrl_set_unique_discovery_ctrl(c, false);
+ discovery_nqn = true;
+ }
+ if (nvme_ctrl_is_discovery_ctrl(c))
+ discover = true;
+ hostnqn = nvme_host_get_hostnqn(h);
+ hostid = nvme_host_get_hostid(h);
+ hostkey = nvme_host_get_dhchap_key(h);
+ if (!hostkey)
+ hostkey = nvme_ctrl_get_dhchap_host_key(c);
+ ctrlkey = nvme_ctrl_get_dhchap_key(c);
+ if (add_argument(r, argstr, transport, transport) ||
+ add_argument(r, argstr, traddr,
+ nvme_ctrl_get_traddr(c)) ||
+ add_argument(r, argstr, host_traddr,
+ cfg->host_traddr) ||
+ add_argument(r, argstr, host_iface,
+ cfg->host_iface) ||
+ add_argument(r, argstr, trsvcid,
+ nvme_ctrl_get_trsvcid(c)) ||
+ (hostnqn && add_argument(r, argstr, hostnqn, hostnqn)) ||
+ (hostid && add_argument(r, argstr, hostid, hostid)) ||
+ (discover && !discovery_nqn &&
+ add_bool_argument(r, argstr, discovery, true)) ||
+ (!discover && hostkey &&
+ add_argument(r, argstr, dhchap_secret, hostkey)) ||
+ (!discover && ctrlkey &&
+ add_argument(r, argstr, dhchap_ctrl_secret, ctrlkey)) ||
+ (!discover &&
+ add_int_argument(r, argstr, nr_io_queues,
+ cfg->nr_io_queues, false)) ||
+ (!discover &&
+ add_int_argument(r, argstr, nr_write_queues,
+ cfg->nr_write_queues, false)) ||
+ (!discover &&
+ add_int_argument(r, argstr, nr_poll_queues,
+ cfg->nr_poll_queues, false)) ||
+ (!discover &&
+ add_int_argument(r, argstr, queue_size,
+ cfg->queue_size, false)) ||
+ add_int_argument(r, argstr, keep_alive_tmo,
+ cfg->keep_alive_tmo, false) ||
+ add_int_argument(r, argstr, reconnect_delay,
+ cfg->reconnect_delay, false) ||
+ (strcmp(transport, "loop") &&
+ add_int_or_minus_one_argument(r, argstr, ctrl_loss_tmo,
+ cfg->ctrl_loss_tmo)) ||
+ (strcmp(transport, "loop") &&
+ add_int_argument(r, argstr, fast_io_fail_tmo,
+ cfg->fast_io_fail_tmo, false)) ||
+ (strcmp(transport, "loop") &&
+ add_int_argument(r, argstr, tos, cfg->tos, true)) ||
+ add_int_argument(r, argstr, keyring, cfg->keyring, false) ||
+ (!strcmp(transport, "tcp") &&
+ add_int_argument(r, argstr, tls_key, cfg->tls_key, false)) ||
+ add_bool_argument(r, argstr, duplicate_connect,
+ cfg->duplicate_connect) ||
+ add_bool_argument(r, argstr, disable_sqflow,
+ cfg->disable_sqflow) ||
+ (!strcmp(transport, "tcp") &&
+ add_bool_argument(r, argstr, hdr_digest, cfg->hdr_digest)) ||
+ (!strcmp(transport, "tcp") &&
+ add_bool_argument(r, argstr, data_digest, cfg->data_digest)) ||
+ (!strcmp(transport, "tcp") &&
+ add_bool_argument(r, argstr, tls, cfg->tls)) ||
+ (!strcmp(transport, "tcp") &&
+ add_bool_argument(r, argstr, concat, cfg->concat))) {
+ free(*argstr);
+ return -1;
+ }
+
+ return 0;
+}
+
+#define parse_option(r, v, name) \
+ if (!strcmp(v, stringify(name))) { \
+ r->options->name = true; \
+ continue; \
+ }
+
+static int __nvmf_supported_options(nvme_root_t r)
+{
+ char buf[0x1000], *options, *p, *v;
+ _cleanup_fd_ int fd = -1;
+ ssize_t len;
+
+ if (r->options)
+ return 0;
+
+ r->options = calloc(1, sizeof(*r->options));
+ if (!r->options)
+ return -ENOMEM;
+
+ fd = open(nvmf_dev, O_RDONLY);
+ if (fd < 0) {
+ nvme_msg(r, LOG_ERR, "Failed to open %s: %s\n",
+ nvmf_dev, strerror(errno));
+ return -ENVME_CONNECT_OPEN;
+ }
+
+ memset(buf, 0x0, sizeof(buf));
+ len = read(fd, buf, sizeof(buf) - 1);
+ if (len < 0) {
+ if (errno == EINVAL) {
+ /*
+ * Older Linux kernels don't allow reading from nvmf_dev
+ * to get supported options, so use a default set
+ */
+ nvme_msg(r, LOG_DEBUG,
+ "Cannot read %s, using default options\n",
+ nvmf_dev);
+ *r->options = default_supported_options;
+ return 0;
+ }
+
+ nvme_msg(r, LOG_ERR, "Failed to read from %s: %s\n",
+ nvmf_dev, strerror(errno));
+ return -ENVME_CONNECT_READ;
+ }
+
+ buf[len] = '\0';
+ options = buf;
+
+ nvme_msg(r, LOG_DEBUG, "kernel supports: ");
+
+ while ((p = strsep(&options, ",\n")) != NULL) {
+ if (!*p)
+ continue;
+ v = strsep(&p, "= ");
+ if (!v)
+ continue;
+ nvme_msg(r, LOG_DEBUG, "%s ", v);
+
+ parse_option(r, v, cntlid);
+ parse_option(r, v, concat);
+ parse_option(r, v, ctrl_loss_tmo);
+ parse_option(r, v, data_digest);
+ parse_option(r, v, dhchap_ctrl_secret);
+ parse_option(r, v, dhchap_secret);
+ parse_option(r, v, disable_sqflow);
+ parse_option(r, v, discovery);
+ parse_option(r, v, duplicate_connect);
+ parse_option(r, v, fast_io_fail_tmo);
+ parse_option(r, v, hdr_digest);
+ parse_option(r, v, host_iface);
+ parse_option(r, v, host_traddr);
+ parse_option(r, v, hostid);
+ parse_option(r, v, hostnqn);
+ parse_option(r, v, instance);
+ parse_option(r, v, keep_alive_tmo);
+ parse_option(r, v, keyring);
+ parse_option(r, v, nqn);
+ parse_option(r, v, nr_io_queues);
+ parse_option(r, v, nr_poll_queues);
+ parse_option(r, v, nr_write_queues);
+ parse_option(r, v, queue_size);
+ parse_option(r, v, reconnect_delay);
+ parse_option(r, v, tls);
+ parse_option(r, v, tls_key);
+ parse_option(r, v, tos);
+ parse_option(r, v, traddr);
+ parse_option(r, v, transport);
+ parse_option(r, v, trsvcid);
+ }
+ nvme_msg(r, LOG_DEBUG, "\n");
+ return 0;
+}
+
+static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr)
+{
+ _cleanup_fd_ int fd = -1;
+ int ret, len = strlen(argstr);
+ char buf[0x1000], *options, *p;
+
+ fd = open(nvmf_dev, O_RDWR);
+ if (fd < 0) {
+ nvme_msg(r, LOG_ERR, "Failed to open %s: %s\n",
+ nvmf_dev, strerror(errno));
+ return -ENVME_CONNECT_OPEN;
+ }
+
+ nvme_msg(r, LOG_DEBUG, "connect ctrl, '%.*s'\n",
+ (int)strcspn(argstr,"\n"), argstr);
+ ret = write(fd, argstr, len);
+ if (ret != len) {
+ nvme_msg(r, LOG_NOTICE, "Failed to write to %s: %s\n",
+ nvmf_dev, strerror(errno));
+ switch (errno) {
+ case EALREADY:
+ return -ENVME_CONNECT_ALREADY;
+ case EINVAL:
+ return -ENVME_CONNECT_INVAL;
+ case EADDRINUSE:
+ return -ENVME_CONNECT_ADDRINUSE;
+ case ENODEV:
+ return -ENVME_CONNECT_NODEV;
+ case EOPNOTSUPP:
+ return -ENVME_CONNECT_OPNOTSUPP;
+ case ECONNREFUSED:
+ return -ENVME_CONNECT_CONNREFUSED;
+ case EADDRNOTAVAIL:
+ return -ENVME_CONNECT_ADDRNOTAVAIL;
+ default:
+ return -ENVME_CONNECT_WRITE;
+ }
+ }
+
+ memset(buf, 0x0, sizeof(buf));
+ len = read(fd, buf, sizeof(buf) - 1);
+ if (len < 0) {
+ nvme_msg(r, LOG_ERR, "Failed to read from %s: %s\n",
+ nvmf_dev, strerror(errno));
+ return -ENVME_CONNECT_READ;
+ }
+ nvme_msg(r, LOG_DEBUG, "connect ctrl, response '%.*s'\n",
+ (int)strcspn(buf, "\n"), buf);
+ buf[len] = '\0';
+ options = buf;
+ while ((p = strsep(&options, ",\n")) != NULL) {
+ if (!*p)
+ continue;
+ if (sscanf(p, "instance=%d", &ret) == 1)
+ return ret;
+ }
+
+ nvme_msg(r, LOG_ERR, "Failed to parse ctrl info for \"%s\"\n", argstr);
+ return -ENVME_CONNECT_PARSE;
+}
+
+static const char *lookup_context(nvme_root_t r, nvme_ctrl_t c)
+{
+
+ nvme_host_t h;
+ nvme_subsystem_t s;
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ if (__nvme_lookup_ctrl(s, nvme_ctrl_get_transport(c),
+ nvme_ctrl_get_traddr(c),
+ NULL,
+ NULL,
+ nvme_ctrl_get_trsvcid(c),
+ NULL,
+ NULL))
+ return nvme_subsystem_get_application(s);
+ }
+ }
+
+ return NULL;
+}
+
+int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c,
+ const struct nvme_fabrics_config *cfg)
+{
+ nvme_subsystem_t s;
+ const char *root_app, *app;
+ _cleanup_free_ char *argstr = NULL;
+ int ret;
+
+ /* highest prio have configs from command line */
+ cfg = merge_config(c, cfg);
+
+ /* apply configuration from config file (JSON) */
+ s = nvme_lookup_subsystem(h, NULL, nvme_ctrl_get_subsysnqn(c));
+ if (s) {
+ nvme_ctrl_t fc;
+
+ fc = __nvme_lookup_ctrl(s, nvme_ctrl_get_transport(c),
+ nvme_ctrl_get_traddr(c),
+ nvme_ctrl_get_host_traddr(c),
+ nvme_ctrl_get_host_iface(c),
+ nvme_ctrl_get_trsvcid(c),
+ NULL,
+ NULL);
+ if (fc) {
+ const char *key;
+
+ cfg = merge_config(c, nvme_ctrl_get_config(fc));
+ /*
+ * An authentication key might already been set
+ * in @cfg, so ensure to update @c with the correct
+ * controller key.
+ */
+ key = nvme_ctrl_get_dhchap_host_key(fc);
+ if (key)
+ nvme_ctrl_set_dhchap_host_key(c, key);
+ key = nvme_ctrl_get_dhchap_key(fc);
+ if (key)
+ nvme_ctrl_set_dhchap_key(c, key);
+ }
+
+ }
+
+ root_app = nvme_root_get_application(h->r);
+ if (root_app) {
+ app = nvme_subsystem_get_application(s);
+ if (!app && nvme_ctrl_is_discovery_ctrl(c))
+ app = lookup_context(h->r, c);
+
+ /*
+ * configuration is managed by an application,
+ * refuse to act on subsystems which either have
+ * no application set or which habe a different
+ * application string.
+ */
+ if (app && strcmp(app, root_app)) {
+ nvme_msg(h->r, LOG_INFO, "skip %s, not managed by %s\n",
+ nvme_subsystem_get_nqn(s), root_app);
+ errno = ENVME_CONNECT_IGNORED;
+ return -1;
+ }
+ }
+
+ nvme_ctrl_set_discovered(c, true);
+ if (traddr_is_hostname(h->r, c)) {
+ char *traddr = c->traddr;
+
+ c->traddr = hostname2traddr(h->r, traddr);
+ if (!c->traddr) {
+ c->traddr = traddr;
+ errno = ENVME_CONNECT_TRADDR;
+ return -1;
+ }
+ free(traddr);
+ }
+
+ ret = build_options(h, c, &argstr);
+ if (ret)
+ return ret;
+
+ ret = __nvmf_add_ctrl(h->r, argstr);
+ if (ret < 0) {
+ errno = -ret;
+ return -1;
+ }
+
+ nvme_msg(h->r, LOG_INFO, "nvme%d: %s connected\n", ret,
+ nvme_ctrl_get_subsysnqn(c));
+ return nvme_init_ctrl(h, c, ret);
+}
+
+nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h,
+ struct nvmf_disc_log_entry *e,
+ const struct nvme_fabrics_config *cfg,
+ bool *discover)
+{
+ const char *transport;
+ char *traddr = NULL, *trsvcid = NULL;
+ nvme_ctrl_t c;
+ int ret;
+
+ switch (e->trtype) {
+ case NVMF_TRTYPE_RDMA:
+ case NVMF_TRTYPE_TCP:
+ switch (e->adrfam) {
+ case NVMF_ADDR_FAMILY_IP4:
+ case NVMF_ADDR_FAMILY_IP6:
+ traddr = e->traddr;
+ trsvcid = e->trsvcid;
+ break;
+ default:
+ nvme_msg(h->r, LOG_ERR,
+ "skipping unsupported adrfam %d\n",
+ e->adrfam);
+ errno = EINVAL;
+ return NULL;
+ }
+ break;
+ case NVMF_TRTYPE_FC:
+ switch (e->adrfam) {
+ case NVMF_ADDR_FAMILY_FC:
+ traddr = e->traddr;
+ break;
+ default:
+ nvme_msg(h->r, LOG_ERR,
+ "skipping unsupported adrfam %d\n",
+ e->adrfam);
+ errno = EINVAL;
+ return NULL;
+ }
+ break;
+ case NVMF_TRTYPE_LOOP:
+ traddr = strlen(e->traddr) ? e->traddr : NULL;
+ break;
+ default:
+ nvme_msg(h->r, LOG_ERR, "skipping unsupported transport %d\n",
+ e->trtype);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ transport = nvmf_trtype_str(e->trtype);
+
+ nvme_msg(h->r, LOG_DEBUG, "lookup ctrl "
+ "(transport: %s, traddr: %s, trsvcid %s)\n",
+ transport, traddr, trsvcid);
+ c = nvme_create_ctrl(h->r, e->subnqn, transport, traddr,
+ cfg->host_traddr, cfg->host_iface, trsvcid);
+ if (!c) {
+ nvme_msg(h->r, LOG_DEBUG, "skipping discovery entry, "
+ "failed to allocate %s controller with traddr %s\n",
+ transport, traddr);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ switch (e->subtype) {
+ case NVME_NQN_CURR:
+ nvme_ctrl_set_discovered(c, true);
+ nvme_ctrl_set_unique_discovery_ctrl(c,
+ strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME));
+ break;
+ case NVME_NQN_DISC:
+ if (discover)
+ *discover = true;
+ nvme_ctrl_set_discovery_ctrl(c, true);
+ nvme_ctrl_set_unique_discovery_ctrl(c,
+ strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME));
+ break;
+ default:
+ nvme_msg(h->r, LOG_ERR, "unsupported subtype %d\n",
+ e->subtype);
+ fallthrough;
+ case NVME_NQN_NVME:
+ nvme_ctrl_set_discovery_ctrl(c, false);
+ nvme_ctrl_set_unique_discovery_ctrl(c, false);
+ break;
+ }
+
+ if (nvme_ctrl_is_discovered(c)) {
+ nvme_free_ctrl(c);
+ errno = EAGAIN;
+ return NULL;
+ }
+
+ if (e->treq & NVMF_TREQ_DISABLE_SQFLOW &&
+ nvmf_check_option(h->r, disable_sqflow))
+ c->cfg.disable_sqflow = true;
+
+ if (e->trtype == NVMF_TRTYPE_TCP &&
+ e->tsas.tcp.sectype != NVMF_TCP_SECTYPE_NONE)
+ c->cfg.tls = true;
+
+ ret = nvmf_add_ctrl(h, c, cfg);
+ if (!ret)
+ return c;
+
+ if (errno == EINVAL && c->cfg.disable_sqflow) {
+ errno = 0;
+ /* disable_sqflow is unrecognized option on older kernels */
+ nvme_msg(h->r, LOG_INFO, "failed to connect controller, "
+ "retry with disabling SQ flow control\n");
+ c->cfg.disable_sqflow = false;
+ ret = nvmf_add_ctrl(h, c, cfg);
+ if (!ret)
+ return c;
+ }
+ nvme_free_ctrl(c);
+ return NULL;
+}
+
+/*
+ * Most of nvmf_discovery_log is reserved, so only fetch the initial bytes.
+ * 8 bytes for GENCTR, 8 for NUMREC, and 2 for RECFMT.
+ * Since only multiples of 4 bytes are allowed, round 18 up to 20.
+ */
+#define DISCOVERY_HEADER_LEN 20
+
+static struct nvmf_discovery_log *nvme_discovery_log(
+ const struct nvme_get_discovery_args *args)
+{
+ nvme_root_t r = root_from_ctrl(args->c);
+ struct nvmf_discovery_log *log;
+ int retries = 0;
+ const char *name = nvme_ctrl_get_name(args->c);
+ uint64_t genctr, numrec;
+ int fd = nvme_ctrl_get_fd(args->c);
+ struct nvme_get_log_args log_args = {
+ .result = args->result,
+ .args_size = sizeof(log_args),
+ .timeout = args->timeout,
+ .lid = NVME_LOG_LID_DISCOVER,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = args->lsp,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ log = __nvme_alloc(sizeof(*log));
+ if (!log) {
+ nvme_msg(r, LOG_ERR,
+ "could not allocate memory for discovery log header\n");
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ nvme_msg(r, LOG_DEBUG, "%s: get header (try %d/%d)\n",
+ name, retries, args->max_retries);
+ log_args.log = log;
+ log_args.len = DISCOVERY_HEADER_LEN;
+ if (nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &log_args)) {
+ nvme_msg(r, LOG_INFO,
+ "%s: discover try %d/%d failed, error %d\n",
+ name, retries, args->max_retries, errno);
+ goto out_free_log;
+ }
+
+ do {
+ size_t entries_size;
+
+ numrec = le64_to_cpu(log->numrec);
+ genctr = le64_to_cpu(log->genctr);
+
+ if (numrec == 0)
+ break;
+
+ free(log);
+ entries_size = sizeof(*log->entries) * numrec;
+ log = __nvme_alloc(sizeof(*log) + entries_size);
+ if (!log) {
+ nvme_msg(r, LOG_ERR,
+ "could not alloc memory for discovery log page\n");
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ nvme_msg(r, LOG_DEBUG,
+ "%s: get %" PRIu64 " records (genctr %" PRIu64 ")\n",
+ name, numrec, genctr);
+
+ log_args.lpo = sizeof(*log);
+ log_args.log = log->entries;
+ log_args.len = entries_size;
+ if (nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &log_args)) {
+ nvme_msg(r, LOG_INFO,
+ "%s: discover try %d/%d failed, error %d\n",
+ name, retries, args->max_retries, errno);
+ goto out_free_log;
+ }
+
+ /*
+ * If the log page was read with multiple Get Log Page commands,
+ * genctr must be checked afterwards to ensure atomicity
+ */
+ nvme_msg(r, LOG_DEBUG, "%s: get header again\n", name);
+
+ log_args.lpo = 0;
+ log_args.log = log;
+ log_args.len = DISCOVERY_HEADER_LEN;
+ if (nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &log_args)) {
+ nvme_msg(r, LOG_INFO,
+ "%s: discover try %d/%d failed, error %d\n",
+ name, retries, args->max_retries, errno);
+ goto out_free_log;
+ }
+ } while (genctr != le64_to_cpu(log->genctr) &&
+ ++retries < args->max_retries);
+
+ if (genctr != le64_to_cpu(log->genctr)) {
+ nvme_msg(r, LOG_INFO, "%s: discover genctr mismatch\n", name);
+ errno = EAGAIN;
+ } else if (numrec != le64_to_cpu(log->numrec)) {
+ nvme_msg(r, LOG_INFO,
+ "%s: numrec changed unexpectedly "
+ "from %" PRIu64 " to %" PRIu64 "\n",
+ name, numrec, le64_to_cpu(log->numrec));
+ errno = EBADSLT;
+ } else {
+ return log;
+ }
+
+out_free_log:
+ free(log);
+ return NULL;
+}
+
+static void sanitize_discovery_log_entry(struct nvmf_disc_log_entry *e)
+{
+ strchomp(e->trsvcid, sizeof(e->trsvcid));
+ strchomp(e->traddr, sizeof(e->traddr));
+}
+
+int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp,
+ int max_retries)
+{
+ struct nvme_get_discovery_args args = {
+ .c = c,
+ .max_retries = max_retries,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lsp = NVMF_LOG_DISC_LSP_NONE,
+ };
+
+ *logp = nvmf_get_discovery_wargs(&args);
+ return *logp ? 0 : -1;
+}
+
+struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args)
+{
+ struct nvmf_discovery_log *log;
+
+ log = nvme_discovery_log(args);
+ if (!log)
+ return NULL;
+
+ for (int i = 0; i < le64_to_cpu(log->numrec); i++)
+ sanitize_discovery_log_entry(&log->entries[i]);
+
+ return log;
+}
+
+#define PATH_UUID_IBM "/proc/device-tree/ibm,partition-uuid"
+
+static char *uuid_ibm_filename(void)
+{
+ char *basepath = getenv("LIBNVME_SYSFS_PATH");
+ char *str;
+
+ if (!basepath)
+ return strdup(PATH_UUID_IBM);
+
+ if (!asprintf(&str, "%s" PATH_UUID_IBM, basepath))
+ return NULL;
+
+ return str;
+}
+
+static int uuid_from_device_tree(char *system_uuid)
+{
+ _cleanup_free_ char *filename = uuid_ibm_filename();
+ _cleanup_fd_ int f = -1;
+ ssize_t len;
+
+ f = open(filename, O_RDONLY);
+ if (f < 0)
+ return -ENXIO;
+
+ memset(system_uuid, 0, NVME_UUID_LEN_STRING);
+ len = read(f, system_uuid, NVME_UUID_LEN_STRING - 1);
+ if (len < 0)
+ return -ENXIO;
+
+ return strlen(system_uuid) ? 0 : -ENXIO;
+}
+
+#define PATH_DMI_ENTRIES "/sys/firmware/dmi/entries"
+
+static char *dmi_entries_dir(void)
+{
+ char *basepath = getenv("LIBNVME_SYSFS_PATH");
+ char *str;
+
+ if (!basepath)
+ return strdup(PATH_DMI_ENTRIES);
+
+ if (!asprintf(&str, "%s" PATH_DMI_ENTRIES, basepath))
+ return NULL;
+
+ return str;
+}
+
+/*
+ * See System Management BIOS (SMBIOS) Reference Specification
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf
+ */
+#define DMI_SYSTEM_INFORMATION 1
+
+static bool is_dmi_uuid_valid(const char *buf, size_t len)
+{
+ int i;
+
+ /* UUID bytes are from byte 8 to 23 */
+ if (len < 24)
+ return false;
+
+ /* Test it's a invalid UUID with all zeros */
+ for (i = 8; i < 24; i++) {
+ if (buf[i])
+ break;
+ }
+ if (i == 24)
+ return false;
+
+ return true;
+}
+
+static int uuid_from_dmi_entries(char *system_uuid)
+{
+ _cleanup_dir_ DIR *d = NULL;
+ _cleanup_free_ char *entries_dir = dmi_entries_dir();
+ int f;
+ struct dirent *de;
+ char buf[512] = {0};
+
+ system_uuid[0] = '\0';
+ d = opendir(entries_dir);
+ if (!d)
+ return -ENXIO;
+ while ((de = readdir(d))) {
+ char filename[PATH_MAX];
+ int len, type;
+
+ if (de->d_name[0] == '.')
+ continue;
+ sprintf(filename, "%s/%s/type", entries_dir, de->d_name);
+ f = open(filename, O_RDONLY);
+ if (f < 0)
+ continue;
+ len = read(f, buf, 512);
+ close(f);
+ if (len <= 0)
+ continue;
+ if (sscanf(buf, "%d", &type) != 1)
+ continue;
+ if (type != DMI_SYSTEM_INFORMATION)
+ continue;
+ sprintf(filename, "%s/%s/raw", entries_dir, de->d_name);
+ f = open(filename, O_RDONLY);
+ if (f < 0)
+ continue;
+ len = read(f, buf, 512);
+ close(f);
+
+ if (!is_dmi_uuid_valid(buf, len))
+ continue;
+
+ /* Sigh. https://en.wikipedia.org/wiki/Overengineering */
+ /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */
+ sprintf(system_uuid,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x",
+ (uint8_t)buf[8 + 3], (uint8_t)buf[8 + 2],
+ (uint8_t)buf[8 + 1], (uint8_t)buf[8 + 0],
+ (uint8_t)buf[8 + 5], (uint8_t)buf[8 + 4],
+ (uint8_t)buf[8 + 7], (uint8_t)buf[8 + 6],
+ (uint8_t)buf[8 + 8], (uint8_t)buf[8 + 9],
+ (uint8_t)buf[8 + 10], (uint8_t)buf[8 + 11],
+ (uint8_t)buf[8 + 12], (uint8_t)buf[8 + 13],
+ (uint8_t)buf[8 + 14], (uint8_t)buf[8 + 15]);
+ break;
+ }
+ return strlen(system_uuid) ? 0 : -ENXIO;
+}
+
+#define PATH_DMI_PROD_UUID "/sys/class/dmi/id/product_uuid"
+
+/**
+ * uuid_from_product_uuid() - Get system UUID from product_uuid
+ * @system_uuid: Where to save the system UUID.
+ *
+ * Return: 0 on success, -ENXIO otherwise.
+ */
+static int uuid_from_product_uuid(char *system_uuid)
+{
+ _cleanup_file_ FILE *stream = NULL;
+ ssize_t nread;
+ _cleanup_free_ char *line = NULL;
+ size_t len = 0;
+
+ stream = fopen(PATH_DMI_PROD_UUID, "re");
+ if (!stream)
+ return -ENXIO;
+ system_uuid[0] = '\0';
+
+ nread = getline(&line, &len, stream);
+ if (nread != NVME_UUID_LEN_STRING)
+ return -ENXIO;
+
+ /* The kernel is handling the byte swapping according DMTF
+ * SMBIOS 3.0 Section 7.2.1 System UUID */
+
+ memcpy(system_uuid, line, NVME_UUID_LEN_STRING - 1);
+ system_uuid[NVME_UUID_LEN_STRING - 1] = '\0';
+
+ return 0;
+}
+
+/**
+ * uuid_from_dmi() - read system UUID
+ * @system_uuid: buffer for the UUID
+ *
+ * The system UUID can be read from two different locations:
+ *
+ * 1) /sys/class/dmi/id/product_uuid
+ * 2) /sys/firmware/dmi/entries
+ *
+ * Note that the second location is not present on Debian-based systems.
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int uuid_from_dmi(char *system_uuid)
+{
+ int ret = uuid_from_product_uuid(system_uuid);
+ if (ret != 0)
+ ret = uuid_from_dmi_entries(system_uuid);
+ return ret;
+}
+
+char *nvmf_hostnqn_generate()
+{
+ char *hostnqn;
+ int ret;
+ char uuid_str[NVME_UUID_LEN_STRING];
+ unsigned char uuid[NVME_UUID_LEN];
+
+ ret = uuid_from_dmi(uuid_str);
+ if (ret < 0) {
+ ret = uuid_from_device_tree(uuid_str);
+ }
+ if (ret < 0) {
+ if (nvme_uuid_random(uuid) < 0)
+ memset(uuid, 0, NVME_UUID_LEN);
+ nvme_uuid_to_string(uuid, uuid_str);
+ }
+
+ if (asprintf(&hostnqn, "nqn.2014-08.org.nvmexpress:uuid:%s", uuid_str) < 0)
+ return NULL;
+
+ return hostnqn;
+}
+
+static char *nvmf_read_file(const char *f, int len)
+{
+ char buf[len];
+ _cleanup_fd_ int fd = -1;
+ int ret;
+
+ fd = open(f, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ memset(buf, 0, len);
+ ret = read(fd, buf, len - 1);
+
+ if (ret < 0 || !strlen(buf))
+ return NULL;
+ return strndup(buf, strcspn(buf, "\n"));
+}
+
+char *nvmf_hostnqn_from_file()
+{
+ char *hostnqn = getenv("LIBNVME_HOSTNQN");
+
+ if (hostnqn)
+ return strdup(hostnqn);
+
+ return nvmf_read_file(NVMF_HOSTNQN_FILE, NVMF_NQN_SIZE);
+}
+
+char *nvmf_hostid_from_file()
+{
+ char *hostid = getenv("LIBNVME_HOSTID");
+
+ if (hostid)
+ return strdup(hostid);
+
+ return nvmf_read_file(NVMF_HOSTID_FILE, NVMF_HOSTID_SIZE);
+}
+
+/**
+ * nvmf_get_tel() - Calculate the amount of memory needed for a DIE.
+ * @hostsymname: Symbolic name (may be NULL)
+ *
+ * Each Discovery Information Entry (DIE) must contain at a minimum an
+ * Extended Attribute for the HostID. The Entry may optionally contain an
+ * Extended Attribute for the Symbolic Name.
+ *
+ * Return: Total Entry Length
+ */
+static __u32 nvmf_get_tel(const char *hostsymname)
+{
+ __u32 tel = sizeof(struct nvmf_ext_die);
+ __u16 len;
+
+ /* Host ID is mandatory */
+ tel += nvmf_exat_size(NVME_UUID_LEN);
+
+ /* Symbolic name is optional */
+ len = hostsymname ? strlen(hostsymname) : 0;
+ if (len)
+ tel += nvmf_exat_size(len);
+
+ return tel;
+}
+
+/**
+ * nvmf_fill_die() - Fill a Discovery Information Entry.
+ * @die: Pointer to Discovery Information Entry to be filled
+ * @h: Pointer to the host data structure
+ * @tel: Length of the DIE
+ * @trtype: Transport type
+ * @adrfam: Address family
+ * @reg_addr: Address to register. Setting this to an empty string tells
+ * the DC to infer address from the source address of the socket.
+ * @tsas: Transport Specific Address Subtype for the address being
+ * registered.
+ */
+static void nvmf_fill_die(struct nvmf_ext_die *die, struct nvme_host *h,
+ __u32 tel, __u8 trtype, __u8 adrfam,
+ const char *reg_addr, union nvmf_tsas *tsas)
+{
+ __u16 numexat = 0;
+ size_t symname_len;
+ struct nvmf_ext_attr *exat;
+
+ die->tel = cpu_to_le32(tel);
+ die->trtype = trtype;
+ die->adrfam = adrfam;
+
+ memcpy(die->nqn, h->hostnqn, MIN(sizeof(die->nqn), strlen(h->hostnqn)));
+ memcpy(die->traddr, reg_addr, MIN(sizeof(die->traddr), strlen(reg_addr)));
+
+ if (tsas)
+ memcpy(&die->tsas, tsas, sizeof(die->tsas));
+
+ /* Extended Attribute for the HostID (mandatory) */
+ numexat++;
+ exat = die->exat;
+ exat->exattype = cpu_to_le16(NVMF_EXATTYPE_HOSTID);
+ exat->exatlen = cpu_to_le16(nvmf_exat_len(NVME_UUID_LEN));
+ nvme_uuid_from_string(h->hostid, exat->exatval);
+
+ /* Extended Attribute for the Symbolic Name (optional) */
+ symname_len = h->hostsymname ? strlen(h->hostsymname) : 0;
+ if (symname_len) {
+ __u16 exatlen = nvmf_exat_len(symname_len);
+
+ numexat++;
+ exat = nvmf_exat_ptr_next(exat);
+ exat->exattype = cpu_to_le16(NVMF_EXATTYPE_SYMNAME);
+ exat->exatlen = cpu_to_le16(exatlen);
+ memcpy(exat->exatval, h->hostsymname, symname_len);
+ /* Per Base specs, ASCII strings must be padded with spaces */
+ memset(&exat->exatval[symname_len], ' ', exatlen - symname_len);
+ }
+
+ die->numexat = cpu_to_le16(numexat);
+}
+
+/**
+ * nvmf_dim() - Explicit reg, dereg, reg-update issuing DIM
+ * @c: Host NVMe controller instance maintaining the admin queue used to
+ * submit the DIM command to the DC.
+ * @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to
+ * perform a Registration, Deregistration, or Registration-update.
+ * @trtype: Transport type (&enum nvmf_trtype - must be NVMF_TRTYPE_TCP)
+ * @adrfam: Address family (&enum nvmf_addr_family)
+ * @reg_addr: Address to register. Setting this to an empty string tells
+ * the DC to infer address from the source address of the socket.
+ * @tsas: Transport Specific Address Subtype for the address being
+ * registered.
+ * @result: Location where to save the command-specific result returned by
+ * the discovery controller.
+ *
+ * Perform explicit registration, deregistration, or
+ * registration-update (specified by @tas) by sending a Discovery
+ * Information Management (DIM) command to the Discovery Controller
+ * (DC).
+ *
+ * Return: 0 on success; on failure -1 is returned and errno is set
+ */
+static int nvmf_dim(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u8 trtype,
+ __u8 adrfam, const char *reg_addr, union nvmf_tsas *tsas,
+ __u32 *result)
+{
+ nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL;
+ _cleanup_free_ struct nvmf_dim_data *dim = NULL;
+ struct nvmf_ext_die *die;
+ __u32 tdl;
+ __u32 tel;
+ int ret;
+
+ struct nvme_dim_args args = {
+ .args_size = sizeof(args),
+ .fd = nvme_ctrl_get_fd(c),
+ .result = result,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .tas = tas
+ };
+
+ if (!c->s) {
+ nvme_msg(r, LOG_ERR,
+ "%s: failed to perform DIM. subsystem undefined.\n",
+ c->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!c->s->h) {
+ nvme_msg(r, LOG_ERR,
+ "%s: failed to perform DIM. host undefined.\n",
+ c->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!c->s->h->hostid) {
+ nvme_msg(r, LOG_ERR,
+ "%s: failed to perform DIM. hostid undefined.\n",
+ c->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!c->s->h->hostnqn) {
+ nvme_msg(r, LOG_ERR,
+ "%s: failed to perform DIM. hostnqn undefined.\n",
+ c->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (strcmp(c->transport, "tcp")) {
+ nvme_msg(r, LOG_ERR,
+ "%s: DIM only supported for TCP connections.\n",
+ c->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Register one Discovery Information Entry (DIE) of size TEL */
+ tel = nvmf_get_tel(c->s->h->hostsymname);
+ tdl = sizeof(struct nvmf_dim_data) + tel;
+
+ dim = (struct nvmf_dim_data *)calloc(1, tdl);
+ if (!dim) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ dim->tdl = cpu_to_le32(tdl);
+ dim->nument = cpu_to_le64(1); /* only one DIE to register */
+ dim->entfmt = cpu_to_le16(NVMF_DIM_ENTFMT_EXTENDED);
+ dim->etype = cpu_to_le16(NVMF_DIM_ETYPE_HOST);
+ dim->ektype = cpu_to_le16(0x5F); /* must be 0x5F per specs */
+
+ memcpy(dim->eid, c->s->h->hostnqn,
+ MIN(sizeof(dim->eid), strlen(c->s->h->hostnqn)));
+
+ ret = get_entity_name(dim->ename, sizeof(dim->ename));
+ if (ret <= 0)
+ nvme_msg(r, LOG_INFO, "%s: Failed to retrieve ENAME. %s.\n",
+ c->name, strerror(errno));
+
+ ret = get_entity_version(dim->ever, sizeof(dim->ever));
+ if (ret <= 0)
+ nvme_msg(r, LOG_INFO, "%s: Failed to retrieve EVER.\n", c->name);
+
+ die = &dim->die->extended;
+ nvmf_fill_die(die, c->s->h, tel, trtype, adrfam, reg_addr, tsas);
+
+ args.data_len = tdl;
+ args.data = dim;
+ return nvme_dim_send(&args);
+}
+
+/**
+ * nvme_get_adrfam() - Get address family for the address we're registering
+ * with the DC.
+ *
+ * We retrieve this info from the socket itself. If we can't get the source
+ * address from the socket, then we'll infer the address family from the
+ * address of the DC since the DC address has the same address family.
+ *
+ * @ctrl: Host NVMe controller instance maintaining the admin queue used to
+ * submit the DIM command to the DC.
+ *
+ * Return: The address family of the source address associated with the
+ * socket connected to the DC.
+ */
+static __u8 nvme_get_adrfam(nvme_ctrl_t c)
+{
+ struct sockaddr_storage addr;
+ __u8 adrfam = NVMF_ADDR_FAMILY_IP4;
+ nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL;
+
+ if (!inet_pton_with_scope(r, AF_UNSPEC, c->traddr, c->trsvcid, &addr)) {
+ if (addr.ss_family == AF_INET6)
+ adrfam = NVMF_ADDR_FAMILY_IP6;
+ }
+
+ return adrfam;
+}
+
+/* These string definitions must match with the kernel */
+static const char *cntrltype_str[] = {
+ [NVME_CTRL_CNTRLTYPE_IO] = "io",
+ [NVME_CTRL_CNTRLTYPE_DISCOVERY] = "discovery",
+ [NVME_CTRL_CNTRLTYPE_ADMIN] = "admin",
+};
+
+static const char *dctype_str[] = {
+ [NVME_CTRL_DCTYPE_NOT_REPORTED] = "none",
+ [NVME_CTRL_DCTYPE_DDC] = "ddc",
+ [NVME_CTRL_DCTYPE_CDC] = "cdc",
+};
+
+/**
+ * nvme_fetch_cntrltype_dctype_from_id - Get cntrltype and dctype from identify command
+ * @c: Controller instance
+ *
+ * On legacy kernels the cntrltype and dctype are not exposed through the
+ * sysfs. We must get them directly from the controller by performing an
+ * identify command.
+ */
+static int nvme_fetch_cntrltype_dctype_from_id(nvme_ctrl_t c)
+{
+ _cleanup_free_ struct nvme_id_ctrl *id = NULL;
+ int ret;
+
+ id = __nvme_alloc(sizeof(*id));
+ if (!id) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = nvme_ctrl_identify(c, id);
+ if (ret)
+ return ret;
+
+ if (!c->cntrltype) {
+ if (id->cntrltype > NVME_CTRL_CNTRLTYPE_ADMIN || !cntrltype_str[id->cntrltype])
+ c->cntrltype = strdup("reserved");
+ else
+ c->cntrltype = strdup(cntrltype_str[id->cntrltype]);
+ }
+
+ if (!c->dctype) {
+ if (id->dctype > NVME_CTRL_DCTYPE_CDC || !dctype_str[id->dctype])
+ c->dctype = strdup("reserved");
+ else
+ c->dctype = strdup(dctype_str[id->dctype]);
+ }
+ return 0;
+}
+
+bool nvmf_is_registration_supported(nvme_ctrl_t c)
+{
+ if (!c->cntrltype || !c->dctype)
+ if (nvme_fetch_cntrltype_dctype_from_id(c))
+ return false;
+
+ return !strcmp(c->dctype, "ddc") || !strcmp(c->dctype, "cdc");
+}
+
+int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result)
+{
+ if (!nvmf_is_registration_supported(c)) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ /* We're registering our source address with the DC. To do
+ * that, we can simply send an empty string. This tells the DC
+ * to retrieve the source address from the socket and use that
+ * as the registration address.
+ */
+ return nvmf_dim(c, tas, NVMF_TRTYPE_TCP, nvme_get_adrfam(c), "", NULL, result);
+}
diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h
new file mode 100644
index 0000000..a2504de
--- /dev/null
+++ b/src/nvme/fabrics.h
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+#ifndef _LIBNVME_FABRICS_H
+#define _LIBNVME_FABRICS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "tree.h"
+
+/**
+ * DOC: fabrics.h
+ *
+ * Fabrics-specific definitions.
+ */
+
+/* default to 600 seconds of reconnect attempts before giving up */
+#define NVMF_DEF_CTRL_LOSS_TMO 600
+
+/**
+ * struct nvme_fabrics_config - Defines all linux nvme fabrics initiator options
+ * @host_traddr: Host transport address
+ * @host_iface: Host interface name
+ * @queue_size: Number of IO queue entries
+ * @nr_io_queues: Number of controller IO queues to establish
+ * @reconnect_delay: Time between two consecutive reconnect attempts.
+ * @ctrl_loss_tmo: Override the default controller reconnect attempt timeout in seconds
+ * @fast_io_fail_tmo: Set the fast I/O fail timeout in seconds.
+ * @keep_alive_tmo: Override the default keep-alive-timeout to this value in seconds
+ * @nr_write_queues: Number of queues to use for exclusively for writing
+ * @nr_poll_queues: Number of queues to reserve for polling completions
+ * @tos: Type of service
+ * @keyring: Keyring to store and lookup keys
+ * @tls_key: TLS PSK for the connection
+ * @duplicate_connect: Allow multiple connections to the same target
+ * @disable_sqflow: Disable controller sq flow control
+ * @hdr_digest: Generate/verify header digest (TCP)
+ * @data_digest: Generate/verify data digest (TCP)
+ * @tls: Start TLS on the connection (TCP)
+ * @concat: Enable secure concatenation (TCP)
+ */
+struct nvme_fabrics_config {
+ char *host_traddr;
+ char *host_iface;
+ int queue_size;
+ int nr_io_queues;
+ int reconnect_delay;
+ int ctrl_loss_tmo;
+ int fast_io_fail_tmo;
+ int keep_alive_tmo;
+ int nr_write_queues;
+ int nr_poll_queues;
+ int tos;
+ int keyring;
+ int tls_key;
+
+ bool duplicate_connect;
+ bool disable_sqflow;
+ bool hdr_digest;
+ bool data_digest;
+ bool tls;
+ bool concat;
+};
+
+/**
+ * nvmf_trtype_str() - Decode TRTYPE field
+ * @trtype: value to be decoded
+ *
+ * Decode the transport type field in the discovery
+ * log page entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_trtype_str(__u8 trtype);
+
+/**
+ * nvmf_adrfam_str() - Decode ADRFAM field
+ * @adrfam: value to be decoded
+ *
+ * Decode the address family field in the discovery
+ * log page entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_adrfam_str(__u8 adrfam);
+
+/**
+ * nvmf_subtype_str() - Decode SUBTYPE field
+ * @subtype: value to be decoded
+ *
+ * Decode the subsystem type field in the discovery
+ * log page entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_subtype_str(__u8 subtype);
+
+/**
+ * nvmf_treq_str() - Decode TREQ field
+ * @treq: value to be decoded
+ *
+ * Decode the transport requirements field in the
+ * discovery log page entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_treq_str(__u8 treq);
+
+/**
+ * nvmf_eflags_str() - Decode EFLAGS field
+ * @eflags: value to be decoded
+ *
+ * Decode the EFLAGS field in the discovery log page
+ * entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_eflags_str(__u16 eflags);
+
+/**
+ * nvmf_sectype_str() - Decode SECTYPE field
+ * @sectype: value to be decoded
+ *
+ * Decode the SECTYPE field in the discovery log page
+ * entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_sectype_str(__u8 sectype);
+
+/**
+ * nvmf_prtype_str() - Decode RDMA Provider type field
+ * @prtype: value to be decoded
+ *
+ * Decode the RDMA Provider type field in the discovery
+ * log page entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_prtype_str(__u8 prtype);
+
+/**
+ * nvmf_qptype_str() - Decode RDMA QP Service type field
+ * @qptype: value to be decoded
+ *
+ * Decode the RDMA QP Service type field in the discovery log page
+ * entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_qptype_str(__u8 qptype);
+
+/**
+ * nvmf_cms_str() - Decode RDMA connection management service field
+ * @cms: value to be decoded
+ *
+ * Decode the RDMA connection management service field in the discovery
+ * log page entry.
+ *
+ * Return: decoded string
+ */
+const char *nvmf_cms_str(__u8 cms);
+
+/**
+ * nvmf_default_config() - Default values for fabrics configuration
+ * @cfg: config values to set
+ *
+ * Initializes @cfg with default values.
+ */
+void nvmf_default_config(struct nvme_fabrics_config *cfg);
+
+/**
+ * nvmf_update_config() - Update fabrics configuration values
+ * @c: Controller to be modified
+ * @cfg: Updated configuration values
+ *
+ * Updates the values from @c with the configuration values from @cfg;
+ * all non-default values from @cfg will overwrite the values in @c.
+ */
+void nvmf_update_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg);
+
+/**
+ * nvmf_add_ctrl() - Connect a controller and update topology
+ * @h: Host to which the controller should be attached
+ * @c: Controller to be connected
+ * @cfg: Default configuration for the controller
+ *
+ * Issues a 'connect' command to the NVMe-oF controller and inserts @c
+ * into the topology using @h as parent.
+ * @c must be initialized and not connected to the topology.
+ *
+ * Return: 0 on success; on failure errno is set and -1 is returned.
+ */
+int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c,
+ const struct nvme_fabrics_config *cfg);
+
+/**
+ * nvmf_get_discovery_log() - Return the discovery log page
+ * @c: Discovery controller to use
+ * @logp: Pointer to the log page to be returned
+ * @max_retries: Number of retries in case of failure
+ *
+ * The memory allocated for the log page and returned in @logp
+ * must be freed by the caller using free().
+ *
+ * Note: Consider using nvmf_get_discovery_wargs() instead.
+ *
+ * Return: 0 on success; on failure -1 is returned and errno is set
+ */
+int nvmf_get_discovery_log(nvme_ctrl_t c, struct nvmf_discovery_log **logp,
+ int max_retries);
+
+/**
+ * struct nvme_get_discovery_args - Arguments for nvmf_get_discovery_wargs()
+ * @c: Discovery controller
+ * @args_size: Length of the structure
+ * @max_retries: Number of retries in case of failure
+ * @result: The command completion result from CQE dword0
+ * @timeout: Timeout in ms (default: NVME_DEFAULT_IOCTL_TIMEOUT)
+ * @lsp: Log specific field (See enum nvmf_log_discovery_lsp)
+ */
+struct nvme_get_discovery_args {
+ nvme_ctrl_t c;
+ int args_size;
+ int max_retries;
+ __u32 *result;
+ __u32 timeout;
+ __u8 lsp;
+};
+
+/**
+ * nvmf_get_discovery_wargs() - Get the discovery log page with args
+ * @args: Argument structure
+ *
+ * This function is similar to nvmf_get_discovery_log(), but
+ * takes an extensible @args parameter. @args provides more
+ * options than nvmf_get_discovery_log().
+ *
+ * This function performs a get discovery log page (DLP) command
+ * and returns the DLP. The memory allocated for the returned
+ * DLP must be freed by the caller using free().
+ *
+ * Return: Pointer to the discovery log page (to be freed). NULL
+ * on failure and errno is set.
+ */
+struct nvmf_discovery_log *nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args);
+
+/**
+ * nvmf_hostnqn_generate() - Generate a machine specific host nqn
+ * Returns: An nvm namespace qualified name string based on the machine
+ * identifier, or NULL if not successful.
+ */
+char *nvmf_hostnqn_generate();
+
+/**
+ * nvmf_hostnqn_from_file() - Reads the host nvm qualified name from the config
+ * default location
+ *
+ * Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme.
+ * $SYSCONFDIR is usually /etc.
+ *
+ * Return: The host nqn, or NULL if unsuccessful. If found, the caller
+ * is responsible to free the string.
+ */
+char *nvmf_hostnqn_from_file();
+
+/**
+ * nvmf_hostid_from_file() - Reads the host identifier from the config default
+ * location
+ *
+ * Retrieve the host idenditifer from the config file located in $SYSCONFDIR/nvme/.
+ * $SYSCONFDIR is usually /etc.
+ *
+ * Return: The host identifier, or NULL if unsuccessful. If found, the caller
+ * is responsible to free the string.
+ */
+char *nvmf_hostid_from_file();
+
+/**
+ * nvmf_connect_disc_entry() - Connect controller based on the discovery log page entry
+ * @h: Host to which the controller should be connected
+ * @e: Discovery log page entry
+ * @defcfg: Default configuration to be used for the new controller
+ * @discover: Set to 'true' if the new controller is a discovery controller
+ *
+ * Return: Pointer to the new controller
+ */
+nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h,
+ struct nvmf_disc_log_entry *e,
+ const struct nvme_fabrics_config *defcfg, bool *discover);
+
+/**
+ * nvmf_is_registration_supported - check whether registration can be performed.
+ * @c: Controller instance
+ *
+ * Only discovery controllers (DC) that comply with TP8010 support
+ * explicit registration with the DIM PDU. These can be identified by
+ * looking at the value of a dctype in the Identify command
+ * response. A value of 1 (DDC) or 2 (CDC) indicates that the DC
+ * supports explicit registration.
+ *
+ * Return: true if controller supports explicit registration. false
+ * otherwise.
+ */
+bool nvmf_is_registration_supported(nvme_ctrl_t c);
+
+/**
+ * nvmf_register_ctrl() - Perform registration task with a DC
+ * @c: Controller instance
+ * @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to
+ * perform a Registration, Deregistration, or Registration-update.
+ * @result: The command-specific result returned by the DC upon command
+ * completion.
+ *
+ * Perform registration task with a Discovery Controller (DC). Three
+ * tasks are supported: register, deregister, and registration update.
+ *
+ * Return: 0 on success; on failure -1 is returned and errno is set
+ */
+int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result);
+
+#endif /* _LIBNVME_FABRICS_H */
diff --git a/src/nvme/filters.c b/src/nvme/filters.c
new file mode 100644
index 0000000..312b8f6
--- /dev/null
+++ b/src/nvme/filters.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <libgen.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "filters.h"
+#include "types.h"
+#include "util.h"
+#include "cleanup.h"
+
+#define PATH_SYSFS_NVME "/sys/class/nvme"
+#define PATH_SYSFS_NVME_SUBSYSTEM "/sys/class/nvme-subsystem"
+#define PATH_SYSFS_BLOCK "/sys/block"
+
+char *nvme_ctrl_sysfs_dir(void)
+{
+ char *basepath = getenv("LIBNVME_SYSFS_PATH");
+ char *str;
+
+ if (!basepath)
+ return strdup(PATH_SYSFS_NVME);
+
+ if (!asprintf(&str, "%s" PATH_SYSFS_NVME, basepath))
+ return NULL;
+
+ return str;
+}
+
+char *nvme_ns_sysfs_dir(void)
+{
+ char *basepath = getenv("LIBNVME_SYSFS_PATH");
+ char *str;
+
+ if (!basepath)
+ return strdup(PATH_SYSFS_BLOCK);
+
+ if (!asprintf(&str, "%s" PATH_SYSFS_BLOCK, basepath))
+ return NULL;
+
+ return str;
+}
+
+char *nvme_subsys_sysfs_dir(void)
+{
+ char *basepath = getenv("LIBNVME_SYSFS_PATH");
+ char *str;
+
+ if (!basepath)
+ return strdup(PATH_SYSFS_NVME_SUBSYSTEM);
+
+ if (!asprintf(&str, "%s" PATH_SYSFS_NVME_SUBSYSTEM, basepath))
+ return NULL;
+
+ return str;
+}
+
+int nvme_namespace_filter(const struct dirent *d)
+{
+ int i, n;
+
+ if (d->d_name[0] == '.')
+ return 0;
+
+ if (strstr(d->d_name, "nvme"))
+ if (sscanf(d->d_name, "nvme%dn%d", &i, &n) == 2)
+ return 1;
+
+ return 0;
+}
+
+int nvme_paths_filter(const struct dirent *d)
+{
+ int i, c, n;
+
+ if (d->d_name[0] == '.')
+ return 0;
+
+ if (strstr(d->d_name, "nvme"))
+ if (sscanf(d->d_name, "nvme%dc%dn%d", &i, &c, &n) == 3)
+ return 1;
+
+ return 0;
+}
+
+int nvme_ctrls_filter(const struct dirent *d)
+{
+ int i, c, n;
+
+ if (d->d_name[0] == '.')
+ return 0;
+
+ if (strstr(d->d_name, "nvme")) {
+ if (sscanf(d->d_name, "nvme%dc%dn%d", &i, &c, &n) == 3)
+ return 0;
+ if (sscanf(d->d_name, "nvme%dn%d", &i, &n) == 2)
+ return 0;
+ if (sscanf(d->d_name, "nvme%d", &i) == 1)
+ return 1;
+ }
+
+ return 0;
+}
+
+int nvme_subsys_filter(const struct dirent *d)
+{
+ int i;
+
+ if (d->d_name[0] == '.')
+ return 0;
+
+ if (strstr(d->d_name, "nvme-subsys"))
+ if (sscanf(d->d_name, "nvme-subsys%d", &i) == 1)
+ return 1;
+
+ return 0;
+}
+
+int nvme_scan_subsystems(struct dirent ***subsys)
+{
+ _cleanup_free_ char *dir = nvme_subsys_sysfs_dir();
+
+ return scandir(dir, subsys, nvme_subsys_filter, alphasort);
+}
+
+int nvme_scan_subsystem_namespaces(nvme_subsystem_t s, struct dirent ***ns)
+{
+ return scandir(nvme_subsystem_get_sysfs_dir(s), ns,
+ nvme_namespace_filter, alphasort);
+}
+
+int nvme_scan_ctrls(struct dirent ***ctrls)
+{
+ _cleanup_free_ char *dir = nvme_ctrl_sysfs_dir();
+
+ return scandir(dir, ctrls, nvme_ctrls_filter, alphasort);
+}
+
+int nvme_scan_ctrl_namespace_paths(nvme_ctrl_t c, struct dirent ***paths)
+{
+ return scandir(nvme_ctrl_get_sysfs_dir(c), paths,
+ nvme_paths_filter, alphasort);
+}
+
+int nvme_scan_ctrl_namespaces(nvme_ctrl_t c, struct dirent ***ns)
+{
+ return scandir(nvme_ctrl_get_sysfs_dir(c), ns,
+ nvme_namespace_filter, alphasort);
+}
diff --git a/src/nvme/filters.h b/src/nvme/filters.h
new file mode 100644
index 0000000..49bbeea
--- /dev/null
+++ b/src/nvme/filters.h
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+#ifndef _LIBNVME_FILTERS_H
+#define _LIBNVME_FILTERS_H
+
+#include <dirent.h>
+#include "tree.h"
+
+/**
+ * DOC: filters.h
+ *
+ * libnvme directory filter
+ */
+
+/**
+ * nvme_namespace_filter() - Filter for namespaces
+ * @d: dirent to check
+ *
+ * Return: 1 if @d matches, 0 otherwise
+ */
+int nvme_namespace_filter(const struct dirent *d);
+
+/**
+ * nvme_paths_filter() - Filter for paths
+ * @d: dirent to check
+ *
+ * Return: 1 if @d matches, 0 otherwise
+ */
+int nvme_paths_filter(const struct dirent *d);
+
+/**
+ * nvme_ctrls_filter() - Filter for controllers
+ * @d: dirent to check
+ *
+ * Return: 1 if @d matches, 0 otherwise
+ */
+int nvme_ctrls_filter(const struct dirent *d);
+
+/**
+ * nvme_subsys_filter() - Filter for subsystems
+ * @d: dirent to check
+ *
+ * Return: 1 if @d matches, 0 otherwise
+ */
+int nvme_subsys_filter(const struct dirent *d);
+
+/**
+ * nvme_scan_subsystems() - Scan for subsystems
+ * @subsys: Pointer to array of dirents
+ *
+ * Return: number of entries in @subsys
+ */
+int nvme_scan_subsystems(struct dirent ***subsys);
+
+/**
+ * nvme_scan_subsystem_namespaces() - Scan for namespaces in a subsystem
+ * @s: Subsystem to scan
+ * @ns: Pointer to array of dirents
+ *
+ * Return: number of entries in @ns
+ */
+int nvme_scan_subsystem_namespaces(nvme_subsystem_t s, struct dirent ***ns);
+
+/**
+ * nvme_scan_ctrls() - Scan for controllers
+ * @ctrls: Pointer to array of dirents
+ *
+ * Return: number of entries in @ctrls
+ */
+int nvme_scan_ctrls(struct dirent ***ctrls);
+
+/**
+ * nvme_scan_ctrl_namespace_paths() - Scan for namespace paths in a controller
+ * @c: Controller to scan
+ * @paths: Pointer to array of dirents
+ *
+ * Return: number of entries in @paths
+ */
+int nvme_scan_ctrl_namespace_paths(nvme_ctrl_t c, struct dirent ***paths);
+
+/**
+ * nvme_scan_ctrl_namespaces() - Scan for namespaces in a controller
+ * @c: Controller to scan
+ * @ns: Pointer to array of dirents
+ *
+ * Return: number of entries in @ns
+ */
+int nvme_scan_ctrl_namespaces(nvme_ctrl_t c, struct dirent ***ns);
+
+#endif /* _LIBNVME_FILTERS_H */
diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c
new file mode 100644
index 0000000..9090b7e
--- /dev/null
+++ b/src/nvme/ioctl.c
@@ -0,0 +1,2258 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ccan/build_assert/build_assert.h>
+#include <ccan/endian/endian.h>
+
+#include "ioctl.h"
+#include "util.h"
+
+static bool nvme_debug;
+
+static int nvme_verify_chr(int fd)
+{
+ static struct stat nvme_stat;
+ int err = fstat(fd, &nvme_stat);
+
+ if (err < 0)
+ return errno;
+
+ if (!S_ISCHR(nvme_stat.st_mode)) {
+ errno = ENOTBLK;
+ return -1;
+ }
+ return 0;
+}
+
+int nvme_subsystem_reset(int fd)
+{
+ int ret;
+
+ ret = nvme_verify_chr(fd);
+ if (ret)
+ return ret;
+ return ioctl(fd, NVME_IOCTL_SUBSYS_RESET);
+}
+
+int nvme_ctrl_reset(int fd)
+{
+ int ret;
+
+ ret = nvme_verify_chr(fd);
+ if (ret)
+ return ret;
+ return ioctl(fd, NVME_IOCTL_RESET);
+}
+
+int nvme_ns_rescan(int fd)
+{
+ int ret;
+
+ ret = nvme_verify_chr(fd);
+ if (ret)
+ return ret;
+ return ioctl(fd, NVME_IOCTL_RESCAN);
+}
+
+int nvme_get_nsid(int fd, __u32 *nsid)
+{
+ errno = 0;
+ *nsid = ioctl(fd, NVME_IOCTL_ID);
+ return -1 * (errno != 0);
+}
+
+static int nvme_submit_passthru64(int fd, unsigned long ioctl_cmd,
+ struct nvme_passthru_cmd64 *cmd,
+ __u64 *result)
+{
+ int err = ioctl(fd, ioctl_cmd, cmd);
+
+ if (err >= 0 && result)
+ *result = cmd->result;
+ return err;
+}
+
+static void nvme_show_command(struct nvme_passthru_cmd *cmd, int err, struct timeval start,
+ struct timeval end)
+{
+ printf("opcode : %02x\n", cmd->opcode);
+ printf("flags : %02x\n", cmd->flags);
+ printf("rsvd1 : %04x\n", cmd->rsvd1);
+ printf("nsid : %08x\n", cmd->nsid);
+ printf("cdw2 : %08x\n", cmd->cdw2);
+ printf("cdw3 : %08x\n", cmd->cdw3);
+ printf("data_len : %08x\n", cmd->data_len);
+ printf("metadata_len : %08x\n", cmd->metadata_len);
+ printf("addr : %"PRIx64"\n", (uint64_t)(uintptr_t)cmd->addr);
+ printf("metadata : %"PRIx64"\n", (uint64_t)(uintptr_t)cmd->metadata);
+ printf("cdw10 : %08x\n", cmd->cdw10);
+ printf("cdw11 : %08x\n", cmd->cdw11);
+ printf("cdw12 : %08x\n", cmd->cdw12);
+ printf("cdw13 : %08x\n", cmd->cdw13);
+ printf("cdw14 : %08x\n", cmd->cdw14);
+ printf("cdw15 : %08x\n", cmd->cdw15);
+ printf("timeout_ms : %08x\n", cmd->timeout_ms);
+ printf("result : %08x\n", cmd->result);
+ printf("err : %d\n", err);
+ printf("latency : %lu us\n",
+ (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec));
+}
+
+void nvme_set_debug(bool debug)
+{
+ nvme_debug = debug;
+}
+
+bool nvme_get_debug(void)
+{
+ return nvme_debug;
+}
+
+static int nvme_submit_passthru(int fd, unsigned long ioctl_cmd,
+ struct nvme_passthru_cmd *cmd, __u32 *result)
+{
+ struct timeval start;
+ struct timeval end;
+ int err;
+
+ if (nvme_get_debug())
+ gettimeofday(&start, NULL);
+
+ err = ioctl(fd, ioctl_cmd, cmd);
+
+ if (nvme_get_debug()) {
+ gettimeofday(&end, NULL);
+ nvme_show_command(cmd, err, start, end);
+ }
+
+ if (err >= 0 && result)
+ *result = cmd->result;
+
+ return err;
+}
+
+static int nvme_passthru64(int fd, unsigned long ioctl_cmd, __u8 opcode,
+ __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2,
+ __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12,
+ __u32 cdw13, __u32 cdw14, __u32 cdw15,
+ __u32 data_len, void *data, __u32 metadata_len,
+ void *metadata, __u32 timeout_ms, __u64 *result)
+{
+ struct nvme_passthru_cmd64 cmd = {
+ .opcode = opcode,
+ .flags = flags,
+ .rsvd1 = rsvd,
+ .nsid = nsid,
+ .cdw2 = cdw2,
+ .cdw3 = cdw3,
+ .metadata = (__u64)(uintptr_t)metadata,
+ .addr = (__u64)(uintptr_t)data,
+ .metadata_len = metadata_len,
+ .data_len = data_len,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = cdw12,
+ .cdw13 = cdw13,
+ .cdw14 = cdw14,
+ .cdw15 = cdw15,
+ .timeout_ms = timeout_ms,
+ };
+
+ return nvme_submit_passthru64(fd, ioctl_cmd, &cmd, result);
+}
+
+static int nvme_passthru(int fd, unsigned long ioctl_cmd, __u8 opcode,
+ __u8 flags, __u16 rsvd, __u32 nsid, __u32 cdw2,
+ __u32 cdw3, __u32 cdw10, __u32 cdw11, __u32 cdw12,
+ __u32 cdw13, __u32 cdw14, __u32 cdw15, __u32 data_len,
+ void *data, __u32 metadata_len, void *metadata,
+ __u32 timeout_ms, __u32 *result)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = opcode,
+ .flags = flags,
+ .rsvd1 = rsvd,
+ .nsid = nsid,
+ .cdw2 = cdw2,
+ .cdw3 = cdw3,
+ .metadata = (__u64)(uintptr_t)metadata,
+ .addr = (__u64)(uintptr_t)data,
+ .metadata_len = metadata_len,
+ .data_len = data_len,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = cdw12,
+ .cdw13 = cdw13,
+ .cdw14 = cdw14,
+ .cdw15 = cdw15,
+ .timeout_ms = timeout_ms,
+ };
+
+ return nvme_submit_passthru(fd, ioctl_cmd, &cmd, result);
+}
+
+int nvme_submit_admin_passthru64(int fd, struct nvme_passthru_cmd64 *cmd,
+ __u64 *result)
+{
+ return nvme_submit_passthru64(fd, NVME_IOCTL_ADMIN64_CMD, cmd, result);
+}
+
+int nvme_admin_passthru64(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10,
+ __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14,
+ __u32 cdw15, __u32 data_len, void *data,
+ __u32 metadata_len, void *metadata, __u32 timeout_ms,
+ __u64 *result)
+{
+ return nvme_passthru64(fd, NVME_IOCTL_ADMIN64_CMD, opcode, flags, rsvd,
+ nsid, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13,
+ cdw14, cdw15, data_len, data, metadata_len,
+ metadata, timeout_ms, result);
+}
+
+int nvme_submit_admin_passthru(int fd, struct nvme_passthru_cmd *cmd, __u32 *result)
+{
+ return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, cmd, result);
+}
+
+int nvme_admin_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10,
+ __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14,
+ __u32 cdw15, __u32 data_len, void *data,
+ __u32 metadata_len, void *metadata, __u32 timeout_ms,
+ __u32 *result)
+{
+ return nvme_passthru(fd, NVME_IOCTL_ADMIN_CMD, opcode, flags, rsvd,
+ nsid, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13,
+ cdw14, cdw15, data_len, data, metadata_len,
+ metadata, timeout_ms, result);
+}
+
+enum nvme_cmd_dword_fields {
+ NVME_DEVICE_SELF_TEST_CDW10_STC_SHIFT = 0,
+ NVME_DEVICE_SELF_TEST_CDW10_STC_MASK = 0xf,
+ NVME_DIRECTIVE_CDW11_DOPER_SHIFT = 0,
+ NVME_DIRECTIVE_CDW11_DTYPE_SHIFT = 8,
+ NVME_DIRECTIVE_CDW11_DPSEC_SHIFT = 16,
+ NVME_DIRECTIVE_CDW11_DOPER_MASK = 0xff,
+ NVME_DIRECTIVE_CDW11_DTYPE_MASK = 0xff,
+ NVME_DIRECTIVE_CDW11_DPSEC_MASK = 0xffff,
+ NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR_SHIFT = 0,
+ NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE_SHIFT = 1,
+ NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR_MASK = 0x1,
+ NVME_DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE_MASK = 0x1,
+ NVME_FW_COMMIT_CDW10_FS_SHIFT = 0,
+ NVME_FW_COMMIT_CDW10_CA_SHIFT = 3,
+ NVME_FW_COMMIT_CDW10_BPID_SHIFT = 31,
+ NVME_FW_COMMIT_CDW10_FS_MASK = 0x7,
+ NVME_FW_COMMIT_CDW10_CA_MASK = 0x7,
+ NVME_FW_COMMIT_CDW10_BPID_MASK = 0x1,
+ NVME_GET_FEATURES_CDW10_SEL_SHIFT = 8,
+ NVME_GET_FEATURES_CDW10_SEL_MASK = 0x7,
+ NVME_SET_FEATURES_CDW10_SAVE_SHIFT = 31,
+ NVME_SET_FEATURES_CDW10_SAVE_MASK = 0x1,
+ NVME_FEATURES_CDW10_FID_SHIFT = 0,
+ NVME_FEATURES_CDW14_UUID_SHIFT = 0,
+ NVME_FEATURES_CDW10_FID_MASK = 0xff,
+ NVME_FEATURES_CDW14_UUID_MASK = 0x7f,
+ NVME_LOG_CDW10_LID_SHIFT = 0,
+ NVME_LOG_CDW10_LSP_SHIFT = 8,
+ NVME_LOG_CDW10_RAE_SHIFT = 15,
+ NVME_LOG_CDW10_NUMDL_SHIFT = 16,
+ NVME_LOG_CDW11_NUMDU_SHIFT = 0,
+ NVME_LOG_CDW11_LSI_SHIFT = 16,
+ NVME_LOG_CDW14_UUID_SHIFT = 0,
+ NVME_LOG_CDW14_CSI_SHIFT = 24,
+ NVME_LOG_CDW14_OT_SHIFT = 23,
+ NVME_LOG_CDW10_LID_MASK = 0xff,
+ NVME_LOG_CDW10_LSP_MASK = 0x7f,
+ NVME_LOG_CDW10_RAE_MASK = 0x1,
+ NVME_LOG_CDW10_NUMDL_MASK = 0xffff,
+ NVME_LOG_CDW11_NUMDU_MASK = 0xffff,
+ NVME_LOG_CDW11_LSI_MASK = 0xffff,
+ NVME_LOG_CDW14_UUID_MASK = 0x7f,
+ NVME_LOG_CDW14_CSI_MASK = 0xff,
+ NVME_LOG_CDW14_OT_MASK = 0x1,
+ NVME_IDENTIFY_CDW10_CNS_SHIFT = 0,
+ NVME_IDENTIFY_CDW10_CNTID_SHIFT = 16,
+ NVME_IDENTIFY_CDW11_CNSSPECID_SHIFT = 0,
+ NVME_IDENTIFY_CDW14_UUID_SHIFT = 0,
+ NVME_IDENTIFY_CDW11_CSI_SHIFT = 24,
+ NVME_IDENTIFY_CDW10_CNS_MASK = 0xff,
+ NVME_IDENTIFY_CDW10_CNTID_MASK = 0xffff,
+ NVME_IDENTIFY_CDW11_CNSSPECID_MASK = 0xffff,
+ NVME_IDENTIFY_CDW14_UUID_MASK = 0x7f,
+ NVME_IDENTIFY_CDW11_CSI_MASK = 0xff,
+ NVME_NAMESPACE_ATTACH_CDW10_SEL_SHIFT = 0,
+ NVME_NAMESPACE_ATTACH_CDW10_SEL_MASK = 0xf,
+ NVME_NAMESPACE_MGMT_CDW10_SEL_SHIFT = 0,
+ NVME_NAMESPACE_MGMT_CDW10_SEL_MASK = 0xf,
+ NVME_NAMESPACE_MGMT_CDW11_CSI_SHIFT = 24,
+ NVME_NAMESPACE_MGMT_CDW11_CSI_MASK = 0xff,
+ NVME_VIRT_MGMT_CDW10_ACT_SHIFT = 0,
+ NVME_VIRT_MGMT_CDW10_RT_SHIFT = 8,
+ NVME_VIRT_MGMT_CDW10_CNTLID_SHIFT = 16,
+ NVME_VIRT_MGMT_CDW11_NR_SHIFT = 0,
+ NVME_VIRT_MGMT_CDW10_ACT_MASK = 0xf,
+ NVME_VIRT_MGMT_CDW10_RT_MASK = 0x7,
+ NVME_VIRT_MGMT_CDW10_CNTLID_MASK = 0xffff,
+ NVME_VIRT_MGMT_CDW11_NR_MASK = 0xffff,
+ NVME_FORMAT_CDW10_LBAF_SHIFT = 0,
+ NVME_FORMAT_CDW10_MSET_SHIFT = 4,
+ NVME_FORMAT_CDW10_PI_SHIFT = 5,
+ NVME_FORMAT_CDW10_PIL_SHIFT = 8,
+ NVME_FORMAT_CDW10_SES_SHIFT = 9,
+ NVME_FORMAT_CDW10_LBAFU_SHIFT = 12,
+ NVME_FORMAT_CDW10_LBAF_MASK = 0xf,
+ NVME_FORMAT_CDW10_MSET_MASK = 0x1,
+ NVME_FORMAT_CDW10_PI_MASK = 0x7,
+ NVME_FORMAT_CDW10_PIL_MASK = 0x1,
+ NVME_FORMAT_CDW10_SES_MASK = 0x7,
+ NVME_FORMAT_CDW10_LBAFU_MASK = 0x3,
+ NVME_SANITIZE_CDW10_SANACT_SHIFT = 0,
+ NVME_SANITIZE_CDW10_AUSE_SHIFT = 3,
+ NVME_SANITIZE_CDW10_OWPASS_SHIFT = 4,
+ NVME_SANITIZE_CDW10_OIPBP_SHIFT = 8,
+ NVME_SANITIZE_CDW10_NODAS_SHIFT = 9,
+ NVME_SANITIZE_CDW10_SANACT_MASK = 0x7,
+ NVME_SANITIZE_CDW10_AUSE_MASK = 0x1,
+ NVME_SANITIZE_CDW10_OWPASS_MASK = 0xf,
+ NVME_SANITIZE_CDW10_OIPBP_MASK = 0x1,
+ NVME_SANITIZE_CDW10_NODAS_MASK = 0x1,
+ NVME_SECURITY_NSSF_SHIFT = 0,
+ NVME_SECURITY_SPSP0_SHIFT = 8,
+ NVME_SECURITY_SPSP1_SHIFT = 16,
+ NVME_SECURITY_SECP_SHIFT = 24,
+ NVME_SECURITY_NSSF_MASK = 0xff,
+ NVME_SECURITY_SPSP0_MASK = 0xff,
+ NVME_SECURITY_SPSP1_MASK = 0xff,
+ NVME_SECURITY_SECP_MASK = 0xffff,
+ NVME_GET_LBA_STATUS_CDW13_RL_SHIFT = 0,
+ NVME_GET_LBA_STATUS_CDW13_ATYPE_SHIFT = 24,
+ NVME_GET_LBA_STATUS_CDW13_RL_MASK = 0xffff,
+ NVME_GET_LBA_STATUS_CDW13_ATYPE_MASK = 0xff,
+ NVME_ZNS_MGMT_SEND_ZSASO_SHIFT = 9,
+ NVME_ZNS_MGMT_SEND_ZSASO_MASK = 0x1,
+ NVME_ZNS_MGMT_SEND_SEL_SHIFT = 8,
+ NVME_ZNS_MGMT_SEND_SEL_MASK = 0x1,
+ NVME_ZNS_MGMT_SEND_ZSA_SHIFT = 0,
+ NVME_ZNS_MGMT_SEND_ZSA_MASK = 0xff,
+ NVME_ZNS_MGMT_RECV_ZRA_SHIFT = 0,
+ NVME_ZNS_MGMT_RECV_ZRA_MASK = 0xff,
+ NVME_ZNS_MGMT_RECV_ZRASF_SHIFT = 8,
+ NVME_ZNS_MGMT_RECV_ZRASF_MASK = 0xff,
+ NVME_ZNS_MGMT_RECV_ZRAS_FEAT_SHIFT = 16,
+ NVME_ZNS_MGMT_RECV_ZRAS_FEAT_MASK = 0x1,
+ NVME_DIM_TAS_SHIFT = 0,
+ NVME_DIM_TAS_MASK = 0xF,
+};
+
+enum features {
+ NVME_FEATURES_ARBITRATION_BURST_SHIFT = 0,
+ NVME_FEATURES_ARBITRATION_LPW_SHIFT = 8,
+ NVME_FEATURES_ARBITRATION_MPW_SHIFT = 16,
+ NVME_FEATURES_ARBITRATION_HPW_SHIFT = 24,
+ NVME_FEATURES_ARBITRATION_BURST_MASK = 0x7,
+ NVME_FEATURES_ARBITRATION_LPW_MASK = 0xff,
+ NVME_FEATURES_ARBITRATION_MPW_MASK = 0xff,
+ NVME_FEATURES_ARBITRATION_HPW_MASK = 0xff,
+ NVME_FEATURES_PWRMGMT_PS_SHIFT = 0,
+ NVME_FEATURES_PWRMGMT_WH_SHIFT = 5,
+ NVME_FEATURES_PWRMGMT_PS_MASK = 0x1f,
+ NVME_FEATURES_PWRMGMT_WH_MASK = 0x7,
+ NVME_FEATURES_TMPTH_SHIFT = 0,
+ NVME_FEATURES_TMPSEL_SHIFT = 16,
+ NVME_FEATURES_THSEL_SHIFT = 20,
+ NVME_FEATURES_TMPTH_MASK = 0xff,
+ NVME_FEATURES_TMPSEL_MASK = 0xf,
+ NVME_FEATURES_THSEL_MASK = 0x3,
+ NVME_FEATURES_ERROR_RECOVERY_TLER_SHIFT = 0,
+ NVME_FEATURES_ERROR_RECOVERY_DULBE_SHIFT = 16,
+ NVME_FEATURES_ERROR_RECOVERY_TLER_MASK = 0xff,
+ NVME_FEATURES_ERROR_RECOVERY_DULBE_MASK = 0x1,
+ NVME_FEATURES_VWC_WCE_SHIFT = 0,
+ NVME_FEATURES_VWC_WCE_MASK = 0x1,
+ NVME_FEATURES_IRQC_THR_SHIFT = 0,
+ NVME_FEATURES_IRQC_TIME_SHIFT = 8,
+ NVME_FEATURES_IRQC_THR_MASK = 0xff,
+ NVME_FEATURES_IRQC_TIME_MASK = 0xff,
+ NVME_FEATURES_IVC_IV_SHIFT = 0,
+ NVME_FEATURES_IVC_CD_SHIFT = 16,
+ NVME_FEATURES_IVC_IV_MASK = 0xffff,
+ NVME_FEATURES_IVC_CD_MASK = 0x1,
+ NVME_FEATURES_WAN_DN_SHIFT = 0,
+ NVME_FEATURES_WAN_DN_MASK = 0x1,
+ NVME_FEATURES_APST_APSTE_SHIFT = 0,
+ NVME_FEATURES_APST_APSTE_MASK = 0x1,
+ NVME_FEATURES_HCTM_TMT2_SHIFT = 0,
+ NVME_FEATURES_HCTM_TMT1_SHIFT = 16,
+ NVME_FEATURES_HCTM_TMT2_MASK = 0xffff,
+ NVME_FEATURES_HCTM_TMT1_MASK = 0xffff,
+ NVME_FEATURES_NOPS_NOPPME_SHIFT = 0,
+ NVME_FEATURES_NOPS_NOPPME_MASK = 0x1,
+ NVME_FEATURES_PLM_PLE_SHIFT = 0,
+ NVME_FEATURES_PLM_PLE_MASK = 0x1,
+ NVME_FEATURES_PLM_WINDOW_SELECT_SHIFT = 0,
+ NVME_FEATURES_PLM_WINDOW_SELECT_MASK = 0xf,
+ NVME_FEATURES_LBAS_LSIRI_SHIFT = 0,
+ NVME_FEATURES_LBAS_LSIPI_SHIFT = 16,
+ NVME_FEATURES_LBAS_LSIRI_MASK = 0xffff,
+ NVME_FEATURES_LBAS_LSIPI_MASK = 0xffff,
+ NVME_FEATURES_IOCSP_IOCSCI_SHIFT = 0,
+ NVME_FEATURES_IOCSP_IOCSCI_MASK = 0xff,
+};
+
+int nvme_identify(struct nvme_identify_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->cntid, IDENTIFY_CDW10_CNTID) |
+ NVME_SET(args->cns, IDENTIFY_CDW10_CNS);
+ __u32 cdw11 = NVME_SET(args->cns_specific_id, IDENTIFY_CDW11_CNSSPECID) |
+ NVME_SET(args->csi, IDENTIFY_CDW11_CSI);
+ __u32 cdw14 = NVME_SET(args->uuidx, IDENTIFY_CDW14_UUID);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = args->nsid,
+ .addr = (__u64)(uintptr_t)args->data,
+ .data_len = NVME_IDENTIFY_DATA_SIZE,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw14 = cdw14,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_get_log(struct nvme_get_log_args *args)
+{
+ __u32 numd = (args->len >> 2) - 1;
+ __u16 numdu = numd >> 16, numdl = numd & 0xffff;
+
+ __u32 cdw10 = NVME_SET(args->lid, LOG_CDW10_LID) |
+ NVME_SET(args->lsp, LOG_CDW10_LSP) |
+ NVME_SET(!!args->rae, LOG_CDW10_RAE) |
+ NVME_SET(numdl, LOG_CDW10_NUMDL);
+ __u32 cdw11 = NVME_SET(numdu, LOG_CDW11_NUMDU) |
+ NVME_SET(args->lsi, LOG_CDW11_LSI);
+ __u32 cdw12 = args->lpo & 0xffffffff;
+ __u32 cdw13 = args->lpo >> 32;
+ __u32 cdw14 = NVME_SET(args->uuidx, LOG_CDW14_UUID) |
+ NVME_SET(!!args->ot, LOG_CDW14_OT) |
+ NVME_SET(args->csi, LOG_CDW14_CSI);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_get_log_page,
+ .nsid = args->nsid,
+ .addr = (__u64)(uintptr_t)args->log,
+ .data_len = args->len,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = cdw12,
+ .cdw13 = cdw13,
+ .cdw14 = cdw14,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(struct nvme_get_log_args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args)
+{
+ __u64 offset = 0, xfer, data_len = args->len;
+ __u64 start = args->lpo;
+ bool retain = args->rae;
+ void *ptr = args->log;
+ int ret;
+
+ args->fd = fd;
+
+ /*
+ * 4k is the smallest possible transfer unit, so restricting to 4k
+ * avoids having to check the MDTS value of the controller.
+ */
+ do {
+ xfer = data_len - offset;
+ if (xfer > xfer_len)
+ xfer = xfer_len;
+
+ /*
+ * Always retain regardless of the RAE parameter until the very
+ * last portion of this log page so the data remains latched
+ * during the fetch sequence.
+ */
+ args->lpo = start + offset;
+ args->len = xfer;
+ args->log = ptr;
+ args->rae = offset + xfer < data_len || retain;
+ ret = nvme_get_log(args);
+ if (ret)
+ return ret;
+
+ offset += xfer;
+ ptr += xfer;
+ } while (offset < data_len);
+
+ return 0;
+}
+
+int nvme_set_features(struct nvme_set_features_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->fid, FEATURES_CDW10_FID) |
+ NVME_SET(!!args->save, SET_FEATURES_CDW10_SAVE);
+ __u32 cdw14 = NVME_SET(args->uuidx, FEATURES_CDW14_UUID);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = args->nsid,
+ .addr = (__u64)(uintptr_t)args->data,
+ .data_len = args->data_len,
+ .cdw10 = cdw10,
+ .cdw11 = args->cdw11,
+ .cdw12 = args->cdw12,
+ .cdw13 = args->cdw13,
+ .cdw14 = cdw14,
+ .cdw15 = args->cdw15,
+ .timeout_ms = args->timeout,
+ };
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+static int __nvme_set_features(int fd, __u8 fid, __u32 cdw11, bool save,
+ __u32 *result)
+{
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = fid,
+ .nsid = NVME_NSID_NONE,
+ .cdw11 = cdw11,
+ .cdw12 = 0,
+ .save = save,
+ .uuidx = NVME_UUID_NONE,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+ return nvme_set_features(&args);
+}
+
+int nvme_set_features_arbitration(int fd, __u8 ab, __u8 lpw, __u8 mpw,
+ __u8 hpw, bool save, __u32 *result)
+{
+ __u32 value = NVME_SET(ab, FEAT_ARBITRATION_BURST) |
+ NVME_SET(lpw, FEAT_ARBITRATION_LPW) |
+ NVME_SET(mpw, FEAT_ARBITRATION_MPW) |
+ NVME_SET(hpw, FEAT_ARBITRATION_HPW);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_ARBITRATION, value, save,
+ result);
+}
+
+int nvme_set_features_power_mgmt(int fd, __u8 ps, __u8 wh, bool save,
+ __u32 *result)
+{
+ __u32 value = NVME_SET(ps, FEAT_PWRMGMT_PS) |
+ NVME_SET(wh, FEAT_PWRMGMT_WH);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_POWER_MGMT, value, save,
+ result);
+}
+
+int nvme_set_features_lba_range(int fd, __u32 nsid, __u8 nr_ranges, bool save,
+ struct nvme_lba_range_type *data, __u32 *result)
+{
+ return nvme_set_features_data(
+ fd, NVME_FEAT_FID_LBA_RANGE, nsid, nr_ranges - 1, save,
+ sizeof(*data), data, result);
+}
+
+int nvme_set_features_temp_thresh(int fd, __u16 tmpth, __u8 tmpsel,
+ enum nvme_feat_tmpthresh_thsel thsel,
+ bool save, __u32 *result)
+{
+ __u32 value = NVME_SET(tmpth, FEAT_TT_TMPTH) |
+ NVME_SET(tmpsel, FEAT_TT_TMPSEL) |
+ NVME_SET(thsel, FEAT_TT_THSEL);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_TEMP_THRESH, value, save,
+ result);
+}
+
+int nvme_set_features_err_recovery(int fd, __u32 nsid, __u16 tler, bool dulbe,
+ bool save, __u32 *result)
+{
+ __u32 value = NVME_SET(tler, FEAT_ERROR_RECOVERY_TLER) |
+ NVME_SET(!!dulbe, FEAT_ERROR_RECOVERY_DULBE);
+
+ return nvme_set_features_simple(
+ fd, NVME_FEAT_FID_ERR_RECOVERY, nsid, value, save, result);
+}
+
+int nvme_set_features_volatile_wc(int fd, bool wce, bool save, __u32 *result)
+{
+ __u32 value = NVME_SET(!!wce, FEAT_VWC_WCE);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_VOLATILE_WC, value, save,
+ result);
+}
+
+int nvme_set_features_irq_coalesce(int fd, __u8 thr, __u8 time, bool save,
+ __u32 *result)
+{
+ __u32 value = NVME_SET(thr, FEAT_IRQC_THR) |
+ NVME_SET(time, FEAT_IRQC_TIME);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_IRQ_COALESCE, value, save,
+ result);
+}
+
+int nvme_set_features_irq_config(int fd, __u16 iv, bool cd, bool save,
+ __u32 *result)
+{
+ __u32 value = NVME_SET(iv, FEAT_ICFG_IV) |
+ NVME_SET(!!cd, FEAT_ICFG_CD);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_IRQ_CONFIG, value, save,
+ result);
+}
+
+int nvme_set_features_write_atomic(int fd, bool dn, bool save, __u32 *result)
+{
+ __u32 value = NVME_SET(!!dn, FEAT_WA_DN);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_WRITE_ATOMIC, value, save,
+ result);
+}
+
+int nvme_set_features_async_event(int fd, __u32 events,
+ bool save, __u32 *result)
+{
+ return __nvme_set_features(fd, NVME_FEAT_FID_ASYNC_EVENT, events, save,
+ result);
+}
+
+int nvme_set_features_auto_pst(int fd, bool apste, bool save,
+ struct nvme_feat_auto_pst *apst, __u32 *result)
+{
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_AUTO_PST,
+ .nsid = NVME_NSID_NONE,
+ .cdw11 = NVME_SET(!!apste, FEAT_APST_APSTE),
+ .save = save,
+ .uuidx = NVME_UUID_NONE,
+ .data = apst,
+ .data_len = sizeof(*apst),
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_set_features(&args);
+}
+
+int nvme_set_features_timestamp(int fd, bool save, __u64 timestamp)
+{
+ __le64 t = cpu_to_le64(timestamp);
+ struct nvme_timestamp ts = {};
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_TIMESTAMP,
+ .nsid = NVME_NSID_NONE,
+ .cdw11 = 0,
+ .cdw12 = 0,
+ .save = save,
+ .uuidx = NVME_UUID_NONE,
+ .cdw15 = 0,
+ .data_len = sizeof(ts),
+ .data = &ts,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ memcpy(ts.timestamp, &t, sizeof(ts.timestamp));
+ return nvme_set_features(&args);
+}
+
+int nvme_set_features_hctm(int fd, __u16 tmt2, __u16 tmt1,
+ bool save, __u32 *result)
+{
+ __u32 value = NVME_SET(tmt2, FEAT_HCTM_TMT2) |
+ NVME_SET(tmt1, FEAT_HCTM_TMT1);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_HCTM, value, save,
+ result);
+}
+
+int nvme_set_features_nopsc(int fd, bool noppme, bool save, __u32 *result)
+{
+ __u32 value = NVME_SET(noppme, FEAT_NOPS_NOPPME);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_NOPSC, value, save,
+ result);
+}
+
+int nvme_set_features_rrl(int fd, __u8 rrl, __u16 nvmsetid,
+ bool save, __u32 *result)
+{
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_RRL,
+ .nsid = NVME_NSID_NONE,
+ .cdw11 = nvmsetid,
+ .cdw12 = rrl,
+ .save = save,
+ .uuidx = NVME_UUID_NONE,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_set_features(&args);
+}
+
+int nvme_set_features_plm_config(int fd, bool plm, __u16 nvmsetid, bool save,
+ struct nvme_plm_config *data, __u32 *result)
+{
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_PLM_CONFIG,
+ .nsid = NVME_NSID_NONE,
+ .cdw11 = nvmsetid,
+ .cdw12 = !!plm,
+ .save = save,
+ .uuidx = NVME_UUID_NONE,
+ .cdw15 = 0,
+ .data_len = sizeof(*data),
+ .data = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_set_features(&args);
+}
+
+int nvme_set_features_plm_window(int fd, enum nvme_feat_plm_window_select sel,
+ __u16 nvmsetid, bool save, __u32 *result)
+{
+ __u32 cdw12 = NVME_SET(sel, FEAT_PLMW_WS);
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_PLM_WINDOW,
+ .nsid = NVME_NSID_NONE,
+ .cdw11 = nvmsetid,
+ .cdw12 = cdw12,
+ .save = save,
+ .uuidx = NVME_UUID_NONE,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_set_features(&args);
+}
+
+int nvme_set_features_lba_sts_interval(int fd, __u16 lsiri, __u16 lsipi,
+ bool save, __u32 *result)
+{
+ __u32 value = NVME_SET(lsiri, FEAT_LBAS_LSIRI) |
+ NVME_SET(lsipi, FEAT_LBAS_LSIPI);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_LBA_STS_INTERVAL, value,
+ save, result);
+}
+
+int nvme_set_features_host_behavior(int fd, bool save,
+ struct nvme_feat_host_behavior *data)
+{
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_HOST_BEHAVIOR,
+ .nsid = NVME_NSID_NONE,
+ .cdw11 = 0,
+ .cdw12 = 0,
+ .save = false,
+ .uuidx = NVME_UUID_NONE,
+ .cdw15 = 0,
+ .data_len = sizeof(*data),
+ .data = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ return nvme_set_features(&args);
+}
+
+int nvme_set_features_sanitize(int fd, bool nodrm, bool save, __u32 *result)
+{
+ return __nvme_set_features(fd, NVME_FEAT_FID_SANITIZE, !!nodrm, save,
+ result);
+}
+
+int nvme_set_features_endurance_evt_cfg(int fd, __u16 endgid, __u8 egwarn,
+ bool save, __u32 *result)
+{
+ __u32 value = endgid | egwarn << 16;
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_ENDURANCE_EVT_CFG, value,
+ save, result);
+}
+
+int nvme_set_features_sw_progress(int fd, __u8 pbslc, bool save,
+ __u32 *result)
+{
+ return __nvme_set_features(fd, NVME_FEAT_FID_SW_PROGRESS, pbslc, save,
+ result);
+}
+
+int nvme_set_features_host_id(int fd, bool exhid, bool save, __u8 *hostid)
+{
+ __u32 len = exhid ? 16 : 8;
+ __u32 value = !!exhid;
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_HOST_ID,
+ .nsid = NVME_NSID_NONE,
+ .cdw11 = value,
+ .cdw12 = 0,
+ .save = save,
+ .uuidx = NVME_UUID_NONE,
+ .cdw15 = 0,
+ .data_len = len,
+ .data = hostid,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ return nvme_set_features(&args);
+}
+
+int nvme_set_features_resv_mask(int fd, __u32 mask, bool save, __u32 *result)
+{
+ return __nvme_set_features(fd, NVME_FEAT_FID_RESV_MASK, mask, save,
+ result);
+}
+
+int nvme_set_features_resv_mask2(int fd, __u32 nsid, __u32 mask, bool save,
+ __u32 *result)
+{
+ return nvme_set_features_simple(
+ fd, NVME_FEAT_FID_RESV_MASK, nsid, mask, save, result);
+}
+
+int nvme_set_features_resv_persist(int fd, bool ptpl, bool save, __u32 *result)
+{
+ return __nvme_set_features(fd, NVME_FEAT_FID_RESV_PERSIST, !!ptpl, save,
+ result);
+}
+
+int nvme_set_features_resv_persist2(int fd, __u32 nsid, bool ptpl, bool save,
+ __u32 *result)
+{
+ return nvme_set_features_simple(
+ fd, NVME_FEAT_FID_RESV_PERSIST, nsid, !!ptpl, save, result);
+}
+
+int nvme_set_features_write_protect(int fd, enum nvme_feat_nswpcfg_state state,
+ bool save, __u32 *result)
+{
+ return __nvme_set_features(fd, NVME_FEAT_FID_WRITE_PROTECT, state,
+ false, result);
+}
+
+int nvme_set_features_write_protect2(int fd, __u32 nsid,
+ enum nvme_feat_nswpcfg_state state,
+ bool save, __u32 *result)
+{
+ return nvme_set_features_simple(
+ fd, NVME_FEAT_FID_WRITE_PROTECT, nsid, state, false, result);
+}
+
+int nvme_set_features_iocs_profile(int fd, __u16 iocsi, bool save)
+{
+ __u32 value = NVME_SET(iocsi, FEAT_IOCSP_IOCSCI);
+
+ return __nvme_set_features(fd, NVME_FEAT_FID_IOCS_PROFILE, value,
+ save, NULL);
+}
+
+int nvme_get_features(struct nvme_get_features_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->fid, FEATURES_CDW10_FID) |
+ NVME_SET(args->sel, GET_FEATURES_CDW10_SEL);
+ __u32 cdw14 = NVME_SET(args->uuidx, FEATURES_CDW14_UUID);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = args->nsid,
+ .addr = (__u64)(uintptr_t)args->data,
+ .data_len = args->data_len,
+ .cdw10 = cdw10,
+ .cdw11 = args->cdw11,
+ .cdw14 = cdw14,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+static int __nvme_get_features(int fd, enum nvme_features_id fid,
+ enum nvme_get_features_sel sel, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = fid,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = 0,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_arbitration(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_ARBITRATION, sel, result);
+}
+
+int nvme_get_features_power_mgmt(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_POWER_MGMT, sel, result);
+}
+
+int nvme_get_features_lba_range(int fd, enum nvme_get_features_sel sel,
+ struct nvme_lba_range_type *data,
+ __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_LBA_RANGE,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = 0,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = sizeof(*data),
+ .data = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_lba_range2(int fd, enum nvme_get_features_sel sel,
+ __u32 nsid, struct nvme_lba_range_type *data,
+ __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_LBA_RANGE,
+ .nsid = nsid,
+ .sel = sel,
+ .uuidx = NVME_UUID_NONE,
+ .data = data,
+ .data_len = sizeof(*data),
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_temp_thresh(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_TEMP_THRESH, sel, result);
+}
+
+int nvme_get_features_err_recovery(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_ERR_RECOVERY, sel,
+ result);
+}
+
+int nvme_get_features_err_recovery2(int fd, enum nvme_get_features_sel sel,
+ __u32 nsid, __u32 *result)
+{
+
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_ERR_RECOVERY,
+ .nsid = nsid,
+ .sel = sel,
+ .uuidx = NVME_UUID_NONE,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_volatile_wc(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_VOLATILE_WC, sel, result);
+}
+
+int nvme_get_features_num_queues(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_NUM_QUEUES, sel, result);
+}
+
+int nvme_get_features_irq_coalesce(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_IRQ_COALESCE, sel,
+ result);
+}
+
+int nvme_get_features_irq_config(int fd, enum nvme_get_features_sel sel,
+ __u16 iv, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_IRQ_CONFIG,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = iv,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_write_atomic(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_WRITE_ATOMIC, sel,
+ result);
+}
+
+int nvme_get_features_async_event(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_ASYNC_EVENT, sel, result);
+}
+
+int nvme_get_features_auto_pst(int fd, enum nvme_get_features_sel sel,
+ struct nvme_feat_auto_pst *apst, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_AUTO_PST,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = 0,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = sizeof(*apst),
+ .data = apst,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_host_mem_buf(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_HOST_MEM_BUF, sel, result);
+}
+
+int nvme_get_features_host_mem_buf2(int fd, enum nvme_get_features_sel sel,
+ struct nvme_host_mem_buf_attrs *attrs,
+ __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_HOST_MEM_BUF,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .uuidx = NVME_UUID_NONE,
+ .data = attrs,
+ .data_len = sizeof(*attrs),
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_timestamp(int fd, enum nvme_get_features_sel sel,
+ struct nvme_timestamp *ts)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_TIMESTAMP,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = 0,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = sizeof(*ts),
+ .data = ts,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_kato(int fd, enum nvme_get_features_sel sel, __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_KATO, sel, result);
+}
+
+int nvme_get_features_hctm(int fd, enum nvme_get_features_sel sel, __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_HCTM, sel, result);
+}
+
+int nvme_get_features_nopsc(int fd, enum nvme_get_features_sel sel, __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_NOPSC, sel, result);
+}
+
+int nvme_get_features_rrl(int fd, enum nvme_get_features_sel sel, __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_RRL, sel, result);
+}
+
+int nvme_get_features_plm_config(int fd, enum nvme_get_features_sel sel,
+ __u16 nvmsetid, struct nvme_plm_config *data,
+ __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_PLM_CONFIG,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = nvmsetid,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = sizeof(*data),
+ .data = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_plm_window(int fd, enum nvme_get_features_sel sel,
+ __u16 nvmsetid, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_PLM_WINDOW,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = nvmsetid,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_lba_sts_interval(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_LBA_STS_INTERVAL, sel,
+ result);
+}
+
+int nvme_get_features_host_behavior(int fd, enum nvme_get_features_sel sel,
+ struct nvme_feat_host_behavior *data,
+ __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_HOST_BEHAVIOR,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = 0,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = sizeof(*data),
+ .data = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_sanitize(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_SANITIZE, sel, result);
+}
+
+int nvme_get_features_endurance_event_cfg(int fd, enum nvme_get_features_sel sel,
+ __u16 endgid, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_ENDURANCE_EVT_CFG,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = endgid,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_sw_progress(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_SW_PROGRESS, sel, result);
+}
+
+int nvme_get_features_host_id(int fd, enum nvme_get_features_sel sel,
+ bool exhid, __u32 len, __u8 *hostid)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_HOST_ID,
+ .nsid = NVME_NSID_NONE,
+ .sel = sel,
+ .cdw11 = !!exhid,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = len,
+ .data = hostid,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_resv_mask(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_RESV_MASK, sel, result);
+}
+
+int nvme_get_features_resv_mask2(int fd, enum nvme_get_features_sel sel,
+ __u32 nsid, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_RESV_MASK,
+ .nsid = nsid,
+ .sel = sel,
+ .uuidx = NVME_UUID_NONE,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_resv_persist(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_RESV_PERSIST, sel, result);
+}
+
+int nvme_get_features_resv_persist2(int fd, enum nvme_get_features_sel sel,
+ __u32 nsid, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_RESV_PERSIST,
+ .nsid = nsid,
+ .sel = sel,
+ .uuidx = NVME_UUID_NONE,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_write_protect(int fd, __u32 nsid,
+ enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = NVME_FEAT_FID_WRITE_PROTECT,
+ .nsid = nsid,
+ .sel = sel,
+ .cdw11 = 0,
+ .uuidx = NVME_UUID_NONE,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = result,
+ };
+
+ return nvme_get_features(&args);
+}
+
+int nvme_get_features_iocs_profile(int fd, enum nvme_get_features_sel sel,
+ __u32 *result)
+{
+ return __nvme_get_features(fd, NVME_FEAT_FID_IOCS_PROFILE, sel, result);
+}
+
+int nvme_format_nvm(struct nvme_format_nvm_args *args)
+{
+ const size_t size_v1 = sizeof_args(struct nvme_format_nvm_args, lbaf, __u64);
+ const size_t size_v2 = sizeof_args(struct nvme_format_nvm_args, lbafu, __u64);
+ __u32 cdw10;
+
+ if (args->args_size < size_v1 || args->args_size > size_v2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cdw10 = NVME_SET(args->lbaf, FORMAT_CDW10_LBAF) |
+ NVME_SET(args->mset, FORMAT_CDW10_MSET) |
+ NVME_SET(args->pi, FORMAT_CDW10_PI) |
+ NVME_SET(args->pil, FORMAT_CDW10_PIL) |
+ NVME_SET(args->ses, FORMAT_CDW10_SES);
+
+ if (args->args_size == size_v2) {
+ /* set lbafu extension */
+ cdw10 |= NVME_SET(args->lbafu, FORMAT_CDW10_LBAFU);
+ }
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_format_nvm,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .timeout_ms = args->timeout,
+ };
+
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_ns_mgmt(struct nvme_ns_mgmt_args *args)
+{
+ const size_t size_v1 = sizeof_args(struct nvme_ns_mgmt_args, csi, __u64);
+ const size_t size_v2 = sizeof_args(struct nvme_ns_mgmt_args, data, __u64);
+ __u32 cdw10 = NVME_SET(args->sel, NAMESPACE_MGMT_CDW10_SEL);
+ __u32 cdw11 = NVME_SET(args->csi, NAMESPACE_MGMT_CDW11_CSI);
+
+ if (args->args_size < size_v1 || args->args_size > size_v2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ struct nvme_passthru_cmd cmd = {
+ .nsid = args->nsid,
+ .opcode = nvme_admin_ns_mgmt,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size == size_v2) {
+ if (args->data) {
+ cmd.data_len = sizeof(*args->data);
+ cmd.addr = (__u64)(uintptr_t)args->data;
+ }
+ }
+ else {
+ if (args->ns) {
+ cmd.data_len = sizeof(*args->ns);
+ cmd.addr = (__u64)(uintptr_t)args->ns;
+ }
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_ns_attach(struct nvme_ns_attach_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->sel, NAMESPACE_ATTACH_CDW10_SEL);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_ns_attach,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .data_len = sizeof(*args->ctrlist),
+ .addr = (__u64)(uintptr_t)args->ctrlist,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_fw_download(struct nvme_fw_download_args *args)
+{
+ __u32 cdw10 = (args->data_len >> 2) - 1;
+ __u32 cdw11 = args->offset >> 2;
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_fw_download,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .data_len = args->data_len,
+ .addr = (__u64)(uintptr_t)args->data,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_fw_commit(struct nvme_fw_commit_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->slot, FW_COMMIT_CDW10_FS) |
+ NVME_SET(args->action, FW_COMMIT_CDW10_CA) |
+ NVME_SET(args->bpid, FW_COMMIT_CDW10_BPID);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_fw_commit,
+ .cdw10 = cdw10,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_security_send(struct nvme_security_send_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->secp, SECURITY_SECP) |
+ NVME_SET(args->spsp0, SECURITY_SPSP0) |
+ NVME_SET(args->spsp1, SECURITY_SPSP1) |
+ NVME_SET(args->nssf, SECURITY_NSSF);
+ __u32 cdw11 = args->tl;
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_security_send,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .data_len = args->data_len,
+ .addr = (__u64)(uintptr_t)args->data,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_security_receive(struct nvme_security_receive_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->secp, SECURITY_SECP) |
+ NVME_SET(args->spsp0, SECURITY_SPSP0) |
+ NVME_SET(args->spsp1, SECURITY_SPSP1) |
+ NVME_SET(args->nssf, SECURITY_NSSF);
+ __u32 cdw11 = args->al;
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_security_recv,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .data_len = args->data_len,
+ .addr = (__u64)(uintptr_t)args->data,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_get_lba_status(struct nvme_get_lba_status_args *args)
+{
+ __u32 cdw10 = args->slba & 0xffffffff;
+ __u32 cdw11 = args->slba >> 32;
+ __u32 cdw12 = args->mndw;
+ __u32 cdw13 = NVME_SET(args->rl, GET_LBA_STATUS_CDW13_RL) |
+ NVME_SET(args->atype, GET_LBA_STATUS_CDW13_ATYPE);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_get_lba_status,
+ .nsid = args->nsid,
+ .addr = (__u64)(uintptr_t)args->lbas,
+ .data_len = (args->mndw + 1) << 2,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = cdw12,
+ .cdw13 = cdw13,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_directive_send(struct nvme_directive_send_args *args)
+{
+ __u32 cdw10 = args->data_len ? (args->data_len >> 2) - 1 : 0;
+ __u32 cdw11 = NVME_SET(args->doper, DIRECTIVE_CDW11_DOPER) |
+ NVME_SET(args->dtype, DIRECTIVE_CDW11_DTYPE) |
+ NVME_SET(args->dspec, DIRECTIVE_CDW11_DPSEC);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_directive_send,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = args->cdw12,
+ .data_len = args->data_len,
+ .addr = (__u64)(uintptr_t)args->data,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_directive_send_id_endir(int fd, __u32 nsid, bool endir,
+ enum nvme_directive_dtype dtype,
+ struct nvme_id_directives *id)
+{
+ __u32 cdw12 = NVME_SET(dtype, DIRECTIVE_SEND_IDENTIFY_CDW12_DTYPE) |
+ NVME_SET(endir, DIRECTIVE_SEND_IDENTIFY_CDW12_ENDIR);
+ struct nvme_directive_send_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .nsid = nsid,
+ .dspec = 0,
+ .dtype = NVME_DIRECTIVE_DTYPE_IDENTIFY,
+ .doper = NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR,
+ .cdw12 = cdw12,
+ .data_len = sizeof(*id),
+ .data = id,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ return nvme_directive_send(&args);
+}
+
+int nvme_directive_recv(struct nvme_directive_recv_args *args)
+{
+ __u32 cdw10 = args->data_len ? (args->data_len >> 2) - 1 : 0;
+ __u32 cdw11 = NVME_SET(args->doper, DIRECTIVE_CDW11_DOPER) |
+ NVME_SET(args->dtype, DIRECTIVE_CDW11_DTYPE) |
+ NVME_SET(args->dspec, DIRECTIVE_CDW11_DPSEC);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_directive_recv,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = args->cdw12,
+ .data_len = args->data_len,
+ .addr = (__u64)(uintptr_t)args->data,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_capacity_mgmt(struct nvme_capacity_mgmt_args *args)
+{
+ __u32 cdw10 = args->op | args->element_id << 16;
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_capacity_mgmt,
+ .cdw10 = cdw10,
+ .cdw11 = args->cdw11,
+ .cdw12 = args->cdw12,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_lockdown(struct nvme_lockdown_args *args)
+{
+ __u32 cdw10 = args->ofi << 8 |
+ (args->ifc & 0x3) << 5 |
+ (args->prhbt & 0x1) << 4 |
+ (args->scp & 0xF);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_lockdown,
+ .cdw10 = cdw10,
+ .cdw14 = args->uuidx & 0x3F,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_set_property(struct nvme_set_property_args *args)
+{
+ __u32 cdw10 = nvme_is_64bit_reg(args->offset);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_fabrics,
+ .nsid = nvme_fabrics_type_property_set,
+ .cdw10 = cdw10,
+ .cdw11 = args->offset,
+ .cdw12 = args->value & 0xffffffff,
+ .cdw13 = args->value >> 32,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_get_property(struct nvme_get_property_args *args)
+{
+ __u32 cdw10 = nvme_is_64bit_reg(args->offset);
+
+ struct nvme_passthru_cmd64 cmd = {
+ .opcode = nvme_admin_fabrics,
+ .nsid = nvme_fabrics_type_property_get,
+ .cdw10 = cdw10,
+ .cdw11 = args->offset,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru64(args->fd, &cmd, args->value);
+}
+
+int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->sanact, SANITIZE_CDW10_SANACT) |
+ NVME_SET(!!args->ause, SANITIZE_CDW10_AUSE) |
+ NVME_SET(args->owpass, SANITIZE_CDW10_OWPASS) |
+ NVME_SET(!!args->oipbp, SANITIZE_CDW10_OIPBP) |
+ NVME_SET(!!args->nodas, SANITIZE_CDW10_NODAS);
+ __u32 cdw11 = args->ovrpat;
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_sanitize_nvm,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_dev_self_test(struct nvme_dev_self_test_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->stc, DEVICE_SELF_TEST_CDW10_STC);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_dev_self_test,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_virtual_mgmt(struct nvme_virtual_mgmt_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->act, VIRT_MGMT_CDW10_ACT) |
+ NVME_SET(args->rt, VIRT_MGMT_CDW10_RT) |
+ NVME_SET(args->cntlid, VIRT_MGMT_CDW10_CNTLID);
+ __u32 cdw11 = NVME_SET(args->nr, VIRT_MGMT_CDW11_NR);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_virtual_mgmt,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_submit_io_passthru64(int fd, struct nvme_passthru_cmd64 *cmd,
+ __u64 *result)
+{
+ return nvme_submit_passthru64(fd, NVME_IOCTL_IO64_CMD, cmd, result);
+}
+
+int nvme_io_passthru64(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10,
+ __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14,
+ __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len,
+ void *metadata, __u32 timeout_ms, __u64 *result)
+{
+ return nvme_passthru64(fd, NVME_IOCTL_IO64_CMD, opcode, flags, rsvd,
+ nsid, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13,
+ cdw14, cdw15, data_len, data, metadata_len, metadata,
+ timeout_ms, result);
+}
+
+int nvme_submit_io_passthru(int fd, struct nvme_passthru_cmd *cmd, __u32 *result)
+{
+ return nvme_submit_passthru(fd, NVME_IOCTL_IO_CMD, cmd, result);
+}
+
+int nvme_io_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10,
+ __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14,
+ __u32 cdw15, __u32 data_len, void *data, __u32 metadata_len,
+ void *metadata, __u32 timeout_ms, __u32 *result)
+{
+ return nvme_passthru(fd, NVME_IOCTL_IO_CMD, opcode, flags, rsvd, nsid,
+ cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, cdw14,
+ cdw15, data_len, data, metadata_len, metadata,
+ timeout_ms, result);
+}
+
+static int nvme_set_var_size_tags(__u32 *cmd_dw2, __u32 *cmd_dw3, __u32 *cmd_dw14,
+ __u8 pif, __u8 sts, __u64 reftag, __u64 storage_tag)
+{
+ __u32 cdw2 = 0, cdw3 = 0, cdw14;
+ beint64_t be_reftag = cpu_to_be64(reftag);
+ beint64_t be_storage_tag = cpu_to_be64(storage_tag);
+
+ switch (pif) {
+ /* 16b Protection Information */
+ case 0:
+ cdw14 = be_reftag & 0xffffffff;
+ cdw14 |= ((be_storage_tag << (32 - sts)) & 0xffffffff);
+ break;
+ /* 32b Protection Information */
+ case 1:
+ cdw14 = be_reftag & 0xffffffff;
+ cdw3 = be_reftag >> 32;
+ cdw14 |= ((be_storage_tag << (80 - sts)) & 0xffff0000);
+ if (sts >= 48)
+ cdw3 |= ((be_storage_tag >> (sts - 48)) & 0xffffffff);
+ else
+ cdw3 |= ((be_storage_tag << (48 - sts)) & 0xffffffff);
+ cdw2 = (be_storage_tag >> (sts - 16)) & 0xffff;
+ break;
+ /* 64b Protection Information */
+ case 2:
+ cdw14 = be_reftag & 0xffffffff;
+ cdw3 = (be_reftag >> 32) & 0xffff;
+ cdw14 |= ((be_storage_tag << (48 - sts)) & 0xffffffff);
+ if (sts >= 16)
+ cdw3 |= ((be_storage_tag >> (sts - 16)) & 0xffff);
+ else
+ cdw3 |= ((be_storage_tag << (16 - sts)) & 0xffff);
+ break;
+ default:
+ perror("Unsupported Protection Information Format");
+ errno = EINVAL;
+ return -1;
+ }
+
+ *cmd_dw2 = cdw2;
+ *cmd_dw3 = cdw3;
+ *cmd_dw14 = cdw14;
+ return 0;
+}
+
+int nvme_io(struct nvme_io_args *args, __u8 opcode)
+{
+ const size_t size_v1 = sizeof_args(struct nvme_io_args, dsm, __u64);
+ const size_t size_v2 = sizeof_args(struct nvme_io_args, pif, __u64);
+ __u32 cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, cdw14, cdw15;
+
+ if (args->args_size < size_v1 || args->args_size > size_v2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cdw10 = args->slba & 0xffffffff;
+ cdw11 = args->slba >> 32;
+ cdw12 = args->nlb | (args->control << 16);
+ cdw13 = args->dsm | (args->dspec << 16);
+ cdw15 = args->apptag | (args->appmask << 16);
+
+ if (args->args_size == size_v1) {
+ cdw2 = (args->storage_tag >> 32) & 0xffff;
+ cdw3 = args->storage_tag & 0xffffffff;
+ cdw14 = args->reftag;
+ } else {
+ if (nvme_set_var_size_tags(&cdw2, &cdw3, &cdw14,
+ args->pif,
+ args->sts,
+ args->reftag_u64,
+ args->storage_tag)) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = opcode,
+ .nsid = args->nsid,
+ .cdw2 = cdw2,
+ .cdw3 = cdw3,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = cdw12,
+ .cdw13 = cdw13,
+ .cdw14 = cdw14,
+ .cdw15 = cdw15,
+ .data_len = args->data_len,
+ .metadata_len = args->metadata_len,
+ .addr = (__u64)(uintptr_t)args->data,
+ .metadata = (__u64)(uintptr_t)args->metadata,
+ .timeout_ms = args->timeout,
+ };
+
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_dsm(struct nvme_dsm_args *args)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_cmd_dsm,
+ .nsid = args->nsid,
+ .addr = (__u64)(uintptr_t)args->dsm,
+ .data_len = args->nr_ranges * sizeof(*args->dsm),
+ .cdw10 = args->nr_ranges - 1,
+ .cdw11 = args->attrs,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_copy(struct nvme_copy_args *args)
+{
+ const size_t size_v1 = sizeof_args(struct nvme_copy_args, format, __u64);
+ const size_t size_v2 = sizeof_args(struct nvme_copy_args, ilbrt_u64, __u64);
+ __u32 cdw3, cdw12, cdw14, data_len;
+
+ if (args->args_size < size_v1 || args->args_size > size_v2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cdw12 = ((args->nr - 1) & 0xff) | ((args->format & 0xf) << 8) |
+ ((args->prinfor & 0xf) << 12) | ((args->dtype & 0xf) << 20) |
+ ((args->prinfow & 0xf) << 26) | ((args->fua & 0x1) << 30) |
+ ((args->lr & 0x1) << 31);
+
+ if (args->args_size == size_v1) {
+ cdw3 = 0;
+ cdw14 = args->ilbrt;
+ } else {
+ cdw3 = (args->ilbrt_u64 >> 32) & 0xffffffff;
+ cdw14 = args->ilbrt_u64 & 0xffffffff;
+ }
+
+ if (args->format == 1)
+ data_len = args->nr * sizeof(struct nvme_copy_range_f1);
+ else if (args->format == 2)
+ data_len = args->nr * sizeof(struct nvme_copy_range_f2);
+ else if (args->format == 3)
+ data_len = args->nr * sizeof(struct nvme_copy_range_f3);
+ else
+ data_len = args->nr * sizeof(struct nvme_copy_range);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_cmd_copy,
+ .nsid = args->nsid,
+ .addr = (__u64)(uintptr_t)args->copy,
+ .data_len = data_len,
+ .cdw3 = cdw3,
+ .cdw10 = args->sdlba & 0xffffffff,
+ .cdw11 = args->sdlba >> 32,
+ .cdw12 = cdw12,
+ .cdw13 = (args->dspec & 0xffff) << 16,
+ .cdw14 = cdw14,
+ .cdw15 = (args->lbatm << 16) | args->lbat,
+ .timeout_ms = args->timeout,
+ };
+
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_resv_acquire(struct nvme_resv_acquire_args *args)
+{
+ __le64 payload[2] = {
+ cpu_to_le64(args->crkey),
+ cpu_to_le64(args->nrkey)
+ };
+ __u32 cdw10 = (args->racqa & 0x7) |
+ (args->iekey ? 1 << 3 : 0) |
+ (args->rtype << 8);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_cmd_resv_acquire,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .data_len = sizeof(payload),
+ .addr = (__u64)(uintptr_t)(payload),
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_resv_register(struct nvme_resv_register_args *args)
+{
+ __le64 payload[2] = {
+ cpu_to_le64(args->crkey),
+ cpu_to_le64(args->nrkey)
+ };
+ __u32 cdw10 = (args->rrega & 0x7) |
+ (args->iekey ? 1 << 3 : 0) |
+ (args->cptpl << 30);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_cmd_resv_register,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .data_len = sizeof(payload),
+ .addr = (__u64)(uintptr_t)(payload),
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_resv_release(struct nvme_resv_release_args *args)
+{
+ __le64 payload[1] = { cpu_to_le64(args->crkey) };
+ __u32 cdw10 = (args->rrela & 0x7) |
+ (args->iekey ? 1 << 3 : 0) |
+ (args->rtype << 8);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_cmd_resv_release,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .addr = (__u64)(uintptr_t)(payload),
+ .data_len = sizeof(payload),
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_resv_report(struct nvme_resv_report_args *args)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_cmd_resv_report,
+ .nsid = args->nsid,
+ .cdw10 = (args->len >> 2) - 1,
+ .cdw11 = args->eds ? 1 : 0,
+ .addr = (__u64)(uintptr_t)args->report,
+ .data_len = args->len,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_io_mgmt_recv(struct nvme_io_mgmt_recv_args *args)
+{
+ __u32 cdw10 = args->mo | (args->mos << 16);
+ __u32 cdw11 = (args->data_len >> 2) - 1;
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_cmd_io_mgmt_recv,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .addr = (__u64)(uintptr_t)args->data,
+ .data_len = args->data_len,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return nvme_submit_io_passthru(args->fd, &cmd, NULL);
+}
+
+int nvme_io_mgmt_send(struct nvme_io_mgmt_send_args *args)
+{
+ __u32 cdw10 = args->mo | (args->mos << 16);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_cmd_io_mgmt_send,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .addr = (__u64)(uintptr_t)args->data,
+ .data_len = args->data_len,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return nvme_submit_io_passthru(args->fd, &cmd, NULL);
+}
+
+int nvme_zns_mgmt_send(struct nvme_zns_mgmt_send_args *args)
+{
+ __u32 cdw10 = args->slba & 0xffffffff;
+ __u32 cdw11 = args->slba >> 32;
+ __u32 cdw13 = NVME_SET(args->zsaso, ZNS_MGMT_SEND_ZSASO) |
+ NVME_SET(!!args->select_all, ZNS_MGMT_SEND_SEL) |
+ NVME_SET(args->zsa, ZNS_MGMT_SEND_ZSA);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_zns_cmd_mgmt_send,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw13 = cdw13,
+ .addr = (__u64)(uintptr_t)args->data,
+ .data_len = args->data_len,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_zns_mgmt_recv(struct nvme_zns_mgmt_recv_args *args)
+{
+ __u32 cdw10 = args->slba & 0xffffffff;
+ __u32 cdw11 = args->slba >> 32;
+ __u32 cdw12 = (args->data_len >> 2) - 1;
+ __u32 cdw13 = NVME_SET(args->zra, ZNS_MGMT_RECV_ZRA) |
+ NVME_SET(args->zrasf, ZNS_MGMT_RECV_ZRASF) |
+ NVME_SET(args->zras_feat, ZNS_MGMT_RECV_ZRAS_FEAT);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_zns_cmd_mgmt_recv,
+ .nsid = args->nsid,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = cdw12,
+ .cdw13 = cdw13,
+ .addr = (__u64)(uintptr_t)args->data,
+ .data_len = args->data_len,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return nvme_submit_io_passthru(args->fd, &cmd, args->result);
+}
+
+int nvme_zns_append(struct nvme_zns_append_args *args)
+{
+ const size_t size_v1 = sizeof_args(struct nvme_zns_append_args, lbatm, __u64);
+ const size_t size_v2 = sizeof_args(struct nvme_zns_append_args, ilbrt_u64, __u64);
+ __u32 cdw3, cdw10, cdw11, cdw12, cdw14, cdw15;
+
+ if (args->args_size < size_v1 || args->args_size > size_v2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cdw10 = args->zslba & 0xffffffff;
+ cdw11 = args->zslba >> 32;
+ cdw12 = args->nlb | (args->control << 16);
+ cdw15 = args->lbat | (args->lbatm << 16);
+
+ if (args->args_size == size_v1) {
+ cdw3 = 0;
+ cdw14 = args->ilbrt;
+ } else {
+ cdw3 = (args->ilbrt_u64 >> 32) & 0xffffffff;
+ cdw14 = args->ilbrt_u64 & 0xffffffff;
+ }
+
+ struct nvme_passthru_cmd64 cmd = {
+ .opcode = nvme_zns_cmd_append,
+ .nsid = args->nsid,
+ .cdw3 = cdw3,
+ .cdw10 = cdw10,
+ .cdw11 = cdw11,
+ .cdw12 = cdw12,
+ .cdw14 = cdw14,
+ .cdw15 = cdw15,
+ .data_len = args->data_len,
+ .addr = (__u64)(uintptr_t)args->data,
+ .metadata_len = args->metadata_len,
+ .metadata = (__u64)(uintptr_t)args->metadata,
+ .timeout_ms = args->timeout,
+ };
+
+ return nvme_submit_io_passthru64(args->fd, &cmd, args->result);
+}
+
+int nvme_dim_send(struct nvme_dim_args *args)
+{
+ __u32 cdw10 = NVME_SET(args->tas, DIM_TAS);
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_discovery_info_mgmt,
+ .cdw10 = cdw10,
+ .addr = (__u64)(uintptr_t)args->data,
+ .data_len = args->data_len,
+ .timeout_ms = args->timeout,
+ };
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
+}
diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h
new file mode 100644
index 0000000..4a0698f
--- /dev/null
+++ b/src/nvme/ioctl.h
@@ -0,0 +1,4062 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+
+#ifndef _LIBNVME_IOCTL_H
+#define _LIBNVME_IOCTL_H
+
+#include <stddef.h>
+#include <sys/ioctl.h>
+#include "types.h"
+#include "api-types.h"
+
+/*
+ * We can not always count on the kernel UAPI being installed. Use the same
+ * 'ifdef' guard to avoid double definitions just in case.
+ */
+#ifndef _UAPI_LINUX_NVME_IOCTL_H
+#define _UAPI_LINUX_NVME_IOCTL_H
+
+#ifndef _LINUX_NVME_IOCTL_H
+#define _LINUX_NVME_IOCTL_H
+
+/**
+ * DOC: ioctl.h
+ *
+ * Linux NVMe ioctl interface functions
+ */
+
+/* '0' is interpreted by the kernel to mean 'apply the default timeout' */
+#define NVME_DEFAULT_IOCTL_TIMEOUT 0
+
+/*
+ * 4k is the smallest possible transfer unit, so restricting to 4k
+ * avoids having to check the MDTS value of the controller.
+ */
+#define NVME_LOG_PAGE_PDU_SIZE 4096
+
+/**
+ * struct nvme_passthru_cmd - nvme passthrough command structure
+ * @opcode: Operation code, see &enum nvme_io_opcodes and &enum nvme_admin_opcodes
+ * @flags: Not supported: intended for command flags (eg: SGL, FUSE)
+ * @rsvd1: Reserved for future use
+ * @nsid: Namespace Identifier, or Fabrics type
+ * @cdw2: Command Dword 2 (no spec defined use)
+ * @cdw3: Command Dword 3 (no spec defined use)
+ * @metadata: User space address to metadata buffer (NULL if not used)
+ * @addr: User space address to data buffer (NULL if not used)
+ * @metadata_len: Metadata buffer transfer length
+ * @data_len: Data buffer transfer length
+ * @cdw10: Command Dword 10 (command specific)
+ * @cdw11: Command Dword 11 (command specific)
+ * @cdw12: Command Dword 12 (command specific)
+ * @cdw13: Command Dword 13 (command specific)
+ * @cdw14: Command Dword 14 (command specific)
+ * @cdw15: Command Dword 15 (command specific)
+ * @timeout_ms: If non-zero, overrides system default timeout in milliseconds
+ * @result: Set on completion to the command's CQE DWORD 0 controller response
+ */
+struct nvme_passthru_cmd {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u32 cdw10;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 result;
+};
+
+/**
+ * struct nvme_passthru_cmd64 - 64-bit nvme passthrough command structure
+ * @opcode: Operation code, see &enum nvme_io_opcodes and &enum nvme_admin_opcodes
+ * @flags: Not supported: intended for command flags (eg: SGL, FUSE)
+ * @rsvd1: Reserved for future use
+ * @nsid: Namespace Identifier, or Fabrics type
+ * @cdw2: Command Dword 2 (no spec defined use)
+ * @cdw3: Command Dword 3 (no spec defined use)
+ * @metadata: User space address to metadata buffer (NULL if not used)
+ * @addr: User space address to data buffer (NULL if not used)
+ * @metadata_len: Metadata buffer transfer length
+ * @data_len: Data buffer transfer length
+ * @cdw10: Command Dword 10 (command specific)
+ * @cdw11: Command Dword 11 (command specific)
+ * @cdw12: Command Dword 12 (command specific)
+ * @cdw13: Command Dword 13 (command specific)
+ * @cdw14: Command Dword 14 (command specific)
+ * @cdw15: Command Dword 15 (command specific)
+ * @timeout_ms: If non-zero, overrides system default timeout in milliseconds
+ * @rsvd2: Reserved for future use (and fills an implicit struct pad
+ * @result: Set on completion to the command's CQE DWORD 0-1 controller response
+ */
+struct nvme_passthru_cmd64 {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u32 cdw10;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 rsvd2;
+ __u64 result;
+};
+
+/**
+ * struct nvme_uring_cmd - nvme uring command structure
+ * @opcode: Operation code, see &enum nvme_io_opcodes and &enum nvme_admin_opcodes
+ * @flags: Not supported: intended for command flags (eg: SGL, FUSE)
+ * @rsvd1: Reserved for future use
+ * @nsid: Namespace Identifier, or Fabrics type
+ * @cdw2: Command Dword 2 (no spec defined use)
+ * @cdw3: Command Dword 3 (no spec defined use)
+ * @metadata: User space address to metadata buffer (NULL if not used)
+ * @addr: User space address to data buffer (NULL if not used)
+ * @metadata_len: Metadata buffer transfer length
+ * @data_len: Data buffer transfer length
+ * @cdw10: Command Dword 10 (command specific)
+ * @cdw11: Command Dword 11 (command specific)
+ * @cdw12: Command Dword 12 (command specific)
+ * @cdw13: Command Dword 13 (command specific)
+ * @cdw14: Command Dword 14 (command specific)
+ * @cdw15: Command Dword 15 (command specific)
+ * @timeout_ms: If non-zero, overrides system default timeout in milliseconds
+ * @rsvd2: Reserved for future use (and fills an implicit struct pad
+ */
+struct nvme_uring_cmd {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u32 cdw10;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 rsvd2;
+};
+
+#define NVME_IOCTL_ID _IO('N', 0x40)
+#define NVME_IOCTL_RESET _IO('N', 0x44)
+#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45)
+#define NVME_IOCTL_RESCAN _IO('N', 0x46)
+#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_passthru_cmd)
+#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct nvme_passthru_cmd)
+#define NVME_IOCTL_ADMIN64_CMD _IOWR('N', 0x47, struct nvme_passthru_cmd64)
+#define NVME_IOCTL_IO64_CMD _IOWR('N', 0x48, struct nvme_passthru_cmd64)
+
+/* io_uring async commands: */
+#define NVME_URING_CMD_IO _IOWR('N', 0x80, struct nvme_uring_cmd)
+#define NVME_URING_CMD_IO_VEC _IOWR('N', 0x81, struct nvme_uring_cmd)
+
+#endif /* _UAPI_LINUX_NVME_IOCTL_H */
+
+#endif /* _LINUX_NVME_IOCTL_H */
+
+/**
+ * sizeof_args - Helper function used to determine structure sizes
+ * @type: Argument structure type
+ * @member: Member inside the type
+ * @align: Alignment information
+ */
+#define sizeof_args(type, member, align) \
+({ \
+ type s; \
+ size_t t = offsetof(type, member) + sizeof(s.member); \
+ size_t p = (sizeof(align) - (t % sizeof(align))) % sizeof(align); \
+ t + p; \
+})
+
+/**
+ * nvme_submit_admin_passthru64() - Submit a 64-bit nvme passthrough admin
+ * command
+ * @fd: File descriptor of nvme device
+ * @cmd: The nvme admin command to send
+ * @result: Optional field to return the result from the CQE DW0-1
+ *
+ * Uses NVME_IOCTL_ADMIN64_CMD for the ioctl request.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_submit_admin_passthru64(int fd, struct nvme_passthru_cmd64 *cmd,
+ __u64 *result);
+
+/**
+ * nvme_admin_passthru64() - Submit a 64-bit nvme passthrough command
+ * @fd: File descriptor of nvme device
+ * @opcode: The nvme io command to send
+ * @flags: NVMe command flags (not used)
+ * @rsvd: Reserved for future use
+ * @nsid: Namespace identifier
+ * @cdw2: Command dword 2
+ * @cdw3: Command dword 3
+ * @cdw10: Command dword 10
+ * @cdw11: Command dword 11
+ * @cdw12: Command dword 12
+ * @cdw13: Command dword 13
+ * @cdw14: Command dword 14
+ * @cdw15: Command dword 15
+ * @data_len: Length of the data transferred in this command in bytes
+ * @data: Pointer to user address of the data buffer
+ * @metadata_len:Length of metadata transferred in this command
+ * @metadata: Pointer to user address of the metadata buffer
+ * @timeout_ms: How long the kernel waits for the command to complete
+ * @result: Optional field to return the result from the CQE dword 0
+ *
+ * Parameterized form of nvme_submit_admin_passthru64(). This sets up and
+ * submits a &struct nvme_passthru_cmd64.
+ *
+ * Known values for @opcode are defined in &enum nvme_admin_opcode.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_admin_passthru64(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11,
+ __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15,
+ __u32 data_len, void *data, __u32 metadata_len, void *metadata,
+ __u32 timeout_ms, __u64 *result);
+
+/**
+ * nvme_submit_admin_passthru() - Submit an nvme passthrough admin command
+ * @fd: File descriptor of nvme device
+ * @cmd: The nvme admin command to send
+ * @result: Optional field to return the result from the CQE DW0
+ *
+ * Uses NVME_IOCTL_ADMIN_CMD for the ioctl request.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_submit_admin_passthru(int fd, struct nvme_passthru_cmd *cmd,
+ __u32 *result);
+
+/**
+ * nvme_admin_passthru() - Submit an nvme passthrough command
+ * @fd: File descriptor of nvme device
+ * @opcode: The nvme io command to send
+ * @flags: NVMe command flags (not used)
+ * @rsvd: Reserved for future use
+ * @nsid: Namespace identifier
+ * @cdw2: Command dword 2
+ * @cdw3: Command dword 3
+ * @cdw10: Command dword 10
+ * @cdw11: Command dword 11
+ * @cdw12: Command dword 12
+ * @cdw13: Command dword 13
+ * @cdw14: Command dword 14
+ * @cdw15: Command dword 15
+ * @data_len: Length of the data transferred in this command in bytes
+ * @data: Pointer to user address of the data buffer
+ * @metadata_len:Length of metadata transferred in this command
+ * @metadata: Pointer to user address of the metadata buffer
+ * @timeout_ms: How long the kernel waits for the command to complete
+ * @result: Optional field to return the result from the CQE dword 0
+ *
+ * Parameterized form of nvme_submit_admin_passthru(). This sets up and
+ * submits a &struct nvme_passthru_cmd.
+ *
+ * Known values for @opcode are defined in &enum nvme_admin_opcode.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_admin_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11,
+ __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15,
+ __u32 data_len, void *data, __u32 metadata_len, void *metadata,
+ __u32 timeout_ms, __u32 *result);
+
+/**
+ * nvme_submit_io_passthru64() - Submit a 64-bit nvme passthrough command
+ * @fd: File descriptor of nvme device
+ * @cmd: The nvme io command to send
+ * @result: Optional field to return the result from the CQE DW0-1
+ *
+ * Uses NVME_IOCTL_IO64_CMD for the ioctl request.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_submit_io_passthru64(int fd, struct nvme_passthru_cmd64 *cmd,
+ __u64 *result);
+
+/**
+ * nvme_io_passthru64() - Submit an nvme io passthrough command
+ * @fd: File descriptor of nvme device
+ * @opcode: The nvme io command to send
+ * @flags: NVMe command flags (not used)
+ * @rsvd: Reserved for future use
+ * @nsid: Namespace identifier
+ * @cdw2: Command dword 2
+ * @cdw3: Command dword 3
+ * @cdw10: Command dword 10
+ * @cdw11: Command dword 11
+ * @cdw12: Command dword 12
+ * @cdw13: Command dword 13
+ * @cdw14: Command dword 14
+ * @cdw15: Command dword 15
+ * @data_len: Length of the data transferred in this command in bytes
+ * @data: Pointer to user address of the data buffer
+ * @metadata_len:Length of metadata transferred in this command
+ * @metadata: Pointer to user address of the metadata buffer
+ * @timeout_ms: How long the kernel waits for the command to complete
+ * @result: Optional field to return the result from the CQE dword 0
+ *
+ * Parameterized form of nvme_submit_io_passthru64(). This sets up and submits
+ * a &struct nvme_passthru_cmd64.
+ *
+ * Known values for @opcode are defined in &enum nvme_io_opcode.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_io_passthru64(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11,
+ __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15,
+ __u32 data_len, void *data, __u32 metadata_len, void *metadata,
+ __u32 timeout_ms, __u64 *result);
+
+/**
+ * nvme_submit_io_passthru() - Submit an nvme passthrough command
+ * @fd: File descriptor of nvme device
+ * @cmd: The nvme io command to send
+ * @result: Optional field to return the result from the CQE dword 0
+ * @result: Optional field to return the result from the CQE DW0
+ *
+ * Uses NVME_IOCTL_IO_CMD for the ioctl request.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_submit_io_passthru(int fd, struct nvme_passthru_cmd *cmd,
+ __u32 *result);
+
+/**
+ * nvme_io_passthru() - Submit an nvme io passthrough command
+ * @fd: File descriptor of nvme device
+ * @opcode: The nvme io command to send
+ * @flags: NVMe command flags (not used)
+ * @rsvd: Reserved for future use
+ * @nsid: Namespace identifier
+ * @cdw2: Command dword 2
+ * @cdw3: Command dword 3
+ * @cdw10: Command dword 10
+ * @cdw11: Command dword 11
+ * @cdw12: Command dword 12
+ * @cdw13: Command dword 13
+ * @cdw14: Command dword 14
+ * @cdw15: Command dword 15
+ * @data_len: Length of the data transferred in this command in bytes
+ * @data: Pointer to user address of the data buffer
+ * @metadata_len:Length of metadata transferred in this command
+ * @metadata: Pointer to user address of the metadata buffer
+ * @timeout_ms: How long the kernel waits for the command to complete
+ * @result: Optional field to return the result from the CQE dword 0
+ *
+ * Parameterized form of nvme_submit_io_passthru(). This sets up and submits
+ * a &struct nvme_passthru_cmd.
+ *
+ * Known values for @opcode are defined in &enum nvme_io_opcode.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_io_passthru(int fd, __u8 opcode, __u8 flags, __u16 rsvd,
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11,
+ __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15,
+ __u32 data_len, void *data, __u32 metadata_len, void *metadata,
+ __u32 timeout_ms, __u32 *result);
+
+/**
+ * nvme_subsystem_reset() - Initiate a subsystem reset
+ * @fd: File descriptor of nvme device
+ *
+ * This should only be sent to controller handles, not to namespaces.
+ *
+ * Return: Zero if a subsystem reset was initiated or -1 with errno set
+ * otherwise.
+ */
+int nvme_subsystem_reset(int fd);
+
+/**
+ * nvme_ctrl_reset() - Initiate a controller reset
+ * @fd: File descriptor of nvme device
+ *
+ * This should only be sent to controller handles, not to namespaces.
+ *
+ * Return: 0 if a reset was initiated or -1 with errno set otherwise.
+ */
+int nvme_ctrl_reset(int fd);
+
+/**
+ * nvme_ns_rescan() - Initiate a controller rescan
+ * @fd: File descriptor of nvme device
+ *
+ * This should only be sent to controller handles, not to namespaces.
+ *
+ * Return: 0 if a rescan was initiated or -1 with errno set otherwise.
+ */
+int nvme_ns_rescan(int fd);
+
+/**
+ * nvme_get_nsid() - Retrieve the NSID from a namespace file descriptor
+ * @fd: File descriptor of nvme namespace
+ * @nsid: User pointer to namespace id
+ *
+ * This should only be sent to namespace handles, not to controllers. The
+ * kernel's interface returns the nsid as the return value. This is unfortunate
+ * for many architectures that are incapable of allowing distinguishing a
+ * namespace id > 0x80000000 from a negative error number.
+ *
+ * Return: 0 if @nsid was set successfully or -1 with errno set otherwise.
+ */
+int nvme_get_nsid(int fd, __u32 *nsid);
+
+/**
+ * nvme_identify() - Send the NVMe Identify command
+ * @args: &struct nvme_identify_args argument structure
+ *
+ * The Identify command returns a data buffer that describes information about
+ * the NVM subsystem, the controller or the namespace(s).
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_identify(struct nvme_identify_args *args);
+
+static inline int nvme_identify_cns_nsid(int fd, enum nvme_identify_cns cns,
+ __u32 nsid, void *data)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = cns,
+ .csi = NVME_CSI_NVM,
+ .nsid = nsid,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_ctrl() - Retrieves nvme identify controller
+ * @fd: File descriptor of nvme device
+ * @id: User space destination address to transfer the data,
+ *
+ * Sends nvme identify with CNS value %NVME_IDENTIFY_CNS_CTRL.
+ *
+ * See &struct nvme_id_ctrl for details on the data returned.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_ctrl(int fd, struct nvme_id_ctrl *id)
+{
+ return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_CTRL,
+ NVME_NSID_NONE, id);
+}
+
+/**
+ * nvme_identify_ns() - Retrieves nvme identify namespace
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace to identify
+ * @ns: User space destination address to transfer the data
+ *
+ * If the Namespace Identifier (NSID) field specifies an active NSID, then the
+ * Identify Namespace data structure is returned to the host for that specified
+ * namespace.
+ *
+ * If the controller supports the Namespace Management capability and the NSID
+ * field is set to %NVME_NSID_ALL, then the controller returns an Identify Namespace
+ * data structure that specifies capabilities that are common across namespaces
+ * for this controller.
+ *
+ * See &struct nvme_id_ns for details on the structure returned.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_ns(int fd, __u32 nsid, struct nvme_id_ns *ns)
+{
+ return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_NS, nsid, ns);
+}
+
+/**
+ * nvme_identify_allocated_ns() - Same as nvme_identify_ns, but only for
+ * allocated namespaces
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace to identify
+ * @ns: User space destination address to transfer the data
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_allocated_ns(int fd, __u32 nsid,
+ struct nvme_id_ns *ns)
+{
+ return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_ALLOCATED_NS,
+ nsid, ns);
+}
+
+/**
+ * nvme_identify_active_ns_list() - Retrieves active namespaces id list
+ * @fd: File descriptor of nvme device
+ * @nsid: Return namespaces greater than this identifier
+ * @list: User space destination address to transfer the data
+ *
+ * A list of 1024 namespace IDs is returned to the host containing NSIDs in
+ * increasing order that are greater than the value specified in the Namespace
+ * Identifier (nsid) field of the command.
+ *
+ * See &struct nvme_ns_list for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_active_ns_list(int fd, __u32 nsid,
+ struct nvme_ns_list *list)
+{
+ return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_NS_ACTIVE_LIST,
+ nsid, list);
+}
+
+/**
+ * nvme_identify_allocated_ns_list() - Retrieves allocated namespace id list
+ * @fd: File descriptor of nvme device
+ * @nsid: Return namespaces greater than this identifier
+ * @list: User space destination address to transfer the data
+ *
+ * A list of 1024 namespace IDs is returned to the host containing NSIDs in
+ * increasing order that are greater than the value specified in the Namespace
+ * Identifier (nsid) field of the command.
+ *
+ * See &struct nvme_ns_list for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_allocated_ns_list(int fd, __u32 nsid,
+ struct nvme_ns_list *list)
+{
+ return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST,
+ nsid, list);
+}
+
+/**
+ * nvme_identify_ctrl_list() - Retrieves identify controller list
+ * @fd: File descriptor of nvme device
+ * @cntid: Starting CNTLID to return in the list
+ * @cntlist: User space destination address to transfer the data
+ *
+ * Up to 2047 controller identifiers is returned containing a controller
+ * identifier greater than or equal to the controller identifier specified in
+ * @cntid.
+ *
+ * See &struct nvme_ctrl_list for a definition of the structure returned.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_ctrl_list(int fd, __u16 cntid,
+ struct nvme_ctrl_list *cntlist)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = cntlist,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_CTRL_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_nsid_ctrl_list() - Retrieves controller list attached to an nsid
+ * @fd: File descriptor of nvme device
+ * @nsid: Return controllers that are attached to this nsid
+ * @cntid: Starting CNTLID to return in the list
+ * @cntlist: User space destination address to transfer the data
+ *
+ * Up to 2047 controller identifiers are returned containing a controller
+ * identifier greater than or equal to the controller identifier specified in
+ * @cntid attached to @nsid.
+ *
+ * See &struct nvme_ctrl_list for a definition of the structure returned.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1
+ */
+static inline int nvme_identify_nsid_ctrl_list(int fd, __u32 nsid, __u16 cntid,
+ struct nvme_ctrl_list *cntlist)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = cntlist,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_NS_CTRL_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = nsid,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_ns_descs() - Retrieves namespace descriptor list
+ * @fd: File descriptor of nvme device
+ * @nsid: The namespace id to retrieve descriptors
+ * @descs: User space destination address to transfer the data
+ *
+ * A list of Namespace Identification Descriptor structures is returned to the
+ * host for the namespace specified in the Namespace Identifier (NSID) field if
+ * it is an active NSID.
+ *
+ * The data returned is in the form of an array of 'struct nvme_ns_id_desc'.
+ *
+ * See &struct nvme_ns_id_desc for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_ns_descs(int fd, __u32 nsid,
+ struct nvme_ns_id_desc *descs)
+{
+ return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_NS_DESC_LIST,
+ nsid, descs);
+}
+
+/**
+ * nvme_identify_nvmset_list() - Retrieves NVM Set List
+ * @fd: File descriptor of nvme device
+ * @nvmsetid: NVM Set Identifier
+ * @nvmset: User space destination address to transfer the data
+ *
+ * Retrieves an NVM Set List, &struct nvme_id_nvmset_list. The data structure
+ * is an ordered list by NVM Set Identifier, starting with the first NVM Set
+ * Identifier supported by the NVM subsystem that is equal to or greater than
+ * the NVM Set Identifier.
+ *
+ * See &struct nvme_id_nvmset_list for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_nvmset_list(int fd, __u16 nvmsetid,
+ struct nvme_id_nvmset_list *nvmset)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = nvmset,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_NVMSET_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = nvmsetid,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_primary_ctrl() - Retrieve NVMe Primary Controller
+ * identification
+ * @fd: File descriptor of nvme device
+ * @cntid: Return controllers starting at this identifier
+ * @cap: User space destination buffer address to transfer the data
+ *
+ * See &struct nvme_primary_ctrl_cap for the definition of the returned structure, @cap.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_primary_ctrl(int fd, __u16 cntid,
+ struct nvme_primary_ctrl_cap *cap)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = cap,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_secondary_ctrl_list() - Retrieves secondary controller list
+ * @fd: File descriptor of nvme device
+ * @cntid: Return controllers starting at this identifier
+ * @sc_list: User space destination address to transfer the data
+ *
+ * A Secondary Controller List is returned to the host for up to 127 secondary
+ * controllers associated with the primary controller processing this command.
+ * The list contains entries for controller identifiers greater than or equal
+ * to the value specified in the Controller Identifier (cntid).
+ *
+ * See &struct nvme_secondary_ctrls_list for a definition of the returned
+ * structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_secondary_ctrl_list(int fd,
+ __u16 cntid, struct nvme_secondary_ctrl_list *sc_list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = sc_list,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_ns_granularity() - Retrieves namespace granularity
+ * identification
+ * @fd: File descriptor of nvme device
+ * @gr_list: User space destination address to transfer the data
+ *
+ * If the controller supports reporting of Namespace Granularity, then a
+ * Namespace Granularity List is returned to the host for up to sixteen
+ * namespace granularity descriptors
+ *
+ * See &struct nvme_id_ns_granularity_list for the definition of the returned
+ * structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_ns_granularity(int fd,
+ struct nvme_id_ns_granularity_list *gr_list)
+{
+ return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_NS_GRANULARITY,
+ NVME_NSID_NONE, gr_list);
+}
+
+/**
+ * nvme_identify_uuid() - Retrieves device's UUIDs
+ * @fd: File descriptor of nvme device
+ * @uuid_list: User space destination address to transfer the data
+ *
+ * Each UUID List entry is either 0h, the NVMe Invalid UUID, or a valid UUID.
+ * Valid UUIDs are those which are non-zero and are not the NVMe Invalid UUID.
+ *
+ * See &struct nvme_id_uuid_list for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_uuid(int fd, struct nvme_id_uuid_list *uuid_list)
+{
+ return nvme_identify_cns_nsid(fd, NVME_IDENTIFY_CNS_UUID_LIST,
+ NVME_NSID_NONE, uuid_list);
+}
+
+/**
+ * nvme_identify_ns_csi() - I/O command set specific identify namespace data
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace to identify
+ * @uuidx: UUID Index for differentiating vendor specific encoding
+ * @csi: Command Set Identifier
+ * @data: User space destination address to transfer the data
+ *
+ * An I/O Command Set specific Identify Namespace data structure is returned
+ * for the namespace specified in @nsid.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_ns_csi(int fd, __u32 nsid, __u8 uuidx,
+ enum nvme_csi csi, void *data)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_CSI_NS,
+ .csi = csi,
+ .nsid = nsid,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = uuidx,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_ctrl_csi() - I/O command set specific Identify Controller data
+ * @fd: File descriptor of nvme device
+ * @csi: Command Set Identifier
+ * @data: User space destination address to transfer the data
+ *
+ * An I/O Command Set specific Identify Controller data structure is returned
+ * to the host for the controller processing the command. The specific Identify
+ * Controller data structure to be returned is specified by @csi.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_ctrl_csi(int fd, enum nvme_csi csi, void *data)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_CSI_CTRL,
+ .csi = csi,
+ .nsid = NVME_NSID_NONE,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_active_ns_list_csi() - Active namespace ID list associated with a specified I/O command set
+ * @fd: File descriptor of nvme device
+ * @nsid: Return namespaces greater than this identifier
+ * @csi: Command Set Identifier
+ * @ns_list: User space destination address to transfer the data
+ *
+ * A list of 1024 namespace IDs is returned to the host containing active
+ * NSIDs in increasing order that are greater than the value specified in
+ * the Namespace Identifier (nsid) field of the command and matching the
+ * I/O Command Set specified in the @csi argument.
+ *
+ * See &struct nvme_ns_list for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_active_ns_list_csi(int fd, __u32 nsid,
+ enum nvme_csi csi, struct nvme_ns_list *ns_list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = ns_list,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST,
+ .csi = csi,
+ .nsid = nsid,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_allocated_ns_list_csi() - Allocated namespace ID list associated with a specified I/O command set
+ * @fd: File descriptor of nvme device
+ * @nsid: Return namespaces greater than this identifier
+ * @csi: Command Set Identifier
+ * @ns_list: User space destination address to transfer the data
+ *
+ * A list of 1024 namespace IDs is returned to the host containing allocated
+ * NSIDs in increasing order that are greater than the value specified in
+ * the @nsid field of the command and matching the I/O Command Set
+ * specified in the @csi argument.
+ *
+ * See &struct nvme_ns_list for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_allocated_ns_list_csi(int fd, __u32 nsid,
+ enum nvme_csi csi, struct nvme_ns_list *ns_list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = ns_list,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST,
+ .csi = csi,
+ .nsid = nsid,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_independent_identify_ns() - I/O command set independent Identify namespace data
+ * @fd: File descriptor of nvme device
+ * @nsid: Return namespaces greater than this identifier
+ * @ns: I/O Command Set Independent Identify Namespace data
+ * structure
+ *
+ * The I/O command set independent Identify namespace data structure for
+ * the namespace identified with @ns is returned to the host.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_independent_identify_ns(int fd, __u32 nsid,
+ struct nvme_id_independent_id_ns *ns)
+{
+ return nvme_identify_cns_nsid(
+ fd, NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS, nsid, ns);
+}
+
+/**
+ * nvme_identify_ns_csi_user_data_format() - Identify namespace user data format
+ * @fd: File descriptor of nvme device
+ * @user_data_format: Return namespaces capability of identifier
+ * @uuidx: UUID selection, if supported
+ * @csi: Command Set Identifier
+ * @data: User space destination address to transfer the data
+ *
+ * Identify Namespace data structure for the specified User Data Format
+ * index containing the namespace capabilities for the NVM Command Set.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_ns_csi_user_data_format(int fd,
+ __u16 user_data_format, __u8 uuidx,
+ enum nvme_csi csi, void *data)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT,
+ .csi = csi,
+ .nsid = NVME_NSID_NONE,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = user_data_format,
+ .uuidx = uuidx,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_iocs_ns_csi_user_data_format() - Identify I/O command set namespace data structure
+ * @fd: File descriptor of nvme device
+ * @user_data_format: Return namespaces capability of identifier
+ * @uuidx: UUID selection, if supported
+ * @csi: Command Set Identifier
+ * @data: User space destination address to transfer the data
+ *
+ * I/O Command Set specific Identify Namespace data structure for
+ * the specified User Data Format index containing the namespace
+ * capabilities for the I/O Command Set specified in the CSI field.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_iocs_ns_csi_user_data_format(int fd,
+ __u16 user_data_format, __u8 uuidx,
+ enum nvme_csi csi, void *data)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT,
+ .csi = csi,
+ .nsid = NVME_NSID_NONE,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = user_data_format,
+ .uuidx = uuidx,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_nvm_identify_ctrl() - Identify controller data
+ * @fd: File descriptor of nvme device
+ * @id: User space destination address to transfer the data
+ *
+ * Return an identify controller data structure to the host of
+ * processing controller.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_nvm_identify_ctrl(int fd, struct nvme_id_ctrl_nvm *id)
+{
+ return nvme_identify_ctrl_csi(fd, NVME_CSI_NVM, id);
+}
+
+/**
+ * nvme_identify_domain_list() - Domain list data
+ * @fd: File descriptor of nvme device
+ * @domid: Domain ID
+ * @list: User space destination address to transfer data
+ *
+ * A list of 31 domain IDs is returned to the host containing domain
+ * attributes in increasing order that are greater than the value
+ * specified in the @domid field.
+ *
+ * See &struct nvme_identify_domain_attr for the definition of the
+ * returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_domain_list(int fd, __u16 domid,
+ struct nvme_id_domain_list *list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = list,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_DOMAIN_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = domid,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_endurance_group_list() - Endurance group list data
+ * @fd: File descriptor of nvme device
+ * @endgrp_id: Endurance group identifier
+ * @list: Array of endurance group identifiers
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_endurance_group_list(int fd, __u16 endgrp_id,
+ struct nvme_id_endurance_group_list *list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = list,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = endgrp_id,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_identify_iocs() - I/O command set data structure
+ * @fd: File descriptor of nvme device
+ * @cntlid: Controller ID
+ * @iocs: User space destination address to transfer the data
+ *
+ * Retrieves list of the controller's supported io command set vectors. See
+ * &struct nvme_id_iocs.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_identify_iocs(int fd, __u16 cntlid,
+ struct nvme_id_iocs *iocs)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = iocs,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .cns = NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = cntlid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_identify(&args);
+}
+
+/**
+ * nvme_zns_identify_ns() - ZNS identify namespace data
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace to identify
+ * @data: User space destination address to transfer the data
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_zns_identify_ns(int fd, __u32 nsid,
+ struct nvme_zns_id_ns *data)
+{
+ return nvme_identify_ns_csi(
+ fd, nsid, NVME_UUID_NONE, NVME_CSI_ZNS, data);
+}
+
+/**
+ * nvme_zns_identify_ctrl() - ZNS identify controller data
+ * @fd: File descriptor of nvme device
+ * @id: User space destination address to transfer the data
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_zns_identify_ctrl(int fd, struct nvme_zns_id_ctrl *id)
+{
+ return nvme_identify_ctrl_csi(fd, NVME_CSI_ZNS, id);
+}
+
+/**
+ * nvme_get_log() - NVMe Admin Get Log command
+ * @args: &struct nvme_get_log_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_log(struct nvme_get_log_args *args);
+
+/**
+ * nvme_get_log_page() - Get log page data
+ * @fd: File descriptor of nvme device
+ * @xfer_len: Max log transfer size per request to split the total.
+ * @args: &struct nvme_get_log_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args);
+
+static inline int nvme_get_nsid_log(int fd, bool rae,
+ enum nvme_cmd_get_log_lid lid,
+ __u32 nsid, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = lid,
+ .len = len,
+ .nsid = nsid,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+static inline int nvme_get_log_simple(int fd, enum nvme_cmd_get_log_lid lid,
+ __u32 len, void *log)
+{
+ return nvme_get_nsid_log(fd, false, lid, NVME_NSID_ALL, len, log);
+}
+
+/**
+ * nvme_get_log_supported_log_pages() - Retrieve nmve supported log pages
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @log: Array of LID supported and Effects data structures
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_supported_log_pages(int fd, bool rae,
+ struct nvme_supported_log_pages *log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_SUPPORTED_LOG_PAGES,
+ NVME_NSID_ALL, sizeof(*log), log);
+}
+
+/**
+ * nvme_get_log_error() - Retrieve nvme error log
+ * @fd: File descriptor of nvme device
+ * @nr_entries: Number of error log entries allocated
+ * @rae: Retain asynchronous events
+ * @err_log: Array of error logs of size 'entries'
+ *
+ * This log page describes extended error information for a command that
+ * completed with error, or may report an error that is not specific to a
+ * particular command.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_error(int fd, unsigned int nr_entries, bool rae,
+ struct nvme_error_log_page *err_log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_ERROR,
+ NVME_NSID_ALL, sizeof(*err_log) * nr_entries,
+ err_log);
+}
+
+/**
+ * nvme_get_log_smart() - Retrieve nvme smart log
+ * @fd: File descriptor of nvme device
+ * @nsid: Optional namespace identifier
+ * @rae: Retain asynchronous events
+ * @smart_log: User address to store the smart log
+ *
+ * This log page provides SMART and general health information. The information
+ * provided is over the life of the controller and is retained across power
+ * cycles. To request the controller log page, the namespace identifier
+ * specified is FFFFFFFFh. The controller may also support requesting the log
+ * page on a per namespace basis, as indicated by bit 0 of the LPA field in the
+ * Identify Controller data structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_smart(int fd, __u32 nsid, bool rae,
+ struct nvme_smart_log *smart_log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_SMART,
+ nsid, sizeof(*smart_log), smart_log);
+}
+
+/**
+ * nvme_get_log_fw_slot() - Retrieves the controller firmware log
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @fw_log: User address to store the log page
+ *
+ * This log page describes the firmware revision stored in each firmware slot
+ * supported. The firmware revision is indicated as an ASCII string. The log
+ * page also indicates the active slot number.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_fw_slot(int fd, bool rae,
+ struct nvme_firmware_slot *fw_log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_FW_SLOT,
+ NVME_NSID_ALL, sizeof(*fw_log), fw_log);
+}
+
+/**
+ * nvme_get_log_changed_ns_list() - Retrieve namespace changed list
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @ns_log: User address to store the log page
+ *
+ * This log page describes namespaces attached to this controller that have
+ * changed since the last time the namespace was identified, been added, or
+ * deleted.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_changed_ns_list(int fd, bool rae,
+ struct nvme_ns_list *ns_log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_CHANGED_NS,
+ NVME_NSID_ALL, sizeof(*ns_log), ns_log);
+}
+
+/**
+ * nvme_get_log_cmd_effects() - Retrieve nvme command effects log
+ * @fd: File descriptor of nvme device
+ * @csi: Command Set Identifier
+ * @effects_log:User address to store the effects log
+ *
+ * This log page describes the commands that the controller supports and the
+ * effects of those commands on the state of the NVM subsystem.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_cmd_effects(int fd, enum nvme_csi csi,
+ struct nvme_cmd_effects_log *effects_log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = effects_log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_CMD_EFFECTS,
+ .len = sizeof(*effects_log),
+ .nsid = NVME_NSID_ALL,
+ .csi = csi,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_device_self_test() - Retrieve the device self test log
+ * @fd: File descriptor of nvme device
+ * @log: Userspace address of the log payload
+ *
+ * The log page indicates the status of an in progress self test and the
+ * percent complete of that operation, and the results of the previous 20
+ * self-test operations.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_device_self_test(int fd,
+ struct nvme_self_test_log *log)
+{
+ return nvme_get_nsid_log(fd, false, NVME_LOG_LID_DEVICE_SELF_TEST,
+ NVME_NSID_ALL, sizeof(*log), log);
+}
+
+/**
+ * nvme_get_log_create_telemetry_host() - Create host telemetry log
+ * @fd: File descriptor of nvme device
+ * @log: Userspace address of the log payload
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_create_telemetry_host(int fd,
+ struct nvme_telemetry_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_TELEMETRY_HOST,
+ .len = sizeof(*log),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_TELEM_HOST_LSP_CREATE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_telemetry_host() - Get Telemetry Host-Initiated log page
+ * @fd: File descriptor of nvme device
+ * @offset: Offset into the telemetry data
+ * @len: Length of provided user buffer to hold the log data in bytes
+ * @log: User address for log page data
+ *
+ * Retrieves the Telemetry Host-Initiated log page at the requested offset
+ * using the previously existing capture.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_telemetry_host(int fd, __u64 offset,
+ __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_TELEMETRY_HOST,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_TELEM_HOST_LSP_RETAIN,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_telemetry_ctrl() - Get Telemetry Controller-Initiated log page
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @offset: Offset into the telemetry data
+ * @len: Length of provided user buffer to hold the log data in bytes
+ * @log: User address for log page data
+ *
+ * Retrieves the Telemetry Controller-Initiated log page at the requested offset
+ * using the previously existing capture.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_telemetry_ctrl(int fd, bool rae,
+ __u64 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_TELEMETRY_CTRL,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_endurance_group() - Get Endurance Group log
+ * @fd: File descriptor of nvme device
+ * @endgid: Starting group identifier to return in the list
+ * @log: User address to store the endurance log
+ *
+ * This log page indicates if an Endurance Group Event has occurred for a
+ * particular Endurance Group. If an Endurance Group Event has occurred, the
+ * details of the particular event are included in the Endurance Group
+ * Information log page for that Endurance Group. An asynchronous event is
+ * generated when an entry for an Endurance Group is newly added to this log
+ * page.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_endurance_group(int fd, __u16 endgid,
+ struct nvme_endurance_group_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_ENDURANCE_GROUP,
+ .len = sizeof(*log),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = endgid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_predictable_lat_nvmset() - Predictable Latency Per NVM Set
+ * @fd: File descriptor of nvme device
+ * @nvmsetid: NVM set id
+ * @log: User address to store the predictable latency log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_predictable_lat_nvmset(int fd, __u16 nvmsetid,
+ struct nvme_nvmset_predictable_lat_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_PREDICTABLE_LAT_NVMSET,
+ .len = sizeof(*log),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = nvmsetid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_predictable_lat_event() - Retrieve Predictable Latency Event Aggregate Log Page
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @offset: Offset into the predictable latency event
+ * @len: Length of provided user buffer to hold the log data in bytes
+ * @log: User address for log page data
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_predictable_lat_event(int fd, bool rae,
+ __u32 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_PREDICTABLE_LAT_AGG,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_fdp_configurations() - Get list of Flexible Data Placement configurations
+ * @fd: File descriptor of nvme device
+ * @egid: Endurance group identifier
+ * @offset: Offset into log page
+ * @len: Length (in bytes) of provided user buffer to hold the log data
+ * @log: Log page data buffer
+ */
+static inline int nvme_get_log_fdp_configurations(int fd, __u16 egid,
+ __u32 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_FDP_CONFIGS,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = egid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_get_log(&args);
+}
+
+/**
+ * nvme_get_log_reclaim_unit_handle_usage() - Get reclaim unit handle usage
+ * @fd: File descriptor of nvme device
+ * @egid: Endurance group identifier
+ * @offset: Offset into log page
+ * @len: Length (in bytes) of provided user buffer to hold the log data
+ * @log: Log page data buffer
+ */
+static inline int nvme_get_log_reclaim_unit_handle_usage(int fd, __u16 egid,
+ __u32 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_FDP_RUH_USAGE,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = egid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_get_log(&args);
+}
+
+/**
+ * nvme_get_log_fdp_stats() - Get Flexible Data Placement statistics
+ * @fd: File descriptor of nvme device
+ * @egid: Endurance group identifier
+ * @offset: Offset into log page
+ * @len: Length (in bytes) of provided user buffer to hold the log data
+ * @log: Log page data buffer
+ */
+static inline int nvme_get_log_fdp_stats(int fd, __u16 egid, __u32 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_FDP_STATS,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = egid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_get_log(&args);
+}
+
+/**
+ * nvme_get_log_fdp_events() - Get Flexible Data Placement events
+ * @fd: File descriptor of nvme device
+ * @egid: Endurance group identifier
+ * @host_events: Whether to report host or controller events
+ * @offset: Offset into log page
+ * @len: Length (in bytes) of provided user buffer to hold the log data
+ * @log: Log page data buffer
+ */
+static inline int nvme_get_log_fdp_events(int fd, __u16 egid, bool host_events, __u32 offset,
+ __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_FDP_EVENTS,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = egid,
+ .lsp = (__u8)(host_events ? 0x1 : 0x0),
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_get_log(&args);
+}
+
+/**
+ * nvme_get_log_ana() - Retrieve Asymmetric Namespace Access log page
+ * @fd: File descriptor of nvme device
+ * @lsp: Log specific, see &enum nvme_get_log_ana_lsp
+ * @rae: Retain asynchronous events
+ * @offset: Offset to the start of the log page
+ * @len: The allocated length of the log page
+ * @log: User address to store the ana log
+ *
+ * This log consists of a header describing the log and descriptors containing
+ * the asymmetric namespace access information for ANA Groups that contain
+ * namespaces that are attached to the controller processing the command.
+ *
+ * See &struct nvme_ana_rsp_hdr for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_ana(int fd, enum nvme_log_ana_lsp lsp, bool rae,
+ __u64 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_ANA,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = (__u8)lsp,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_ana_groups() - Retrieve Asymmetric Namespace Access groups only log page
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @len: The allocated length of the log page
+ * @log: User address to store the ana group log
+ *
+ * See &struct nvme_ana_group_desc for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_ana_groups(int fd, bool rae, __u32 len,
+ struct nvme_ana_group_desc *log)
+{
+ return nvme_get_log_ana(fd, NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY, rae, 0,
+ len, log);
+}
+
+/**
+ * nvme_get_log_lba_status() - Retrieve LBA Status
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @offset: Offset to the start of the log page
+ * @len: The allocated length of the log page
+ * @log: User address to store the log page
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_lba_status(int fd, bool rae,
+ __u64 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_LBA_STATUS,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_endurance_grp_evt() - Retrieve Rotational Media Information
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @offset: Offset to the start of the log page
+ * @len: The allocated length of the log page
+ * @log: User address to store the log page
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_endurance_grp_evt(int fd, bool rae,
+ __u32 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_ENDURANCE_GRP_EVT,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_fid_supported_effects() - Retrieve Feature Identifiers Supported and Effects
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @log: FID Supported and Effects data structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise
+ */
+static inline int nvme_get_log_fid_supported_effects(int fd, bool rae,
+ struct nvme_fid_supported_effects_log *log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_FID_SUPPORTED_EFFECTS,
+ NVME_NSID_NONE, sizeof(*log), log);
+}
+
+/**
+ * nvme_get_log_mi_cmd_supported_effects() - displays the MI Commands Supported by the controller
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @log: MI Command Supported and Effects data structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise
+ */
+static inline int nvme_get_log_mi_cmd_supported_effects(int fd, bool rae,
+ struct nvme_mi_cmd_supported_effects_log *log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS,
+ NVME_NSID_NONE, sizeof(*log), log);
+}
+
+/**
+ * nvme_get_log_boot_partition() - Retrieve Boot Partition
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @lsp: The log specified field of LID
+ * @len: The allocated size, minimum
+ * struct nvme_boot_partition
+ * @part: User address to store the log page
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise
+ */
+static inline int nvme_get_log_boot_partition(int fd, bool rae,
+ __u8 lsp, __u32 len, struct nvme_boot_partition *part)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = part,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_BOOT_PARTITION,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = lsp,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_phy_rx_eom() - Retrieve Physical Interface Receiver Eye Opening Measurement Log
+ * @fd: File descriptor of nvme device
+ * @lsp: Log specific, controls action and measurement quality
+ * @controller: Target controller ID
+ * @len: The allocated size, minimum
+ * struct nvme_phy_rx_eom_log
+ * @log: User address to store the log page
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise
+ */
+static inline int nvme_get_log_phy_rx_eom(int fd, __u8 lsp, __u16 controller,
+ __u32 len, struct nvme_phy_rx_eom_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_PHY_RX_EOM,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = controller,
+ .lsp = lsp,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_discovery() - Retrieve Discovery log page
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @offset: Offset of this log to retrieve
+ * @len: The allocated size for this portion of the log
+ * @log: User address to store the discovery log
+ *
+ * Supported only by fabrics discovery controllers, returning discovery
+ * records.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_discovery(int fd, bool rae,
+ __u32 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_DISCOVER,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_media_unit_stat() - Retrieve Media Unit Status
+ * @fd: File descriptor of nvme device
+ * @domid: Domain Identifier selection, if supported
+ * @mus: User address to store the Media Unit statistics log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise
+ */
+static inline int nvme_get_log_media_unit_stat(int fd, __u16 domid,
+ struct nvme_media_unit_stat_log *mus)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = mus,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_MEDIA_UNIT_STATUS,
+ .len = sizeof(*mus),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = domid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_support_cap_config_list() - Retrieve Supported Capacity Configuration List
+ * @fd: File descriptor of nvme device
+ * @domid: Domain Identifier selection, if supported
+ * @cap: User address to store supported capabilities config list
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise
+ */
+static inline int nvme_get_log_support_cap_config_list(int fd, __u16 domid,
+ struct nvme_supported_cap_config_list_log *cap)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = cap,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST,
+ .len = sizeof(*cap),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = domid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_reservation() - Retrieve Reservation Notification
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @log: User address to store the reservation log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise
+ */
+static inline int nvme_get_log_reservation(int fd, bool rae,
+ struct nvme_resv_notification_log *log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_RESERVATION,
+ NVME_NSID_ALL, sizeof(*log), log);
+}
+
+/**
+ * nvme_get_log_sanitize() - Retrieve Sanitize Status
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @log: User address to store the sanitize log
+ *
+ * The Sanitize Status log page reports sanitize operation time estimates and
+ * information about the most recent sanitize operation.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_sanitize(int fd, bool rae,
+ struct nvme_sanitize_log_page *log)
+{
+ return nvme_get_nsid_log(fd, rae, NVME_LOG_LID_SANITIZE,
+ NVME_NSID_ALL, sizeof(*log), log);
+}
+
+/**
+ * nvme_get_log_zns_changed_zones() - Retrieve list of zones that have changed
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @rae: Retain asynchronous events
+ * @log: User address to store the changed zone log
+ *
+ * The list of zones that have changed state due to an exceptional event.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_zns_changed_zones(int fd, __u32 nsid, bool rae,
+ struct nvme_zns_changed_zone_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_ZNS_CHANGED_ZONES,
+ .len = sizeof(*log),
+ .nsid = nsid,
+ .csi = NVME_CSI_ZNS,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_get_log_persistent_event() - Retrieve Persistent Event Log
+ * @fd: File descriptor of nvme device
+ * @action: Action the controller should take during processing this command
+ * @size: Size of @pevent_log
+ * @pevent_log: User address to store the persistent event log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_persistent_event(int fd,
+ enum nvme_pevent_log_action action,
+ __u32 size, void *pevent_log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = pevent_log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_PERSISTENT_EVENT,
+ .len = size,
+ .nsid = NVME_NSID_ALL,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = (__u8)action,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
+ * nvme_set_features() - Set a feature attribute
+ * @args: &struct nvme_set_features_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features(struct nvme_set_features_args *args);
+
+/**
+ * nvme_set_features_data() - Helper function for @nvme_set_features()
+ * @fd: File descriptor of nvme device
+ * @fid: Feature identifier
+ * @nsid: Namespace ID, if applicable
+ * @cdw11: Value to set the feature to
+ * @save: Save value across power states
+ * @data_len: Length of feature data, if applicable, in bytes
+ * @data: User address of feature data, if applicable
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_set_features_data(int fd, __u8 fid, __u32 nsid,
+ __u32 cdw11, bool save, __u32 data_len, void *data,
+ __u32 *result)
+{
+ struct nvme_set_features_args args = {
+ .result = result,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .cdw11 = cdw11,
+ .cdw12 = 0,
+ .cdw13 = 0,
+ .cdw15 = 0,
+ .data_len = data_len,
+ .save = save,
+ .uuidx = 0,
+ .fid = fid,
+ };
+ return nvme_set_features(&args);
+}
+
+/**
+ * nvme_set_features_simple() - Helper function for @nvme_set_features()
+ * @fd: File descriptor of nvme device
+ * @fid: Feature identifier
+ * @nsid: Namespace ID, if applicable
+ * @cdw11: Value to set the feature to
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_set_features_simple(int fd, __u8 fid, __u32 nsid,
+ __u32 cdw11, bool save, __u32 *result)
+{
+ return nvme_set_features_data(fd, fid, nsid, cdw11, save, 0, NULL,
+ result);
+}
+
+/**
+ * nvme_set_features_arbitration() - Set arbitration features
+ * @fd: File descriptor of nvme device
+ * @ab: Arbitration Burst
+ * @lpw: Low Priority Weight
+ * @mpw: Medium Priority Weight
+ * @hpw: High Priority Weight
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_arbitration(int fd, __u8 ab, __u8 lpw, __u8 mpw,
+ __u8 hpw, bool save, __u32 *result);
+
+/**
+ * nvme_set_features_power_mgmt() - Set power management feature
+ * @fd: File descriptor of nvme device
+ * @ps: Power State
+ * @wh: Workload Hint
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_power_mgmt(int fd, __u8 ps, __u8 wh, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_lba_range() - Set LBA range feature
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @nr_ranges: Number of ranges in @data
+ * @save: Save value across power states
+ * @data: User address of feature data
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_lba_range(int fd, __u32 nsid, __u8 nr_ranges, bool save,
+ struct nvme_lba_range_type *data, __u32 *result);
+
+/**
+ * nvme_set_features_temp_thresh() - Set temperature threshold feature
+ * @fd: File descriptor of nvme device
+ * @tmpth: Temperature Threshold
+ * @tmpsel: Threshold Temperature Select
+ * @thsel: Threshold Type Select
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_temp_thresh(int fd, __u16 tmpth, __u8 tmpsel,
+ enum nvme_feat_tmpthresh_thsel thsel,
+ bool save, __u32 *result);
+
+/**
+ * nvme_set_features_err_recovery() - Set error recovery feature
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @tler: Time-limited error recovery value
+ * @dulbe: Deallocated or Unwritten Logical Block Error Enable
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_err_recovery(int fd, __u32 nsid, __u16 tler,
+ bool dulbe, bool save, __u32 *result);
+
+/**
+ * nvme_set_features_volatile_wc() - Set volatile write cache feature
+ * @fd: File descriptor of nvme device
+ * @wce: Write cache enable
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_volatile_wc(int fd, bool wce, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_irq_coalesce() - Set IRQ coalesce feature
+ * @fd: File descriptor of nvme device
+ * @thr: Aggregation Threshold
+ * @time: Aggregation Time
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_irq_coalesce(int fd, __u8 thr, __u8 time,
+ bool save, __u32 *result);
+
+/**
+ * nvme_set_features_irq_config() - Set IRQ config feature
+ * @fd: File descriptor of nvme device
+ * @iv: Interrupt Vector
+ * @cd: Coalescing Disable
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_irq_config(int fd, __u16 iv, bool cd, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_write_atomic() - Set write atomic feature
+ * @fd: File descriptor of nvme device
+ * @dn: Disable Normal
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_write_atomic(int fd, bool dn, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_async_event() - Set asynchronous event feature
+ * @fd: File descriptor of nvme device
+ * @events: Events to enable
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_async_event(int fd, __u32 events, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_auto_pst() - Set autonomous power state feature
+ * @fd: File descriptor of nvme device
+ * @apste: Autonomous Power State Transition Enable
+ * @apst: Autonomous Power State Transition
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_auto_pst(int fd, bool apste, bool save,
+ struct nvme_feat_auto_pst *apst,
+ __u32 *result);
+
+/**
+ * nvme_set_features_timestamp() - Set timestamp feature
+ * @fd: File descriptor of nvme device
+ * @save: Save value across power states
+ * @timestamp: The current timestamp value to assign to this feature
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_timestamp(int fd, bool save, __u64 timestamp);
+
+/**
+ * nvme_set_features_hctm() - Set thermal management feature
+ * @fd: File descriptor of nvme device
+ * @tmt2: Thermal Management Temperature 2
+ * @tmt1: Thermal Management Temperature 1
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_hctm(int fd, __u16 tmt2, __u16 tmt1, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_nopsc() - Set non-operational power state feature
+ * @fd: File descriptor of nvme device
+ * @noppme: Non-Operational Power State Permissive Mode Enable
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_nopsc(int fd, bool noppme, bool save, __u32 *result);
+
+/**
+ * nvme_set_features_rrl() - Set read recovery level feature
+ * @fd: File descriptor of nvme device
+ * @rrl: Read recovery level setting
+ * @nvmsetid: NVM set id
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_rrl(int fd, __u8 rrl, __u16 nvmsetid, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_plm_config() - Set predictable latency feature
+ * @fd: File descriptor of nvme device
+ * @enable: Predictable Latency Enable
+ * @nvmsetid: NVM Set Identifier
+ * @save: Save value across power states
+ * @data: Pointer to structure nvme_plm_config
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_plm_config(int fd, bool enable, __u16 nvmsetid,
+ bool save, struct nvme_plm_config *data,
+ __u32 *result);
+
+/**
+ * nvme_set_features_plm_window() - Set window select feature
+ * @fd: File descriptor of nvme device
+ * @sel: Window Select
+ * @nvmsetid: NVM Set Identifier
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_plm_window(int fd, enum nvme_feat_plm_window_select sel,
+ __u16 nvmsetid, bool save, __u32 *result);
+
+/**
+ * nvme_set_features_lba_sts_interval() - Set LBA status information feature
+ * @fd: File descriptor of nvme device
+ * @save: Save value across power states
+ * @lsiri: LBA Status Information Report Interval
+ * @lsipi: LBA Status Information Poll Interval
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_lba_sts_interval(int fd, __u16 lsiri, __u16 lsipi,
+ bool save, __u32 *result);
+
+/**
+ * nvme_set_features_host_behavior() - Set host behavior feature
+ * @fd: File descriptor of nvme device
+ * @save: Save value across power states
+ * @data: Pointer to structure nvme_feat_host_behavior
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_host_behavior(int fd, bool save,
+ struct nvme_feat_host_behavior *data);
+
+/**
+ * nvme_set_features_sanitize() - Set sanitize feature
+ * @fd: File descriptor of nvme device
+ * @nodrm: No-Deallocate Response Mode
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_sanitize(int fd, bool nodrm, bool save, __u32 *result);
+
+/**
+ * nvme_set_features_endurance_evt_cfg() - Set endurance event config feature
+ * @fd: File descriptor of nvme device
+ * @endgid: Endurance Group Identifier
+ * @egwarn: Flags to enable warning, see &enum nvme_eg_critical_warning_flags
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_endurance_evt_cfg(int fd, __u16 endgid, __u8 egwarn,
+ bool save, __u32 *result);
+
+/**
+ * nvme_set_features_sw_progress() - Set pre-boot software load count feature
+ * @fd: File descriptor of nvme device
+ * @pbslc: Pre-boot Software Load Count
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_sw_progress(int fd, __u8 pbslc, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_host_id() - Set enable extended host identifiers feature
+ * @fd: File descriptor of nvme device
+ * @exhid: Enable Extended Host Identifier
+ * @save: Save value across power states
+ * @hostid: Host ID to set
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_host_id(int fd, bool exhid, bool save, __u8 *hostid);
+
+/**
+ * nvme_set_features_resv_mask() - Set reservation notification mask feature
+ *
+ * Deprecated: doesn't support specifying a NSID.
+ * Use nvme_set_features_resv_mask2() instead.
+ *
+ * @fd: File descriptor of nvme device
+ * @mask: Reservation Notification Mask Field
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_resv_mask(int fd, __u32 mask, bool save, __u32 *result)
+ __attribute__((deprecated));
+
+/**
+ * nvme_set_features_resv_mask2() - Set reservation notification mask feature
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @mask: Reservation Notification Mask Field
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_resv_mask2(int fd, __u32 nsid, __u32 mask, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_resv_persist() - Set persist through power loss feature
+ *
+ * Deprecated: doesn't support specifying a NSID.
+ * Use nvme_set_features_resv_persist2() instead.
+ *
+ * @fd: File descriptor of nvme device
+ * @ptpl: Persist Through Power Loss
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_resv_persist(int fd, bool ptpl, bool save, __u32 *result)
+ __attribute__((deprecated));
+
+/**
+ * nvme_set_features_resv_persist2() - Set persist through power loss feature
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @ptpl: Persist Through Power Loss
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_resv_persist2(int fd, __u32 nsid, bool ptpl, bool save,
+ __u32 *result);
+
+/**
+ * nvme_set_features_write_protect() - Set write protect feature
+ *
+ * Deprecated: doesn't support specifying a NSID.
+ * Use nvme_set_features_write_protect2() instead.
+ *
+ * @fd: File descriptor of nvme device
+ * @state: Write Protection State
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_write_protect(int fd, enum nvme_feat_nswpcfg_state state,
+ bool save, __u32 *result)
+ __attribute__((deprecated));
+
+/**
+ * nvme_set_features_write_protect2() - Set write protect feature
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @state: Write Protection State
+ * @save: Save value across power states
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_write_protect2(int fd, __u32 nsid,
+ enum nvme_feat_nswpcfg_state state,
+ bool save, __u32 *result);
+
+/**
+ * nvme_set_features_iocs_profile() - Set I/O command set profile feature
+ * @fd: File descriptor of nvme device
+ * @iocsi: I/O Command Set Combination Index
+ * @save: Save value across power states
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_features_iocs_profile(int fd, __u16 iocsi, bool save);
+
+/**
+ * nvme_get_features() - Retrieve a feature attribute
+ * @args: &struct nvme_get_features_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features(struct nvme_get_features_args *args);
+
+/**
+ * nvme_get_features_data() - Helper function for @nvme_get_features()
+ * @fd: File descriptor of nvme device
+ * @fid: Feature identifier
+ * @nsid: Namespace ID, if applicable
+ * @data_len: Length of feature data, if applicable, in bytes
+ * @data: User address of feature data, if applicable
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_features_data(int fd, enum nvme_features_id fid,
+ __u32 nsid, __u32 data_len, void *data, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .result = result,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .sel = NVME_GET_FEATURES_SEL_CURRENT,
+ .cdw11 = 0,
+ .data_len = data_len,
+ .fid = (__u8)fid,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_get_features(&args);
+}
+
+/**
+ * nvme_get_features_simple() - Helper function for @nvme_get_features()
+ * @fd: File descriptor of nvme device
+ * @fid: Feature identifier
+ * @nsid: Namespace ID, if applicable
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_features_simple(int fd, enum nvme_features_id fid,
+ __u32 nsid, __u32 *result)
+{
+ return nvme_get_features_data(fd, fid, nsid, 0, NULL, result);
+}
+
+/**
+ * nvme_get_features_arbitration() - Get arbitration feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_arbitration(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_power_mgmt() - Get power management feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_power_mgmt(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_lba_range() - Get LBA range feature
+ *
+ * Deprecated: doesn't support specifying a NSID.
+ * Use nvme_get_features_lba_range2() instead.
+ *
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @data: User address of feature data, if applicable
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_lba_range(int fd, enum nvme_get_features_sel sel,
+ struct nvme_lba_range_type *data,
+ __u32 *result) __attribute__((deprecated));
+
+/**
+ * nvme_get_features_lba_range2() - Get LBA range feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @nsid: Namespace ID
+ * @data: Buffer to receive LBA Range Type data structure
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_lba_range2(int fd, enum nvme_get_features_sel sel,
+ __u32 nsid, struct nvme_lba_range_type *data,
+ __u32 *result);
+
+/**
+ * nvme_get_features_temp_thresh() - Get temperature threshold feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_temp_thresh(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_err_recovery() - Get error recovery feature
+ *
+ * Deprecated: doesn't support specifying a NSID.
+ * Use nvme_get_features_err_recovery2() instead.
+ *
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_err_recovery(int fd, enum nvme_get_features_sel sel,
+ __u32 *result) __attribute__((deprecated));
+
+/**
+ * nvme_get_features_err_recovery2() - Get error recovery feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @nsid: Namespace ID
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_err_recovery2(int fd, enum nvme_get_features_sel sel,
+ __u32 nsid, __u32 *result);
+
+/**
+ * nvme_get_features_volatile_wc() - Get volatile write cache feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_volatile_wc(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_num_queues() - Get number of queues feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_num_queues(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_irq_coalesce() - Get IRQ coalesce feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_irq_coalesce(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_irq_config() - Get IRQ config feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @iv:
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_irq_config(int fd, enum nvme_get_features_sel sel,
+ __u16 iv, __u32 *result);
+
+/**
+ * nvme_get_features_write_atomic() - Get write atomic feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_write_atomic(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_async_event() - Get asynchronous event feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_async_event(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_auto_pst() - Get autonomous power state feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @apst:
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_auto_pst(int fd, enum nvme_get_features_sel sel,
+ struct nvme_feat_auto_pst *apst, __u32 *result);
+
+/**
+ * nvme_get_features_host_mem_buf() - Get host memory buffer feature
+ *
+ * Deprecated: doesn't fetch the Host Memory Buffer Attributes data structure.
+ * Use nvme_get_features_host_mem_buf2() instead.
+ *
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_host_mem_buf(int fd, enum nvme_get_features_sel sel,
+ __u32 *result) __attribute__((deprecated));
+
+/**
+ * nvme_get_features_host_mem_buf2() - Get host memory buffer feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @attrs: Buffer for returned Host Memory Buffer Attributes
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_host_mem_buf2(int fd, enum nvme_get_features_sel sel,
+ struct nvme_host_mem_buf_attrs *attrs,
+ __u32 *result);
+
+/**
+ * nvme_get_features_timestamp() - Get timestamp feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @ts: Current timestamp
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_timestamp(int fd, enum nvme_get_features_sel sel,
+ struct nvme_timestamp *ts);
+
+/**
+ * nvme_get_features_kato() - Get keep alive timeout feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_kato(int fd, enum nvme_get_features_sel sel, __u32 *result);
+
+/**
+ * nvme_get_features_hctm() - Get thermal management feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_hctm(int fd, enum nvme_get_features_sel sel, __u32 *result);
+
+/**
+ * nvme_get_features_nopsc() - Get non-operational power state feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_nopsc(int fd, enum nvme_get_features_sel sel, __u32 *result);
+
+/**
+ * nvme_get_features_rrl() - Get read recovery level feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_rrl(int fd, enum nvme_get_features_sel sel, __u32 *result);
+
+/**
+ * nvme_get_features_plm_config() - Get predictable latency feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @nvmsetid: NVM set id
+ * @data:
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_plm_config(int fd, enum nvme_get_features_sel sel,
+ __u16 nvmsetid, struct nvme_plm_config *data,
+ __u32 *result);
+
+/**
+ * nvme_get_features_plm_window() - Get window select feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @nvmsetid: NVM set id
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_plm_window(int fd, enum nvme_get_features_sel sel,
+ __u16 nvmsetid, __u32 *result);
+
+/**
+ * nvme_get_features_lba_sts_interval() - Get LBA status information feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_lba_sts_interval(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_host_behavior() - Get host behavior feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @data: Pointer to structure nvme_feat_host_behavior
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_host_behavior(int fd, enum nvme_get_features_sel sel,
+ struct nvme_feat_host_behavior *data,
+ __u32 *result);
+
+/**
+ * nvme_get_features_sanitize() - Get sanitize feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_sanitize(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_endurance_event_cfg() - Get endurance event config feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @endgid: Endurance Group Identifier
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_endurance_event_cfg(int fd, enum nvme_get_features_sel sel,
+ __u16 endgid, __u32 *result);
+
+/**
+ * nvme_get_features_sw_progress() - Get software progress feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_sw_progress(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_host_id() - Get host id feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @exhid: Enable Extended Host Identifier
+ * @len: Length of @hostid
+ * @hostid: Buffer for returned host ID
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_host_id(int fd, enum nvme_get_features_sel sel,
+ bool exhid, __u32 len, __u8 *hostid);
+
+/**
+ * nvme_get_features_resv_mask() - Get reservation mask feature
+ *
+ * Deprecated: doesn't support specifying a NSID.
+ * Use nvme_get_features_resv_mask2() instead.
+ *
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_resv_mask(int fd, enum nvme_get_features_sel sel,
+ __u32 *result) __attribute__((deprecated));
+
+/**
+ * nvme_get_features_resv_mask2() - Get reservation mask feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @nsid: Namespace ID
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_resv_mask2(int fd, enum nvme_get_features_sel sel,
+ __u32 nsid, __u32 *result);
+
+/**
+ * nvme_get_features_resv_persist() - Get reservation persist feature
+ *
+ * Deprecated: doesn't support specifying a NSID.
+ * Use nvme_get_features_resv_persist2() instead.
+ *
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_resv_persist(int fd, enum nvme_get_features_sel sel,
+ __u32 *result) __attribute__((deprecated));
+
+/**
+ * nvme_get_features_resv_persist2() - Get reservation persist feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @nsid: Namespace ID
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_resv_persist2(int fd, enum nvme_get_features_sel sel,
+ __u32 nsid, __u32 *result);
+
+/**
+ * nvme_get_features_write_protect() - Get write protect feature
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_write_protect(int fd, __u32 nsid,
+ enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_get_features_iocs_profile() - Get IOCS profile feature
+ * @fd: File descriptor of nvme device
+ * @sel: Select which type of attribute to return, see &enum nvme_get_features_sel
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_features_iocs_profile(int fd, enum nvme_get_features_sel sel,
+ __u32 *result);
+
+/**
+ * nvme_format_nvm() - Format nvme namespace(s)
+ * @args: &struct nvme_format_nvme_args argument structure
+ *
+ * The Format NVM command low level formats the NVM media. This command is used
+ * by the host to change the LBA data size and/or metadata size. A low level
+ * format may destroy all data and metadata associated with all namespaces or
+ * only the specific namespace associated with the command
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_format_nvm(struct nvme_format_nvm_args *args);
+
+/**
+ * nvme_ns_mgmt() - Issue a Namespace management command
+ * @args: &struct nvme_ns_mgmt_args Argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_ns_mgmt(struct nvme_ns_mgmt_args *args);
+
+/**
+ * nvme_ns_mgmt_create() - Create a non attached namespace
+ * @fd: File descriptor of nvme device
+ * @ns: Namespace identification that defines ns creation parameters
+ * @nsid: On success, set to the namespace id that was created
+ * @timeout: Override the default timeout to this value in milliseconds;
+ * set to 0 to use the system default.
+ * @csi: Command Set Identifier
+ * @data: Host Software Specified Fields that defines ns creation parameters
+ *
+ * On successful creation, the namespace exists in the subsystem, but is not
+ * attached to any controller. Use the nvme_ns_attach_ctrls() to assign the
+ * namespace to one or more controllers.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_ns_mgmt_create(int fd, struct nvme_id_ns *ns,
+ __u32 *nsid, __u32 timeout, __u8 csi,
+ struct nvme_ns_mgmt_host_sw_specified *data)
+{
+ struct nvme_ns_mgmt_args args = {
+ .result = nsid,
+ .ns = ns,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = timeout,
+ .nsid = NVME_NSID_NONE,
+ .sel = NVME_NS_MGMT_SEL_CREATE,
+ .csi = csi,
+ .rsvd1 = { 0, },
+ .rsvd2 = NULL,
+ .data = data,
+ };
+
+ return nvme_ns_mgmt(&args);
+}
+
+/**
+ * nvme_ns_mgmt_delete() - Delete a non attached namespace
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace identifier to delete
+ *
+ * It is recommended that a namespace being deleted is not attached to any
+ * controller. Use the nvme_ns_detach_ctrls() first if the namespace is still
+ * attached.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_ns_mgmt_delete(int fd, __u32 nsid)
+{
+ struct nvme_ns_mgmt_args args = {
+ .result = NULL,
+ .ns = NULL,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = 0,
+ .nsid = nsid,
+ .sel = NVME_NS_MGMT_SEL_DELETE,
+ .csi = 0,
+ .rsvd1 = { 0, },
+ .rsvd2 = NULL,
+ .data = NULL,
+ };
+
+ return nvme_ns_mgmt(&args);
+}
+
+/**
+ * nvme_ns_attach() - Attach or detach namespace to controller(s)
+ * @args: &struct nvme_ns_attach_args Argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_ns_attach(struct nvme_ns_attach_args *args);
+
+/**
+ * nvme_ns_attach_ctrls() - Attach namespace to controllers
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID to attach
+ * @ctrlist: Controller list to modify attachment state of nsid
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_ns_attach_ctrls(int fd, __u32 nsid,
+ struct nvme_ctrl_list *ctrlist)
+{
+ struct nvme_ns_attach_args args = {
+ .result = NULL,
+ .ctrlist = ctrlist,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .sel = NVME_NS_ATTACH_SEL_CTRL_ATTACH,
+ };
+
+ return nvme_ns_attach(&args);
+}
+
+/**
+ * nvme_ns_detach_ctrls() - Detach namespace from controllers
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID to detach
+ * @ctrlist: Controller list to modify attachment state of nsid
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_ns_detach_ctrls(int fd, __u32 nsid,
+ struct nvme_ctrl_list *ctrlist)
+{
+ struct nvme_ns_attach_args args = {
+ .result = NULL,
+ .ctrlist = ctrlist,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH,
+ };
+
+ return nvme_ns_attach(&args);
+}
+
+/**
+ * nvme_fw_download() - Download part or all of a firmware image to the
+ * controller
+ * @args: &struct nvme_fw_download_args argument structure
+ *
+ * The Firmware Image Download command downloads all or a portion of an image
+ * for a future update to the controller. The Firmware Image Download command
+ * downloads a new image (in whole or in part) to the controller.
+ *
+ * The image may be constructed of multiple pieces that are individually
+ * downloaded with separate Firmware Image Download commands. Each Firmware
+ * Image Download command includes a Dword Offset and Number of Dwords that
+ * specify a dword range.
+ *
+ * The new firmware image is not activated as part of the Firmware Image
+ * Download command. Use the nvme_fw_commit() to activate a newly downloaded
+ * image.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_fw_download(struct nvme_fw_download_args *args);
+
+/**
+ * nvme_fw_commit() - Commit firmware using the specified action
+ * @args: &struct nvme_fw_commit_args argument structure
+ *
+ * The Firmware Commit command modifies the firmware image or Boot Partitions.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise. The command
+ * status response may specify additional reset actions required to complete
+ * the commit process.
+ */
+int nvme_fw_commit(struct nvme_fw_commit_args *args);
+
+/**
+ * nvme_security_send() - Security Send command
+ * @args: &struct nvme_security_send argument structure
+ *
+ * The Security Send command transfers security protocol data to the
+ * controller. The data structure transferred to the controller as part of this
+ * command contains security protocol specific commands to be performed by the
+ * controller. The data structure transferred may also contain data or
+ * parameters associated with the security protocol commands.
+ *
+ * The security data is protocol specific and is not defined by the NVMe
+ * specification.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_security_send(struct nvme_security_send_args *args);
+
+/**
+ * nvme_security_receive() - Security Receive command
+ * @args: &struct nvme_security_receive argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_security_receive(struct nvme_security_receive_args *args);
+
+/**
+ * nvme_get_lba_status() - Retrieve information on possibly unrecoverable LBAs
+ * @args: &struct nvme_get_lba_status_args argument structure
+ *
+ * The Get LBA Status command requests information about Potentially
+ * Unrecoverable LBAs. Refer to the specification for action type descriptions.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_lba_status(struct nvme_get_lba_status_args *args);
+
+/**
+ * nvme_directive_send() - Send directive command
+ * @args: &struct nvme_directive_send_args argument structure
+ *
+ * Directives is a mechanism to enable host and NVM subsystem or controller
+ * information exchange. The Directive Send command transfers data related to a
+ * specific Directive Type from the host to the controller.
+ *
+ * See the NVMe specification for more information.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_directive_send(struct nvme_directive_send_args *args);
+
+/**
+ * nvme_directive_send_id_endir() - Directive Send Enable Directive
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace Identifier
+ * @endir: Enable Directive
+ * @dtype: Directive Type
+ * @id: Pointer to structure nvme_id_directives
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_directive_send_id_endir(int fd, __u32 nsid, bool endir,
+ enum nvme_directive_dtype dtype,
+ struct nvme_id_directives *id);
+
+/**
+ * nvme_directive_send_stream_release_identifier() - Directive Send Stream release
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @stream_id: Stream identifier
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_directive_send_stream_release_identifier(int fd,
+ __u32 nsid, __u16 stream_id)
+{
+ struct nvme_directive_send_args args = {
+ .result = NULL,
+ .data = NULL,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .doper = NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER,
+ .dtype = NVME_DIRECTIVE_DTYPE_STREAMS,
+ .cdw12 = 0,
+ .data_len = 0,
+ .dspec = stream_id,
+ };
+
+ return nvme_directive_send(&args);
+}
+
+/**
+ * nvme_directive_send_stream_release_resource() - Directive Send Stream release resources
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_directive_send_stream_release_resource(int fd, __u32 nsid)
+{
+ struct nvme_directive_send_args args = {
+ .result = NULL,
+ .data = NULL,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .doper = NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE,
+ .dtype = NVME_DIRECTIVE_DTYPE_STREAMS,
+ .cdw12 = 0,
+ .data_len = 0,
+ .dspec = 0,
+ };
+
+ return nvme_directive_send(&args);
+}
+
+/**
+ * nvme_directive_recv() - Receive directive specific data
+ * @args: &struct nvme_directive_recv_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_directive_recv(struct nvme_directive_recv_args *args);
+
+/**
+ * nvme_directive_recv_identify_parameters() - Directive receive identifier parameters
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @id: Identify parameters buffer
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_directive_recv_identify_parameters(int fd, __u32 nsid,
+ struct nvme_id_directives *id)
+{
+ struct nvme_directive_recv_args args = {
+ .result = NULL,
+ .data = id,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .doper = NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM,
+ .dtype = NVME_DIRECTIVE_DTYPE_IDENTIFY,
+ .cdw12 = 0,
+ .data_len = sizeof(*id),
+ .dspec = 0,
+ };
+
+ return nvme_directive_recv(&args);
+}
+
+/**
+ * nvme_directive_recv_stream_parameters() - Directive receive stream parameters
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @parms: Streams directive parameters buffer
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_directive_recv_stream_parameters(int fd, __u32 nsid,
+ struct nvme_streams_directive_params *parms)
+{
+ struct nvme_directive_recv_args args = {
+ .result = NULL,
+ .data = parms,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .doper = NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM,
+ .dtype = NVME_DIRECTIVE_DTYPE_STREAMS,
+ .cdw12 = 0,
+ .data_len = sizeof(*parms),
+ .dspec = 0,
+ };
+
+ return nvme_directive_recv(&args);
+}
+
+/**
+ * nvme_directive_recv_stream_status() - Directive receive stream status
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @nr_entries: Number of streams to receive
+ * @id: Stream status buffer
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_directive_recv_stream_status(int fd, __u32 nsid,
+ unsigned int nr_entries,
+ struct nvme_streams_directive_status *id)
+{
+ struct nvme_directive_recv_args args = {
+ .result = NULL,
+ .data = id,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .doper = NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS,
+ .dtype = NVME_DIRECTIVE_DTYPE_STREAMS,
+ .cdw12 = 0,
+ .data_len = sizeof(*id),
+ .dspec = 0,
+ };
+
+ return nvme_directive_recv(&args);
+}
+
+/**
+ * nvme_directive_recv_stream_allocate() - Directive receive stream allocate
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @nsr: Namespace Streams Requested
+ * @result: If successful, the CQE dword0 value
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_directive_recv_stream_allocate(int fd, __u32 nsid,
+ __u16 nsr, __u32 *result)
+{
+ struct nvme_directive_recv_args args = {
+ .result = result,
+ .data = NULL,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = nsid,
+ .doper = NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE,
+ .dtype = NVME_DIRECTIVE_DTYPE_STREAMS,
+ .cdw12 = nsr,
+ .data_len = 0,
+ .dspec = 0,
+ };
+
+ return nvme_directive_recv(&args);
+}
+
+/**
+ * nvme_capacity_mgmt() - Capacity management command
+ * @args: &struct nvme_capacity_mgmt_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_capacity_mgmt(struct nvme_capacity_mgmt_args *args);
+
+/**
+ * nvme_lockdown() - Issue lockdown command
+ * @args: &struct nvme_lockdown_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_lockdown(struct nvme_lockdown_args *args);
+
+/**
+ * nvme_set_property() - Set controller property
+ * @args: &struct nvme_set_property_args argument structure
+ *
+ * This is an NVMe-over-Fabrics specific command, not applicable to PCIe. These
+ * properties align to the PCI MMIO controller registers.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_set_property(struct nvme_set_property_args *args);
+
+/**
+ * nvme_get_property() - Get a controller property
+ * @args: &struct nvme_get_propert_args argument structure
+ *
+ * This is an NVMe-over-Fabrics specific command, not applicable to PCIe. These
+ * properties align to the PCI MMIO controller registers.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_property(struct nvme_get_property_args *args);
+
+/**
+ * nvme_sanitize_nvm() - Start a sanitize operation
+ * @args: &struct nvme_sanitize_nvm_args argument structure
+ *
+ * A sanitize operation alters all user data in the NVM subsystem such that
+ * recovery of any previous user data from any cache, the non-volatile media,
+ * or any Controller Memory Buffer is not possible.
+ *
+ * The Sanitize command starts a sanitize operation or to recover from a
+ * previously failed sanitize operation. The sanitize operation types that may
+ * be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize
+ * operations are processed in the background, i.e., completion of the sanitize
+ * command does not indicate completion of the sanitize operation.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args);
+
+/**
+ * nvme_dev_self_test() - Start or abort a self test
+ * @args: &struct nvme_dev_self_test argument structure
+ *
+ * The Device Self-test command starts a device self-test operation or abort a
+ * device self-test operation. A device self-test operation is a diagnostic
+ * testing sequence that tests the integrity and functionality of the
+ * controller and may include testing of the media associated with namespaces.
+ * The controller may return a response to this command immediately while
+ * running the self-test in the background.
+ *
+ * Set the 'nsid' field to 0 to not include namespaces in the test. Set to
+ * 0xffffffff to test all namespaces. All other values tests a specific
+ * namespace, if present.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_dev_self_test(struct nvme_dev_self_test_args *args);
+
+/**
+ * nvme_virtual_mgmt() - Virtualization resource management
+ * @args: &struct nvme_virtual_mgmt_args argument structure
+ *
+ * The Virtualization Management command is supported by primary controllers
+ * that support the Virtualization Enhancements capability. This command is
+ * used for several functions:
+ *
+ * - Modifying Flexible Resource allocation for the primary controller
+ * - Assigning Flexible Resources for secondary controllers
+ * - Setting the Online and Offline state for secondary controllers
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_virtual_mgmt(struct nvme_virtual_mgmt_args *args);
+
+/**
+ * nvme_flush() - Send an nvme flush command
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace identifier
+ *
+ * The Flush command requests that the contents of volatile write cache be made
+ * non-volatile.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_flush(int fd, __u32 nsid)
+{
+ struct nvme_passthru_cmd cmd = {};
+
+ cmd.opcode = nvme_cmd_flush;
+ cmd.nsid = nsid;
+
+ return nvme_submit_io_passthru(fd, &cmd, NULL);
+}
+
+/**
+ * nvme_io() - Submit an nvme user I/O command
+ * @args: &struct nvme_io_args argument structure
+ * @opcode: Opcode to execute
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_io(struct nvme_io_args *args, __u8 opcode);
+
+/**
+ * nvme_read() - Submit an nvme user read command
+ * @args: &struct nvme_io_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_read(struct nvme_io_args *args)
+{
+ return nvme_io(args, nvme_cmd_read);
+}
+
+/**
+ * nvme_write() - Submit an nvme user write command
+ * @args: &struct nvme_io_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_write(struct nvme_io_args *args)
+{
+ return nvme_io(args, nvme_cmd_write);
+}
+
+/**
+ * nvme_compare() - Submit an nvme user compare command
+ * @args: &struct nvme_io_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_compare(struct nvme_io_args *args)
+{
+ return nvme_io(args, nvme_cmd_compare);
+}
+
+/**
+ * nvme_write_zeros() - Submit an nvme write zeroes command
+ * @args: &struct nvme_io_args argument structure
+ *
+ * The Write Zeroes command sets a range of logical blocks to zero. After
+ * successful completion of this command, the value returned by subsequent
+ * reads of logical blocks in this range shall be all bytes cleared to 0h until
+ * a write occurs to this LBA range.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_write_zeros(struct nvme_io_args *args)
+{
+ return nvme_io(args, nvme_cmd_write_zeroes);
+}
+
+/**
+ * nvme_write_uncorrectable() - Submit an nvme write uncorrectable command
+ * @args: &struct nvme_io_args argument structure
+ *
+ * The Write Uncorrectable command marks a range of logical blocks as invalid.
+ * When the specified logical block(s) are read after this operation, a failure
+ * is returned with Unrecovered Read Error status. To clear the invalid logical
+ * block status, a write operation on those logical blocks is required.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_write_uncorrectable(struct nvme_io_args *args)
+{
+ return nvme_io(args, nvme_cmd_write_uncor);
+}
+
+/**
+ * nvme_verify() - Send an nvme verify command
+ * @args: &struct nvme_io_args argument structure
+ *
+ * The Verify command verifies integrity of stored information by reading data
+ * and metadata, if applicable, for the LBAs indicated without transferring any
+ * data or metadata to the host.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_verify(struct nvme_io_args *args)
+{
+ return nvme_io(args, nvme_cmd_verify);
+}
+
+/**
+ * nvme_dsm() - Send an nvme data set management command
+ * @args: &struct nvme_dsm_args argument structure
+ *
+ * The Dataset Management command is used by the host to indicate attributes
+ * for ranges of logical blocks. This includes attributes like frequency that
+ * data is read or written, access size, and other information that may be used
+ * to optimize performance and reliability, and may be used to
+ * deallocate/unmap/trim those logical blocks.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_dsm(struct nvme_dsm_args *args);
+
+/**
+ * nvme_copy() - Copy command
+ *
+ * @args: &struct nvme_copy_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_copy(struct nvme_copy_args *args);
+
+/**
+ * nvme_resv_acquire() - Send an nvme reservation acquire
+ * @args: &struct nvme_resv_acquire argument structure
+ *
+ * The Reservation Acquire command acquires a reservation on a namespace,
+ * preempt a reservation held on a namespace, and abort a reservation held on a
+ * namespace.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_resv_acquire(struct nvme_resv_acquire_args *args);
+
+/**
+ * nvme_resv_register() - Send an nvme reservation register
+ * @args: &struct nvme_resv_register_args argument structure
+ *
+ * The Reservation Register command registers, unregisters, or replaces a
+ * reservation key.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_resv_register(struct nvme_resv_register_args *args);
+
+/**
+ * nvme_resv_release() - Send an nvme reservation release
+ * @args: &struct nvme_resv_release_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_resv_release(struct nvme_resv_release_args *args);
+
+/**
+ * nvme_resv_report() - Send an nvme reservation report
+ * @args: struct nvme_resv_report_args argument structure
+ *
+ * Returns a Reservation Status data structure to memory that describes the
+ * registration and reservation status of a namespace. See the definition for
+ * the returned structure, &struct nvme_reservation_status, for more details.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_resv_report(struct nvme_resv_report_args *args);
+
+/**
+ * nvme_io_mgmt_recv() - I/O Management Receive command
+ * @args: &struct nvme_io_mgmt_recv_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_io_mgmt_recv(struct nvme_io_mgmt_recv_args *args);
+
+/**
+ * nvme_fdp_reclaim_unit_handle_status() - Get reclaim unit handle status
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace identifier
+ * @data_len: Length of response buffer
+ * @data: Response buffer
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_fdp_reclaim_unit_handle_status(int fd, __u32 nsid,
+ __u32 data_len, void *data)
+{
+ struct nvme_io_mgmt_recv_args args = {
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .nsid = nsid,
+ .data_len = data_len,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .mos = 0,
+ .mo = NVME_IO_MGMT_RECV_RUH_STATUS,
+ };
+
+ return nvme_io_mgmt_recv(&args);
+}
+
+/**
+ * nvme_io_mgmt_send() - I/O Management Send command
+ * @args: &struct nvme_io_mgmt_send_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_io_mgmt_send(struct nvme_io_mgmt_send_args *args);
+
+/**
+ * nvme_fdp_reclaim_unit_handle_update() - Update a list of reclaim unit handles
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace identifier
+ * @npids: Number of placement identifiers
+ * @pids: List of placement identifiers
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_fdp_reclaim_unit_handle_update(int fd, __u32 nsid,
+ unsigned int npids, __u16 *pids)
+{
+ struct nvme_io_mgmt_send_args args = {
+ .data = (void *)pids,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .nsid = nsid,
+ .data_len = (__u32)(npids * sizeof(__u16)),
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .mos = (__u16)(npids - 1),
+ .mo = NVME_IO_MGMT_SEND_RUH_UPDATE,
+ };
+
+ return nvme_io_mgmt_send(&args);
+}
+
+/**
+ * nvme_zns_mgmt_send() - ZNS management send command
+ * @args: &struct nvme_zns_mgmt_send_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_zns_mgmt_send(struct nvme_zns_mgmt_send_args *args);
+
+
+/**
+ * nvme_zns_mgmt_recv() - ZNS management receive command
+ * @args: &struct nvme_zns_mgmt_recv_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_zns_mgmt_recv(struct nvme_zns_mgmt_recv_args *args);
+
+/**
+ * nvme_zns_report_zones() - Return the list of zones
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID
+ * @slba: Starting LBA
+ * @opts: Reporting options
+ * @extended: Extended report
+ * @partial: Partial report requested
+ * @data_len: Length of the data buffer
+ * @data: Userspace address of the report zones data
+ * @timeout: timeout in ms
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_zns_report_zones(int fd, __u32 nsid, __u64 slba,
+ enum nvme_zns_report_options opts,
+ bool extended, bool partial,
+ __u32 data_len, void *data,
+ __u32 timeout, __u32 *result)
+{
+ struct nvme_zns_mgmt_recv_args args = {
+ .slba = slba,
+ .result = result,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = timeout,
+ .nsid = nsid,
+ .zra = extended ? NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES :
+ NVME_ZNS_ZRA_REPORT_ZONES,
+ .data_len = data_len,
+ .zrasf = (__u16)opts,
+ .zras_feat = partial,
+ };
+
+ return nvme_zns_mgmt_recv(&args);
+}
+
+/**
+ * nvme_zns_append() - Append data to a zone
+ * @args: &struct nvme_zns_append_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_zns_append(struct nvme_zns_append_args *args);
+
+/**
+ * nvme_dim_send - Send a Discovery Information Management (DIM) command
+ * @args: &struct nvme_dim_args argument structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_dim_send(struct nvme_dim_args *args);
+
+/**
+ * nvme_set_debug - Set NVMe command debugging output
+ * @debug: true to enable or false to disable
+ */
+void nvme_set_debug(bool debug);
+
+/**
+ * nvme_get_debug - Get NVMe command debugging output
+ *
+ * Return: false if disabled or true if enabled.
+ */
+bool nvme_get_debug(void);
+#endif /* _LIBNVME_IOCTL_H */
diff --git a/src/nvme/json.c b/src/nvme/json.c
new file mode 100644
index 0000000..b49498a
--- /dev/null
+++ b/src/nvme/json.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021 SUSE Software Solutions
+ *
+ * Authors: Hannes Reinecke <hare@suse.de>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <json.h>
+
+#include "cleanup.h"
+#include "fabrics.h"
+#include "log.h"
+#include "private.h"
+#include "linux.h"
+
+#define JSON_UPDATE_INT_OPTION(c, k, a, o) \
+ if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_int(o);
+#define JSON_UPDATE_BOOL_OPTION(c, k, a, o) \
+ if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_boolean(o);
+
+static void json_update_attributes(nvme_ctrl_t c,
+ struct json_object *ctrl_obj)
+{
+ struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c);
+
+ json_object_object_foreach(ctrl_obj, key_str, val_obj) {
+ JSON_UPDATE_INT_OPTION(cfg, key_str,
+ nr_io_queues, val_obj);
+ JSON_UPDATE_INT_OPTION(cfg, key_str,
+ nr_write_queues, val_obj);
+ JSON_UPDATE_INT_OPTION(cfg, key_str,
+ nr_poll_queues, val_obj);
+ JSON_UPDATE_INT_OPTION(cfg, key_str,
+ queue_size, val_obj);
+ JSON_UPDATE_INT_OPTION(cfg, key_str,
+ keep_alive_tmo, val_obj);
+ JSON_UPDATE_INT_OPTION(cfg, key_str,
+ reconnect_delay, val_obj);
+ if (!strcmp("ctrl_loss_tmo", key_str) &&
+ cfg->ctrl_loss_tmo != NVMF_DEF_CTRL_LOSS_TMO)
+ cfg->ctrl_loss_tmo = json_object_get_int(val_obj);
+ JSON_UPDATE_INT_OPTION(cfg, key_str,
+ fast_io_fail_tmo, val_obj);
+ if (!strcmp("tos", key_str) && cfg->tos != -1)
+ cfg->tos = json_object_get_int(val_obj);
+ JSON_UPDATE_BOOL_OPTION(cfg, key_str,
+ duplicate_connect, val_obj);
+ JSON_UPDATE_BOOL_OPTION(cfg, key_str,
+ disable_sqflow, val_obj);
+ JSON_UPDATE_BOOL_OPTION(cfg, key_str,
+ hdr_digest, val_obj);
+ JSON_UPDATE_BOOL_OPTION(cfg, key_str,
+ data_digest, val_obj);
+ JSON_UPDATE_BOOL_OPTION(cfg, key_str,
+ tls, val_obj);
+ JSON_UPDATE_BOOL_OPTION(cfg, key_str,
+ concat, val_obj);
+ if (!strcmp("persistent", key_str) &&
+ !nvme_ctrl_is_persistent(c))
+ nvme_ctrl_set_persistent(c, true);
+ if (!strcmp("discovery", key_str) &&
+ !nvme_ctrl_is_discovery_ctrl(c))
+ nvme_ctrl_set_discovery_ctrl(c, true);
+ /*
+ * The JSON configuration holds the keyring description
+ * which needs to be converted into the keyring serial number.
+ */
+ if (!strcmp("keyring", key_str) && cfg->keyring == 0) {
+ long keyring;
+
+ keyring = nvme_lookup_keyring(json_object_get_string(val_obj));
+ if (keyring) {
+ cfg->keyring = keyring;
+ nvme_set_keyring(cfg->keyring);
+ }
+ }
+ if (!strcmp("tls_key", key_str) && cfg->tls_key == 0) {
+ long key;
+
+ key = nvme_lookup_key("psk",
+ json_object_get_string(val_obj));
+ if (key)
+ cfg->tls_key = key;
+ }
+ }
+}
+
+static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj)
+{
+ nvme_ctrl_t c;
+ struct json_object *attr_obj;
+ const char *transport, *traddr = NULL;
+ const char *host_traddr = NULL, *host_iface = NULL, *trsvcid = NULL;
+
+ attr_obj = json_object_object_get(port_obj, "transport");
+ if (!attr_obj)
+ return;
+ transport = json_object_get_string(attr_obj);
+ attr_obj = json_object_object_get(port_obj, "traddr");
+ if (attr_obj)
+ traddr = json_object_get_string(attr_obj);
+ attr_obj = json_object_object_get(port_obj, "host_traddr");
+ if (attr_obj)
+ host_traddr = json_object_get_string(attr_obj);
+ attr_obj = json_object_object_get(port_obj, "host_iface");
+ if (attr_obj)
+ host_iface = json_object_get_string(attr_obj);
+ attr_obj = json_object_object_get(port_obj, "trsvcid");
+ if (attr_obj)
+ trsvcid = json_object_get_string(attr_obj);
+ c = nvme_lookup_ctrl(s, transport, traddr, host_traddr,
+ host_iface, trsvcid, NULL);
+ if (!c)
+ return;
+ json_update_attributes(c, port_obj);
+ attr_obj = json_object_object_get(port_obj, "dhchap_key");
+ if (attr_obj)
+ nvme_ctrl_set_dhchap_host_key(c, json_object_get_string(attr_obj));
+ attr_obj = json_object_object_get(port_obj, "dhchap_ctrl_key");
+ if (attr_obj)
+ nvme_ctrl_set_dhchap_key(c, json_object_get_string(attr_obj));
+}
+
+static void json_parse_subsys(nvme_host_t h, struct json_object *subsys_obj)
+{
+ struct json_object *nqn_obj, *app_obj, *port_array;
+ nvme_subsystem_t s;
+ const char *nqn;
+ int p;
+
+ nqn_obj = json_object_object_get(subsys_obj, "nqn");
+ if (!nqn_obj)
+ return;
+ nqn = json_object_get_string(nqn_obj);
+ s = nvme_lookup_subsystem(h, NULL, nqn);
+ if (!s)
+ return;
+ app_obj = json_object_object_get(subsys_obj, "application");
+ if (app_obj)
+ nvme_subsystem_set_application(s, json_object_get_string(app_obj));
+
+ port_array = json_object_object_get(subsys_obj, "ports");
+ if (!port_array)
+ return;
+ for (p = 0; p < json_object_array_length(port_array); p++) {
+ struct json_object *port_obj;
+
+ port_obj = json_object_array_get_idx(port_array, p);
+ if (port_obj)
+ json_parse_port(s, port_obj);
+ }
+}
+
+static void json_parse_host(nvme_root_t r, struct json_object *host_obj)
+{
+ struct json_object *attr_obj, *subsys_array, *subsys_obj;
+ nvme_host_t h;
+ const char *hostnqn, *hostid = NULL;
+ int s;
+
+ attr_obj = json_object_object_get(host_obj, "hostnqn");
+ if (!attr_obj)
+ return;
+ hostnqn = json_object_get_string(attr_obj);
+ attr_obj = json_object_object_get(host_obj, "hostid");
+ if (attr_obj)
+ hostid = json_object_get_string(attr_obj);
+ h = nvme_lookup_host(r, hostnqn, hostid);
+ attr_obj = json_object_object_get(host_obj, "dhchap_key");
+ if (attr_obj)
+ nvme_host_set_dhchap_key(h, json_object_get_string(attr_obj));
+ attr_obj = json_object_object_get(host_obj, "hostsymname");
+ if (attr_obj)
+ nvme_host_set_hostsymname(h, json_object_get_string(attr_obj));
+ attr_obj = json_object_object_get(host_obj, "persistent_discovery_ctrl");
+ if (attr_obj)
+ nvme_host_set_pdc_enabled(h, json_object_get_boolean(attr_obj));
+ subsys_array = json_object_object_get(host_obj, "subsystems");
+ if (!subsys_array)
+ return;
+ for (s = 0; s < json_object_array_length(subsys_array); s++) {
+ subsys_obj = json_object_array_get_idx(subsys_array, s);
+ if (subsys_obj)
+ json_parse_subsys(h, subsys_obj);
+ }
+}
+
+static DEFINE_CLEANUP_FUNC(cleanup_tokener, json_tokener *, json_tokener_free)
+#define _cleanup_tokener_ __cleanup__(cleanup_tokener)
+
+static struct json_object *parse_json(nvme_root_t r, int fd)
+{
+ char buf[JSON_FILE_BUF_SIZE];
+ struct json_object *obj;
+ char *str = NULL;
+ _cleanup_tokener_ json_tokener *tok = NULL;
+ int ret;
+ _cleanup_free_ void *ptr = NULL;
+ int len = 0;
+
+ while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) {
+ str = realloc(ptr, len + ret);
+ if (!str)
+ return NULL;
+ memcpy(&str[len], buf, ret);
+ len += ret;
+ ptr = str;
+ }
+
+ if (ret < 0 || !len)
+ return NULL;
+
+ tok = json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH);
+ if (!tok)
+ return NULL;
+
+ /* Enforce correctly formatted JSON */
+ tok->flags = JSON_TOKENER_STRICT;
+
+ obj = json_tokener_parse_ex(tok, str, len);
+ if (!obj)
+ nvme_msg(r, LOG_DEBUG, "JSON parsing failed: %s\n",
+ json_util_get_last_err());
+
+ return obj;
+}
+
+int json_read_config(nvme_root_t r, const char *config_file)
+{
+ struct json_object *json_root, *host_obj;
+ int fd, h;
+
+ fd = open(config_file, O_RDONLY);
+ if (fd < 0) {
+ nvme_msg(r, LOG_DEBUG, "Error opening %s, %s\n",
+ config_file, strerror(errno));
+ return fd;
+ }
+ json_root = parse_json(r, fd);
+ close(fd);
+ if (!json_root) {
+ errno = EPROTO;
+ return -1;
+ }
+ if (!json_object_is_type(json_root, json_type_array)) {
+ nvme_msg(r, LOG_DEBUG, "Wrong format, expected array\n");
+ json_object_put(json_root);
+ errno = EPROTO;
+ return -1;
+ }
+ for (h = 0; h < json_object_array_length(json_root); h++) {
+ host_obj = json_object_array_get_idx(json_root, h);
+ if (host_obj)
+ json_parse_host(r, host_obj);
+ }
+ json_object_put(json_root);
+ return 0;
+}
+
+#define JSON_STRING_OPTION(c, p, o) \
+ if ((c)->o && strcmp((c)->o, "none")) \
+ json_object_object_add((p), # o , \
+ json_object_new_string((c)->o))
+#define JSON_INT_OPTION(c, p, o, d) \
+ if ((c)->o != d) \
+ json_object_object_add((p), # o , \
+ json_object_new_int((c)->o))
+#define JSON_BOOL_OPTION(c, p, o) \
+ if ((c)->o) \
+ json_object_object_add((p), # o , \
+ json_object_new_boolean((c)->o))
+
+static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c)
+{
+ struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c);
+ struct json_object *port_obj = json_object_new_object();
+ const char *transport, *value;
+
+ transport = nvme_ctrl_get_transport(c);
+ json_object_object_add(port_obj, "transport",
+ json_object_new_string(transport));
+ value = nvme_ctrl_get_traddr(c);
+ if (value)
+ json_object_object_add(port_obj, "traddr",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_host_traddr(c);
+ if (value)
+ json_object_object_add(port_obj, "host_traddr",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_host_iface(c);
+ if (value)
+ json_object_object_add(port_obj, "host_iface",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_trsvcid(c);
+ if (value)
+ json_object_object_add(port_obj, "trsvcid",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_dhchap_host_key(c);
+ if (value)
+ json_object_object_add(port_obj, "dhchap_key",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_dhchap_key(c);
+ if (value)
+ json_object_object_add(port_obj, "dhchap_ctrl_key",
+ json_object_new_string(value));
+ JSON_INT_OPTION(cfg, port_obj, nr_io_queues, 0);
+ JSON_INT_OPTION(cfg, port_obj, nr_write_queues, 0);
+ JSON_INT_OPTION(cfg, port_obj, nr_poll_queues, 0);
+ JSON_INT_OPTION(cfg, port_obj, queue_size, 0);
+ JSON_INT_OPTION(cfg, port_obj, keep_alive_tmo, 0);
+ JSON_INT_OPTION(cfg, port_obj, reconnect_delay, 0);
+ if (strcmp(transport, "loop")) {
+ JSON_INT_OPTION(cfg, port_obj, ctrl_loss_tmo,
+ NVMF_DEF_CTRL_LOSS_TMO);
+ JSON_INT_OPTION(cfg, port_obj, fast_io_fail_tmo, 0);
+ }
+ JSON_INT_OPTION(cfg, port_obj, tos, -1);
+ JSON_BOOL_OPTION(cfg, port_obj, duplicate_connect);
+ JSON_BOOL_OPTION(cfg, port_obj, disable_sqflow);
+ JSON_BOOL_OPTION(cfg, port_obj, hdr_digest);
+ JSON_BOOL_OPTION(cfg, port_obj, data_digest);
+ JSON_BOOL_OPTION(cfg, port_obj, tls);
+ JSON_BOOL_OPTION(cfg, port_obj, concat);
+ if (nvme_ctrl_is_persistent(c))
+ json_object_object_add(port_obj, "persistent",
+ json_object_new_boolean(true));
+ if (nvme_ctrl_is_discovery_ctrl(c))
+ json_object_object_add(port_obj, "discovery",
+ json_object_new_boolean(true));
+ /*
+ * Store the keyring description in the JSON config file.
+ */
+ if (cfg->keyring) {
+ _cleanup_free_ char *desc =
+ nvme_describe_key_serial(cfg->keyring);
+
+ if (desc) {
+ json_object_object_add(port_obj, "keyring",
+ json_object_new_string(desc));
+ }
+ }
+ if (cfg->tls_key) {
+ _cleanup_free_ char *desc =
+ nvme_describe_key_serial(cfg->tls_key);
+
+ if (desc) {
+ json_object_object_add(port_obj, "tls_key",
+ json_object_new_string(desc));
+ }
+ }
+
+ json_object_array_add(ctrl_array, port_obj);
+}
+
+static void json_update_subsys(struct json_object *subsys_array,
+ nvme_subsystem_t s)
+{
+ nvme_ctrl_t c;
+ const char *subsysnqn = nvme_subsystem_get_nqn(s), *app;
+ struct json_object *subsys_obj = json_object_new_object();
+ struct json_object *port_array;
+
+ /* Skip discovery subsystems as the nqn is not unique */
+ if (!strcmp(subsysnqn, NVME_DISC_SUBSYS_NAME))
+ return;
+
+ json_object_object_add(subsys_obj, "nqn",
+ json_object_new_string(subsysnqn));
+ app = nvme_subsystem_get_application(s);
+ if (app)
+ json_object_object_add(subsys_obj, "application",
+ json_object_new_string(app));
+ port_array = json_object_new_array();
+ nvme_subsystem_for_each_ctrl(s, c) {
+ json_update_port(port_array, c);
+ }
+ if (json_object_array_length(port_array))
+ json_object_object_add(subsys_obj, "ports", port_array);
+ else
+ json_object_put(port_array);
+ json_object_array_add(subsys_array, subsys_obj);
+}
+
+int json_update_config(nvme_root_t r, const char *config_file)
+{
+ nvme_host_t h;
+ struct json_object *json_root, *host_obj;
+ struct json_object *subsys_array;
+ int ret = 0;
+
+ json_root = json_object_new_array();
+ nvme_for_each_host(r, h) {
+ nvme_subsystem_t s;
+ const char *hostnqn, *hostid, *dhchap_key, *hostsymname;
+
+ host_obj = json_object_new_object();
+ if (!host_obj)
+ continue;
+ hostnqn = nvme_host_get_hostnqn(h);
+ json_object_object_add(host_obj, "hostnqn",
+ json_object_new_string(hostnqn));
+ hostid = nvme_host_get_hostid(h);
+ if (hostid)
+ json_object_object_add(host_obj, "hostid",
+ json_object_new_string(hostid));
+ dhchap_key = nvme_host_get_dhchap_key(h);
+ if (dhchap_key)
+ json_object_object_add(host_obj, "dhchap_key",
+ json_object_new_string(dhchap_key));
+ hostsymname = nvme_host_get_hostsymname(h);
+ if (hostsymname)
+ json_object_object_add(host_obj, "hostsymname",
+ json_object_new_string(hostsymname));
+ if (h->pdc_enabled_valid)
+ json_object_object_add(host_obj, "persistent_discovery_ctrl",
+ json_object_new_boolean(h->pdc_enabled));
+ subsys_array = json_object_new_array();
+ nvme_for_each_subsystem(h, s) {
+ json_update_subsys(subsys_array, s);
+ }
+ if (json_object_array_length(subsys_array))
+ json_object_object_add(host_obj, "subsystems",
+ subsys_array);
+ else
+ json_object_put(subsys_array);
+ json_object_array_add(json_root, host_obj);
+ }
+ if (!config_file) {
+ ret = json_object_to_fd(1, json_root, JSON_C_TO_STRING_PRETTY);
+ printf("\n");
+ } else
+ ret = json_object_to_file_ext(config_file, json_root,
+ JSON_C_TO_STRING_PRETTY);
+ if (ret < 0) {
+ nvme_msg(r, LOG_ERR, "Failed to write to %s, %s\n",
+ config_file ? "stdout" : config_file,
+ json_util_get_last_err());
+ ret = -1;
+ errno = EIO;
+ }
+ json_object_put(json_root);
+
+ return ret;
+}
+
+static void json_dump_ctrl(struct json_object *ctrl_array, nvme_ctrl_t c)
+{
+ struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c);
+ struct json_object *ctrl_obj = json_object_new_object();
+ const char *name, *transport, *value;
+
+ name = nvme_ctrl_get_name(c);
+ if (name && strlen(name))
+ json_object_object_add(ctrl_obj, "name",
+ json_object_new_string(name));
+ transport = nvme_ctrl_get_transport(c);
+ json_object_object_add(ctrl_obj, "transport",
+ json_object_new_string(transport));
+ value = nvme_ctrl_get_traddr(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "traddr",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_host_traddr(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "host_traddr",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_host_iface(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "host_iface",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_trsvcid(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "trsvcid",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_dhchap_host_key(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "dhchap_key",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_dhchap_key(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "dhchap_ctrl_key",
+ json_object_new_string(value));
+ JSON_INT_OPTION(cfg, ctrl_obj, nr_io_queues, 0);
+ JSON_INT_OPTION(cfg, ctrl_obj, nr_write_queues, 0);
+ JSON_INT_OPTION(cfg, ctrl_obj, nr_poll_queues, 0);
+ JSON_INT_OPTION(cfg, ctrl_obj, queue_size, 0);
+ JSON_INT_OPTION(cfg, ctrl_obj, keep_alive_tmo, 0);
+ JSON_INT_OPTION(cfg, ctrl_obj, reconnect_delay, 0);
+ if (strcmp(transport, "loop")) {
+ JSON_INT_OPTION(cfg, ctrl_obj, ctrl_loss_tmo,
+ NVMF_DEF_CTRL_LOSS_TMO);
+ JSON_INT_OPTION(cfg, ctrl_obj, fast_io_fail_tmo, 0);
+ }
+ JSON_INT_OPTION(cfg, ctrl_obj, tos, -1);
+ JSON_BOOL_OPTION(cfg, ctrl_obj, duplicate_connect);
+ JSON_BOOL_OPTION(cfg, ctrl_obj, disable_sqflow);
+ JSON_BOOL_OPTION(cfg, ctrl_obj, hdr_digest);
+ JSON_BOOL_OPTION(cfg, ctrl_obj, data_digest);
+ JSON_BOOL_OPTION(cfg, ctrl_obj, tls);
+ JSON_BOOL_OPTION(cfg, ctrl_obj, concat);
+ if (nvme_ctrl_is_persistent(c))
+ json_object_object_add(ctrl_obj, "persistent",
+ json_object_new_boolean(true));
+ if (nvme_ctrl_is_discovery_ctrl(c))
+ json_object_object_add(ctrl_obj, "discovery",
+ json_object_new_boolean(true));
+ json_object_array_add(ctrl_array, ctrl_obj);
+}
+
+static void json_dump_subsys(struct json_object *subsys_array,
+ nvme_subsystem_t s)
+{
+ nvme_ctrl_t c;
+ struct json_object *subsys_obj = json_object_new_object();
+ struct json_object *ctrl_array;
+
+ json_object_object_add(subsys_obj, "name",
+ json_object_new_string(nvme_subsystem_get_name(s)));
+ json_object_object_add(subsys_obj, "nqn",
+ json_object_new_string(nvme_subsystem_get_nqn(s)));
+ ctrl_array = json_object_new_array();
+ nvme_subsystem_for_each_ctrl(s, c) {
+ json_dump_ctrl(ctrl_array, c);
+ }
+ if (json_object_array_length(ctrl_array))
+ json_object_object_add(subsys_obj, "controllers", ctrl_array);
+ else
+ json_object_put(ctrl_array);
+ json_object_array_add(subsys_array, subsys_obj);
+}
+
+int json_dump_tree(nvme_root_t r)
+{
+ nvme_host_t h;
+ struct json_object *json_root, *host_obj;
+ struct json_object *host_array, *subsys_array;
+ int ret = 0;
+
+ json_root = json_object_new_object();
+ host_array = json_object_new_array();
+ nvme_for_each_host(r, h) {
+ nvme_subsystem_t s;
+ const char *hostid, *dhchap_key;
+
+ host_obj = json_object_new_object();
+ json_object_object_add(host_obj, "hostnqn",
+ json_object_new_string(nvme_host_get_hostnqn(h)));
+ hostid = nvme_host_get_hostid(h);
+ if (hostid)
+ json_object_object_add(host_obj, "hostid",
+ json_object_new_string(hostid));
+ dhchap_key = nvme_host_get_dhchap_key(h);
+ if (dhchap_key)
+ json_object_object_add(host_obj, "dhchap_key",
+ json_object_new_string(dhchap_key));
+ if (h->pdc_enabled_valid)
+ json_object_object_add(host_obj, "persistent_discovery_ctrl",
+ json_object_new_boolean(h->pdc_enabled));
+ subsys_array = json_object_new_array();
+ nvme_for_each_subsystem(h, s) {
+ json_dump_subsys(subsys_array, s);
+ }
+ if (json_object_array_length(subsys_array))
+ json_object_object_add(host_obj, "subsystems",
+ subsys_array);
+ else
+ json_object_put(subsys_array);
+ json_object_array_add(host_array, host_obj);
+ }
+ json_object_object_add(json_root, "hosts", host_array);
+
+ ret = json_object_to_fd(fileno(r->fp), json_root, JSON_C_TO_STRING_PRETTY);
+ if (ret < 0) {
+ nvme_msg(r, LOG_ERR, "Failed to write, %s\n",
+ json_util_get_last_err());
+ ret = -1;
+ errno = EIO;
+ }
+ json_object_put(json_root);
+
+ return ret;
+}
diff --git a/src/nvme/linux.c b/src/nvme/linux.c
new file mode 100644
index 0000000..e29d9e7
--- /dev/null
+++ b/src/nvme/linux.c
@@ -0,0 +1,1297 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef CONFIG_OPENSSL
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/kdf.h>
+
+#ifdef CONFIG_OPENSSL_3
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#endif
+#endif
+
+#ifdef CONFIG_KEYUTILS
+#include <keyutils.h>
+#endif
+
+#include <ccan/endian/endian.h>
+
+#include "cleanup.h"
+#include "linux.h"
+#include "tree.h"
+#include "log.h"
+#include "private.h"
+#include "base64.h"
+
+static int __nvme_open(const char *name)
+{
+ _cleanup_free_ char *path = NULL;
+ int ret;
+
+ ret = asprintf(&path, "%s/%s", "/dev", name);
+ if (ret < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return open(path, O_RDONLY);
+}
+
+int nvme_open(const char *name)
+{
+ int ret, fd, id, ns;
+ struct stat stat;
+ bool c;
+
+ ret = sscanf(name, "nvme%dn%d", &id, &ns);
+ if (ret != 1 && ret != 2) {
+ errno = EINVAL;
+ return -1;
+ }
+ c = ret == 1;
+
+ fd = __nvme_open(name);
+ if (fd < 0)
+ return fd;
+
+ ret = fstat(fd, &stat);
+ if (ret < 0)
+ goto close_fd;
+
+ if (c) {
+ if (!S_ISCHR(stat.st_mode)) {
+ errno = EINVAL;
+ goto close_fd;
+ }
+ } else if (!S_ISBLK(stat.st_mode)) {
+ errno = EINVAL;
+ goto close_fd;
+ }
+
+ return fd;
+
+close_fd:
+ close(fd);
+ return -1;
+}
+
+int nvme_fw_download_seq(int fd, __u32 size, __u32 xfer, __u32 offset,
+ void *buf)
+{
+ int err = 0;
+ struct nvme_fw_download_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .offset = offset,
+ .data_len = xfer,
+ .data = buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ while (size > 0) {
+ args.data_len = MIN(xfer, size);
+ err = nvme_fw_download(&args);
+ if (err)
+ break;
+
+ args.data += xfer;
+ size -= xfer;
+ args.offset += xfer;
+ }
+
+ return err;
+}
+
+int nvme_get_telemetry_max(int fd, enum nvme_telemetry_da *da, size_t *data_tx)
+{
+ _cleanup_free_ struct nvme_id_ctrl *id_ctrl = NULL;
+ int err;
+
+ id_ctrl = __nvme_alloc(sizeof(*id_ctrl));
+ if (!id_ctrl) {
+ errno = ENOMEM;
+ return -1;
+ }
+ err = nvme_identify_ctrl(fd, id_ctrl);
+ if (err)
+ return err;
+
+ if (data_tx) {
+ *data_tx = id_ctrl->mdts;
+ if (id_ctrl->mdts) {
+ /* assuming CAP.MPSMIN is zero minimum Memory Page Size is at least
+ * 4096 bytes
+ */
+ *data_tx = (1 << id_ctrl->mdts) * 4096;
+ }
+ }
+ if (da) {
+ if (id_ctrl->lpa & 0x8)
+ *da = NVME_TELEMETRY_DA_3;
+ if (id_ctrl->lpa & 0x40)
+ *da = NVME_TELEMETRY_DA_4;
+
+ }
+ return err;
+}
+
+int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_data_tx,
+ enum nvme_telemetry_da da, struct nvme_telemetry_log **buf,
+ size_t *size)
+{
+ static const __u32 xfer = NVME_LOG_TELEM_BLOCK_SIZE;
+
+ struct nvme_telemetry_log *telem;
+ enum nvme_cmd_get_log_lid lid;
+ _cleanup_free_ void *log;
+ void *tmp;
+ int err;
+ size_t dalb;
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .nsid = NVME_NSID_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = NVME_LOG_LSI_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ .csi = NVME_CSI_NVM,
+ .rae = rae,
+ .ot = false,
+ };
+
+ *size = 0;
+
+ log = malloc(xfer);
+ if (!log) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (ctrl) {
+ err = nvme_get_log_telemetry_ctrl(fd, true, 0, xfer, log);
+ lid = NVME_LOG_LID_TELEMETRY_CTRL;
+ } else {
+ lid = NVME_LOG_LID_TELEMETRY_HOST;
+ if (create)
+ err = nvme_get_log_create_telemetry_host(fd, log);
+ else
+ err = nvme_get_log_telemetry_host(fd, 0, xfer, log);
+ }
+
+ if (err)
+ return err;
+
+ telem = log;
+ if (ctrl && !telem->ctrlavail) {
+ *buf = log;
+ log = NULL;
+ *size = xfer;
+ return 0;
+ }
+
+ switch (da) {
+ case NVME_TELEMETRY_DA_1:
+ dalb = le16_to_cpu(telem->dalb1);
+ break;
+ case NVME_TELEMETRY_DA_2:
+ dalb = le16_to_cpu(telem->dalb2);
+ break;
+ case NVME_TELEMETRY_DA_3:
+ /* dalb3 >= dalb2 >= dalb1 */
+ dalb = le16_to_cpu(telem->dalb3);
+ break;
+ case NVME_TELEMETRY_DA_4:
+ dalb = le32_to_cpu(telem->dalb4);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (dalb == 0) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ *size = (dalb + 1) * xfer;
+ tmp = realloc(log, *size);
+ if (!tmp) {
+ errno = ENOMEM;
+ return -1;
+ }
+ log = tmp;
+
+ args.lid = lid;
+ args.log = log;
+ args.len = *size;
+ err = nvme_get_log_page(fd, max_data_tx, &args);
+ if (err)
+ return err;
+
+ *buf = log;
+ log = NULL;
+ return 0;
+}
+
+
+static int nvme_check_get_telemetry_log(int fd, bool create, bool ctrl, bool rae,
+ struct nvme_telemetry_log **log, enum nvme_telemetry_da da,
+ size_t *size)
+{
+ enum nvme_telemetry_da max_da = 0;
+ int err = nvme_get_telemetry_max(fd, &max_da, NULL);
+
+ if (err)
+ return err;
+ if (da > max_da) {
+ errno = ENOENT;
+ return -1;
+ }
+ return nvme_get_telemetry_log(fd, create, ctrl, rae, 4096, da, log, size);
+}
+
+
+int nvme_get_ctrl_telemetry(int fd, bool rae, struct nvme_telemetry_log **log,
+ enum nvme_telemetry_da da, size_t *size)
+{
+ return nvme_check_get_telemetry_log(fd, false, true, rae, log, da, size);
+}
+
+int nvme_get_host_telemetry(int fd, struct nvme_telemetry_log **log,
+ enum nvme_telemetry_da da, size_t *size)
+{
+ return nvme_check_get_telemetry_log(fd, false, false, false, log, da, size);
+}
+
+int nvme_get_new_host_telemetry(int fd, struct nvme_telemetry_log **log,
+ enum nvme_telemetry_da da, size_t *size)
+{
+ return nvme_check_get_telemetry_log(fd, true, false, false, log, da, size);
+}
+
+int nvme_get_lba_status_log(int fd, bool rae, struct nvme_lba_status_log **log)
+{
+ __u32 size;
+ _cleanup_free_ struct nvme_lba_status_log *buf;
+ void *tmp;
+ int err;
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .nsid = NVME_NSID_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = NVME_LOG_LSI_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ .csi = NVME_CSI_NVM,
+ .rae = rae,
+ .ot = false,
+ };
+
+ buf = malloc(sizeof(*buf));
+ if (!buf)
+ return -1;
+
+ err = nvme_get_log_lba_status(fd, true, 0, sizeof(*buf), buf);
+ if (err) {
+ *log = NULL;
+ return err;
+ }
+
+ size = le32_to_cpu(buf->lslplen);
+ if (!size) {
+ *log = buf;
+ buf = NULL;
+ return 0;
+ }
+
+ tmp = realloc(buf, size);
+ if (!tmp) {
+ *log = NULL;
+ return -1;
+ }
+ buf = tmp;
+
+ args.lid = NVME_LOG_LID_LBA_STATUS;
+ args.log = buf;
+ args.len = size;
+ err = nvme_get_log_page(fd, 4096, &args);
+ if (err) {
+ *log = NULL;
+ return err;
+ }
+
+ *log = buf;
+ buf = NULL;
+ return 0;
+}
+
+static int nvme_ns_attachment(int fd, __u32 nsid, __u16 num_ctrls,
+ __u16 *ctrlist, bool attach, __u32 timeout)
+{
+ struct nvme_ctrl_list cntlist = { 0 };
+ struct nvme_ns_attach_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .nsid = nsid,
+ .sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH,
+ .ctrlist = &cntlist,
+ .timeout = timeout,
+ };
+
+ if (attach)
+ args.sel = NVME_NS_ATTACH_SEL_CTRL_ATTACH;
+
+ nvme_init_ctrl_list(args.ctrlist, num_ctrls, ctrlist);
+ return nvme_ns_attach(&args);
+}
+
+int nvme_namespace_attach_ctrls(int fd, __u32 nsid, __u16 num_ctrls,
+ __u16 *ctrlist)
+{
+ return nvme_ns_attachment(fd, nsid, num_ctrls, ctrlist, true,
+ NVME_DEFAULT_IOCTL_TIMEOUT);
+}
+
+int nvme_namespace_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls,
+ __u16 *ctrlist)
+{
+ return nvme_ns_attachment(fd, nsid, num_ctrls, ctrlist, false,
+ NVME_DEFAULT_IOCTL_TIMEOUT);
+}
+
+int nvme_get_ana_log_len(int fd, size_t *analen)
+{
+ _cleanup_free_ struct nvme_id_ctrl *ctrl = NULL;
+ int ret;
+
+ ctrl = __nvme_alloc(sizeof(*ctrl));
+ if (!ctrl) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ret = nvme_identify_ctrl(fd, ctrl);
+ if (ret)
+ return ret;
+
+ *analen = sizeof(struct nvme_ana_log) +
+ le32_to_cpu(ctrl->nanagrpid) * sizeof(struct nvme_ana_group_desc) +
+ le32_to_cpu(ctrl->mnan) * sizeof(__le32);
+ return 0;
+}
+
+int nvme_get_logical_block_size(int fd, __u32 nsid, int *blksize)
+{
+ _cleanup_free_ struct nvme_id_ns *ns = NULL;
+ __u8 flbas;
+ int ret;
+
+ ns = __nvme_alloc(sizeof(*ns));
+ if (!ns) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ret = nvme_identify_ns(fd, nsid, ns);
+ if (ret)
+ return ret;
+
+ nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &flbas);
+ *blksize = 1 << ns->lbaf[flbas].ds;
+
+ return 0;
+}
+
+static int __nvme_set_attr(const char *path, const char *value)
+{
+ _cleanup_fd_ int fd = -1;
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+#if 0
+ nvme_msg(LOG_DEBUG, "Failed to open %s: %s\n", path,
+ strerror(errno));
+#endif
+ return -1;
+ }
+ return write(fd, value, strlen(value));
+}
+
+int nvme_set_attr(const char *dir, const char *attr, const char *value)
+{
+ _cleanup_free_ char *path = NULL;
+ int ret;
+
+ ret = asprintf(&path, "%s/%s", dir, attr);
+ if (ret < 0)
+ return -1;
+
+ return __nvme_set_attr(path, value);
+}
+
+static char *__nvme_get_attr(const char *path)
+{
+ char value[4096] = { 0 };
+ int ret, fd;
+ int saved_errno;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ ret = read(fd, value, sizeof(value) - 1);
+ saved_errno = errno;
+ close(fd);
+ if (ret < 0) {
+ errno = saved_errno;
+ return NULL;
+ }
+ errno = 0;
+ if (!strlen(value))
+ return NULL;
+
+ if (value[strlen(value) - 1] == '\n')
+ value[strlen(value) - 1] = '\0';
+ while (strlen(value) > 0 && value[strlen(value) - 1] == ' ')
+ value[strlen(value) - 1] = '\0';
+
+ return strlen(value) ? strdup(value) : NULL;
+}
+
+char *nvme_get_attr(const char *dir, const char *attr)
+{
+ _cleanup_free_ char *path = NULL;
+ int ret;
+
+ ret = asprintf(&path, "%s/%s", dir, attr);
+ if (ret < 0) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ return __nvme_get_attr(path);
+}
+
+char *nvme_get_subsys_attr(nvme_subsystem_t s, const char *attr)
+{
+ return nvme_get_attr(nvme_subsystem_get_sysfs_dir(s), attr);
+}
+
+char *nvme_get_ctrl_attr(nvme_ctrl_t c, const char *attr)
+{
+ return nvme_get_attr(nvme_ctrl_get_sysfs_dir(c), attr);
+}
+
+char *nvme_get_ns_attr(nvme_ns_t n, const char *attr)
+{
+ return nvme_get_attr(nvme_ns_get_sysfs_dir(n), attr);
+}
+
+char *nvme_get_path_attr(nvme_path_t p, const char *attr)
+{
+ return nvme_get_attr(nvme_path_get_sysfs_dir(p), attr);
+}
+
+#ifndef CONFIG_OPENSSL
+int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
+ unsigned int key_len, unsigned char *secret,
+ unsigned char *key)
+{
+ if (hmac != NVME_HMAC_ALG_NONE) {
+ nvme_msg(NULL, LOG_ERR, "HMAC transformation not supported; " \
+ "recompile with OpenSSL support.\n");
+ errno = -EINVAL;
+ return -1;
+ }
+
+ memcpy(key, secret, key_len);
+ return 0;
+}
+
+static int derive_retained_key(int hmac, const char *hostnqn,
+ unsigned char *generated,
+ unsigned char *retained,
+ size_t key_len)
+{
+ nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; "
+ "recompile with OpenSSL support.\n");
+ errno = ENOTSUP;
+ return -1;
+}
+
+static int gen_tls_identity(const char *hostnqn, const char *subsysnqn,
+ int version, int hmac, char *identity,
+ unsigned char *retained, size_t key_len)
+{
+ if (version != 0) {
+ nvme_msg(NULL, LOG_ERR, "NVMe TLS 2.0 is not supported; "
+ "recompile with OpenSSL support.\n");
+ errno = ENOTSUP;
+ return -1;
+ }
+ sprintf(identity, "NVMe0R%02d %s %s",
+ hmac, hostnqn, subsysnqn);
+ return strlen(identity);
+}
+
+static int derive_tls_key(int hmac, const char *identity,
+ unsigned char *retained,
+ unsigned char *psk, size_t key_len)
+{
+ nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; "
+ "recompile with OpenSSL support.\n");
+ errno = ENOTSUP;
+ return -1;
+}
+#else /* CONFIG_OPENSSL */
+static const EVP_MD *select_hmac(int hmac, size_t *key_len)
+{
+ const EVP_MD *md = NULL;
+
+ switch (hmac) {
+ case NVME_HMAC_ALG_SHA2_256:
+ md = EVP_sha256();
+ *key_len = 32;
+ break;
+ case NVME_HMAC_ALG_SHA2_384:
+ md = EVP_sha384();
+ *key_len = 48;
+ break;
+ default:
+ break;
+ }
+ return md;
+}
+
+static DEFINE_CLEANUP_FUNC(
+ cleanup_evp_pkey_ctx, EVP_PKEY_CTX *, EVP_PKEY_CTX_free)
+#define _cleanup_evp_pkey_ctx_ __cleanup__(cleanup_evp_pkey_ctx)
+
+static int derive_retained_key(int hmac, const char *hostnqn,
+ unsigned char *generated,
+ unsigned char *retained,
+ size_t key_len)
+{
+ const EVP_MD *md;
+ _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
+ uint16_t length = key_len & 0xFFFF;
+ size_t hmac_len;
+
+ md = select_hmac(hmac, &hmac_len);
+ if (!md || hmac_len > key_len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(ctx) <= 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_set1_hkdf_key(ctx, generated, key_len) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)&length, 2) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)"tls13 ", 6) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)"HostNQN", 7) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)hostnqn, strlen(hostnqn)) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ if (EVP_PKEY_derive(ctx, retained, &key_len) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ return key_len;
+}
+
+static int derive_tls_key(int hmac, const char *identity,
+ unsigned char *retained,
+ unsigned char *psk, size_t key_len)
+{
+ const EVP_MD *md;
+ _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
+ size_t hmac_len;
+ uint16_t length = key_len & 0xFFFF;
+
+ md = select_hmac(hmac, &hmac_len);
+ if (!md || hmac_len > key_len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(ctx) <= 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)&length, 2) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)"tls13 ", 6) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)"nvme-tls-psk", 12) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)identity,
+ strlen(identity)) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ if (EVP_PKEY_derive(ctx, psk, &key_len) <= 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ return key_len;
+}
+#endif /* CONFIG_OPENSSL */
+
+#ifdef CONFIG_OPENSSL_1
+static DEFINE_CLEANUP_FUNC(cleanup_hmac_ctx, HMAC_CTX *, HMAC_CTX_free)
+#define _cleanup_hmac_ctx_ __cleanup__(cleanup_hmac_ctx)
+
+int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
+ unsigned int key_len, unsigned char *secret,
+ unsigned char *key)
+{
+ const char hmac_seed[] = "NVMe-over-Fabrics";
+ _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx = NULL;
+ const EVP_MD *md;
+
+ ENGINE_load_builtin_engines();
+ ENGINE_register_all_complete();
+
+ hmac_ctx = HMAC_CTX_new();
+ if (!hmac_ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ switch (hmac) {
+ case NVME_HMAC_ALG_NONE:
+ memcpy(key, secret, key_len);
+ return 0;
+ case NVME_HMAC_ALG_SHA2_256:
+ md = EVP_sha256();
+ break;
+ case NVME_HMAC_ALG_SHA2_384:
+ md = EVP_sha384();
+ break;
+ case NVME_HMAC_ALG_SHA2_512:
+ md = EVP_sha512();
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!md) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!HMAC_Init_ex(hmac_ctx, secret, key_len, md, NULL)) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (!HMAC_Update(hmac_ctx, (unsigned char *)hostnqn,
+ strlen(hostnqn))) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ if (!HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed,
+ strlen(hmac_seed))) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ if (!HMAC_Final(hmac_ctx, key, &key_len)) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int gen_tls_identity(const char *hostnqn, const char *subsysnqn,
+ int version, int hmac, char *identity,
+ unsigned char *retained, size_t key_len)
+{
+ static const char hmac_seed[] = "NVMe-over-Fabrics";
+ size_t hmac_len;
+ const EVP_MD *md = select_hmac(hmac, &hmac_len);
+ _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx = NULL;
+ _cleanup_free_ unsigned char *psk_ctx = NULL;
+ _cleanup_free_ char *enc_ctx = NULL;
+ size_t len;
+
+ if (version == 0) {
+ sprintf(identity, "NVMe%01dR%02d %s %s",
+ version, hmac, hostnqn, subsysnqn);
+ return strlen(identity);
+ }
+ if (version > 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ hmac_ctx = HMAC_CTX_new();
+ if (!hmac_ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (!md) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ psk_ctx = malloc(key_len);
+ if (!psk_ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (!HMAC_Init_ex(hmac_ctx, retained, key_len, md, NULL)) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (!HMAC_Update(hmac_ctx, (unsigned char *)hostnqn,
+ strlen(hostnqn))) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!HMAC_Update(hmac_ctx, (unsigned char *)" ", 1)) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!HMAC_Update(hmac_ctx, (unsigned char *)subsysnqn,
+ strlen(subsysnqn))) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!HMAC_Update(hmac_ctx, (unsigned char *)" ", 1)) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed,
+ strlen(hmac_seed))) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!HMAC_Final(hmac_ctx, psk_ctx, (unsigned int *)&key_len)) {
+ errno = ENOKEY;
+ return -1;
+ }
+ enc_ctx = malloc(key_len * 2);
+ memset(enc_ctx, 0, key_len * 2);
+ len = base64_encode(psk_ctx, key_len, enc_ctx);
+ if (len < 0) {
+ errno = ENOKEY;
+ return len;
+ }
+ sprintf(identity, "NVMe%01dR%02d %s %s %s",
+ version, hmac, hostnqn, subsysnqn, enc_ctx);
+ return strlen(identity);
+}
+#endif /* !CONFIG_OPENSSL_1 */
+
+#ifdef CONFIG_OPENSSL_3
+static DEFINE_CLEANUP_FUNC(
+ cleanup_ossl_lib_ctx, OSSL_LIB_CTX *, OSSL_LIB_CTX_free)
+#define _cleanup_ossl_lib_ctx_ __cleanup__(cleanup_ossl_lib_ctx)
+static DEFINE_CLEANUP_FUNC(cleanup_evp_mac_ctx, EVP_MAC_CTX *, EVP_MAC_CTX_free)
+#define _cleanup_evp_mac_ctx_ __cleanup__(cleanup_evp_mac_ctx)
+static DEFINE_CLEANUP_FUNC(cleanup_evp_mac, EVP_MAC *, EVP_MAC_free)
+#define _cleanup_evp_mac_ __cleanup__(cleanup_evp_mac)
+
+int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
+ unsigned int key_len, unsigned char *secret,
+ unsigned char *key)
+{
+ const char hmac_seed[] = "NVMe-over-Fabrics";
+ OSSL_PARAM params[2], *p = params;
+ _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL;
+ _cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL;
+ _cleanup_evp_mac_ EVP_MAC *mac = NULL;
+ char *progq = NULL;
+ char *digest;
+ size_t len;
+
+ lib_ctx = OSSL_LIB_CTX_new();
+ if (!lib_ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mac = EVP_MAC_fetch(lib_ctx, OSSL_MAC_NAME_HMAC, progq);
+ if (!mac) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mac_ctx = EVP_MAC_CTX_new(mac);
+ if (!mac_ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ switch (hmac) {
+ case NVME_HMAC_ALG_NONE:
+ memcpy(key, secret, key_len);
+ return 0;
+ case NVME_HMAC_ALG_SHA2_256:
+ digest = OSSL_DIGEST_NAME_SHA2_256;
+ break;
+ case NVME_HMAC_ALG_SHA2_384:
+ digest = OSSL_DIGEST_NAME_SHA2_384;
+ break;
+ case NVME_HMAC_ALG_SHA2_512:
+ digest = OSSL_DIGEST_NAME_SHA2_512;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
+ digest,
+ 0);
+ *p = OSSL_PARAM_construct_end();
+
+ if (!EVP_MAC_init(mac_ctx, secret, key_len, params)) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ if (!EVP_MAC_update(mac_ctx, (unsigned char *)hostnqn,
+ strlen(hostnqn))) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ if (!EVP_MAC_update(mac_ctx, (unsigned char *)hmac_seed,
+ strlen(hmac_seed))) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ if (!EVP_MAC_final(mac_ctx, key, &len, key_len)) {
+ errno = ENOKEY;
+ return -1;
+ }
+
+ if (len != key_len) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int gen_tls_identity(const char *hostnqn, const char *subsysnqn,
+ int version, int hmac, char *identity,
+ unsigned char *retained, size_t key_len)
+{
+ static const char hmac_seed[] = "NVMe-over-Fabrics";
+ size_t hmac_len;
+ OSSL_PARAM params[2], *p = params;
+ _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL;
+ _cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL;
+ _cleanup_evp_mac_ EVP_MAC *mac = NULL;
+ char *progq = NULL;
+ char *digest = NULL;
+ _cleanup_free_ unsigned char *psk_ctx = NULL;
+ _cleanup_free_ char *enc_ctx = NULL;
+ size_t len;
+
+ if (version == 0) {
+ sprintf(identity, "NVMe%01dR%02d %s %s",
+ version, hmac, hostnqn, subsysnqn);
+ return strlen(identity);
+ }
+ if (version > 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ lib_ctx = OSSL_LIB_CTX_new();
+ if (!lib_ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+ mac = EVP_MAC_fetch(lib_ctx, OSSL_MAC_NAME_HMAC, progq);
+ if (!mac) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mac_ctx = EVP_MAC_CTX_new(mac);
+ if (!mac_ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+ switch (hmac) {
+ case NVME_HMAC_ALG_SHA2_256:
+ digest = OSSL_DIGEST_NAME_SHA2_256;
+ break;
+ case NVME_HMAC_ALG_SHA2_384:
+ digest = OSSL_DIGEST_NAME_SHA2_384;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ if (!digest)
+ return -1;
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
+ digest, 0);
+ *p = OSSL_PARAM_construct_end();
+
+ psk_ctx = malloc(key_len);
+ if (!psk_ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (!EVP_MAC_init(mac_ctx, retained, key_len, params)) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!EVP_MAC_update(mac_ctx, (unsigned char *)hostnqn,
+ strlen(hostnqn))) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!EVP_MAC_update(mac_ctx, (unsigned char *)" ", 1)) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!EVP_MAC_update(mac_ctx, (unsigned char *)subsysnqn,
+ strlen(subsysnqn))) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!EVP_MAC_update(mac_ctx, (unsigned char *)" ", 1)) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!EVP_MAC_update(mac_ctx, (unsigned char *)hmac_seed,
+ strlen(hmac_seed))) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (!EVP_MAC_final(mac_ctx, psk_ctx, &hmac_len, key_len)) {
+ errno = ENOKEY;
+ return -1;
+ }
+ if (hmac_len > key_len) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+ enc_ctx = malloc(hmac_len * 2);
+ memset(enc_ctx, 0, hmac_len * 2);
+ len = base64_encode(psk_ctx, hmac_len, enc_ctx);
+ if (len < 0) {
+ errno = ENOKEY;
+ return len;
+ }
+ sprintf(identity, "NVMe%01dR%02d %s %s %s",
+ version, hmac, hostnqn, subsysnqn, enc_ctx);
+ return strlen(identity);
+}
+#endif /* !CONFIG_OPENSSL_3 */
+
+static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
+ char *identity, int version,
+ int hmac, unsigned char *configured,
+ unsigned char *psk, int key_len)
+{
+ _cleanup_free_ unsigned char *retained = NULL;
+ int ret = -1;
+
+ if (!hostnqn || !subsysnqn || !identity || !psk) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ retained = malloc(key_len);
+ if (!retained) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ret = derive_retained_key(hmac, hostnqn, configured, retained, key_len);
+ if (ret < 0)
+ return ret;
+ ret = gen_tls_identity(hostnqn, subsysnqn, version, hmac,
+ identity, retained, key_len);
+ if (ret < 0)
+ return ret;
+ return derive_tls_key(hmac, identity, retained, psk, key_len);
+}
+
+static size_t nvme_identity_len(int hmac, int version, const char *hostnqn,
+ const char *subsysnqn)
+{
+ size_t len;
+
+ if (!hostnqn || !subsysnqn) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(hostnqn) + strlen(subsysnqn) + 12;
+ if (version == 1) {
+ len += 66;
+ if (hmac == NVME_HMAC_ALG_SHA2_384)
+ len += 32;
+ } else if (version > 1) {
+ errno = EINVAL;
+ return -1;
+ }
+ return len;
+}
+
+char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn,
+ int version, int hmac,
+ unsigned char *configured_key, int key_len)
+{
+ char *identity;
+ size_t identity_len;
+ _cleanup_free_ unsigned char *psk = NULL;
+ int ret = -1;
+
+ identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn);
+ if (identity_len < 0)
+ return NULL;
+
+ identity = malloc(identity_len);
+ if (!identity)
+ return NULL;
+
+ psk = malloc(key_len);
+ if (!psk)
+ goto out_free_identity;
+
+ memset(psk, 0, key_len);
+ ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
+ configured_key, psk, key_len);
+out_free_identity:
+ if (ret < 0) {
+ free(identity);
+ identity = NULL;
+ }
+ return identity;
+}
+
+#ifdef CONFIG_KEYUTILS
+long nvme_lookup_keyring(const char *keyring)
+{
+ key_serial_t keyring_id;
+
+ keyring_id = find_key_by_type_and_desc("keyring", keyring, 0);
+ if (keyring_id < 0)
+ return 0;
+ return keyring_id;
+}
+
+char *nvme_describe_key_serial(long key_id)
+{
+ char *desc;
+
+ if (keyctl_describe_alloc(key_id, &desc) < 0)
+ desc = NULL;
+ return desc;
+}
+
+long nvme_lookup_key(const char *type, const char *identity)
+{
+ key_serial_t key;
+
+ key = keyctl_search(KEY_SPEC_SESSION_KEYRING, type, identity, 0);
+ if (key < 0)
+ return 0;
+ return key;
+}
+
+int nvme_set_keyring(long key_id)
+{
+ long err;
+
+ err = keyctl_link(key_id, KEY_SPEC_SESSION_KEYRING);
+ if (err < 0)
+ return -1;
+ return 0;
+}
+
+long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn,
+ int version, int hmac,
+ unsigned char *configured_key, int key_len)
+{
+ key_serial_t keyring_id, key;
+ _cleanup_free_ char *identity = NULL;
+ size_t identity_len;
+ _cleanup_free_ unsigned char *psk = NULL;
+ int ret = -1;
+
+ keyring_id = nvme_lookup_keyring(keyring);
+ if (keyring_id == 0)
+ return -1;
+
+ identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn);
+ if (identity_len < 0)
+ return -1;
+
+ identity = malloc(identity_len);
+ if (!identity) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ psk = malloc(key_len);
+ if (!psk) {
+ errno = ENOMEM;
+ return 0;
+ }
+ memset(psk, 0, key_len);
+ ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
+ configured_key, psk, key_len);
+ if (ret != key_len)
+ return 0;
+
+ key = keyctl_search(keyring_id, key_type, identity, 0);
+ if (key > 0) {
+ if (keyctl_update(key, psk, key_len) < 0)
+ key = 0;
+ } else {
+ key = add_key(key_type, identity,
+ psk, key_len, keyring_id);
+ if (key < 0)
+ key = 0;
+ }
+ return key;
+}
+
+#else
+long nvme_lookup_keyring(const char *keyring)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "\
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return 0;
+}
+
+char *nvme_describe_key_serial(long key_id)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "\
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return NULL;
+}
+
+long nvme_lookup_key(const char *type, const char *identity)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "\
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return 0;
+}
+
+int nvme_set_keyring(long key_id)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "\
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return -1;
+}
+
+long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn,
+ int version, int hmac,
+ unsigned char *configured_key, int key_len)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return -1;
+}
+#endif
+
+long nvme_insert_tls_key(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn, int hmac,
+ unsigned char *configured_key, int key_len)
+{
+ return nvme_insert_tls_key_versioned(keyring, key_type,
+ hostnqn, subsysnqn, 0, hmac,
+ configured_key, key_len);
+}
diff --git a/src/nvme/linux.h b/src/nvme/linux.h
new file mode 100644
index 0000000..11ee76e
--- /dev/null
+++ b/src/nvme/linux.h
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+#ifndef _LIBNVME_LINUX_H
+#define _LIBNVME_LINUX_H
+
+#include <stddef.h>
+
+#include "ioctl.h"
+#include "types.h"
+
+/**
+ * DOC: linux.h
+ *
+ * linux-specific utility functions
+ */
+
+/**
+ * nvme_fw_download_seq() - Firmware download sequence
+ * @fd: File descriptor of nvme device
+ * @size: Total size of the firmware image to transfer
+ * @xfer: Maximum size to send with each partial transfer
+ * @offset: Starting offset to send with this firmware download
+ * @buf: Address of buffer containing all or part of the firmware image.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_fw_download_seq(int fd, __u32 size, __u32 xfer, __u32 offset,
+ void *buf);
+
+/**
+ * enum nvme_telemetry_da - Telemetry Log Data Area
+ * @NVME_TELEMETRY_DA_1: Data Area 1
+ * @NVME_TELEMETRY_DA_2: Data Area 2
+ * @NVME_TELEMETRY_DA_3: Data Area 3
+ * @NVME_TELEMETRY_DA_4: Data Area 4
+ */
+enum nvme_telemetry_da {
+ NVME_TELEMETRY_DA_1 = 1,
+ NVME_TELEMETRY_DA_2 = 2,
+ NVME_TELEMETRY_DA_3 = 3,
+ NVME_TELEMETRY_DA_4 = 4,
+};
+
+/**
+ * nvme_get_telemetry_max() - Get telemetry limits
+ * @fd: File descriptor of nvme device
+ * @da: On success return max supported data area
+ * @max_data_tx: On success set to max transfer chunk supported by the controller
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_telemetry_max(int fd, enum nvme_telemetry_da *da, size_t *max_data_tx);
+
+/**
+ * nvme_get_telemetry_log() - Get specified telemetry log
+ * @fd: File descriptor of nvme device
+ * @create: Generate new host initated telemetry capture
+ * @ctrl: Get controller Initiated log
+ * @rae: Retain asynchronous events
+ * @max_data_tx: Set the max data transfer size to be used retrieving telemetry.
+ * @da: Log page data area, valid values: &enum nvme_telemetry_da.
+ * @log: On success, set to the value of the allocated and retrieved log.
+ * @size: Ptr to the telemetry log size, so it can be returned
+ *
+ * The total size allocated can be calculated as:
+ * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_data_tx,
+ enum nvme_telemetry_da da, struct nvme_telemetry_log **log,
+ size_t *size);
+/**
+ * nvme_get_ctrl_telemetry() - Get controller telemetry log
+ * @fd: File descriptor of nvme device
+ * @rae: Retain asynchronous events
+ * @log: On success, set to the value of the allocated and retrieved log.
+ * @da: Log page data area, valid values: &enum nvme_telemetry_da
+ * @size: Ptr to the telemetry log size, so it can be returned
+ *
+ * The total size allocated can be calculated as:
+ * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_ctrl_telemetry(int fd, bool rae, struct nvme_telemetry_log **log,
+ enum nvme_telemetry_da da, size_t *size);
+
+/**
+ * nvme_get_host_telemetry() - Get host telemetry log
+ * @fd: File descriptor of nvme device
+ * @log: On success, set to the value of the allocated and retrieved log.
+ * @da: Log page data area, valid values: &enum nvme_telemetry_da
+ * @size: Ptr to the telemetry log size, so it can be returned
+ *
+ * The total size allocated can be calculated as:
+ * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_host_telemetry(int fd, struct nvme_telemetry_log **log,
+ enum nvme_telemetry_da da, size_t *size);
+
+/**
+ * nvme_get_new_host_telemetry() - Get new host telemetry log
+ * @fd: File descriptor of nvme device
+ * @log: On success, set to the value of the allocated and retrieved log.
+ * @da: Log page data area, valid values: &enum nvme_telemetry_da
+ * @size: Ptr to the telemetry log size, so it can be returned
+ *
+ * The total size allocated can be calculated as:
+ * (nvme_telemetry_log da size + 1) * NVME_LOG_TELEM_BLOCK_SIZE.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_new_host_telemetry(int fd, struct nvme_telemetry_log **log,
+ enum nvme_telemetry_da da, size_t *size);
+
+/**
+ * nvme_get_ana_log_len() - Retrieve size of the current ANA log
+ * @fd: File descriptor of nvme device
+ * @analen: Pointer to where the length will be set on success
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_ana_log_len(int fd, size_t *analen);
+
+/**
+ * nvme_get_logical_block_size() - Retrieve block size
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace id
+ * @blksize: Pointer to where the block size will be set on success
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_logical_block_size(int fd, __u32 nsid, int *blksize);
+
+/**
+ * nvme_get_lba_status_log() - Retrieve the LBA Status log page
+ * @fd: File descriptor of the nvme device
+ * @rae: Retain asynchronous events
+ * @log: On success, set to the value of the allocated and retrieved log.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_get_lba_status_log(int fd, bool rae, struct nvme_lba_status_log **log);
+
+/**
+ * nvme_namespace_attach_ctrls() - Attach namespace to controller(s)
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID to attach
+ * @num_ctrls: Number of controllers in ctrlist
+ * @ctrlist: List of controller IDs to perform the attach action
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_namespace_attach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist);
+
+/**
+ * nvme_namespace_detach_ctrls() - Detach namespace from controller(s)
+ * @fd: File descriptor of nvme device
+ * @nsid: Namespace ID to detach
+ * @num_ctrls: Number of controllers in ctrlist
+ * @ctrlist: List of controller IDs to perform the detach action
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_namespace_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist);
+
+/**
+ * nvme_open() - Open an nvme controller or namespace device
+ * @name: The basename of the device to open
+ *
+ * This will look for the handle in /dev/ and validate the name and filetype
+ * match linux conventions.
+ *
+ * Return: A file descriptor for the device on a successful open, or -1 with
+ * errno set otherwise.
+ */
+int nvme_open(const char *name);
+
+/**
+ * enum nvme_hmac_alg - HMAC algorithm
+ * @NVME_HMAC_ALG_NONE: No HMAC algorithm
+ * @NVME_HMAC_ALG_SHA2_256: SHA2-256
+ * @NVME_HMAC_ALG_SHA2_384: SHA2-384
+ * @NVME_HMAC_ALG_SHA2_512: SHA2-512
+ */
+enum nvme_hmac_alg {
+ NVME_HMAC_ALG_NONE = 0,
+ NVME_HMAC_ALG_SHA2_256 = 1,
+ NVME_HMAC_ALG_SHA2_384 = 2,
+ NVME_HMAC_ALG_SHA2_512 = 3,
+};
+
+/**
+ * nvme_gen_dhchap_key() - DH-HMAC-CHAP key generation
+ * @hostnqn: Host NVMe Qualified Name
+ * @hmac: HMAC algorithm
+ * @key_len: Output key length
+ * @secret: Secret to used for digest
+ * @key: Generated DH-HMAC-CHAP key
+ *
+ * Return: If key generation was successful the function returns 0 or
+ * -1 with errno set otherwise.
+ */
+int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
+ unsigned int key_len, unsigned char *secret,
+ unsigned char *key);
+
+/**
+ * nvme_lookup_keyring() - Lookup keyring serial number
+ * @keyring: Keyring name
+ *
+ * Looks up the serial number of the keyring @keyring.
+ *
+ * Return: The key serial number of the keyring
+ * or 0 with errno set otherwise.
+ */
+long nvme_lookup_keyring(const char *keyring);
+
+/**
+ * nvme_describe_key_serial() - Return key description
+ * @key_id: Key serial number
+ *
+ * Fetches the description of the key or keyring identified
+ * by the serial number @key_id.
+ *
+ * Return: The description of @key_id or NULL on failure.
+ * The returned string needs to be freed by the caller.
+ */
+char *nvme_describe_key_serial(long key_id);
+
+/**
+ * nvme_lookup_key() - Lookup key serial number
+ * @type: Key type
+ * @identity: Key description
+ *
+ * Looks up the serial number of the key @identity
+ * with type %type in the current session keyring.
+ *
+ * Return: The key serial number of the key
+ * or 0 with errno set otherwise.
+ */
+long nvme_lookup_key(const char *type, const char *identity);
+
+/**
+ * nvme_set_keyring() - Link keyring for lookup
+ * @keyring_id: Keyring id
+ *
+ * Links @keyring_id into the session keyring such that
+ * its keys are available for further key lookups.
+ *
+ * Return: 0 on success, a negative number on error
+ * with errno set.
+ */
+int nvme_set_keyring(long keyring_id);
+
+/**
+ * nvme_insert_tls_key() - Derive and insert TLS key
+ * @keyring: Keyring to use
+ * @key_type: Type of the resulting key
+ * @hostnqn: Host NVMe Qualified Name
+ * @subsysnqn: Subsystem NVMe Qualified Name
+ * @hmac: HMAC algorithm
+ * @configured_key: Configured key data to derive the key from
+ * @key_len: Length of @configured_key
+ *
+ * Derives a 'retained' TLS key as specified in NVMe TCP 1.0a and
+ * stores it as type @key_type in the keyring specified by @keyring.
+ *
+ * Return: The key serial number if the key could be inserted into
+ * the keyring or 0 with errno otherwise.
+ */
+long nvme_insert_tls_key(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn, int hmac,
+ unsigned char *configured_key, int key_len);
+
+/**
+ * nvme_insert_tls_key_versioned() - Derive and insert TLS key
+ * @keyring: Keyring to use
+ * @key_type: Type of the resulting key
+ * @hostnqn: Host NVMe Qualified Name
+ * @subsysnqn: Subsystem NVMe Qualified Name
+ * @version: Key version to use
+ * @hmac: HMAC algorithm
+ * @configured_key: Configured key data to derive the key from
+ * @key_len: Length of @configured_key
+ *
+ * Derives a 'retained' TLS key as specified in NVMe TCP 1.0a (if
+ * @version s set to '0') or NVMe TP8028 (if @version is set to '1) and
+ * stores it as type @key_type in the keyring specified by @keyring.
+ *
+ * Return: The key serial number if the key could be inserted into
+ * the keyring or 0 with errno otherwise.
+ */
+long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn,
+ int version, int hmac,
+ unsigned char *configured_key, int key_len);
+
+/**
+ * nvme_generate_tls_key_identity() - Generate the TLS key identity
+ * @hostnqn: Host NVMe Qualified Name
+ * @subsysnqn: Subsystem NVMe Qualified Name
+ * @version: Key version to use
+ * @hmac: HMAC algorithm
+ * @configured_key: Configured key data to derive the key from
+ * @key_len: Length of @configured_key
+ *
+ * Derives a 'retained' TLS key as specified in NVMe TCP and
+ * generate the corresponding TLs identity.
+ *
+ * Return: The string containing the TLS identity. It is the responsibility
+ * of the caller to free the returned string.
+ */
+char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn,
+ int version, int hmac,
+ unsigned char *configured_key, int key_len);
+
+#endif /* _LIBNVME_LINUX_H */
diff --git a/src/nvme/log.c b/src/nvme/log.c
new file mode 100644
index 0000000..2ffca3e
--- /dev/null
+++ b/src/nvme/log.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (C) 2021 SUSE LLC
+ *
+ * Authors: Hannes Reinecke <hare@suse.de>
+ *
+ * This file implements basic logging functionality.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#define LOG_FUNCNAME 1
+#include "private.h"
+#include "log.h"
+#include "cleanup.h"
+
+#ifndef LOG_CLOCK
+#define LOG_CLOCK CLOCK_MONOTONIC
+#endif
+
+static nvme_root_t root;
+
+void __attribute__((format(printf, 4, 5)))
+__nvme_msg(nvme_root_t r, int lvl,
+ const char *func, const char *format, ...)
+{
+ FILE *fp = stderr;
+ va_list ap;
+ char pidbuf[16];
+ char timebuf[32];
+ static const char *const formats[] = {
+ "%s%s%s",
+ "%s%s%s: ",
+ "%s<%s>%s ",
+ "%s<%s> %s: ",
+ "[%s] %s%s ",
+ "[%s]%s %s: ",
+ "[%s] <%s>%s ",
+ "[%s] <%s> %s: ",
+ };
+ _cleanup_free_ char *header = NULL;
+ _cleanup_free_ char *message = NULL;
+ int idx = 0;
+
+ if (!r)
+ r = root;
+
+ if (r)
+ fp = r->fp;
+
+ if (r && lvl > r->log_level)
+ return;
+
+ if (r && r->log_timestamp) {
+ struct timespec now;
+
+ clock_gettime(LOG_CLOCK, &now);
+ snprintf(timebuf, sizeof(timebuf), "%6ld.%06ld",
+ (long)now.tv_sec, now.tv_nsec / 1000);
+ idx |= 1 << 2;
+ } else
+ *timebuf = '\0';
+
+ if (r && r->log_pid) {
+ snprintf(pidbuf, sizeof(pidbuf), "%ld", (long)getpid());
+ idx |= 1 << 1;
+ } else
+ *pidbuf = '\0';
+
+ if (func)
+ idx |= 1 << 0;
+
+ if (asprintf(&header, formats[idx],
+ timebuf, pidbuf, func ? func : "") == -1)
+ header = NULL;
+
+ va_start(ap, format);
+ if (vasprintf(&message, format, ap) == -1)
+ message = NULL;
+ va_end(ap);
+
+ fprintf(fp, "%s%s",
+ header ? header : "<error>",
+ message ? message : "<error>");
+}
+
+void nvme_init_logging(nvme_root_t r, int lvl, bool log_pid, bool log_tstamp)
+{
+ r->log_level = lvl;
+ r->log_pid = log_pid;
+ r->log_timestamp = log_tstamp;
+}
+
+void nvme_set_root(nvme_root_t r)
+{
+ root = r;
+}
diff --git a/src/nvme/log.h b/src/nvme/log.h
new file mode 100644
index 0000000..7c345f6
--- /dev/null
+++ b/src/nvme/log.h
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Copyright (c) 2021 Martin Wilck, SUSE LLC
+ */
+#ifndef _LOG_H
+#define _LOG_H
+
+#include <stdbool.h>
+#include <syslog.h>
+
+/* for nvme_root_t */
+#include "tree.h"
+
+#ifndef MAX_LOGLEVEL
+# define MAX_LOGLEVEL LOG_DEBUG
+#endif
+#ifndef DEFAULT_LOGLEVEL
+# define DEFAULT_LOGLEVEL LOG_NOTICE
+#endif
+
+/**
+ * DOC: log.h
+ *
+ * logging functions
+ */
+
+/**
+ * nvme_init_logging() - Initialize logging
+ * @r: nvme_root_t context
+ * @lvl: Logging level to set
+ * @log_pid: Boolean to enable logging of the PID
+ * @log_tstamp: Boolean to enable logging of the timestamp
+ *
+ * Sets the default logging variables for the library.
+ */
+void nvme_init_logging(nvme_root_t r, int lvl, bool log_pid, bool log_tstamp);
+
+/**
+ * nvme_set_root() - Set nvme_root_t context
+ * @r: nvme_root_t context
+ *
+ * In order to be able to log from code paths where no root object is passed in
+ * via the arguments use the the default one which can be set via this call.
+ * When creating a new root object with @nvme_create_root the global root object
+ * will be set as well. This means the global root object is always pointing to
+ * the latest created root object. Note the first @nvme_free_tree call will reset
+ * the global root object.
+ */
+void nvme_set_root(nvme_root_t r);
+
+#endif /* _LOG_H */
diff --git a/src/nvme/mi-mctp.c b/src/nvme/mi-mctp.c
new file mode 100644
index 0000000..86c4c29
--- /dev/null
+++ b/src/nvme/mi-mctp.c
@@ -0,0 +1,824 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021 Code Construct Pty Ltd
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#if HAVE_LINUX_MCTP_H
+#include <linux/mctp.h>
+#endif
+
+#include <ccan/endian/endian.h>
+
+#ifdef CONFIG_DBUS
+#include <dbus/dbus.h>
+
+#define MCTP_DBUS_PATH "/xyz/openbmc_project/mctp"
+#define MCTP_DBUS_IFACE "xyz.openbmc_project.MCTP"
+#define MCTP_DBUS_IFACE_ENDPOINT "xyz.openbmc_project.MCTP.Endpoint"
+#endif
+
+#include "private.h"
+#include "log.h"
+#include "mi.h"
+
+
+#if !defined(AF_MCTP)
+#define AF_MCTP 45
+#endif
+
+#if !HAVE_LINUX_MCTP_H
+/* As of kernel v5.15, these AF_MCTP-related definitions are provided by
+ * linux/mctp.h. However, we provide a set here while that header percolates
+ * through to standard includes.
+ *
+ * These were all introduced in the same version as AF_MCTP was defined,
+ * so we can key off the presence of that.
+ */
+
+typedef __u8 mctp_eid_t;
+
+struct mctp_addr {
+ mctp_eid_t s_addr;
+};
+
+struct sockaddr_mctp {
+ unsigned short int smctp_family;
+ __u16 __smctp_pad0;
+ unsigned int smctp_network;
+ struct mctp_addr smctp_addr;
+ __u8 smctp_type;
+ __u8 smctp_tag;
+ __u8 __smctp_pad1;
+};
+
+#define MCTP_NET_ANY 0x0
+
+#define MCTP_ADDR_NULL 0x00
+#define MCTP_ADDR_ANY 0xff
+
+#define MCTP_TAG_MASK 0x07
+#define MCTP_TAG_OWNER 0x08
+
+#endif /* !AF_MCTP */
+
+#define MCTP_TYPE_NVME 0x04
+#define MCTP_TYPE_MIC 0x80
+
+struct nvme_mi_transport_mctp {
+ int net;
+ __u8 eid;
+ int sd;
+ void *resp_buf;
+ size_t resp_buf_size;
+};
+
+static int ioctl_tag(int sd, unsigned long req, struct mctp_ioc_tag_ctl *ctl)
+{
+ return ioctl(sd, req, ctl);
+}
+
+static struct __mi_mctp_socket_ops ops = {
+ socket,
+ sendmsg,
+ recvmsg,
+ poll,
+ ioctl_tag,
+};
+
+void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops)
+{
+ ops = *newops;
+}
+static const struct nvme_mi_transport nvme_mi_transport_mctp;
+
+#ifdef SIOCMCTPALLOCTAG
+static __u8 nvme_mi_mctp_tag_alloc(struct nvme_mi_ep *ep)
+{
+ struct nvme_mi_transport_mctp *mctp;
+ struct mctp_ioc_tag_ctl ctl = { 0 };
+ static bool logged;
+ int rc;
+
+ mctp = ep->transport_data;
+
+ ctl.peer_addr = mctp->eid;
+
+ errno = 0;
+ rc = ops.ioctl_tag(mctp->sd, SIOCMCTPALLOCTAG, &ctl);
+ if (rc) {
+ if (!logged) {
+ /* not necessarily fatal, just means we can't handle
+ * "more processing required" messages */
+ nvme_msg(ep->root, LOG_INFO,
+ "System does not support explicit tag allocation\n");
+ logged = true;
+ }
+ return MCTP_TAG_OWNER;
+ }
+
+ return ctl.tag;
+}
+
+static void nvme_mi_mctp_tag_drop(struct nvme_mi_ep *ep, __u8 tag)
+{
+ struct nvme_mi_transport_mctp *mctp;
+ struct mctp_ioc_tag_ctl ctl = { 0 };
+
+ mctp = ep->transport_data;
+
+ if (!(tag & MCTP_TAG_PREALLOC))
+ return;
+
+ ctl.peer_addr = mctp->eid;
+ ctl.tag = tag;
+
+ ops.ioctl_tag(mctp->sd, SIOCMCTPDROPTAG, &ctl);
+}
+
+#else /* !defined SIOMCTPTAGALLOC */
+
+static __u8 nvme_mi_mctp_tag_alloc(struct nvme_mi_ep *ep)
+{
+ static bool logged;
+ if (!logged) {
+ nvme_msg(ep->root, LOG_INFO,
+ "Build does not support explicit tag allocation\n");
+ logged = true;
+ }
+ return MCTP_TAG_OWNER;
+}
+
+static void nvme_mi_mctp_tag_drop(struct nvme_mi_ep *ep, __u8 tag)
+{
+}
+
+#endif /* !defined SIOMCTPTAGALLOC */
+
+struct nvme_mi_msg_resp_mpr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 rsvd0;
+ __u16 mprt;
+};
+
+/* Check if this response was a More Processing Required response; if so,
+ * populate the worst-case expected processing time, given in milliseconds.
+ *
+ * buf is the incoming message data, including type byte, but excluding
+ * the MIC which has been extracted into the mic argument already.
+ */
+static bool nvme_mi_mctp_resp_is_mpr(void *buf, size_t len,
+ __le32 mic, unsigned int *mpr_time)
+{
+ struct nvme_mi_msg_resp_mpr *msg;
+ __u32 crc;
+
+ /* We need at least the minimal header */
+ if (len < sizeof(*msg))
+ return false;
+
+ msg = (struct nvme_mi_msg_resp_mpr *)buf;
+
+ if (msg->status != NVME_MI_RESP_MPR)
+ return false;
+
+ /* Devices may send a MPR response as a full-sized Admin response,
+ * rather than the minimal MI-only header. Allow this, but only if the
+ * type indicates admin, and the allocated response header is the
+ * correct size for an Admin response.
+ */
+ if (!(len == sizeof(*msg) ||
+ ((msg->hdr.nmp >> 3 & 0x0f) == NVME_MI_MT_ADMIN &&
+ len == sizeof(struct nvme_mi_admin_resp_hdr))))
+ return false;
+
+ /* Verify the MIC from the response. We're dealing with linear
+ * header data here, and need to preserve the resp pointer & size
+ * values, so can't use verify_resp_mic here.
+ */
+ crc = ~nvme_mi_crc32_update(0xffffffff, buf, len);
+ if (le32_to_cpu(mic) != crc)
+ return false;
+
+ if (mpr_time)
+ *mpr_time = cpu_to_le16(msg->mprt) * 100;
+
+ return true;
+}
+
+static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp)
+{
+ ssize_t len, resp_len, resp_hdr_len, resp_data_len;
+ struct nvme_mi_transport_mctp *mctp;
+ struct iovec req_iov[3], resp_iov[1];
+ struct msghdr req_msg, resp_msg;
+ int i, rc, errno_save, timeout;
+ struct sockaddr_mctp addr;
+ struct pollfd pollfds[1];
+ unsigned int mpr_time;
+ __le32 mic;
+ __u8 tag;
+
+ if (ep->transport != &nvme_mi_transport_mctp) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* we need enough space for at least a generic (/error) response */
+ if (resp->hdr_len < sizeof(struct nvme_mi_msg_resp)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ mctp = ep->transport_data;
+ tag = nvme_mi_mctp_tag_alloc(ep);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.smctp_family = AF_MCTP;
+ addr.smctp_network = mctp->net;
+ addr.smctp_addr.s_addr = mctp->eid;
+ addr.smctp_type = MCTP_TYPE_NVME | MCTP_TYPE_MIC;
+ addr.smctp_tag = tag;
+
+ i = 0;
+ req_iov[i].iov_base = ((__u8 *)req->hdr) + 1;
+ req_iov[i].iov_len = req->hdr_len - 1;
+ i++;
+
+ if (req->data_len) {
+ req_iov[i].iov_base = req->data;
+ req_iov[i].iov_len = req->data_len;
+ i++;
+ }
+
+ mic = cpu_to_le32(req->mic);
+ req_iov[i].iov_base = &mic;
+ req_iov[i].iov_len = sizeof(mic);
+ i++;
+
+ memset(&req_msg, 0, sizeof(req_msg));
+ req_msg.msg_name = &addr;
+ req_msg.msg_namelen = sizeof(addr);
+ req_msg.msg_iov = req_iov;
+ req_msg.msg_iovlen = i;
+
+ len = ops.sendmsg(mctp->sd, &req_msg, 0);
+ if (len < 0) {
+ errno_save = errno;
+ nvme_msg(ep->root, LOG_ERR,
+ "Failure sending MCTP message: %m\n");
+ errno = errno_save;
+ rc = -1;
+ goto out;
+ }
+
+ resp_len = resp->hdr_len + resp->data_len + sizeof(mic);
+ if (resp_len > mctp->resp_buf_size) {
+ void *tmp = realloc(mctp->resp_buf, resp_len);
+ if (!tmp) {
+ errno_save = errno;
+ nvme_msg(ep->root, LOG_ERR,
+ "Failure allocating response buffer: %m\n");
+ errno = errno_save;
+ rc = -1;
+ goto out;
+ }
+ mctp->resp_buf = tmp;
+ mctp->resp_buf_size = resp_len;
+ }
+
+ /* offset by one: the MCTP message type is excluded from the buffer */
+ resp_iov[0].iov_base = mctp->resp_buf + 1;
+ resp_iov[0].iov_len = resp_len - 1;
+
+ memset(&resp_msg, 0, sizeof(resp_msg));
+ resp_msg.msg_name = &addr;
+ resp_msg.msg_namelen = sizeof(addr);
+ resp_msg.msg_iov = resp_iov;
+ resp_msg.msg_iovlen = 1;
+
+ pollfds[0].fd = mctp->sd;
+ pollfds[0].events = POLLIN;
+ timeout = ep->timeout ?: -1;
+retry:
+ rc = ops.poll(pollfds, 1, timeout);
+ if (rc < 0) {
+ if (errno == EINTR)
+ goto retry;
+ errno_save = errno;
+ nvme_msg(ep->root, LOG_ERR,
+ "Failed polling on MCTP socket: %m");
+ errno = errno_save;
+ goto out;
+ }
+
+ if (rc == 0) {
+ nvme_msg(ep->root, LOG_DEBUG, "Timeout on MCTP socket");
+ errno = ETIMEDOUT;
+ rc = -1;
+ goto out;
+ }
+
+ rc = -1;
+ len = ops.recvmsg(mctp->sd, &resp_msg, MSG_DONTWAIT);
+
+ if (len < 0) {
+ errno_save = errno;
+ nvme_msg(ep->root, LOG_ERR,
+ "Failure receiving MCTP message: %m\n");
+ errno = errno_save;
+ goto out;
+ }
+
+
+ if (len == 0) {
+ nvme_msg(ep->root, LOG_WARNING, "No data from MCTP endpoint\n");
+ errno = EIO;
+ goto out;
+ }
+
+ /* Re-add the type byte, so we can work on aligned lengths from here */
+ ((uint8_t *)mctp->resp_buf)[0] = MCTP_TYPE_NVME | MCTP_TYPE_MIC;
+ len += 1;
+
+ /* The smallest response data is 8 bytes: generic 4-byte message header
+ * plus four bytes of error data (excluding MIC). Ensure we have enough.
+ */
+ if (len < 8 + sizeof(mic)) {
+ nvme_msg(ep->root, LOG_ERR,
+ "Invalid MCTP response: too short (%zd bytes, needed %zd)\n",
+ len, 8 + sizeof(mic));
+ errno = EPROTO;
+ goto out;
+ }
+
+ /* Start unpacking the linear resp buffer into the split header + data
+ * + MIC. We check for a MPR response before fully unpacking, as we'll
+ * need to preserve the resp layout if we need to retry the receive.
+ */
+
+ /* MIC is always at the tail */
+ memcpy(&mic, mctp->resp_buf + len - sizeof(mic), sizeof(mic));
+ len -= 4;
+
+ /* Check for a More Processing Required response. This is a slight
+ * layering violation, as we're pre-checking the MIC and inspecting
+ * header fields. However, we need to do this in the transport in order
+ * to keep the tag allocated and retry the recvmsg
+ */
+ if (nvme_mi_mctp_resp_is_mpr(mctp->resp_buf, len, mic, &mpr_time)) {
+ nvme_msg(ep->root, LOG_DEBUG,
+ "Received More Processing Required, waiting for response\n");
+
+ /* if the controller hasn't set MPRT, fall back to our command/
+ * response timeout, or the largest possible MPRT if none set */
+ if (!mpr_time)
+ mpr_time = ep->timeout ?: 0xffff;
+
+ /* clamp to the endpoint max */
+ if (ep->mprt_max && mpr_time > ep->mprt_max)
+ mpr_time = ep->mprt_max;
+
+ timeout = mpr_time;
+ goto retry;
+ }
+
+ /* we expect resp->hdr_len bytes, but we may have less */
+ resp_hdr_len = resp->hdr_len;
+ if (resp_hdr_len > len)
+ resp_hdr_len = len;
+ memcpy(resp->hdr, mctp->resp_buf, resp_hdr_len);
+ resp->hdr_len = resp_hdr_len;
+ len -= resp_hdr_len;
+
+ /* any remaining bytes are the data payload */
+ resp_data_len = resp->data_len;
+ if (resp_data_len > len)
+ resp_data_len = len;
+ memcpy(resp->data, mctp->resp_buf + resp_hdr_len, resp_data_len);
+ resp->data_len = resp_data_len;
+
+ resp->mic = le32_to_cpu(mic);
+
+ rc = 0;
+
+out:
+ nvme_mi_mctp_tag_drop(ep, tag);
+
+ return rc;
+}
+
+static void nvme_mi_mctp_close(struct nvme_mi_ep *ep)
+{
+ struct nvme_mi_transport_mctp *mctp;
+
+ if (ep->transport != &nvme_mi_transport_mctp)
+ return;
+
+ mctp = ep->transport_data;
+ close(mctp->sd);
+ free(mctp->resp_buf);
+ free(ep->transport_data);
+}
+
+static int nvme_mi_mctp_desc_ep(struct nvme_mi_ep *ep, char *buf, size_t len)
+{
+ struct nvme_mi_transport_mctp *mctp;
+
+ if (ep->transport != &nvme_mi_transport_mctp) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ mctp = ep->transport_data;
+
+ snprintf(buf, len, "net %d eid %d", mctp->net, mctp->eid);
+
+ return 0;
+}
+
+static const struct nvme_mi_transport nvme_mi_transport_mctp = {
+ .name = "mctp",
+ .mic_enabled = true,
+ .submit = nvme_mi_mctp_submit,
+ .close = nvme_mi_mctp_close,
+ .desc_ep = nvme_mi_mctp_desc_ep,
+};
+
+nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, __u8 eid)
+{
+ struct nvme_mi_transport_mctp *mctp;
+ struct nvme_mi_ep *ep;
+ int errno_save;
+
+ ep = nvme_mi_init_ep(root);
+ if (!ep)
+ return NULL;
+
+ mctp = malloc(sizeof(*mctp));
+ if (!mctp) {
+ errno_save = errno;
+ goto err_close_ep;
+ }
+
+ memset(mctp, 0, sizeof(*mctp));
+ mctp->sd = -1;
+
+ mctp->resp_buf_size = 4096;
+ mctp->resp_buf = malloc(mctp->resp_buf_size);
+ if (!mctp->resp_buf) {
+ errno_save = errno;
+ goto err_free_mctp;
+ }
+
+ mctp->net = netid;
+ mctp->eid = eid;
+
+ mctp->sd = ops.socket(AF_MCTP, SOCK_DGRAM, 0);
+ if (mctp->sd < 0) {
+ errno_save = errno;
+ goto err_free_rspbuf;
+ }
+
+ ep->transport = &nvme_mi_transport_mctp;
+ ep->transport_data = mctp;
+
+ /* Assuming an i2c transport at 100kHz, smallest MTU (64+4). Given
+ * a worst-case clock stretch, and largest-sized packets, we can
+ * expect up to 1.6s per command/response pair. Allowing for a
+ * retry or two (handled by lower layers), 5s is a reasonable timeout.
+ */
+ ep->timeout = 5000;
+
+ nvme_mi_ep_probe(ep);
+
+ return ep;
+
+err_free_rspbuf:
+ free(mctp->resp_buf);
+err_free_mctp:
+ free(mctp);
+err_close_ep:
+ /* the ep->transport is not set yet, so this will not call back
+ * into nvme_mi_mctp_close() */
+ nvme_mi_close(ep);
+ errno = errno_save;
+ return NULL;
+}
+
+#ifdef CONFIG_DBUS
+
+static int nvme_mi_mctp_add(nvme_root_t root, unsigned int netid, __u8 eid)
+{
+ nvme_mi_ep_t ep = NULL;
+
+ /* ensure we don't already have an endpoint with the same net/eid. if
+ * we do, just skip, no need to re-add. */
+ list_for_each(&root->endpoints, ep, root_entry) {
+ if (ep->transport != &nvme_mi_transport_mctp) {
+ continue;
+ }
+ const struct nvme_mi_transport_mctp *t = ep->transport_data;
+ if (t->eid == eid && t->net == netid)
+ return 0;
+ }
+
+ ep = nvme_mi_open_mctp(root, netid, eid);
+ if (!ep)
+ return -1;
+
+ return 0;
+}
+
+static bool dbus_object_is_type(DBusMessageIter *obj, int type)
+{
+ return dbus_message_iter_get_arg_type(obj) == type;
+}
+
+static bool dbus_object_is_dict(DBusMessageIter *obj)
+{
+ return dbus_object_is_type(obj, DBUS_TYPE_ARRAY) &&
+ dbus_message_iter_get_element_type(obj) == DBUS_TYPE_DICT_ENTRY;
+}
+
+static int read_variant_basic(DBusMessageIter *var, int type, void *val)
+{
+ if (!dbus_object_is_type(var, type))
+ return -1;
+
+ dbus_message_iter_get_basic(var, val);
+
+ return 0;
+}
+
+static bool has_message_type(DBusMessageIter *prop, uint8_t type)
+{
+ DBusMessageIter inner;
+ uint8_t *types;
+ int i, n;
+
+ if (!dbus_object_is_type(prop, DBUS_TYPE_ARRAY) ||
+ dbus_message_iter_get_element_type(prop) != DBUS_TYPE_BYTE)
+ return false;
+
+ dbus_message_iter_recurse(prop, &inner);
+
+ dbus_message_iter_get_fixed_array(&inner, &types, &n);
+
+ for (i = 0; i < n; i++) {
+ if (types[i] == type)
+ return true;
+ }
+
+ return false;
+}
+
+static int handle_mctp_endpoint(nvme_root_t root, const char* objpath,
+ DBusMessageIter *props)
+{
+ bool have_eid = false, have_net = false, have_nvmemi = false;
+ mctp_eid_t eid;
+ int net;
+ int rc;
+
+ /* for each property */
+ for (;;) {
+ DBusMessageIter prop, val;
+ const char *propname;
+
+ dbus_message_iter_recurse(props, &prop);
+
+ if (!dbus_object_is_type(&prop, DBUS_TYPE_STRING)) {
+ nvme_msg(root, LOG_ERR,
+ "error unmashalling object (propname)\n");
+ return -1;
+ }
+
+ dbus_message_iter_get_basic(&prop, &propname);
+
+ dbus_message_iter_next(&prop);
+
+ if (!dbus_object_is_type(&prop, DBUS_TYPE_VARIANT)) {
+ nvme_msg(root, LOG_ERR,
+ "error unmashalling object (propval)\n");
+ return -1;
+ }
+
+ dbus_message_iter_recurse(&prop, &val);
+
+ if (!strcmp(propname, "EID")) {
+ rc = read_variant_basic(&val, DBUS_TYPE_BYTE, &eid);
+ have_eid = true;
+
+ } else if (!strcmp(propname, "NetworkId")) {
+ rc = read_variant_basic(&val, DBUS_TYPE_INT32, &net);
+ have_net = true;
+
+ } else if (!strcmp(propname, "SupportedMessageTypes")) {
+ have_nvmemi = has_message_type(&val, MCTP_TYPE_NVME);
+ }
+
+ if (rc)
+ return rc;
+
+ if (!dbus_message_iter_next(props))
+ break;
+ }
+
+ if (have_nvmemi) {
+ if (!(have_eid && have_net)) {
+ nvme_msg(root, LOG_ERR,
+ "Missing property for %s\n", objpath);
+ errno = ENOENT;
+ return -1;
+ }
+ rc = nvme_mi_mctp_add(root, net, eid);
+ if (rc < 0) {
+ int errno_save = errno;
+ nvme_msg(root, LOG_ERR,
+ "Error adding net %d eid %d: %m\n", net, eid);
+ errno = errno_save;
+ }
+ } else {
+ /* Ignore other endpoints */
+ rc = 0;
+ }
+ return rc;
+}
+
+/* obj is an array of (object path, interfaces) dict entries - ie., dbus type
+ * a{oa{sa{sv}}}
+ */
+static int handle_mctp_obj(nvme_root_t root, DBusMessageIter *obj)
+{
+ const char *objpath = NULL;
+ DBusMessageIter intfs;
+
+ if (!dbus_object_is_type(obj, DBUS_TYPE_OBJECT_PATH)) {
+ nvme_msg(root, LOG_ERR, "error unmashalling object (path)\n");
+ return -1;
+ }
+
+ dbus_message_iter_get_basic(obj, &objpath);
+
+ dbus_message_iter_next(obj);
+
+ if (!dbus_object_is_dict(obj)) {
+ nvme_msg(root, LOG_ERR, "error unmashalling object (intfs)\n");
+ return -1;
+ }
+
+ dbus_message_iter_recurse(obj, &intfs);
+
+ /* for each interface */
+ for (;;) {
+ DBusMessageIter props, intf;
+ const char *intfname;
+
+ dbus_message_iter_recurse(&intfs, &intf);
+
+ if (!dbus_object_is_type(&intf, DBUS_TYPE_STRING)) {
+ nvme_msg(root, LOG_ERR,
+ "error unmashalling object (intf)\n");
+ return -1;
+ }
+
+ dbus_message_iter_get_basic(&intf, &intfname);
+
+ if (strcmp(intfname, MCTP_DBUS_IFACE_ENDPOINT)) {
+ if (!dbus_message_iter_next(&intfs))
+ break;
+ continue;
+ }
+
+ dbus_message_iter_next(&intf);
+
+ if (!dbus_object_is_dict(&intf)) {
+ nvme_msg(root, LOG_ERR,
+ "error unmarshalling object (props)\n");
+ return -1;
+ }
+
+ dbus_message_iter_recurse(&intf, &props);
+ return handle_mctp_endpoint(root, objpath, &props);
+ }
+
+ return 0;
+}
+
+nvme_root_t nvme_mi_scan_mctp(void)
+{
+ DBusMessage *msg, *resp = NULL;
+ DBusConnection *bus = NULL;
+ DBusMessageIter args, objs;
+ int errno_save, rc = -1;
+ nvme_root_t root;
+ dbus_bool_t drc;
+ DBusError berr;
+
+ root = nvme_mi_create_root(NULL, DEFAULT_LOGLEVEL);
+ if (!root) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ dbus_error_init(&berr);
+
+ bus = dbus_bus_get(DBUS_BUS_SYSTEM, &berr);
+ if (!bus) {
+ nvme_msg(root, LOG_ERR, "Failed connecting to D-Bus: %s (%s)\n",
+ berr.message, berr.name);
+ goto out;
+ }
+
+ msg = dbus_message_new_method_call(MCTP_DBUS_IFACE,
+ MCTP_DBUS_PATH,
+ "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+ if (!msg) {
+ nvme_msg(root, LOG_ERR, "Failed creating call message\n");
+ goto out;
+ }
+
+ resp = dbus_connection_send_with_reply_and_block(bus, msg,
+ DBUS_TIMEOUT_USE_DEFAULT,
+ &berr);
+ dbus_message_unref(msg);
+ if (!resp) {
+ nvme_msg(root, LOG_ERR, "Failed querying MCTP D-Bus: %s (%s)\n",
+ berr.message, berr.name);
+ goto out;
+ }
+
+ /* argument container */
+ drc = dbus_message_iter_init(resp, &args);
+ if (!drc) {
+ nvme_msg(root, LOG_ERR, "can't read dbus reply args\n");
+ goto out;
+ }
+
+ if (!dbus_object_is_dict(&args)) {
+ nvme_msg(root, LOG_ERR, "error unmashalling args\n");
+ goto out;
+ }
+
+ /* objects container */
+ dbus_message_iter_recurse(&args, &objs);
+
+ rc = 0;
+
+ for (;;) {
+ DBusMessageIter ent;
+
+ dbus_message_iter_recurse(&objs, &ent);
+
+ rc = handle_mctp_obj(root, &ent);
+ if (rc)
+ break;
+
+ if (!dbus_message_iter_next(&objs))
+ break;
+ }
+
+out:
+ errno_save = errno;
+ if (resp)
+ dbus_message_unref(resp);
+ if (bus)
+ dbus_connection_unref(bus);
+ dbus_error_free(&berr);
+
+ if (rc < 0) {
+ if (root) {
+ nvme_mi_free_root(root);
+ }
+ errno = errno_save;
+ root = NULL;
+ }
+ return root;
+}
+
+#else /* CONFIG_DBUS */
+
+nvme_root_t nvme_mi_scan_mctp(void)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_DBUS */
diff --git a/src/nvme/mi.c b/src/nvme/mi.c
new file mode 100644
index 0000000..84d51b0
--- /dev/null
+++ b/src/nvme/mi.c
@@ -0,0 +1,1651 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021 Code Construct Pty Ltd
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <ccan/array_size/array_size.h>
+#include <ccan/endian/endian.h>
+
+#include "log.h"
+#include "mi.h"
+#include "private.h"
+
+static const int default_timeout = 1000; /* milliseconds; endpoints may
+ override */
+
+static bool nvme_mi_probe_enabled_default(void)
+{
+ char *val;
+
+ val = getenv("LIBNVME_MI_PROBE_ENABLED");
+ if (!val)
+ return true;
+
+ return strcmp(val, "0") &&
+ strcasecmp(val, "false") &&
+ strncasecmp(val, "disable", 7);
+
+}
+
+/* MI-equivalent of nvme_create_root, but avoids clashing symbol names
+ * when linking against both libnvme and libnvme-mi.
+ */
+nvme_root_t nvme_mi_create_root(FILE *fp, int log_level)
+{
+ struct nvme_root *r = calloc(1, sizeof(*r));
+
+ if (!r) {
+ return NULL;
+ }
+ r->log_level = log_level;
+ r->fp = stderr;
+ r->mi_probe_enabled = nvme_mi_probe_enabled_default();
+ if (fp)
+ r->fp = fp;
+ list_head_init(&r->hosts);
+ list_head_init(&r->endpoints);
+ return r;
+}
+
+void nvme_mi_free_root(nvme_root_t root)
+{
+ nvme_mi_ep_t ep, tmp;
+
+ nvme_mi_for_each_endpoint_safe(root, ep, tmp)
+ nvme_mi_close(ep);
+
+ free(root);
+}
+
+void nvme_mi_set_probe_enabled(nvme_root_t root, bool enabled)
+{
+ root->mi_probe_enabled = enabled;
+}
+
+static void nvme_mi_record_resp_time(struct nvme_mi_ep *ep)
+{
+ int rc;
+
+ rc = clock_gettime(CLOCK_MONOTONIC, &ep->last_resp_time);
+ ep->last_resp_time_valid = !rc;
+}
+
+static bool nvme_mi_compare_vid_mn(struct nvme_mi_ep *ep,
+ struct nvme_id_ctrl *id,
+ __u16 vid, const char *mn)
+
+{
+ int len;
+
+ len = strlen(mn);
+ if (len >= sizeof(id->mn)) {
+ nvme_msg(ep->root, LOG_ERR,
+ "Internal error: invalid model number for %s\n",
+ __func__);
+ return false;
+ }
+
+ return le16_to_cpu(id->vid) == vid && !strncmp(id->mn, mn, len);
+}
+
+static void __nvme_mi_format_mn(struct nvme_id_ctrl *id,
+ char *mn, size_t mn_len)
+{
+ const size_t id_mn_size = sizeof(id->mn);
+ int i;
+
+ /* A BUILD_ASSERT() would be nice here, but we're not const enough for
+ * that
+ */
+ if (mn_len <= id_mn_size)
+ abort();
+
+ memcpy(mn, id->mn, id_mn_size);
+ mn[id_mn_size] = '\0';
+
+ for (i = id_mn_size - 1; i >= 0; i--) {
+ if (mn[i] != '\0' && mn[i] != ' ')
+ break;
+ mn[i] = '\0';
+ }
+}
+
+#define nvme_mi_format_mn(id, m) __nvme_mi_format_mn(id, m, sizeof(m))
+
+void nvme_mi_ep_probe(struct nvme_mi_ep *ep)
+{
+ struct nvme_identify_args id_args = { 0 };
+ struct nvme_id_ctrl id = { 0 };
+ struct nvme_mi_ctrl *ctrl;
+ int rc;
+
+ if (!ep->root->mi_probe_enabled)
+ return;
+
+ /* start with no quirks, detect as we go */
+ ep->quirks = 0;
+
+ ctrl = nvme_mi_init_ctrl(ep, 0);
+ if (!ctrl)
+ return;
+
+ /* Do enough of an identify (assuming controller 0) to retrieve
+ * device and firmware identification information. This gives us the
+ * following fields in id:
+ *
+ * - vid (PCI vendor ID)
+ * - ssvid (PCI subsystem vendor ID)
+ * - sn (Serial number)
+ * - mn (Model number)
+ * - fr (Firmware revision)
+ *
+ * all other fields - rab and onwards - will be zero!
+ */
+ id_args.args_size = sizeof(id_args);
+ id_args.data = &id;
+ id_args.cns = NVME_IDENTIFY_CNS_CTRL;
+ id_args.nsid = NVME_NSID_NONE;
+ id_args.cntid = 0;
+ id_args.csi = NVME_CSI_NVM;
+
+ rc = nvme_mi_admin_identify_partial(ctrl, &id_args, 0,
+ offsetof(struct nvme_id_ctrl, rab));
+ if (rc) {
+ nvme_msg(ep->root, LOG_WARNING,
+ "Identify Controller failed, no quirks applied\n");
+ goto out_close;
+ }
+
+ /* Samsung MZUL2512: cannot receive commands sent within ~1ms of
+ * the previous response. Set an inter-command delay of 1.2ms for
+ * a little extra tolerance.
+ */
+ if (nvme_mi_compare_vid_mn(ep, &id, 0x144d, "MZUL2512HCJQ")) {
+ ep->quirks |= NVME_QUIRK_MIN_INTER_COMMAND_TIME;
+ ep->inter_command_us = 1200;
+ }
+
+ /* If we're quirking for the inter-command time, record the last
+ * command time now, so we don't conflict with the just-sent identify.
+ */
+ if (ep->quirks & NVME_QUIRK_MIN_INTER_COMMAND_TIME)
+ nvme_mi_record_resp_time(ep);
+
+ if (ep->quirks) {
+ char tmp[sizeof(id.mn) + 1];
+
+ nvme_mi_format_mn(&id, tmp);
+ nvme_msg(ep->root, LOG_DEBUG,
+ "device %02x:%s: applying quirks 0x%08lx\n",
+ id.vid, tmp, ep->quirks);
+ }
+
+out_close:
+ nvme_mi_close_ctrl(ctrl);
+}
+
+static const int nsec_per_sec = 1000 * 1000 * 1000;
+/* timercmp and timersub, but for struct timespec */
+#define timespec_cmp(a, b, CMP) \
+ (((a)->tv_sec == (b)->tv_sec) \
+ ? ((a)->tv_nsec CMP (b)->tv_nsec) \
+ : ((a)->tv_sec CMP (b)->tv_sec))
+
+#define timespec_sub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
+ if ((result)->tv_nsec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_nsec += nsec_per_sec; \
+ } \
+ } while (0)
+
+static void nvme_mi_insert_delay(struct nvme_mi_ep *ep)
+{
+ struct timespec now, next, delay;
+ int rc;
+
+ if (!ep->last_resp_time_valid)
+ return;
+
+ /* calculate earliest next command time */
+ next.tv_nsec = ep->last_resp_time.tv_nsec + ep->inter_command_us * 1000;
+ next.tv_sec = ep->last_resp_time.tv_sec;
+ if (next.tv_nsec > nsec_per_sec) {
+ next.tv_nsec -= nsec_per_sec;
+ next.tv_sec += 1;
+ }
+
+ rc = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (rc) {
+ /* not much we can do; continue immediately */
+ return;
+ }
+
+ if (timespec_cmp(&now, &next, >=))
+ return;
+
+ timespec_sub(&next, &now, &delay);
+
+ nanosleep(&delay, NULL);
+}
+
+struct nvme_mi_ep *nvme_mi_init_ep(nvme_root_t root)
+{
+ struct nvme_mi_ep *ep;
+
+ ep = calloc(1, sizeof(*ep));
+ if (!ep)
+ return NULL;
+
+ list_node_init(&ep->root_entry);
+ ep->root = root;
+ ep->controllers_scanned = false;
+ ep->timeout = default_timeout;
+ ep->mprt_max = 0;
+ list_head_init(&ep->controllers);
+
+ list_add(&root->endpoints, &ep->root_entry);
+
+ return ep;
+}
+
+int nvme_mi_ep_set_timeout(nvme_mi_ep_t ep, unsigned int timeout_ms)
+{
+ if (ep->transport->check_timeout) {
+ int rc;
+ rc = ep->transport->check_timeout(ep, timeout_ms);
+ if (rc)
+ return rc;
+ }
+
+ ep->timeout = timeout_ms;
+ return 0;
+}
+
+void nvme_mi_ep_set_mprt_max(nvme_mi_ep_t ep, unsigned int mprt_max_ms)
+{
+ ep->mprt_max = mprt_max_ms;
+}
+
+unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep)
+{
+ return ep->timeout;
+}
+
+static bool nvme_mi_ep_has_quirk(nvme_mi_ep_t ep, unsigned long quirk)
+{
+ return ep->quirks & quirk;
+}
+
+struct nvme_mi_ctrl *nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id)
+{
+ struct nvme_mi_ctrl *ctrl;
+
+ ctrl = malloc(sizeof(*ctrl));
+ if (!ctrl)
+ return NULL;
+
+ ctrl->ep = ep;
+ ctrl->id = ctrl_id;
+
+ list_add_tail(&ep->controllers, &ctrl->ep_entry);
+
+ return ctrl;
+}
+
+__u16 nvme_mi_ctrl_id(nvme_mi_ctrl_t ctrl)
+{
+ return ctrl->id;
+}
+
+int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan)
+{
+ struct nvme_ctrl_list list;
+ unsigned int i, n_ctrl;
+ int rc;
+
+ if (ep->controllers_scanned) {
+ if (force_rescan) {
+ struct nvme_mi_ctrl *ctrl, *tmp;
+ nvme_mi_for_each_ctrl_safe(ep, ctrl, tmp)
+ nvme_mi_close_ctrl(ctrl);
+ } else {
+ return 0;
+ }
+ }
+
+ rc = nvme_mi_mi_read_mi_data_ctrl_list(ep, 0, &list);
+ if (rc)
+ return -1;
+
+ n_ctrl = le16_to_cpu(list.num);
+ if (n_ctrl > NVME_ID_CTRL_LIST_MAX) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ for (i = 0; i < n_ctrl; i++) {
+ struct nvme_mi_ctrl *ctrl;
+ __u16 id;
+
+ id = le16_to_cpu(list.identifier[i]);
+
+ ctrl = nvme_mi_init_ctrl(ep, id);
+ if (!ctrl)
+ break;
+ }
+
+ ep->controllers_scanned = true;
+ return 0;
+}
+
+__u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len)
+{
+ int i;
+
+ while (len--) {
+ crc ^= *(unsigned char *)(data++);
+ for (i = 0; i < 8; i++)
+ crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78 : 0);
+ }
+ return crc;
+}
+
+static void nvme_mi_calc_req_mic(struct nvme_mi_req *req)
+{
+ __u32 crc = 0xffffffff;
+
+ crc = nvme_mi_crc32_update(crc, req->hdr, req->hdr_len);
+ crc = nvme_mi_crc32_update(crc, req->data, req->data_len);
+
+ req->mic = ~crc;
+}
+
+/* returns zero on correct MIC */
+static int nvme_mi_verify_resp_mic(struct nvme_mi_resp *resp)
+{
+ __u32 crc = 0xffffffff;
+
+ crc = nvme_mi_crc32_update(crc, resp->hdr, resp->hdr_len);
+ crc = nvme_mi_crc32_update(crc, resp->data, resp->data_len);
+
+ return resp->mic != ~crc;
+}
+
+int nvme_mi_submit(nvme_mi_ep_t ep, struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp)
+{
+ int rc;
+
+ if (req->hdr_len < sizeof(struct nvme_mi_msg_hdr)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (req->hdr_len & 0x3) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (req->data_len & 0x3) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (resp->hdr_len < sizeof(struct nvme_mi_msg_hdr)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (resp->hdr_len & 0x3) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ep->transport->mic_enabled)
+ nvme_mi_calc_req_mic(req);
+
+ if (nvme_mi_ep_has_quirk(ep, NVME_QUIRK_MIN_INTER_COMMAND_TIME))
+ nvme_mi_insert_delay(ep);
+
+ rc = ep->transport->submit(ep, req, resp);
+
+ if (nvme_mi_ep_has_quirk(ep, NVME_QUIRK_MIN_INTER_COMMAND_TIME))
+ nvme_mi_record_resp_time(ep);
+
+ if (rc) {
+ nvme_msg(ep->root, LOG_INFO, "transport failure\n");
+ return rc;
+ }
+
+ if (ep->transport->mic_enabled) {
+ rc = nvme_mi_verify_resp_mic(resp);
+ if (rc) {
+ nvme_msg(ep->root, LOG_WARNING, "crc mismatch\n");
+ errno = EBADMSG;
+ return -1;
+ }
+ }
+
+ /* basic response checks */
+ if (resp->hdr_len < sizeof(struct nvme_mi_msg_hdr)) {
+ nvme_msg(ep->root, LOG_DEBUG,
+ "Bad response header len: %zd\n", resp->hdr_len);
+ errno = EPROTO;
+ return -1;
+ }
+
+ if (resp->hdr->type != NVME_MI_MSGTYPE_NVME) {
+ nvme_msg(ep->root, LOG_DEBUG,
+ "Invalid message type 0x%02x\n", resp->hdr->type);
+ errno = EPROTO;
+ return -1;
+ }
+
+ if (!(resp->hdr->nmp & (NVME_MI_ROR_RSP << 7))) {
+ nvme_msg(ep->root, LOG_DEBUG,
+ "ROR value in response indicates a request\n");
+ errno = EIO;
+ return -1;
+ }
+
+ if ((resp->hdr->nmp & 0x1) != (req->hdr->nmp & 0x1)) {
+ nvme_msg(ep->root, LOG_WARNING,
+ "Command slot mismatch: req %d, resp %d\n",
+ req->hdr->nmp & 0x1,
+ resp->hdr->nmp & 0x1);
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void nvme_mi_admin_init_req(struct nvme_mi_req *req,
+ struct nvme_mi_admin_req_hdr *hdr,
+ __u16 ctrl_id, __u8 opcode)
+{
+ memset(req, 0, sizeof(*req));
+ memset(hdr, 0, sizeof(*hdr));
+
+ hdr->hdr.type = NVME_MI_MSGTYPE_NVME;
+ hdr->hdr.nmp = (NVME_MI_ROR_REQ << 7) |
+ (NVME_MI_MT_ADMIN << 3); /* we always use command slot 0 */
+ hdr->opcode = opcode;
+ hdr->ctrl_id = cpu_to_le16(ctrl_id);
+
+ req->hdr = &hdr->hdr;
+ req->hdr_len = sizeof(*hdr);
+}
+
+static void nvme_mi_admin_init_resp(struct nvme_mi_resp *resp,
+ struct nvme_mi_admin_resp_hdr *hdr)
+{
+ memset(resp, 0, sizeof(*resp));
+ resp->hdr = &hdr->hdr;
+ resp->hdr_len = sizeof(*hdr);
+}
+
+static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result)
+{
+ struct nvme_mi_admin_resp_hdr *admin_hdr;
+ struct nvme_mi_msg_resp *resp_hdr;
+ __u32 nvme_status;
+ __u32 nvme_result;
+
+ /* we have a few different sources of "result" here: the status header
+ * in the MI response, the cdw3 status field, and (command specific)
+ * return values in cdw0. The latter is returned in the result pointer,
+ * the former two generate return values here
+ */
+
+ if (resp->hdr_len < sizeof(*resp_hdr)) {
+ errno = -EPROTO;
+ return -1;
+ }
+ resp_hdr = (struct nvme_mi_msg_resp *)resp->hdr;
+
+ /* If we have a MI error, we can't be sure there's an admin header
+ * following; return just the MI status, with the status type
+ * indicator of MI.
+ */
+ if (resp_hdr->status)
+ return resp_hdr->status |
+ (NVME_STATUS_TYPE_MI << NVME_STATUS_TYPE_SHIFT);
+
+ /* We shouldn't hit this, as we'd have an error reported earlier.
+ * However, for pointer safety, ensure we have a full admin header
+ */
+ if (resp->hdr_len < sizeof(*admin_hdr)) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ admin_hdr = (struct nvme_mi_admin_resp_hdr *)resp->hdr;
+ nvme_result = le32_to_cpu(admin_hdr->cdw0);
+
+ /* Shift down 17 here: the SC starts at bit 17, and the NVME_SC_*
+ * definitions align to this bit (and up). The CRD, MORE and DNR
+ * bits are defined accordingly (eg., DNR is 0x4000).
+ */
+ nvme_status = le32_to_cpu(admin_hdr->cdw3) >> 17;
+
+ /* the result pointer, optionally stored if the caller needs it */
+ if (result)
+ *result = nvme_result;
+
+ return nvme_status;
+}
+
+int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
+ struct nvme_mi_admin_req_hdr *admin_req,
+ size_t req_data_size,
+ struct nvme_mi_admin_resp_hdr *admin_resp,
+ off_t resp_data_offset,
+ size_t *resp_data_size)
+{
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ /* length/offset checks. The common _submit() API will do further
+ * checking on the message lengths too, so these are kept specific
+ * to the requirements of the Admin command set
+ */
+
+ /* NVMe-MI v1.2 imposes a limit of 4096 bytes on the dlen field */
+ if (*resp_data_size > 4096) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* we only have 32 bits of offset */
+ if (resp_data_offset > 0xffffffff) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* request and response lengths & offset must be aligned */
+ if ((req_data_size & 0x3) ||
+ (*resp_data_size & 0x3) ||
+ (resp_data_offset & 0x3)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* bidirectional not permitted (see DLEN definition) */
+ if (req_data_size && *resp_data_size) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!*resp_data_size && resp_data_offset) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ admin_req->hdr.type = NVME_MI_MSGTYPE_NVME;
+ admin_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) |
+ (NVME_MI_MT_ADMIN << 3);
+ admin_req->ctrl_id = cpu_to_le16(ctrl->id);
+ memset(&req, 0, sizeof(req));
+ req.hdr = &admin_req->hdr;
+ req.hdr_len = sizeof(*admin_req);
+ req.data = admin_req + 1;
+ req.data_len = req_data_size;
+
+ nvme_mi_calc_req_mic(&req);
+
+ memset(&resp, 0, sizeof(resp));
+ resp.hdr = &admin_resp->hdr;
+ resp.hdr_len = sizeof(*admin_resp);
+ resp.data = admin_resp + 1;
+ resp.data_len = *resp_data_size;
+
+ /* limit the response size, specify offset */
+ admin_req->flags = 0x3;
+ admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff);
+ admin_req->doff = cpu_to_le32(resp_data_offset & 0xffffffff);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ *resp_data_size = resp.data_len;
+
+ return 0;
+}
+
+int nvme_mi_admin_admin_passthru(nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags,
+ __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3,
+ __u32 cdw10, __u32 cdw11, __u32 cdw12,
+ __u32 cdw13, __u32 cdw14, __u32 cdw15,
+ __u32 data_len, void *data, __u32 metadata_len,
+ void *metadata, __u32 timeout_ms, __u32 *result)
+{
+ /* Input parameters flags, rsvd, metadata, metadata_len are not used */
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+ int direction = opcode & 0x3;
+ bool has_write_data = false;
+ bool has_read_data = false;
+
+ if (direction == NVME_DATA_TFR_BIDIRECTIONAL) {
+ nvme_msg(ctrl->ep->root, LOG_ERR,
+ "nvme_mi_admin_admin_passthru doesn't support bidirectional commands\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (data_len > 4096) {
+ nvme_msg(ctrl->ep->root, LOG_ERR,
+ "nvme_mi_admin_admin_passthru doesn't support data_len over 4096 bytes.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (data != NULL && data_len != 0) {
+ if (direction == NVME_DATA_TFR_HOST_TO_CTRL)
+ has_write_data = true;
+ if (direction == NVME_DATA_TFR_CTRL_TO_HOST)
+ has_read_data = true;
+ }
+
+ if (timeout_ms > nvme_mi_ep_get_timeout(ctrl->ep)) {
+ /* Set timeout if user needs a bigger timeout */
+ nvme_mi_ep_set_timeout(ctrl->ep, timeout_ms);
+ }
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, opcode);
+ req_hdr.cdw1 = cpu_to_le32(nsid);
+ req_hdr.cdw2 = cpu_to_le32(cdw2);
+ req_hdr.cdw3 = cpu_to_le32(cdw3);
+ req_hdr.cdw10 = cpu_to_le32(cdw10);
+ req_hdr.cdw11 = cpu_to_le32(cdw11);
+ req_hdr.cdw12 = cpu_to_le32(cdw12);
+ req_hdr.cdw13 = cpu_to_le32(cdw13);
+ req_hdr.cdw14 = cpu_to_le32(cdw14);
+ req_hdr.cdw15 = cpu_to_le32(cdw15);
+ req_hdr.doff = 0;
+ if (data_len != 0) {
+ req_hdr.dlen = cpu_to_le32(data_len);
+ /* Bit 0 set to 1 means DLEN contains a value */
+ req_hdr.flags = 0x1;
+ }
+
+ if (has_write_data) {
+ req.data = data;
+ req.data_len = data_len;
+ }
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ if (has_read_data) {
+ resp.data = data;
+ resp.data_len = data_len;
+ }
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ rc = nvme_mi_admin_parse_status(&resp, result);
+ if (rc)
+ return rc;
+
+ if (has_read_data && (resp.data_len != data_len)) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
+ struct nvme_identify_args *args,
+ off_t offset, size_t size)
+{
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!size || size > 0xffffffff) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, nvme_admin_identify);
+ req_hdr.cdw1 = cpu_to_le32(args->nsid);
+ req_hdr.cdw10 = cpu_to_le32(args->cntid << 16 | args->cns);
+ req_hdr.cdw11 = cpu_to_le32((args->csi & 0xff) << 24 |
+ args->cns_specific_id);
+ req_hdr.cdw14 = cpu_to_le32(args->uuidx);
+ req_hdr.dlen = cpu_to_le32(size & 0xffffffff);
+ req_hdr.flags = 0x1;
+ if (offset) {
+ req_hdr.flags |= 0x2;
+ req_hdr.doff = cpu_to_le32(offset);
+ }
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+ resp.data = args->data;
+ resp.data_len = size;
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ rc = nvme_mi_admin_parse_status(&resp, args->result);
+ if (rc)
+ return rc;
+
+ /* callers will expect a full response; if the data buffer isn't
+ * fully valid, return an error */
+ if (resp.data_len != size) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* retrieves a MCTP-messsage-sized chunk of log page data. offset and len are
+ * specified within the args->data area. The `offset` parameter is a relative
+ * offset to the args->lpo !
+ *
+ * What's more, we change the LPO of original command to chunk the request
+ * message into proper size which is allowed by MI interface. One reason is that
+ * this option seems to be supported better by devices. For more information
+ * about this option, please check https://github.com/linux-nvme/libnvme/pull/539
+ * */
+static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl,
+ const struct nvme_get_log_args *args,
+ off_t offset, size_t *lenp, bool final)
+{
+ __u64 log_page_offset = args->lpo + offset;
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ size_t len;
+ __u32 ndw;
+ int rc;
+
+ /* MI spec requires that the data length field is less than or equal
+ * to 4096 */
+ len = *lenp;
+ if (!len || len > 4096 || len < 4) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (offset < 0 || offset >= args->len || offset + len > args->len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ndw = (len >> 2) - 1;
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, nvme_admin_get_log_page);
+ req_hdr.cdw1 = cpu_to_le32(args->nsid);
+ req_hdr.cdw10 = cpu_to_le32((ndw & 0xffff) << 16 |
+ ((!final || args->rae) ? 1 : 0) << 15 |
+ args->lsp << 8 |
+ (args->lid & 0xff));
+ req_hdr.cdw11 = cpu_to_le32(args->lsi << 16 |
+ ndw >> 16);
+ req_hdr.cdw12 = cpu_to_le32(log_page_offset & 0xffffffff);
+ req_hdr.cdw13 = cpu_to_le32(log_page_offset >> 32);
+ req_hdr.cdw14 = cpu_to_le32(args->csi << 24 |
+ (args->ot ? 1 : 0) << 23 |
+ args->uuidx);
+ req_hdr.flags = 0x1;
+ req_hdr.dlen = cpu_to_le32(len & 0xffffffff);
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+ resp.data = args->log + offset;
+ resp.data_len = len;
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ rc = nvme_mi_admin_parse_status(&resp, args->result);
+ if (!rc)
+ *lenp = resp.data_len;
+
+ return rc;
+}
+
+int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl, __u32 xfer_size,
+ struct nvme_get_log_args *args)
+{
+ const size_t max_xfer_size = xfer_size;
+ off_t xfer_offset;
+ int rc = 0;
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (args->ot && (args->len > max_xfer_size)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (xfer_offset = 0; xfer_offset < args->len;) {
+ size_t xfered_size, cur_xfer_size = max_xfer_size;
+ bool final;
+
+ if (xfer_offset + cur_xfer_size > args->len)
+ cur_xfer_size = args->len - xfer_offset;
+
+ xfered_size = cur_xfer_size;
+
+ final = xfer_offset + cur_xfer_size >= args->len;
+
+ /* xfered_size is used as both input and output parameter */
+ rc = __nvme_mi_admin_get_log(ctrl, args, xfer_offset,
+ &xfered_size, final);
+ if (rc)
+ break;
+
+ xfer_offset += xfered_size;
+ /* if we returned less data than expected, consider that
+ * the end of the log page */
+ if (xfered_size != cur_xfer_size)
+ break;
+ }
+
+ if (!rc)
+ args->len = xfer_offset;
+
+ return rc;
+}
+
+int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args)
+{
+ return nvme_mi_admin_get_log_page(ctrl, 4096, args);
+}
+
+int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl,
+ struct nvme_security_send_args *args)
+{
+
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (args->data_len > 4096) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_security_send);
+
+ req_hdr.cdw10 = cpu_to_le32(args->secp << 24 |
+ args->spsp1 << 16 |
+ args->spsp0 << 8 |
+ args->nssf);
+
+ req_hdr.cdw11 = cpu_to_le32(args->data_len & 0xffffffff);
+
+ req_hdr.flags = 0x1;
+ req_hdr.dlen = cpu_to_le32(args->data_len & 0xffffffff);
+ req.data = args->data;
+ req.data_len = args->data_len;
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ return nvme_mi_admin_parse_status(&resp, args->result);
+}
+
+int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl,
+ struct nvme_security_receive_args *args)
+{
+
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (args->data_len > 4096) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_security_recv);
+
+ req_hdr.cdw10 = cpu_to_le32(args->secp << 24 |
+ args->spsp1 << 16 |
+ args->spsp0 << 8 |
+ args->nssf);
+
+ req_hdr.cdw11 = cpu_to_le32(args->data_len & 0xffffffff);
+
+ req_hdr.flags = 0x1;
+ req_hdr.dlen = cpu_to_le32(args->data_len & 0xffffffff);
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+ resp.data = args->data;
+ resp.data_len = args->data_len;
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ rc = nvme_mi_admin_parse_status(&resp, args->result);
+ if (rc)
+ return rc;
+
+ args->data_len = resp.data_len;
+
+ return 0;
+}
+
+int nvme_mi_admin_get_features(nvme_mi_ctrl_t ctrl,
+ struct nvme_get_features_args *args)
+{
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args))
+ return -EINVAL;
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_get_features);
+
+ req_hdr.cdw1 = cpu_to_le32(args->nsid);
+ req_hdr.cdw10 = cpu_to_le32((args->sel & 0x7) << 8 | args->fid);
+ req_hdr.cdw14 = cpu_to_le32(args->uuidx & 0x7f);
+ req_hdr.cdw11 = cpu_to_le32(args->cdw11);
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+ resp.data = args->data;
+ resp.data_len = args->data_len;
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ rc = nvme_mi_admin_parse_status(&resp, args->result);
+ if (rc)
+ return rc;
+
+ args->data_len = resp.data_len;
+
+ return 0;
+}
+
+int nvme_mi_admin_set_features(nvme_mi_ctrl_t ctrl,
+ struct nvme_set_features_args *args)
+{
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args))
+ return -EINVAL;
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_set_features);
+
+ req_hdr.cdw1 = cpu_to_le32(args->nsid);
+ req_hdr.cdw10 = cpu_to_le32((__u32)!!args->save << 31 |
+ (args->fid & 0xff));
+ req_hdr.cdw14 = cpu_to_le32(args->uuidx & 0x7f);
+ req_hdr.cdw11 = cpu_to_le32(args->cdw11);
+ req_hdr.cdw12 = cpu_to_le32(args->cdw12);
+ req_hdr.cdw13 = cpu_to_le32(args->cdw13);
+ req_hdr.cdw15 = cpu_to_le32(args->cdw15);
+
+ req.data_len = args->data_len;
+ req.data = args->data;
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ rc = nvme_mi_admin_parse_status(&resp, args->result);
+ if (rc)
+ return rc;
+
+ args->data_len = resp.data_len;
+
+ return 0;
+}
+
+int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl,
+ struct nvme_ns_mgmt_args *args)
+{
+ const size_t size_v1 = sizeof_args(struct nvme_ns_mgmt_args, csi, __u64);
+ const size_t size_v2 = sizeof_args(struct nvme_ns_mgmt_args, data, __u64);
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+ size_t data_len;
+
+ if (args->args_size < size_v1 || args->args_size > size_v2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_ns_mgmt);
+
+ req_hdr.cdw1 = cpu_to_le32(args->nsid);
+ req_hdr.cdw10 = cpu_to_le32(args->sel & 0xf);
+ req_hdr.cdw11 = cpu_to_le32(args->csi << 24);
+
+ if (args->args_size == size_v2) {
+ if (args->data) {
+ req.data = args->data;
+ data_len = sizeof(*args->data);
+ }
+ }
+ else {
+ if (args->ns) {
+ req.data = args->ns;
+ data_len = sizeof(*args->ns);
+ }
+ }
+
+ if (req.data) {
+ req.data_len = data_len;
+ req_hdr.dlen = cpu_to_le32(data_len);
+ req_hdr.flags = 0x1;
+ }
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ return nvme_mi_admin_parse_status(&resp, args->result);
+}
+
+int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl,
+ struct nvme_ns_attach_args *args)
+{
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args))
+ return -EINVAL;
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_ns_attach);
+
+ req_hdr.cdw1 = cpu_to_le32(args->nsid);
+ req_hdr.cdw10 = cpu_to_le32(args->sel & 0xf);
+ req.data = args->ctrlist;
+ req.data_len = sizeof(*args->ctrlist);
+ req_hdr.dlen = cpu_to_le32(sizeof(*args->ctrlist));
+ req_hdr.flags = 0x1;
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ return nvme_mi_admin_parse_status(&resp, args->result);
+}
+
+int nvme_mi_admin_fw_download(nvme_mi_ctrl_t ctrl,
+ struct nvme_fw_download_args *args)
+{
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args))
+ return -EINVAL;
+
+ if (args->data_len & 0x3)
+ return -EINVAL;
+
+ if (args->offset & 0x3)
+ return -EINVAL;
+
+ if (!args->data_len)
+ return -EINVAL;
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_fw_download);
+
+ req_hdr.cdw10 = cpu_to_le32((args->data_len >> 2) - 1);
+ req_hdr.cdw11 = cpu_to_le32(args->offset >> 2);
+ req.data = args->data;
+ req.data_len = args->data_len;
+ req_hdr.dlen = cpu_to_le32(args->data_len);
+ req_hdr.flags = 0x1;
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ return nvme_mi_admin_parse_status(&resp, NULL);
+}
+
+int nvme_mi_admin_fw_commit(nvme_mi_ctrl_t ctrl,
+ struct nvme_fw_commit_args *args)
+{
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args))
+ return -EINVAL;
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_fw_commit);
+
+ req_hdr.cdw10 = cpu_to_le32(((__u32)(args->bpid & 0x1) << 31) |
+ ((args->action & 0x7) << 3) |
+ ((args->slot & 0x7) << 0));
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ return nvme_mi_admin_parse_status(&resp, NULL);
+}
+
+int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl,
+ struct nvme_format_nvm_args *args)
+{
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args))
+ return -EINVAL;
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_format_nvm);
+
+ req_hdr.cdw1 = cpu_to_le32(args->nsid);
+ req_hdr.cdw10 = cpu_to_le32(((args->lbafu & 0x3) << 12)
+ | ((args->ses & 0x7) << 9)
+ | ((args->pil & 0x1) << 8)
+ | ((args->pi & 0x7) << 5)
+ | ((args->mset & 0x1) << 4)
+ | ((args->lbaf & 0xf) << 0));
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ return nvme_mi_admin_parse_status(&resp, args->result);
+}
+
+int nvme_mi_admin_sanitize_nvm(nvme_mi_ctrl_t ctrl,
+ struct nvme_sanitize_nvm_args *args)
+{
+ struct nvme_mi_admin_resp_hdr resp_hdr;
+ struct nvme_mi_admin_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ if (args->args_size < sizeof(*args))
+ return -EINVAL;
+
+ nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id,
+ nvme_admin_sanitize_nvm);
+
+ req_hdr.cdw10 = cpu_to_le32(((args->nodas ? 1 : 0) << 9)
+ | ((args->oipbp ? 1 : 0) << 8)
+ | ((args->owpass & 0xf) << 4)
+ | ((args->ause ? 1 : 0) << 3)
+ | ((args->sanact & 0x7) << 0));
+ req_hdr.cdw11 = cpu_to_le32(args->ovrpat);
+
+ nvme_mi_calc_req_mic(&req);
+
+ nvme_mi_admin_init_resp(&resp, &resp_hdr);
+
+ rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ return nvme_mi_admin_parse_status(&resp, args->result);
+}
+
+static int nvme_mi_read_data(nvme_mi_ep_t ep, __u32 cdw0,
+ void *data, size_t *data_len)
+{
+ struct nvme_mi_mi_resp_hdr resp_hdr;
+ struct nvme_mi_mi_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ memset(&req_hdr, 0, sizeof(req_hdr));
+ req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;
+ req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) |
+ (NVME_MI_MT_MI << 3); /* we always use command slot 0 */
+ req_hdr.opcode = nvme_mi_mi_opcode_mi_data_read;
+ req_hdr.cdw0 = cpu_to_le32(cdw0);
+
+ memset(&req, 0, sizeof(req));
+ req.hdr = &req_hdr.hdr;
+ req.hdr_len = sizeof(req_hdr);
+
+ memset(&resp, 0, sizeof(resp));
+ resp.hdr = &resp_hdr.hdr;
+ resp.hdr_len = sizeof(resp_hdr);
+ resp.data = data;
+ resp.data_len = *data_len;
+
+ rc = nvme_mi_submit(ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ if (resp_hdr.status)
+ return resp_hdr.status;
+
+ *data_len = resp.data_len;
+
+ return 0;
+}
+
+int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep,
+ struct nvme_mi_read_nvm_ss_info *s)
+{
+ size_t len;
+ __u32 cdw0;
+ int rc;
+
+ cdw0 = (__u8)nvme_mi_dtyp_subsys_info << 24;
+ len = sizeof(*s);
+
+ rc = nvme_mi_read_data(ep, cdw0, s, &len);
+ if (rc)
+ return rc;
+
+ if (len != sizeof(*s)) {
+ nvme_msg(ep->root, LOG_WARNING,
+ "MI read data length mismatch: "
+ "got %zd bytes, expected %zd\n",
+ len, sizeof(*s));
+ errno = EPROTO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int nvme_mi_mi_read_mi_data_port(nvme_mi_ep_t ep, __u8 portid,
+ struct nvme_mi_read_port_info *p)
+{
+ size_t len;
+ __u32 cdw0;
+ int rc;
+
+ cdw0 = ((__u8)nvme_mi_dtyp_port_info << 24) | (portid << 16);
+ len = sizeof(*p);
+
+ rc = nvme_mi_read_data(ep, cdw0, p, &len);
+ if (rc)
+ return rc;
+
+ if (len != sizeof(*p)) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int nvme_mi_mi_read_mi_data_ctrl_list(nvme_mi_ep_t ep, __u8 start_ctrlid,
+ struct nvme_ctrl_list *list)
+{
+ size_t len;
+ __u32 cdw0;
+ int rc;
+
+ cdw0 = ((__u8)nvme_mi_dtyp_ctrl_list << 24) | (start_ctrlid << 16);
+ len = sizeof(*list);
+
+ rc = nvme_mi_read_data(ep, cdw0, list, &len);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id,
+ struct nvme_mi_read_ctrl_info *ctrl)
+{
+ size_t len;
+ __u32 cdw0;
+ int rc;
+
+ cdw0 = ((__u8)nvme_mi_dtyp_ctrl_info << 24) | cpu_to_le16(ctrl_id);
+ len = sizeof(*ctrl);
+
+ rc = nvme_mi_read_data(ep, cdw0, ctrl, &len);
+ if (rc)
+ return rc;
+
+ if (len != sizeof(*ctrl)) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
+ struct nvme_mi_nvm_ss_health_status *sshs)
+{
+ struct nvme_mi_mi_resp_hdr resp_hdr;
+ struct nvme_mi_mi_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ memset(&req_hdr, 0, sizeof(req_hdr));
+ req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;;
+ req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) |
+ (NVME_MI_MT_MI << 3);
+ req_hdr.opcode = nvme_mi_mi_opcode_subsys_health_status_poll;
+ req_hdr.cdw1 = (clear ? 1 : 0) << 31;
+
+ memset(&req, 0, sizeof(req));
+ req.hdr = &req_hdr.hdr;
+ req.hdr_len = sizeof(req_hdr);
+
+ memset(&resp, 0, sizeof(resp));
+ resp.hdr = &resp_hdr.hdr;
+ resp.hdr_len = sizeof(resp_hdr);
+ resp.data = sshs;
+ resp.data_len = sizeof(*sshs);
+
+ rc = nvme_mi_submit(ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ if (resp_hdr.status)
+ return resp_hdr.status;
+
+ if (resp.data_len != sizeof(*sshs)) {
+ nvme_msg(ep->root, LOG_WARNING,
+ "MI Subsystem Health Status length mismatch: "
+ "got %zd bytes, expected %zd\n",
+ resp.data_len, sizeof(*sshs));
+ errno = EPROTO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1,
+ __u32 *nmresp)
+{
+ struct nvme_mi_mi_resp_hdr resp_hdr;
+ struct nvme_mi_mi_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ memset(&req_hdr, 0, sizeof(req_hdr));
+ req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;
+ req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3);
+ req_hdr.opcode = nvme_mi_mi_opcode_configuration_get;
+ req_hdr.cdw0 = cpu_to_le32(dw0);
+ req_hdr.cdw1 = cpu_to_le32(dw1);
+
+ memset(&req, 0, sizeof(req));
+ req.hdr = &req_hdr.hdr;
+ req.hdr_len = sizeof(req_hdr);
+
+ memset(&resp, 0, sizeof(resp));
+ resp.hdr = &resp_hdr.hdr;
+ resp.hdr_len = sizeof(resp_hdr);
+
+ rc = nvme_mi_submit(ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ if (resp_hdr.status)
+ return resp_hdr.status;
+
+ *nmresp = resp_hdr.nmresp[0] |
+ resp_hdr.nmresp[1] << 8 |
+ resp_hdr.nmresp[2] << 16;
+
+ return 0;
+}
+
+int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1)
+{
+ struct nvme_mi_mi_resp_hdr resp_hdr;
+ struct nvme_mi_mi_req_hdr req_hdr;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ memset(&req_hdr, 0, sizeof(req_hdr));
+ req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;
+ req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3);
+ req_hdr.opcode = nvme_mi_mi_opcode_configuration_set;
+ req_hdr.cdw0 = cpu_to_le32(dw0);
+ req_hdr.cdw1 = cpu_to_le32(dw1);
+
+ memset(&req, 0, sizeof(req));
+ req.hdr = &req_hdr.hdr;
+ req.hdr_len = sizeof(req_hdr);
+
+ memset(&resp, 0, sizeof(resp));
+ resp.hdr = &resp_hdr.hdr;
+ resp.hdr_len = sizeof(resp_hdr);
+
+ rc = nvme_mi_submit(ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ if (resp_hdr.status)
+ return resp_hdr.status;
+
+ return 0;
+}
+
+void nvme_mi_close(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_ctrl *ctrl, *tmp;
+
+ /* don't look for controllers during destruction */
+ ep->controllers_scanned = true;
+
+ nvme_mi_for_each_ctrl_safe(ep, ctrl, tmp)
+ nvme_mi_close_ctrl(ctrl);
+
+ if (ep->transport && ep->transport->close)
+ ep->transport->close(ep);
+ list_del(&ep->root_entry);
+ free(ep);
+}
+
+void nvme_mi_close_ctrl(nvme_mi_ctrl_t ctrl)
+{
+ list_del(&ctrl->ep_entry);
+ free(ctrl);
+}
+
+char *nvme_mi_endpoint_desc(nvme_mi_ep_t ep)
+{
+ char tsbuf[101], *s = NULL;
+ size_t tslen;
+ int rc;
+
+ rc = -1;
+ memset(tsbuf, 0, sizeof(tsbuf));
+ if (ep->transport->desc_ep)
+ rc = ep->transport->desc_ep(ep, tsbuf, sizeof(tsbuf) - 1);
+
+ if (!rc) {
+ /* don't overflow if the transport gives us an invalid string */
+ tsbuf[sizeof(tsbuf)-1] = '\0';
+ tslen = strlen(tsbuf);
+ } else {
+ tslen = 0;
+ }
+
+ if (tslen)
+ rc = asprintf(&s, "%s: %s", ep->transport->name, tsbuf);
+ else
+ rc = asprintf(&s, "%s endpoint", ep->transport->name);
+
+ if (rc < 0)
+ return NULL;
+
+ return s;
+}
+
+nvme_mi_ep_t nvme_mi_first_endpoint(nvme_root_t m)
+{
+ return list_top(&m->endpoints, struct nvme_mi_ep, root_entry);
+}
+
+nvme_mi_ep_t nvme_mi_next_endpoint(nvme_root_t m, nvme_mi_ep_t ep)
+{
+ return ep ? list_next(&m->endpoints, ep, root_entry) : NULL;
+}
+
+nvme_mi_ctrl_t nvme_mi_first_ctrl(nvme_mi_ep_t ep)
+{
+ return list_top(&ep->controllers, struct nvme_mi_ctrl, ep_entry);
+}
+
+nvme_mi_ctrl_t nvme_mi_next_ctrl(nvme_mi_ep_t ep, nvme_mi_ctrl_t c)
+{
+ return c ? list_next(&ep->controllers, c, ep_entry) : NULL;
+}
+
+
+static const char *const mi_status[] = {
+ [NVME_MI_RESP_MPR] = "More Processing Required: The command message is in progress and requires more time to complete processing",
+ [NVME_MI_RESP_INTERNAL_ERR] = "Internal Error: The request message could not be processed due to a vendor-specific error",
+ [NVME_MI_RESP_INVALID_OPCODE] = "Invalid Command Opcode",
+ [NVME_MI_RESP_INVALID_PARAM] = "Invalid Parameter",
+ [NVME_MI_RESP_INVALID_CMD_SIZE] = "Invalid Command Size: The size of the message body of the request was different than expected",
+ [NVME_MI_RESP_INVALID_INPUT_SIZE] = "Invalid Command Input Data Size: The command requires data and contains too much or too little data",
+ [NVME_MI_RESP_ACCESS_DENIED] = "Access Denied. Processing prohibited due to a vendor-specific mechanism of the Command and Feature lockdown function",
+ [NVME_MI_RESP_VPD_UPDATES_EXCEEDED] = "VPD Updates Exceeded",
+ [NVME_MI_RESP_PCIE_INACCESSIBLE] = "PCIe Inaccessible. The PCIe functionality is not available at this time",
+ [NVME_MI_RESP_MEB_SANITIZED] = "Management Endpoint Buffer Cleared Due to Sanitize",
+ [NVME_MI_RESP_ENC_SERV_FAILURE] = "Enclosure Services Failure",
+ [NVME_MI_RESP_ENC_SERV_XFER_FAILURE] = "Enclosure Services Transfer Failure: Communication with the Enclosure Services Process has failed",
+ [NVME_MI_RESP_ENC_FAILURE] = "An unrecoverable enclosure failure has been detected by the Enclosuer Services Process",
+ [NVME_MI_RESP_ENC_XFER_REFUSED] = "Enclosure Services Transfer Refused: The NVM Subsystem or Enclosure Services Process indicated an error or an invalid format in communication",
+ [NVME_MI_RESP_ENC_FUNC_UNSUP] = "Unsupported Enclosure Function: An SES Send command has been attempted to a simple Subenclosure",
+ [NVME_MI_RESP_ENC_SERV_UNAVAIL] = "Enclosure Services Unavailable: The NVM Subsystem or Enclosure Services Process has encountered an error but may become available again",
+ [NVME_MI_RESP_ENC_DEGRADED] = "Enclosure Degraded: A noncritical failure has been detected by the Enclosure Services Process",
+ [NVME_MI_RESP_SANITIZE_IN_PROGRESS] = "Sanitize In Progress: The requested command is prohibited while a sanitize operation is in progress",
+};
+
+/* kept in mi.c while we have a split libnvme/libnvme-mi; consider moving
+ * to utils.c (with nvme_status_to_string) if we ever merge. */
+const char *nvme_mi_status_to_string(int status)
+{
+ const char *s = "Unknown status";
+
+ if (status < ARRAY_SIZE(mi_status) && mi_status[status])
+ s = mi_status[status];
+
+ return s;
+}
diff --git a/src/nvme/mi.h b/src/nvme/mi.h
new file mode 100644
index 0000000..bd26627
--- /dev/null
+++ b/src/nvme/mi.h
@@ -0,0 +1,2677 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021 Code Construct Pty Ltd
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+
+/**
+ * DOC: mi.h - NVMe Management Interface library (libnvme-mi) definitions.
+ *
+ * These provide an abstraction for the MI messaging between controllers
+ * and a host, typically over an MCTP-over-i2c link to a NVMe device, used
+ * as part of the out-of-band management of a system.
+ *
+ * We have a few data structures define here to reflect the topology
+ * of a MI connection with an NVMe subsystem:
+ *
+ * - &nvme_mi_ep_t: an MI endpoint - our mechanism of communication with a
+ * NVMe subsystem. For MCTP, an endpoint will be the component that
+ * holds the MCTP address (EID), and receives our request message.
+ *
+ * endpoints are defined in the NVMe-MI spec, and are specific to the MI
+ * interface.
+ *
+ * Each endpoint will provide access to one or more of:
+ *
+ * - &nvme_mi_ctrl_t: a NVMe controller, as defined by the NVMe base spec.
+ * The controllers are responsible for processing any NVMe standard
+ * commands (eg, the Admin command set). An endpoint (&nvme_mi_ep_t)
+ * may provide access to multiple controllers - so each of the controller-
+ * type commands will require a &nvme_mi_ctrl_t to be specified, rather than
+ * an endpoint
+ *
+ * A couple of conventions with the libnvme-mi API:
+ *
+ * - All types and functions have the nvme_mi prefix, to distinguish from
+ * the libnvme core.
+ *
+ * - We currently support either MI commands and Admin commands. The
+ * former adds a _mi prefix, the latter an _admin prefix. [This does
+ * result in the MI functions having a double _mi, like
+ * &nvme_mi_mi_subsystem_health_status_poll, which is apparently amusing
+ * for our German-speaking readers]
+ *
+ * For return values: unless specified in the per-function documentation,
+ * all functions:
+ *
+ * - return 0 on success
+ *
+ * - return -1, with errno set, for errors communicating with the MI device,
+ * either in request or response data
+ *
+ * - return >1 on MI status errors. This value is the 8-bit MI status
+ * value, represented by &enum nvme_mi_resp_status. Note that the
+ * status values may be vendor-defined above 0xe0.
+ *
+ * For the second case, we have a few conventions for errno values:
+ *
+ * - EPROTO: response data violated the MI protocol, and libnvme cannot
+ * validly interpret the response
+ *
+ * - EIO: Other I/O error communicating with device (eg., valid but
+ * unexpected response data)
+ *
+ * - EINVAL: invalid input arguments for a command
+ *
+ * In line with the core NVMe API, the Admin command functions take an
+ * `_args` structure to provide the command-specific parameters. However,
+ * for the MI interface, the fd and timeout members of these _args structs
+ * are ignored.
+ *
+ * References to the specifications here will either to be the NVM Express
+ * Management Interface ("NVMe-MI") or the NVM Express Base specification
+ * ("NVMe"). At the time of writing, the versions we're referencing here
+ * are:
+ * - NVMe-MI 1.2b
+ * - NVMe 2.0b
+ * with a couple of accommodations for older spec types, particularly NVMe-MI
+ * 1.1, where possible.
+ *
+ */
+
+#ifndef _LIBNVME_MI_MI_H
+#define _LIBNVME_MI_MI_H
+
+#include <endian.h>
+#include <stdint.h>
+
+#include "types.h"
+#include "tree.h"
+
+/**
+ * NVME_MI_MSGTYPE_NVME - MCTP message type for NVMe-MI messages.
+ *
+ * This is defined by MCTP, but is referenced as part of the NVMe-MI message
+ * spec. This is the MCTP NVMe message type (0x4), with the message-integrity
+ * bit (0x80) set.
+ */
+#define NVME_MI_MSGTYPE_NVME 0x84
+
+/* Basic MI message definitions */
+
+/**
+ * enum nvme_mi_message_type - NVMe-MI message type field.
+ * @NVME_MI_MT_CONTROL: NVME-MI Control Primitive
+ * @NVME_MI_MT_MI: NVMe-MI command
+ * @NVME_MI_MT_ADMIN: NVMe Admin command
+ * @NVME_MI_MT_PCIE: PCIe command
+ *
+ * Used as byte 1 of both request and response messages (NMIMT bits of NMP
+ * byte). Not to be confused with the MCTP message type in byte 0.
+ */
+enum nvme_mi_message_type {
+ NVME_MI_MT_CONTROL = 0,
+ NVME_MI_MT_MI = 1,
+ NVME_MI_MT_ADMIN = 2,
+ NVME_MI_MT_PCIE = 4,
+};
+
+/**
+ * enum nvme_mi_ror: Request or response field.
+ * @NVME_MI_ROR_REQ: request message
+ * @NVME_MI_ROR_RSP: response message
+ */
+enum nvme_mi_ror {
+ NVME_MI_ROR_REQ = 0,
+ NVME_MI_ROR_RSP = 1,
+};
+
+/**
+ * enum nvme_mi_resp_status - values for the response status field
+ * @NVME_MI_RESP_SUCCESS: success
+ * @NVME_MI_RESP_MPR: More Processing Required
+ * @NVME_MI_RESP_INTERNAL_ERR: Internal Error
+ * @NVME_MI_RESP_INVALID_OPCODE: Invalid command opcode
+ * @NVME_MI_RESP_INVALID_PARAM: Invalid command parameter
+ * @NVME_MI_RESP_INVALID_CMD_SIZE: Invalid command size
+ * @NVME_MI_RESP_INVALID_INPUT_SIZE: Invalid command input data size
+ * @NVME_MI_RESP_ACCESS_DENIED: Access Denied
+ * @NVME_MI_RESP_VPD_UPDATES_EXCEEDED: More VPD updates than allowed
+ * @NVME_MI_RESP_PCIE_INACCESSIBLE: PCIe functionality currently unavailable
+ * @NVME_MI_RESP_MEB_SANITIZED: MEB has been cleared due to sanitize
+ * @NVME_MI_RESP_ENC_SERV_FAILURE: Enclosure services process failed
+ * @NVME_MI_RESP_ENC_SERV_XFER_FAILURE: Transfer with enclosure services failed
+ * @NVME_MI_RESP_ENC_FAILURE: Unreoverable enclosure failure
+ * @NVME_MI_RESP_ENC_XFER_REFUSED: Enclosure services transfer refused
+ * @NVME_MI_RESP_ENC_FUNC_UNSUP: Unsupported enclosure services function
+ * @NVME_MI_RESP_ENC_SERV_UNAVAIL: Enclosure services unavailable
+ * @NVME_MI_RESP_ENC_DEGRADED: Noncritical failure detected by enc. services
+ * @NVME_MI_RESP_SANITIZE_IN_PROGRESS: Command prohibited during sanitize
+ */
+enum nvme_mi_resp_status {
+ NVME_MI_RESP_SUCCESS = 0x00,
+ NVME_MI_RESP_MPR = 0x01,
+ NVME_MI_RESP_INTERNAL_ERR = 0x02,
+ NVME_MI_RESP_INVALID_OPCODE = 0x03,
+ NVME_MI_RESP_INVALID_PARAM = 0x04,
+ NVME_MI_RESP_INVALID_CMD_SIZE = 0x05,
+ NVME_MI_RESP_INVALID_INPUT_SIZE = 0x06,
+ NVME_MI_RESP_ACCESS_DENIED = 0x07,
+ /* 0x08 - 0x1f: reserved */
+ NVME_MI_RESP_VPD_UPDATES_EXCEEDED = 0x20,
+ NVME_MI_RESP_PCIE_INACCESSIBLE = 0x21,
+ NVME_MI_RESP_MEB_SANITIZED = 0x22,
+ NVME_MI_RESP_ENC_SERV_FAILURE = 0x23,
+ NVME_MI_RESP_ENC_SERV_XFER_FAILURE = 0x24,
+ NVME_MI_RESP_ENC_FAILURE = 0x25,
+ NVME_MI_RESP_ENC_XFER_REFUSED = 0x26,
+ NVME_MI_RESP_ENC_FUNC_UNSUP = 0x27,
+ NVME_MI_RESP_ENC_SERV_UNAVAIL = 0x28,
+ NVME_MI_RESP_ENC_DEGRADED = 0x29,
+ NVME_MI_RESP_SANITIZE_IN_PROGRESS = 0x2a,
+ /* 0x2b - 0xdf: reserved */
+ /* 0xe0 - 0xff: vendor specific */
+};
+
+/**
+ * struct nvme_mi_msg_hdr - General MI message header.
+ * @type: MCTP message type, will always be NVME_MI_MSGTYPE_NVME
+ * @nmp: NVMe-MI message parameters (including MI message type)
+ * @meb: Management Endpoint Buffer flag; unused for libnvme-mi implementation
+ * @rsvd0: currently reserved
+ *
+ * Wire format shared by both request and response messages, per NVMe-MI
+ * section 3.1. This is used for all message types, MI and Admin.
+ */
+struct nvme_mi_msg_hdr {
+ __u8 type;
+ __u8 nmp;
+ __u8 meb;
+ __u8 rsvd0;
+} __attribute__((packed));
+
+/**
+ * struct nvme_mi_msg_resp - Generic response type.
+ * @hdr: the general request/response message header
+ * @status: response status value (see &enum nvme_mi_resp_status)
+ * @rsvd0: reserved data, may be defined by specific response
+ *
+ * Every response will start with one of these; command-specific responses
+ * will define parts of the reserved data, and may add further fields.
+ */
+struct nvme_mi_msg_resp {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 rsvd0[3];
+};
+
+/**
+ * enum nvme_mi_mi_opcode - Operation code for supported NVMe-MI commands.
+ * @nvme_mi_mi_opcode_mi_data_read: Read NVMe-MI Data Structure
+ * @nvme_mi_mi_opcode_subsys_health_status_poll: Subsystem Health Status Poll
+ * @nvme_mi_mi_opcode_configuration_set: MI Configuration Set
+ * @nvme_mi_mi_opcode_configuration_get: MI Configuration Get
+ */
+enum nvme_mi_mi_opcode {
+ nvme_mi_mi_opcode_mi_data_read = 0x00,
+ nvme_mi_mi_opcode_subsys_health_status_poll = 0x01,
+ nvme_mi_mi_opcode_configuration_set = 0x03,
+ nvme_mi_mi_opcode_configuration_get = 0x04,
+};
+
+/**
+ * struct nvme_mi_mi_req_hdr - MI request message header.
+ * @hdr: generic MI message header
+ * @opcode: opcode (OPC) for the specific MI command
+ * @rsvd0: reserved bytes
+ * @cdw0: Management Request Doubleword 0 - command specific usage
+ * @cdw1: Management Request Doubleword 1 - command specific usage
+ *
+ * Wire format for MI request message headers, defined in section 5 of NVMe-MI.
+ */
+struct nvme_mi_mi_req_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 opcode;
+ __u8 rsvd0[3];
+ __le32 cdw0, cdw1;
+};
+
+/**
+ * struct nvme_mi_mi_resp_hdr - MI response message header.
+ * @hdr: generic MI message header
+ * @status: generic response status from command; non-zero on failure.
+ * @nmresp: NVMe Management Response: command-type-specific response data
+ *
+ * Wire format for MI response message header, defined in section 5 of NVMe-MI.
+ */
+struct nvme_mi_mi_resp_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 nmresp[3];
+};
+
+/**
+ * enum nvme_mi_dtyp - Data Structure Type field.
+ * @nvme_mi_dtyp_subsys_info: NVM Subsystem Information
+ * @nvme_mi_dtyp_port_info: Port information
+ * @nvme_mi_dtyp_ctrl_list: Controller List
+ * @nvme_mi_dtyp_ctrl_info: Controller Information
+ * @nvme_mi_dtyp_opt_cmd_support: Optionally Supported Command List
+ * @nvme_mi_dtyp_meb_support: Management Endpoint Buffer Command Support List
+ *
+ * Data Structure Type field for Read NVMe-MI Data Structure command, used to
+ * indicate the particular structure to query from the endpoint.
+ */
+enum nvme_mi_dtyp {
+ nvme_mi_dtyp_subsys_info = 0x00,
+ nvme_mi_dtyp_port_info = 0x01,
+ nvme_mi_dtyp_ctrl_list = 0x02,
+ nvme_mi_dtyp_ctrl_info = 0x03,
+ nvme_mi_dtyp_opt_cmd_support = 0x04,
+ nvme_mi_dtyp_meb_support = 0x05,
+};
+
+/**
+ * enum nvme_mi_config_id - NVMe-MI Configuration identifier.
+ * @NVME_MI_CONFIG_SMBUS_FREQ: Current SMBus/I2C frequency
+ * @NVME_MI_CONFIG_HEALTH_STATUS_CHANGE: Health Status change - used to clear
+ * health status bits in CCS bits of
+ * status poll. Only for Set ops.
+ * @NVME_MI_CONFIG_MCTP_MTU: MCTP maximum transmission unit size of port
+ * specified in dw 0
+ *
+ * Configuration parameters for the MI Get/Set Configuration commands.
+ *
+ * See &nvme_mi_mi_config_get() and &nvme_mi_config_set().
+ */
+enum nvme_mi_config_id {
+ NVME_MI_CONFIG_SMBUS_FREQ = 0x1,
+ NVME_MI_CONFIG_HEALTH_STATUS_CHANGE = 0x2,
+ NVME_MI_CONFIG_MCTP_MTU = 0x3,
+};
+
+/**
+ * enum nvme_mi_config_smbus_freq - SMBus/I2C frequency values
+ * @NVME_MI_CONFIG_SMBUS_FREQ_100kHz: 100kHz
+ * @NVME_MI_CONFIG_SMBUS_FREQ_400kHz: 400kHz
+ * @NVME_MI_CONFIG_SMBUS_FREQ_1MHz: 1MHz
+ *
+ * Values used in the SMBus Frequency device configuration. See
+ * &nvme_mi_mi_config_get_smbus_freq() and &nvme_mi_mi_config_set_smbus_freq().
+ */
+enum nvme_mi_config_smbus_freq {
+ NVME_MI_CONFIG_SMBUS_FREQ_100kHz = 0x1,
+ NVME_MI_CONFIG_SMBUS_FREQ_400kHz = 0x2,
+ NVME_MI_CONFIG_SMBUS_FREQ_1MHz = 0x3,
+};
+
+/* Admin command definitions */
+
+/**
+ * struct nvme_mi_admin_req_hdr - Admin command request header.
+ * @hdr: Generic MI message header
+ * @opcode: Admin command opcode (using enum nvme_admin_opcode)
+ * @flags: Command Flags, indicating dlen and doff validity; Only defined in
+ * NVMe-MI version 1.1, no fields defined in 1.2 (where the dlen/doff
+ * are always considered valid).
+ * @ctrl_id: Controller ID target of command
+ * @cdw1: Submission Queue Entry doubleword 1
+ * @cdw2: Submission Queue Entry doubleword 2
+ * @cdw3: Submission Queue Entry doubleword 3
+ * @cdw4: Submission Queue Entry doubleword 4
+ * @cdw5: Submission Queue Entry doubleword 5
+ * @doff: Offset of data to return from command
+ * @dlen: Length of sent/returned data
+ * @rsvd0: Reserved
+ * @rsvd1: Reserved
+ * @cdw10: Submission Queue Entry doubleword 10
+ * @cdw11: Submission Queue Entry doubleword 11
+ * @cdw12: Submission Queue Entry doubleword 12
+ * @cdw13: Submission Queue Entry doubleword 13
+ * @cdw14: Submission Queue Entry doubleword 14
+ * @cdw15: Submission Queue Entry doubleword 15
+ *
+ * Wire format for Admin command message headers, defined in section 6 of
+ * NVMe-MI.
+ */
+struct nvme_mi_admin_req_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 opcode;
+ __u8 flags;
+ __le16 ctrl_id;
+ __le32 cdw1, cdw2, cdw3, cdw4, cdw5;
+ __le32 doff;
+ __le32 dlen;
+ __le32 rsvd0, rsvd1;
+ __le32 cdw10, cdw11, cdw12, cdw13, cdw14, cdw15;
+} __attribute((packed));
+
+/**
+ * struct nvme_mi_admin_resp_hdr - Admin command response header.
+ * @hdr: Generic MI message header
+ * @status: Generic response code, non-zero on failure
+ * @rsvd0: Reserved
+ * @cdw0: Completion Queue Entry doubleword 0
+ * @cdw1: Completion Queue Entry doubleword 1
+ * @cdw3: Completion Queue Entry doubleword 3
+ *
+ * This is the generic response format with the three doublewords of completion
+ * queue data, plus optional response data.
+ */
+struct nvme_mi_admin_resp_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 rsvd0[3];
+ __le32 cdw0, cdw1, cdw3;
+} __attribute__((packed));
+
+/**
+ * nvme_mi_status_to_string() - return a string representation of the MI
+ * status.
+ * @status: MI response status
+ *
+ * Gives a string description of @status, as per section 4.1.2 of the NVMe-MI
+ * spec. The status value should be of type NVME_STATUS_MI, and extracted
+ * from the return value using nvme_status_get_value().
+ *
+ * Returned string is const, and should not be free()ed.
+ *
+ * Returns: A string representing the status value
+ */
+const char *nvme_mi_status_to_string(int status);
+
+/**
+ * nvme_mi_create_root() - Create top-level MI (root) handle.
+ * @fp: File descriptor for logging messages
+ * @log_level: Logging level to use
+ *
+ * Create the top-level (library) handle for creating subsequent endpoint
+ * objects. Similar to nvme_create_root(), but we provide this to allow linking
+ * without the core libnvme.
+ *
+ * Return: new root object, or NULL on failure.
+ *
+ * See &nvme_create_root.
+ */
+nvme_root_t nvme_mi_create_root(FILE *fp, int log_level);
+
+/**
+ * nvme_mi_free_root() - Free root object.
+ * @root: root to free
+ */
+void nvme_mi_free_root(nvme_root_t root);
+
+/**
+ * nvme_mi_set_probe_enabled() - enable/disable the probe for new endpoints
+ * @root: &nvme_root_t object
+ * @enabled: whether to probe new endpoints
+ *
+ * Controls whether newly-created endpoints are probed for quirks on creation.
+ * Defaults to enabled, which results in some initial messaging with the
+ * endpoint to determine model-specific details.
+ */
+void nvme_mi_set_probe_enabled(nvme_root_t root, bool enabled);
+
+/* Top level management object: NVMe-MI Management Endpoint */
+struct nvme_mi_ep;
+
+/**
+ * typedef nvme_mi_ep_t - MI Endpoint object.
+ *
+ * Represents our communication endpoint on the remote MI-capable device.
+ * To be used for direct MI commands for the endpoint (through the
+ * nvme_mi_mi_* functions(), or to communicate with individual controllers
+ * (see &nvme_mi_init_ctrl).
+ *
+ * Endpoints are created through a transport-specific constructor; currently
+ * only MCTP-connected endpoints are supported, through &nvme_mi_open_mctp.
+ * Subsequent operations on the endpoint (and related controllers) are
+ * transport-independent.
+ */
+typedef struct nvme_mi_ep * nvme_mi_ep_t;
+
+/**
+ * nvme_mi_first_endpoint - Start endpoint iterator
+ * @m: &nvme_root_t object
+ *
+ * Return: first MI endpoint object under this root, or NULL if no endpoints
+ * are present.
+ *
+ * See: &nvme_mi_next_endpoint, &nvme_mi_for_each_endpoint
+ */
+nvme_mi_ep_t nvme_mi_first_endpoint(nvme_root_t m);
+
+/**
+ * nvme_mi_next_endpoint - Continue endpoint iterator
+ * @m: &nvme_root_t object
+ * @e: &nvme_mi_ep_t current position of iterator
+ *
+ * Return: next endpoint MI endpoint object after @e under this root, or NULL
+ * if no further endpoints are present.
+ *
+ * See: &nvme_mi_first_endpoint, &nvme_mi_for_each_endpoint
+ */
+nvme_mi_ep_t nvme_mi_next_endpoint(nvme_root_t m, nvme_mi_ep_t e);
+
+/**
+ * nvme_mi_for_each_endpoint - Iterator for NVMe-MI endpoints.
+ * @m: &nvme_root_t containing endpoints
+ * @e: &nvme_mi_ep_t object, set on each iteration
+ */
+#define nvme_mi_for_each_endpoint(m, e) \
+ for (e = nvme_mi_first_endpoint(m); e != NULL; \
+ e = nvme_mi_next_endpoint(m, e))
+
+/**
+ * nvme_mi_for_each_endpoint_safe - Iterator for NVMe-MI endpoints, allowing
+ * deletion during traversal
+ * @m: &nvme_root_t containing endpoints
+ * @e: &nvme_mi_ep_t object, set on each iteration
+ * @_e: &nvme_mi_ep_t object used as temporary storage
+ */
+#define nvme_mi_for_each_endpoint_safe(m, e, _e) \
+ for (e = nvme_mi_first_endpoint(m), _e = nvme_mi_next_endpoint(m, e); \
+ e != NULL; \
+ e = _e, _e = nvme_mi_next_endpoint(m, e))
+
+/**
+ * nvme_mi_ep_set_timeout - set a timeout for NVMe-MI responses
+ * @ep: MI endpoint object
+ * @timeout_ms: Timeout for MI responses, given in milliseconds
+ */
+int nvme_mi_ep_set_timeout(nvme_mi_ep_t ep, unsigned int timeout_ms);
+
+/**
+ * nvme_mi_ep_set_mprt_max - set the maximum wait time for a More Processing
+ * Required response
+ * @ep: MI endpoint object
+ * @mprt_max_ms: Maximum more processing required wait time
+ *
+ * NVMe-MI endpoints may respond to a request with a "More Processing Required"
+ * response; this also includes a hint on the worst-case processing time for
+ * the eventual response data, with a specification-defined maximum of 65.535
+ * seconds.
+ *
+ * This function provides a way to limit the maximum time we're prepared to
+ * wait for the final response. Specify zero in @mprt_max_ms for no limit.
+ * This should be larger than the command/response timeout set in
+ * &nvme_mi_ep_set_timeout().
+ */
+void nvme_mi_ep_set_mprt_max(nvme_mi_ep_t ep, unsigned int mprt_max_ms);
+
+/**
+ * nvme_mi_ep_get_timeout - get the current timeout value for NVMe-MI responses
+ * @ep: MI endpoint object
+ *
+ * Returns the current timeout value, in milliseconds, for this endpoint.
+ */
+unsigned int nvme_mi_ep_get_timeout(nvme_mi_ep_t ep);
+
+struct nvme_mi_ctrl;
+
+/**
+ * typedef nvme_mi_ctrl_t - NVMe-MI Controller object.
+ *
+ * Provides NVMe command functionality, through the MI interface.
+ */
+typedef struct nvme_mi_ctrl * nvme_mi_ctrl_t;
+
+/**
+ * nvme_mi_first_ctrl - Start controller iterator
+ * @ep: &nvme_mi_ep_t object
+ *
+ * Return: first MI controller object under this root, or NULL if no controllers
+ * are present.
+ *
+ * See: &nvme_mi_next_ctrl, &nvme_mi_for_each_ctrl
+ */
+nvme_mi_ctrl_t nvme_mi_first_ctrl(nvme_mi_ep_t ep);
+
+/**
+ * nvme_mi_next_ctrl - Continue ctrl iterator
+ * @ep: &nvme_mi_ep_t object
+ * @c: &nvme_mi_ctrl_t current position of iterator
+ *
+ * Return: next MI controller object after @c under this endpoint, or NULL
+ * if no further controllers are present.
+ *
+ * See: &nvme_mi_first_ctrl, &nvme_mi_for_each_ctrl
+ */
+nvme_mi_ctrl_t nvme_mi_next_ctrl(nvme_mi_ep_t ep, nvme_mi_ctrl_t c);
+
+/**
+ * nvme_mi_for_each_ctrl - Iterator for NVMe-MI controllers.
+ * @ep: &nvme_mi_ep_t containing endpoints
+ * @c: &nvme_mi_ctrl_t object, set on each iteration
+ *
+ * Allows iteration of the list of controllers behind an endpoint. Unless the
+ * controllers have already been created explicitly, you'll probably want to
+ * call &nvme_mi_scan_ep() to scan for the controllers first.
+ *
+ * See: &nvme_mi_scan_ep()
+ */
+#define nvme_mi_for_each_ctrl(ep, c) \
+ for (c = nvme_mi_first_ctrl(ep); c != NULL; \
+ c = nvme_mi_next_ctrl(ep, c))
+
+/**
+ * nvme_mi_for_each_ctrl_safe - Iterator for NVMe-MI controllers, allowing
+ * deletion during traversal
+ * @ep: &nvme_mi_ep_t containing controllers
+ * @c: &nvme_mi_ctrl_t object, set on each iteration
+ * @_c: &nvme_mi_ctrl_t object used as temporary storage
+ *
+ * Allows iteration of the list of controllers behind an endpoint, safe against
+ * deletion during iteration. Unless the controllers have already been created
+ * explicitly (or you're just iterating to destroy controllers) you'll probably
+ * want to call &nvme_mi_scan_ep() to scan for the controllers first.
+ *
+ * See: &nvme_mi_scan_ep()
+ */
+#define nvme_mi_for_each_ctrl_safe(ep, c, _c) \
+ for (c = nvme_mi_first_ctrl(ep), _c = nvme_mi_next_ctrl(ep, c); \
+ c != NULL; \
+ c = _c, _c = nvme_mi_next_ctrl(ep, c))
+
+/**
+ * nvme_mi_open_mctp() - Create an endpoint using a MCTP connection.
+ * @root: root object to create under
+ * @netid: MCTP network ID on this system
+ * @eid: MCTP endpoint ID
+ *
+ * Transport-specific endpoint initialization for MI-connected endpoints. Once
+ * an endpoint is created, the rest of the API is transport-independent.
+ *
+ * Return: New endpoint object for @netid & @eid, or NULL on failure.
+ *
+ * See &nvme_mi_close
+ */
+nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, uint8_t eid);
+
+/**
+ * nvme_mi_close() - Close an endpoint connection and release resources,
+ * including controller objects.
+ *
+ * @ep: Endpoint object to close
+ */
+void nvme_mi_close(nvme_mi_ep_t ep);
+
+/**
+ * nvme_mi_scan_mctp - look for MCTP-connected NVMe-MI endpoints.
+ *
+ * Description: This function queries the system MCTP daemon ("mctpd") over
+ * D-Bus, to find MCTP endpoints that report support for NVMe-MI over MCTP.
+ *
+ * This requires libvnme-mi to be compiled with D-Bus support; if not, this
+ * will return NULL.
+ *
+ * Return: A @nvme_root_t populated with a set of MCTP-connected endpoints,
+ * or NULL on failure
+ */
+nvme_root_t nvme_mi_scan_mctp(void);
+
+/**
+ * nvme_mi_scan_ep - query an endpoint for its NVMe controllers.
+ * @ep: Endpoint to scan
+ * @force_rescan: close existing controllers and rescan
+ *
+ * This function queries an MI endpoint for the controllers available, by
+ * performing an MI Read MI Data Structure command (requesting the
+ * controller list). The controllers are stored in the endpoint's internal
+ * list, and can be iterated with nvme_mi_for_each_ctrl.
+ *
+ * This will only scan the endpoint once, unless @force_rescan is set. If
+ * so, all existing controller objects will be freed - the caller must not
+ * hold a reference to those across this call.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &nvme_mi_for_each_ctrl
+ */
+int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan);
+
+/**
+ * nvme_mi_init_ctrl() - initialise a NVMe controller.
+ * @ep: Endpoint to create under
+ * @ctrl_id: ID of controller to initialize.
+ *
+ * Create a connection to a controller behind the endpoint specified in @ep.
+ * Controller IDs may be queried from the endpoint through
+ * &nvme_mi_mi_read_mi_data_ctrl_list.
+ *
+ * Return: New controller object, or NULL on failure.
+ *
+ * See &nvme_mi_close_ctrl
+ */
+nvme_mi_ctrl_t nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id);
+
+/**
+ * nvme_mi_close_ctrl() - free a controller
+ * @ctrl: controller to free
+ */
+void nvme_mi_close_ctrl(nvme_mi_ctrl_t ctrl);
+
+/**
+ * nvme_mi_ctrl_id() - get the ID of a controller
+ * @ctrl: controller to query
+ *
+ * Retrieve the ID of the controller, as defined by hardware, and available
+ * in the Identify (Controller List) data. This is the value passed to
+ * @nvme_mi_init_ctrl, but may have been created internally via
+ * @nvme_mi_scan_ep.
+ *
+ * Return: the (locally-stored) ID of this controller.
+ */
+__u16 nvme_mi_ctrl_id(nvme_mi_ctrl_t ctrl);
+
+
+/**
+ * nvme_mi_endpoint_desc - Get a string describing a MI endpoint.
+ * @ep: endpoint to describe
+ *
+ * Generates a human-readable string describing the endpoint, with possibly
+ * transport-specific data. The string is allocated during the call, and the
+ * caller is responsible for free()-ing the string.
+ *
+ * Return: a newly-allocated string containing the endpoint description, or
+ * NULL on failure.
+ */
+char *nvme_mi_endpoint_desc(nvme_mi_ep_t ep);
+
+/* MI Command API: nvme_mi_mi_ prefix */
+
+/**
+ * nvme_mi_mi_read_mi_data_subsys() - Perform a Read MI Data Structure command,
+ * retrieving subsystem data.
+ * @ep: endpoint for MI communication
+ * @s: subsystem information to populate
+ *
+ * Retrieves the Subsystem information - number of external ports and
+ * NVMe version information. See &struct nvme_mi_read_nvm_ss_info.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep,
+ struct nvme_mi_read_nvm_ss_info *s);
+
+/**
+ * nvme_mi_mi_read_mi_data_port() - Perform a Read MI Data Structure command,
+ * retrieving port data.
+ * @ep: endpoint for MI communication
+ * @portid: id of port data to retrieve
+ * @p: port information to populate
+ *
+ * Retrieves the Port information, for the specified port ID. The subsystem
+ * data (from &nvme_mi_mi_read_mi_data_subsys) nmp field contains the allowed
+ * range of port IDs.
+ *
+ * See &struct nvme_mi_read_port_info.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+int nvme_mi_mi_read_mi_data_port(nvme_mi_ep_t ep, __u8 portid,
+ struct nvme_mi_read_port_info *p);
+
+/**
+ * nvme_mi_mi_read_mi_data_ctrl_list() - Perform a Read MI Data Structure
+ * command, retrieving the list of attached controllers.
+ * @ep: endpoint for MI communication
+ * @start_ctrlid: starting controller ID
+ * @list: controller list to populate
+ *
+ * Retrieves the list of attached controllers, with IDs greater than or
+ * equal to @start_ctrlid.
+ *
+ * See &struct nvme_ctrl_list.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+int nvme_mi_mi_read_mi_data_ctrl_list(nvme_mi_ep_t ep, __u8 start_ctrlid,
+ struct nvme_ctrl_list *list);
+
+/**
+ * nvme_mi_mi_read_mi_data_ctrl() - Perform a Read MI Data Structure command,
+ * retrieving controller information
+ * @ep: endpoint for MI communication
+ * @ctrl_id: ID of controller to query
+ * @ctrl: controller data to populate
+ *
+ * Retrieves the Controller Information Data Structure for the attached
+ * controller with ID @ctrlid.
+ *
+ * See &struct nvme_mi_read_ctrl_info.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id,
+ struct nvme_mi_read_ctrl_info *ctrl);
+
+/**
+ * nvme_mi_mi_subsystem_health_status_poll() - Read the Subsystem Health
+ * Data Structure from the NVM subsystem
+ * @ep: endpoint for MI communication
+ * @clear: flag to clear the Composite Controller Status state
+ * @nshds: subsystem health status data to populate
+ *
+ * Retrieves the Subsystem Health Data Structure into @nshds. If @clear is
+ * set, requests that the Composite Controller Status bits are cleared after
+ * the read. See NVMe-MI section 5.6 for details on the CCS bits.
+ *
+ * See &struct nvme_mi_nvm_ss_health_status.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
+ struct nvme_mi_nvm_ss_health_status *nshds);
+
+/**
+ * nvme_mi_mi_config_get - query a configuration parameter
+ * @ep: endpoint for MI communication
+ * @dw0: management doubleword 0, containing configuration identifier, plus
+ * config-specific fields
+ * @dw1: management doubleword 0, config-specific.
+ * @nmresp: set to queried configuration data in NMRESP field of response.
+ *
+ * Performs a MI Configuration Get command, with the configuration identifier
+ * as the LSB of @dw0. Other @dw0 and @dw1 data is configuration-identifier
+ * specific.
+ *
+ * On a successful Configuration Get, the @nmresp pointer will be populated with
+ * the bytes from the 3-byte NMRESP field, converted to native endian.
+ *
+ * See &enum nvme_mi_config_id for identifiers.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1,
+ __u32 *nmresp);
+
+/**
+ * nvme_mi_mi_config_set - set a configuration parameter
+ * @ep: endpoint for MI communication
+ * @dw0: management doubleword 0, containing configuration identifier, plus
+ * config-specific fields
+ * @dw1: management doubleword 0, config-specific.
+ *
+ * Performs a MI Configuration Set command, with the command as the LSB of
+ * @dw0. Other @dw0 and @dw1 data is configuration-identifier specific.
+ *
+ * See &enum nvme_mi_config_id for identifiers.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1);
+
+/**
+ * nvme_mi_mi_config_get_smbus_freq - get configuration: SMBus port frequency
+ * @ep: endpoint for MI communication
+ * @port: port ID to query
+ * @freq: output value for current frequency configuration
+ *
+ * Performs a MI Configuration Get, to query the current SMBus frequency of
+ * the port specified in @port. On success, populates @freq with the port
+ * frequency
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+static inline int nvme_mi_mi_config_get_smbus_freq(nvme_mi_ep_t ep, __u8 port,
+ enum nvme_mi_config_smbus_freq *freq)
+{
+ __u32 tmp, dw0;
+ int rc;
+
+ dw0 = port << 24 | NVME_MI_CONFIG_SMBUS_FREQ;
+
+ rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp);
+ if (!rc)
+ *freq = (enum nvme_mi_config_smbus_freq)(tmp & 0x3);
+ return rc;
+}
+
+/**
+ * nvme_mi_mi_config_set_smbus_freq - set configuration: SMBus port frequency
+ * @ep: endpoint for MI communication
+ * @port: port ID to set
+ * @freq: new frequency configuration
+ *
+ * Performs a MI Configuration Set, to update the current SMBus frequency of
+ * the port specified in @port.
+ *
+ * See &struct nvme_mi_read_port_info for the maximum supported SMBus frequency
+ * for the port.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+static inline int nvme_mi_mi_config_set_smbus_freq(nvme_mi_ep_t ep, __u8 port,
+ enum nvme_mi_config_smbus_freq freq)
+{
+ __u32 dw0 = port << 24 |
+ (freq & 0x3) << 8 |
+ NVME_MI_CONFIG_SMBUS_FREQ;
+
+ return nvme_mi_mi_config_set(ep, dw0, 0);
+}
+
+/**
+ * nvme_mi_mi_config_set_health_status_change - clear CCS bits in health status
+ * @ep: endpoint for MI communication
+ * @mask: bitmask to clear
+ *
+ * Performs a MI Configuration Set, to update the current health status poll
+ * values of the Composite Controller Status bits. Bits set in @mask will
+ * be cleared from future health status poll data, and may be re-triggered by
+ * a future health change event.
+ *
+ * See &nvme_mi_mi_subsystem_health_status_poll(), &enum nvme_mi_ccs for
+ * values in @mask.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+static inline int nvme_mi_mi_config_set_health_status_change(nvme_mi_ep_t ep,
+ __u32 mask)
+{
+ return nvme_mi_mi_config_set(ep, NVME_MI_CONFIG_HEALTH_STATUS_CHANGE,
+ mask);
+}
+
+/**
+ * nvme_mi_mi_config_get_mctp_mtu - get configuration: MCTP MTU
+ * @ep: endpoint for MI communication
+ * @port: port ID to query
+ * @mtu: output value for current MCTP MTU configuration
+ *
+ * Performs a MI Configuration Get, to query the current MCTP Maximum
+ * Transmission Unit size (MTU) of the port specified in @port. On success,
+ * populates @mtu with the MTU.
+ *
+ * The default reset value is 64, corresponding to the MCTP baseline MTU.
+ *
+ * Some controllers may also use this as the maximum receive unit size, and
+ * may not accept MCTP messages larger than the configured MTU.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+static inline int nvme_mi_mi_config_get_mctp_mtu(nvme_mi_ep_t ep, __u8 port,
+ __u16 *mtu)
+{
+ __u32 tmp, dw0;
+ int rc;
+
+ dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU;
+
+ rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp);
+ if (!rc)
+ *mtu = tmp & 0xffff;
+ return rc;
+}
+
+/**
+ * nvme_mi_mi_config_set_mctp_mtu - set configuration: MCTP MTU
+ * @ep: endpoint for MI communication
+ * @port: port ID to set
+ * @mtu: new MTU configuration
+ *
+ * Performs a MI Configuration Set, to update the current MCTP MTU value for
+ * the port specified in @port.
+ *
+ * Some controllers may also use this as the maximum receive unit size, and
+ * may not accept MCTP messages larger than the configured MTU. When setting
+ * this value, you will likely need to change the MTU of the local MCTP
+ * interface(s) to match.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+static inline int nvme_mi_mi_config_set_mctp_mtu(nvme_mi_ep_t ep, __u8 port,
+ __u16 mtu)
+{
+ __u32 dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU;
+
+ return nvme_mi_mi_config_set(ep, dw0, mtu);
+}
+
+/* Admin channel functions */
+
+/**
+ * nvme_mi_admin_xfer() - Raw admin transfer interface.
+ * @ctrl: controller to send the admin command to
+ * @admin_req: request data
+ * @req_data_size: size of request data payload
+ * @admin_resp: buffer for response data
+ * @resp_data_offset: offset into request data to retrieve from controller
+ * @resp_data_size: size of response data buffer, updated to received size
+ *
+ * Performs an arbitrary NVMe Admin command, using the provided request data,
+ * in @admin_req. The size of the request data *payload* is specified in
+ * @req_data_size - this does not include the standard header length (so a
+ * header-only request would have a size of 0).
+ *
+ * On success, response data is stored in @admin_resp, which has an optional
+ * appended payload buffer of @resp_data_size bytes. The actual payload
+ * transferred will be stored in @resp_data_size. These sizes do not include
+ * the Admin request header, so 0 represents no payload.
+ *
+ * As with all Admin commands, we can request partial data from the Admin
+ * Response payload, offset by @resp_data_offset.
+ *
+ * See: &struct nvme_mi_admin_req_hdr and &struct nvme_mi_admin_resp_hdr.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise..
+ */
+int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
+ struct nvme_mi_admin_req_hdr *admin_req,
+ size_t req_data_size,
+ struct nvme_mi_admin_resp_hdr *admin_resp,
+ off_t resp_data_offset,
+ size_t *resp_data_size);
+
+/**
+ * nvme_mi_admin_admin_passthru() - Submit an nvme admin passthrough command
+ * @ctrl: Controller to send command to
+ * @opcode: The nvme admin command to send
+ * @flags: NVMe command flags (not used)
+ * @rsvd: Reserved for future use
+ * @nsid: Namespace identifier
+ * @cdw2: Command dword 2
+ * @cdw3: Command dword 3
+ * @cdw10: Command dword 10
+ * @cdw11: Command dword 11
+ * @cdw12: Command dword 12
+ * @cdw13: Command dword 13
+ * @cdw14: Command dword 14
+ * @cdw15: Command dword 15
+ * @data_len: Length of the data transferred in this command in bytes
+ * @data: Pointer to user address of the data buffer
+ * @metadata_len:Length of metadata transferred in this command(not used)
+ * @metadata: Pointer to user address of the metadata buffer(not used)
+ * @timeout_ms: How long to wait for the command to complete
+ * @result: Optional field to return the result from the CQE dword 0
+ *
+ * Send a customized NVMe Admin command request message and get the corresponding
+ * response message.
+ *
+ * This interface supports no data, host to controller and controller to
+ * host but it doesn't support bidirectional data transfer.
+ * Also this interface only supports data transfer size range [0, 4096] (bytes)
+ * so the & data_len parameter must be less than 4097.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_mi_admin_admin_passthru(nvme_mi_ctrl_t ctrl, __u8 opcode, __u8 flags,
+ __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3,
+ __u32 cdw10, __u32 cdw11, __u32 cdw12,
+ __u32 cdw13, __u32 cdw14, __u32 cdw15,
+ __u32 data_len, void *data, __u32 metadata_len,
+ void *metadata, __u32 timeout_ms, __u32 *result);
+
+/**
+ * nvme_mi_admin_identify_partial() - Perform an Admin identify command,
+ * and retrieve partial response data.
+ * @ctrl: Controller to process identify command
+ * @args: Identify command arguments
+ * @offset: offset of identify data to retrieve from response
+ * @size: size of identify data to return
+ *
+ * Perform an Identify command, using the Identify command parameters in @args.
+ * The @offset and @size arguments allow the caller to retrieve part of
+ * the identify response. See NVMe-MI section 6.2 for the semantics (and some
+ * handy diagrams) of the offset & size parameters.
+ *
+ * Will return an error if the length of the response data (from the controller)
+ * did not match @size.
+ *
+ * Unless you're performing a vendor-unique identify command, You'll probably
+ * want to use one of the identify helpers (nvme_mi_admin_identify,
+ * nvme_mi_admin_identify_cns_nsid, or nvme_mi_admin_identify_<type>) instead
+ * of this. If the type of your identify command is standardized but not
+ * yet supported by libnvme-mi, please contact the maintainers.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_identify_args
+ */
+int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
+ struct nvme_identify_args *args,
+ off_t offset, size_t size);
+
+/**
+ * nvme_mi_admin_identify() - Perform an Admin identify command.
+ * @ctrl: Controller to process identify command
+ * @args: Identify command arguments
+ *
+ * Perform an Identify command, using the Identify command parameters in @args.
+ * Stores the identify data in ->data, and (if set) the result from cdw0
+ * into args->result.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_identify_args
+ */
+static inline int nvme_mi_admin_identify(nvme_mi_ctrl_t ctrl,
+ struct nvme_identify_args *args)
+{
+ return nvme_mi_admin_identify_partial(ctrl, args,
+ 0, NVME_IDENTIFY_DATA_SIZE);
+}
+
+/**
+ * nvme_mi_admin_identify_cns_nsid() - Perform an Admin identify command using
+ * specific CNS/NSID parameters.
+ * @ctrl: Controller to process identify command
+ * @cns: Controller or Namespace Structure, specifying identified object
+ * @nsid: namespace ID
+ * @data: buffer for identify data response
+ *
+ * Perform an Identify command, using the CNS specifier @cns, and the
+ * namespace ID @nsid if required by the CNS type.
+ *
+ * Stores the identify data in @data, which is expected to be a buffer of
+ * &NVME_IDENTIFY_DATA_SIZE bytes.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_identify_cns_nsid(nvme_mi_ctrl_t ctrl,
+ enum nvme_identify_cns cns,
+ __u32 nsid, void *data)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = data,
+ .args_size = sizeof(args),
+ .cns = cns,
+ .csi = NVME_CSI_NVM,
+ .nsid = nsid,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_identify_ns() - Perform an Admin identify command for a
+ * namespace
+ * @ctrl: Controller to process identify command
+ * @nsid: namespace ID
+ * @ns: Namespace identification to populate
+ *
+ * Perform an Identify (namespace) command, setting the namespace id data
+ * in @ns. The namespace is expected to active and allocated.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_identify_ns(nvme_mi_ctrl_t ctrl, __u32 nsid,
+ struct nvme_id_ns *ns)
+{
+ return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_NS,
+ nsid, ns);
+}
+
+/**
+ * nvme_mi_admin_identify_ns_descs() - Perform an Admin identify Namespace
+ * Identification Descriptor list command for a namespace
+ * @ctrl: Controller to process identify command
+ * @nsid: Namespace ID
+ * @descs: Namespace Identification Descriptor list to populate
+ *
+ * Perform an Identify namespace identification description list command,
+ * setting the namespace identification description list in @descs
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_identify_ns_descs(nvme_mi_ctrl_t ctrl,
+ __u32 nsid,
+ struct nvme_ns_id_desc *descs)
+{
+ return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_NS_DESC_LIST,
+ nsid, descs);
+}
+
+/**
+ * nvme_mi_admin_identify_allocated_ns() - Perform an Admin identify command
+ * for an allocated namespace
+ * @ctrl: Controller to process identify command
+ * @nsid: namespace ID
+ * @ns: Namespace identification to populate
+ *
+ * Perform an Identify (namespace) command, setting the namespace id data
+ * in @ns.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_identify_allocated_ns(nvme_mi_ctrl_t ctrl,
+ __u32 nsid,
+ struct nvme_id_ns *ns)
+{
+ return nvme_mi_admin_identify_cns_nsid(ctrl,
+ NVME_IDENTIFY_CNS_ALLOCATED_NS,
+ nsid, ns);
+}
+
+/**
+ * nvme_mi_admin_identify_ctrl() - Perform an Admin identify for a controller
+ * @ctrl: Controller to process identify command
+ * @id: Controller identify data to populate
+ *
+ * Perform an Identify command, for the controller specified by @ctrl,
+ * writing identify data to @id.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be
+ * fully populated on success.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_id_ctrl
+ */
+static inline int nvme_mi_admin_identify_ctrl(nvme_mi_ctrl_t ctrl,
+ struct nvme_id_ctrl *id)
+{
+ return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_CTRL,
+ NVME_NSID_NONE, id);
+}
+
+/**
+ * nvme_mi_admin_identify_ctrl_list() - Perform an Admin identify for a
+ * controller list.
+ * @ctrl: Controller to process identify command
+ * @cntid: Controller ID to specify list start
+ * @list: List data to populate
+ *
+ * Perform an Identify command, for the controller list starting with
+ * IDs greater than or equal to @cntid.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be
+ * fully populated on success.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_ctrl_list
+ */
+static inline int nvme_mi_admin_identify_ctrl_list(nvme_mi_ctrl_t ctrl,
+ __u16 cntid,
+ struct nvme_ctrl_list *list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = list,
+ .args_size = sizeof(args),
+ .cns = NVME_IDENTIFY_CNS_CTRL_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_identify_nsid_ctrl_list() - Perform an Admin identify for a
+ * controller list with specific namespace ID
+ * @ctrl: Controller to process identify command
+ * @nsid: Namespace identifier
+ * @cntid: Controller ID to specify list start
+ * @list: List data to populate
+ *
+ * Perform an Identify command, for the controller list for @nsid, starting
+ * with IDs greater than or equal to @cntid.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be
+ * fully populated on success.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_ctrl_list
+ */
+static inline int nvme_mi_admin_identify_nsid_ctrl_list(nvme_mi_ctrl_t ctrl,
+ __u32 nsid, __u16 cntid,
+ struct nvme_ctrl_list *list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = list,
+ .args_size = sizeof(args),
+ .cns = NVME_IDENTIFY_CNS_NS_CTRL_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = nsid,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_identify_allocated_ns_list() - Perform an Admin identify for
+ * an allocated namespace list
+ * @ctrl: Controller to process identify command
+ * @nsid: Namespace ID to specify list start
+ * @list: List data to populate
+ *
+ * Perform an Identify command, for the allocated namespace list starting with
+ * IDs greater than or equal to @nsid. Specify &NVME_NSID_NONE for the start
+ * of the list.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be
+ * be fully populated on success.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_ns_list
+ */
+static inline int nvme_mi_admin_identify_allocated_ns_list(nvme_mi_ctrl_t ctrl,
+ __u32 nsid,
+ struct nvme_ns_list *list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = list,
+ .args_size = sizeof(args),
+ .cns = NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = nsid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_identify_active_ns_list() - Perform an Admin identify for an
+ * active namespace list
+ * @ctrl: Controller to process identify command
+ * @nsid: Namespace ID to specify list start
+ * @list: List data to populate
+ *
+ * Perform an Identify command, for the active namespace list starting with
+ * IDs greater than or equal to @nsid. Specify &NVME_NSID_NONE for the start
+ * of the list.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be
+ * be fully populated on success.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_ns_list
+ */
+static inline int nvme_mi_admin_identify_active_ns_list(nvme_mi_ctrl_t ctrl,
+ __u32 nsid,
+ struct nvme_ns_list *list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = list,
+ .args_size = sizeof(args),
+ .cns = NVME_IDENTIFY_CNS_NS_ACTIVE_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = nsid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_identify_primary_ctrl() - Perform an Admin identify for
+ * primary controller capabilities data structure.
+ * @ctrl: Controller to process identify command
+ * @cntid: Controller ID to specify
+ * @cap: Primary Controller Capabilities data structure to populate
+ *
+ * Perform an Identify command to get the Primary Controller Capabilities data
+ * for the controller specified by @cntid
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @cap will be
+ * be fully populated on success.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_primary_ctrl_cap
+ */
+static inline int nvme_mi_admin_identify_primary_ctrl(nvme_mi_ctrl_t ctrl,
+ __u16 cntid,
+ struct nvme_primary_ctrl_cap *cap)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = cap,
+ .args_size = sizeof(args),
+ .cns = NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_identify_secondary_ctrl_list() - Perform an Admin identify for
+ * a secondary controller list.
+ * @ctrl: Controller to process identify command
+ * @cntid: Controller ID to specify list start
+ * @list: List data to populate
+ *
+ * Perform an Identify command, for the secondary controllers associated with
+ * the current primary controller. Only entries with IDs greater than or
+ * equal to @cntid are returned.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @list will be
+ * be fully populated on success.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_secondary_ctrl_list
+ */
+static inline int nvme_mi_admin_identify_secondary_ctrl_list(nvme_mi_ctrl_t ctrl,
+ __u16 cntid,
+ struct nvme_secondary_ctrl_list *list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = list,
+ .args_size = sizeof(args),
+ .cns = NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_page() - Retrieve log page data from controller
+ * @ctrl: Controller to query
+ * @xfer_len: The chunk size of the read
+ * @args: Get Log Page command arguments
+ *
+ * Performs a Get Log Page Admin command as specified by @args. Response data
+ * is stored in @args->data, which should be a buffer of @args->data_len bytes.
+ * Resulting data length is stored in @args->data_len on successful
+ * command completion.
+ *
+ * This request may be implemented as multiple log page commands, in order
+ * to fit within MI message-size limits.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_get_log_args
+ */
+int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl, __u32 xfer_len,
+ struct nvme_get_log_args *args);
+
+/**
+ * nvme_mi_admin_get_log() - Retrieve log page data from controller
+ * @ctrl: Controller to query
+ * @args: Get Log Page command arguments
+ *
+ * Performs a Get Log Page Admin command as specified by @args. Response data
+ * is stored in @args->data, which should be a buffer of @args->data_len bytes.
+ * Resulting data length is stored in @args->data_len on successful
+ * command completion.
+ *
+ * This request may be implemented as multiple log page commands, in order
+ * to fit within MI message-size limits.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_get_log_args
+ */
+int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args);
+
+/**
+ * nvme_mi_admin_get_nsid_log() - Helper for Get Log Page functions
+ * @ctrl: Controller to query
+ * @rae: Retain Asynchronous Events
+ * @lid: Log identifier
+ * @nsid: Namespace ID
+ * @len: length of log buffer
+ * @log: pointer for resulting log data
+ *
+ * Performs a Get Log Page Admin command for a specific log ID @lid and
+ * namespace ID @nsid. Log data is expected to be @len bytes, and is stored
+ * in @log on success. The @rae flag is passed as-is to the Get Log Page
+ * command, and is specific to the Log Page requested.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_nsid_log(nvme_mi_ctrl_t ctrl, bool rae,
+ enum nvme_cmd_get_log_lid lid,
+ __u32 nsid, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = lid,
+ .len = len,
+ .nsid = nsid,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_simple() - Helper for Get Log Page functions with no
+ * NSID or RAE requirements
+ * @ctrl: Controller to query
+ * @lid: Log identifier
+ * @len: length of log buffer
+ * @log: pointer for resulting log data
+ *
+ * Performs a Get Log Page Admin command for a specific log ID @lid, using
+ * NVME_NSID_ALL for the namespace identifier, and rae set to false.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_simple(nvme_mi_ctrl_t ctrl,
+ enum nvme_cmd_get_log_lid lid,
+ __u32 len, void *log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, false, lid, NVME_NSID_ALL,
+ len, log);
+}
+
+/**
+ * nvme_mi_admin_get_log_supported_log_pages() - Retrieve nmve supported log
+ * pages
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @log: Array of LID supported and Effects data structures
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_supported_log_pages(nvme_mi_ctrl_t ctrl,
+ bool rae,
+ struct nvme_supported_log_pages *log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae,
+ NVME_LOG_LID_SUPPORTED_LOG_PAGES,
+ NVME_NSID_ALL, sizeof(*log), log);
+}
+
+/**
+ * nvme_mi_admin_get_log_error() - Retrieve nvme error log
+ * @ctrl: Controller to query
+ * @nr_entries: Number of error log entries allocated
+ * @rae: Retain asynchronous events
+ * @err_log: Array of error logs of size 'entries'
+ *
+ * This log page describes extended error information for a command that
+ * completed with error, or may report an error that is not specific to a
+ * particular command.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_error(nvme_mi_ctrl_t ctrl,
+ unsigned int nr_entries, bool rae,
+ struct nvme_error_log_page *err_log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_ERROR,
+ NVME_NSID_ALL, sizeof(*err_log) * nr_entries,
+ err_log);
+}
+
+/**
+ * nvme_mi_admin_get_log_smart() - Retrieve nvme smart log
+ * @ctrl: Controller to query
+ * @nsid: Optional namespace identifier
+ * @rae: Retain asynchronous events
+ * @smart_log: User address to store the smart log
+ *
+ * This log page provides SMART and general health information. The information
+ * provided is over the life of the controller and is retained across power
+ * cycles. To request the controller log page, the namespace identifier
+ * specified is FFFFFFFFh. The controller may also support requesting the log
+ * page on a per namespace basis, as indicated by bit 0 of the LPA field in the
+ * Identify Controller data structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_smart(nvme_mi_ctrl_t ctrl, __u32 nsid,
+ bool rae,
+ struct nvme_smart_log *smart_log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_SMART,
+ nsid, sizeof(*smart_log), smart_log);
+}
+
+/**
+ * nvme_mi_admin_get_log_fw_slot() - Retrieves the controller firmware log
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @fw_log: User address to store the log page
+ *
+ * This log page describes the firmware revision stored in each firmware slot
+ * supported. The firmware revision is indicated as an ASCII string. The log
+ * page also indicates the active slot number.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_fw_slot(nvme_mi_ctrl_t ctrl, bool rae,
+ struct nvme_firmware_slot *fw_log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_FW_SLOT,
+ NVME_NSID_ALL, sizeof(*fw_log), fw_log);
+}
+
+/**
+ * nvme_mi_admin_get_log_changed_ns_list() - Retrieve namespace changed list
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @ns_log: User address to store the log page
+ *
+ * This log page describes namespaces attached to this controller that have
+ * changed since the last time the namespace was identified, been added, or
+ * deleted.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_changed_ns_list(nvme_mi_ctrl_t ctrl,
+ bool rae,
+ struct nvme_ns_list *ns_log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_CHANGED_NS,
+ NVME_NSID_ALL, sizeof(*ns_log), ns_log);
+}
+
+/**
+ * nvme_mi_admin_get_log_cmd_effects() - Retrieve nvme command effects log
+ * @ctrl: Controller to query
+ * @csi: Command Set Identifier
+ * @effects_log: User address to store the effects log
+ *
+ * This log page describes the commands that the controller supports and the
+ * effects of those commands on the state of the NVM subsystem.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_cmd_effects(nvme_mi_ctrl_t ctrl,
+ enum nvme_csi csi,
+ struct nvme_cmd_effects_log *effects_log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = effects_log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_CMD_EFFECTS,
+ .len = sizeof(*effects_log),
+ .nsid = NVME_NSID_ALL,
+ .csi = csi,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_device_self_test() - Retrieve the device self test log
+ * @ctrl: Controller to query
+ * @log: Userspace address of the log payload
+ *
+ * The log page indicates the status of an in progress self test and the
+ * percent complete of that operation, and the results of the previous 20
+ * self-test operations.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_device_self_test(nvme_mi_ctrl_t ctrl,
+ struct nvme_self_test_log *log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, false,
+ NVME_LOG_LID_DEVICE_SELF_TEST,
+ NVME_NSID_ALL, sizeof(*log), log);
+}
+
+/**
+ * nvme_mi_admin_get_log_create_telemetry_host() - Create host telemetry log
+ * @ctrl: Controller to query
+ * @log: Userspace address of the log payload
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_create_telemetry_host(nvme_mi_ctrl_t ctrl,
+ struct nvme_telemetry_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_TELEMETRY_HOST,
+ .len = sizeof(*log),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_TELEM_HOST_LSP_CREATE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_telemetry_host() - Get Telemetry Host-Initiated log
+ * page
+ * @ctrl: Controller to query
+ * @offset: Offset into the telemetry data
+ * @len: Length of provided user buffer to hold the log data in bytes
+ * @log: User address for log page data
+ *
+ * Retrieves the Telemetry Host-Initiated log page at the requested offset
+ * using the previously existing capture.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_telemetry_host(nvme_mi_ctrl_t ctrl,
+ __u64 offset, __u32 len,
+ void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_TELEMETRY_HOST,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_TELEM_HOST_LSP_RETAIN,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_telemetry_ctrl() - Get Telemetry Controller-Initiated
+ * log page
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @offset: Offset into the telemetry data
+ * @len: Length of provided user buffer to hold the log data in bytes
+ * @log: User address for log page data
+ *
+ * Retrieves the Telemetry Controller-Initiated log page at the requested offset
+ * using the previously existing capture.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_telemetry_ctrl(nvme_mi_ctrl_t ctrl,
+ bool rae,
+ __u64 offset, __u32 len,
+ void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_TELEMETRY_CTRL,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_endurance_group() - Get Endurance Group log
+ * @ctrl: Controller to query
+ * @endgid: Starting group identifier to return in the list
+ * @log: User address to store the endurance log
+ *
+ * This log page indicates if an Endurance Group Event has occurred for a
+ * particular Endurance Group. If an Endurance Group Event has occurred, the
+ * details of the particular event are included in the Endurance Group
+ * Information log page for that Endurance Group. An asynchronous event is
+ * generated when an entry for an Endurance Group is newly added to this log
+ * page.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_endurance_group(nvme_mi_ctrl_t ctrl,
+ __u16 endgid,
+ struct nvme_endurance_group_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_ENDURANCE_GROUP,
+ .len = sizeof(*log),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = endgid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_predictable_lat_nvmset() - Predictable Latency Per NVM
+ * Set
+ * @ctrl: Controller to query
+ * @nvmsetid: NVM set id
+ * @log: User address to store the predictable latency log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_predictable_lat_nvmset(nvme_mi_ctrl_t ctrl,
+ __u16 nvmsetid,
+ struct nvme_nvmset_predictable_lat_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_PREDICTABLE_LAT_NVMSET,
+ .len = sizeof(*log),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = nvmsetid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_predictable_lat_event() - Retrieve Predictable Latency
+ * Event Aggregate Log Page
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @offset: Offset into the predictable latency event
+ * @len: Length of provided user buffer to hold the log data in bytes
+ * @log: User address for log page data
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_predictable_lat_event(nvme_mi_ctrl_t ctrl,
+ bool rae,
+ __u32 offset,
+ __u32 len,
+ void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_PREDICTABLE_LAT_AGG,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_ana() - Retrieve Asymmetric Namespace Access log page
+ * @ctrl: Controller to query
+ * @lsp: Log specific, see &enum nvme_get_log_ana_lsp
+ * @rae: Retain asynchronous events
+ * @offset: Offset to the start of the log page
+ * @len: The allocated length of the log page
+ * @log: User address to store the ana log
+ *
+ * This log consists of a header describing the log and descriptors containing
+ * the asymmetric namespace access information for ANA Groups that contain
+ * namespaces that are attached to the controller processing the command.
+ *
+ * See &struct nvme_ana_rsp_hdr for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_ana(nvme_mi_ctrl_t ctrl,
+ enum nvme_log_ana_lsp lsp, bool rae,
+ __u64 offset, __u32 len, void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_ANA,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = (__u8)lsp,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_ana_groups() - Retrieve Asymmetric Namespace Access
+ * groups only log page
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @len: The allocated length of the log page
+ * @log: User address to store the ana group log
+ *
+ * See &struct nvme_ana_group_desc for the definition of the returned structure.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_ana_groups(nvme_mi_ctrl_t ctrl,
+ bool rae, __u32 len,
+ struct nvme_ana_group_desc *log)
+{
+ return nvme_mi_admin_get_log_ana(ctrl, NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY, rae, 0,
+ len, log);
+}
+
+/**
+ * nvme_mi_admin_get_log_lba_status() - Retrieve LBA Status
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @offset: Offset to the start of the log page
+ * @len: The allocated length of the log page
+ * @log: User address to store the log page
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_lba_status(nvme_mi_ctrl_t ctrl, bool rae,
+ __u64 offset, __u32 len,
+ void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_LBA_STATUS,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_endurance_grp_evt() - Retrieve Rotational Media
+ * Information
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @offset: Offset to the start of the log page
+ * @len: The allocated length of the log page
+ * @log: User address to store the log page
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_endurance_grp_evt(nvme_mi_ctrl_t ctrl,
+ bool rae,
+ __u32 offset,
+ __u32 len,
+ void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_ENDURANCE_GRP_EVT,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_fid_supported_effects() - Retrieve Feature Identifiers
+ * Supported and Effects
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @log: FID Supported and Effects data structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_fid_supported_effects(nvme_mi_ctrl_t ctrl,
+ bool rae,
+ struct nvme_fid_supported_effects_log *log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae,
+ NVME_LOG_LID_FID_SUPPORTED_EFFECTS,
+ NVME_NSID_NONE, sizeof(*log), log);
+}
+
+/**
+ * nvme_mi_admin_get_log_mi_cmd_supported_effects() - displays the MI Commands
+ * Supported by the controller
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @log: MI Command Supported and Effects data structure
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_mi_cmd_supported_effects(nvme_mi_ctrl_t ctrl,
+ bool rae,
+ struct nvme_mi_cmd_supported_effects_log *log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS,
+ NVME_NSID_NONE, sizeof(*log), log);
+}
+
+/**
+ * nvme_mi_admin_get_log_boot_partition() - Retrieve Boot Partition
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @lsp: The log specified field of LID
+ * @len: The allocated size, minimum
+ * struct nvme_boot_partition
+ * @part: User address to store the log page
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_boot_partition(nvme_mi_ctrl_t ctrl,
+ bool rae, __u8 lsp,
+ __u32 len,
+ struct nvme_boot_partition *part)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = part,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_BOOT_PARTITION,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_phy_rx_eom() - Retrieve Physical Interface Receiver Eye Opening Measurement Log
+ * @ctrl: Controller to query
+ * @lsp: Log specific, controls action and measurement quality
+ * @controller: Target controller ID
+ * @len: The allocated size, minimum
+ * struct nvme_phy_rx_eom_log
+ * @log: User address to store the log page
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise
+ */
+static inline int nvme_mi_admin_get_log_phy_rx_eom(nvme_mi_ctrl_t ctrl,
+ __u8 lsp, __u16 controller,
+ __u32 len,
+ struct nvme_phy_rx_eom_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_PHY_RX_EOM,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = controller,
+ .lsp = lsp,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_discovery() - Retrieve Discovery log page
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @offset: Offset of this log to retrieve
+ * @len: The allocated size for this portion of the log
+ * @log: User address to store the discovery log
+ *
+ * Supported only by fabrics discovery controllers, returning discovery
+ * records.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_discovery(nvme_mi_ctrl_t ctrl, bool rae,
+ __u32 offset, __u32 len,
+ void *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_DISCOVER,
+ .len = len,
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_media_unit_stat() - Retrieve Media Unit Status
+ * @ctrl: Controller to query
+ * @domid: Domain Identifier selection, if supported
+ * @mus: User address to store the Media Unit statistics log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_media_unit_stat(nvme_mi_ctrl_t ctrl,
+ __u16 domid,
+ struct nvme_media_unit_stat_log *mus)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = mus,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_MEDIA_UNIT_STATUS,
+ .len = sizeof(*mus),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = domid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_support_cap_config_list() - Retrieve Supported
+ * Capacity Configuration List
+ * @ctrl: Controller to query
+ * @domid: Domain Identifier selection, if supported
+ * @cap: User address to store supported capabilities config list
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_support_cap_config_list(nvme_mi_ctrl_t ctrl,
+ __u16 domid,
+ struct nvme_supported_cap_config_list_log *cap)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = cap,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST,
+ .len = sizeof(*cap),
+ .nsid = NVME_NSID_NONE,
+ .csi = NVME_CSI_NVM,
+ .lsi = domid,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_reservation() - Retrieve Reservation Notification
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @log: User address to store the reservation log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_reservation(nvme_mi_ctrl_t ctrl,
+ bool rae,
+ struct nvme_resv_notification_log *log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_RESERVATION,
+ NVME_NSID_ALL, sizeof(*log), log);
+}
+
+/**
+ * nvme_mi_admin_get_log_sanitize() - Retrieve Sanitize Status
+ * @ctrl: Controller to query
+ * @rae: Retain asynchronous events
+ * @log: User address to store the sanitize log
+ *
+ * The Sanitize Status log page reports sanitize operation time estimates and
+ * information about the most recent sanitize operation.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_sanitize(nvme_mi_ctrl_t ctrl, bool rae,
+ struct nvme_sanitize_log_page *log)
+{
+ return nvme_mi_admin_get_nsid_log(ctrl, rae, NVME_LOG_LID_SANITIZE,
+ NVME_NSID_ALL, sizeof(*log), log);
+}
+
+/**
+ * nvme_mi_admin_get_log_zns_changed_zones() - Retrieve list of zones that have
+ * changed
+ * @ctrl: Controller to query
+ * @nsid: Namespace ID
+ * @rae: Retain asynchronous events
+ * @log: User address to store the changed zone log
+ *
+ * The list of zones that have changed state due to an exceptional event.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_zns_changed_zones(nvme_mi_ctrl_t ctrl,
+ __u32 nsid, bool rae,
+ struct nvme_zns_changed_zone_log *log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_ZNS_CHANGED_ZONES,
+ .len = sizeof(*log),
+ .nsid = nsid,
+ .csi = NVME_CSI_ZNS,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = rae,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_persistent_event() - Retrieve Persistent Event Log
+ * @ctrl: Controller to query
+ * @action: Action the controller should take during processing this command
+ * @size: Size of @pevent_log
+ * @pevent_log: User address to store the persistent event log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_persistent_event(nvme_mi_ctrl_t ctrl,
+ enum nvme_pevent_log_action action,
+ __u32 size, void *pevent_log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = pevent_log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_PERSISTENT_EVENT,
+ .len = size,
+ .nsid = NVME_NSID_ALL,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = (__u8)action,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_security_send() - Perform a Security Send command on a
+ * controller.
+ * @ctrl: Controller to send command to
+ * @args: Security Send command arguments
+ *
+ * Performs a Security Send Admin command as specified by @args. Response data
+ * is stored in @args->data, which should be a buffer of @args->data_len bytes.
+ * Resulting data length is stored in @args->data_len on successful
+ * command completion.
+ *
+ * Security Send data length should not be greater than 4096 bytes to
+ * comply with specification limits.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_get_log_args
+ */
+int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl,
+ struct nvme_security_send_args *args);
+
+/**
+ * nvme_mi_admin_security_recv() - Perform a Security Receive command on a
+ * controller.
+ * @ctrl: Controller to send command to
+ * @args: Security Receive command arguments
+ *
+ * Performs a Security Receive Admin command as specified by @args. Response
+ * data is stored in @args->data, which should be a buffer of @args->data_len
+ * bytes. Resulting data length is stored in @args->data_len on successful
+ * command completion.
+ *
+ * Security Receive data length should not be greater than 4096 bytes to
+ * comply with specification limits.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_get_log_args
+ */
+int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl,
+ struct nvme_security_receive_args *args);
+
+/**
+ * nvme_mi_admin_get_features - Perform a Get Feature command on a controller
+ * @ctrl: Controller to send command to
+ * @args: Get Features command arguments
+ *
+ * Performs a Get Features Admin command as specified by @args. Returned
+ * feature data will be stored in @args->result and @args->data, depending
+ * on the specification of the feature itself; most features do not return
+ * additional data. See section 5.27.1 of the NVMe spec (v2.0b) for
+ * feature-specific information.
+ *
+ * On success, @args->data_len will be updated with the actual data length
+ * received.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_mi_admin_get_features(nvme_mi_ctrl_t ctrl,
+ struct nvme_get_features_args *args);
+
+/**
+ * nvme_mi_admin_get_features_data() - Helper function for &nvme_mi_admin_get_features()
+ * @ctrl: Controller to send command to
+ * @fid: Feature identifier
+ * @nsid: Namespace ID, if applicable for @fid
+ * @data_len: Length of feature data, if applicable for @fid, in bytes
+ * @data: User address of feature data, if applicable
+ * @result: The command completion result from CQE dword0
+ *
+ * Helper for optionally features that optionally return data, using the
+ * SEL_CURRENT selector value.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_features_data(nvme_mi_ctrl_t ctrl,
+ enum nvme_features_id fid,
+ __u32 nsid, __u32 data_len,
+ void *data, __u32 *result)
+{
+ struct nvme_get_features_args args = {
+ .result = result,
+ .data = data,
+ .args_size = sizeof(args),
+ .nsid = nsid,
+ .sel = NVME_GET_FEATURES_SEL_CURRENT,
+ .cdw11 = 0,
+ .data_len = data_len,
+ .fid = (__u8)fid,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_get_features(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_features_simple - Get a simple feature value with no data
+ * @ctrl: Controller to send command to
+ * @fid: Feature identifier
+ * @nsid: Namespace id, if required by @fid
+ * @result: output feature data
+ */
+static inline int nvme_mi_admin_get_features_simple(nvme_mi_ctrl_t ctrl,
+ enum nvme_features_id fid,
+ __u32 nsid,
+ __u32 *result)
+{
+ return nvme_mi_admin_get_features_data(ctrl, fid, nsid,
+ 0, NULL, result);
+}
+
+/**
+ * nvme_mi_admin_set_features - Perform a Set Features command on a controller
+ * @ctrl: Controller to send command to
+ * @args: Set Features command arguments
+ *
+ * Performs a Set Features Admin command as specified by @args. Result
+ * data will be stored in @args->result.
+ * on the specification of the feature itself; most features do not return
+ * additional data. See section 5.27.1 of the NVMe spec (v2.0b) for
+ * feature-specific information.
+ *
+ * On success, @args->data_len will be updated with the actual data length
+ * received.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_mi_admin_set_features(nvme_mi_ctrl_t ctrl,
+ struct nvme_set_features_args *args);
+
+/**
+ * nvme_mi_admin_ns_mgmt - Issue a Namespace Management command
+ * @ctrl: Controller to send command to
+ * @args: Namespace management command arguments
+ *
+ * Issues a Namespace Management command to @ctrl, with arguments specified
+ * from @args.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl,
+ struct nvme_ns_mgmt_args *args);
+
+/**
+ * nvme_mi_admin_ns_mgmt_create - Helper for Namespace Management Create command
+ * @ctrl: Controller to send command to
+ * @ns: New namespace parameters
+ * @csi: Command Set Identifier for new NS
+ * @nsid: Set to new namespace ID on create
+ * @data: Host Software Specified Fields that defines ns creation parameters
+ *
+ * Issues a Namespace Management (Create) command to @ctrl, to create a
+ * new namespace specified by @ns, using command set @csi. On success,
+ * the new namespace ID will be written to @nsid.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_ns_mgmt_create(nvme_mi_ctrl_t ctrl,
+ struct nvme_id_ns *ns, __u8 csi, __u32 *nsid,
+ struct nvme_ns_mgmt_host_sw_specified *data)
+{
+ struct nvme_ns_mgmt_args args = {
+ .result = nsid,
+ .ns = ns,
+ .args_size = sizeof(args),
+ .nsid = NVME_NSID_NONE,
+ .sel = NVME_NS_MGMT_SEL_CREATE,
+ .csi = csi,
+ .data = data,
+ };
+
+ return nvme_mi_admin_ns_mgmt(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_ns_mgmt_delete - Helper for Namespace Management Delete command
+ * @ctrl: Controller to send command to
+ * @nsid: Namespace ID to delete
+ *
+ * Issues a Namespace Management (Delete) command to @ctrl, to delete the
+ * namespace with id @nsid.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_ns_mgmt_delete(nvme_mi_ctrl_t ctrl, __u32 nsid)
+{
+ struct nvme_ns_mgmt_args args = {
+ .args_size = sizeof(args),
+ .nsid = nsid,
+ .sel = NVME_NS_MGMT_SEL_DELETE,
+ };
+
+ return nvme_mi_admin_ns_mgmt(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_ns_attach() - Attach or detach namespace to controller(s)
+ * @ctrl: Controller to send command to
+ * @args: Namespace Attach command arguments
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl,
+ struct nvme_ns_attach_args *args);
+
+/**
+ * nvme_mi_admin_ns_attach_ctrls() - Attach namespace to controllers
+ * @ctrl: Controller to send command to
+ * @nsid: Namespace ID to attach
+ * @ctrlist: Controller list to modify attachment state of nsid
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_ns_attach_ctrls(nvme_mi_ctrl_t ctrl, __u32 nsid,
+ struct nvme_ctrl_list *ctrlist)
+{
+ struct nvme_ns_attach_args args = {
+ .result = NULL,
+ .ctrlist = ctrlist,
+ .args_size = sizeof(args),
+ .nsid = nsid,
+ .sel = NVME_NS_ATTACH_SEL_CTRL_ATTACH,
+ };
+
+ return nvme_mi_admin_ns_attach(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_ns_detach_ctrls() - Detach namespace from controllers
+ * @ctrl: Controller to send command to
+ * @nsid: Namespace ID to detach
+ * @ctrlist: Controller list to modify attachment state of nsid
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_ns_detach_ctrls(nvme_mi_ctrl_t ctrl, __u32 nsid,
+ struct nvme_ctrl_list *ctrlist)
+{
+ struct nvme_ns_attach_args args = {
+ .result = NULL,
+ .ctrlist = ctrlist,
+ .args_size = sizeof(args),
+ .nsid = nsid,
+ .sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH,
+ };
+
+ return nvme_mi_admin_ns_attach(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_fw_download() - Download part or all of a firmware image to
+ * the controller
+ * @ctrl: Controller to send firmware data to
+ * @args: &struct nvme_fw_download_args argument structure
+ *
+ * The Firmware Image Download command downloads all or a portion of an image
+ * for a future update to the controller. The Firmware Image Download command
+ * downloads a new image (in whole or in part) to the controller.
+ *
+ * The image may be constructed of multiple pieces that are individually
+ * downloaded with separate Firmware Image Download commands. Each Firmware
+ * Image Download command includes a Dword Offset and Number of Dwords that
+ * specify a dword range.
+ *
+ * The new firmware image is not activated as part of the Firmware Image
+ * Download command. Use the nvme_mi_admin_fw_commit() to activate a newly
+ * downloaded image.
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+int nvme_mi_admin_fw_download(nvme_mi_ctrl_t ctrl,
+ struct nvme_fw_download_args *args);
+
+/**
+ * nvme_mi_admin_fw_commit() - Commit firmware using the specified action
+ * @ctrl: Controller to send firmware data to
+ * @args: &struct nvme_fw_download_args argument structure
+ *
+ * The Firmware Commit command modifies the firmware image or Boot Partitions.
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+int nvme_mi_admin_fw_commit(nvme_mi_ctrl_t ctrl,
+ struct nvme_fw_commit_args *args);
+
+/**
+ * nvme_mi_admin_format_nvm() - Format NVMe namespace
+ * @ctrl: Controller to send command to
+ * @args: Format NVM command arguments
+ *
+ * Perform a low-level format to set the LBA data & metadata size. May destroy
+ * data & metadata on the specified namespaces
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl,
+ struct nvme_format_nvm_args *args);
+
+/**
+ * nvme_mi_admin_sanitize_nvm() - Start a subsystem Sanitize operation
+ * @ctrl: Controller to send command to
+ * @args: Sanitize command arguments
+ *
+ * A sanitize operation alters all user data in the NVM subsystem such that
+ * recovery of any previous user data from any cache, the non-volatile media,
+ * or any Controller Memory Buffer is not possible.
+ *
+ * The Sanitize command starts a sanitize operation or to recover from a
+ * previously failed sanitize operation. The sanitize operation types that may
+ * be supported are Block Erase, Crypto Erase, and Overwrite. All sanitize
+ * operations are processed in the background, i.e., completion of the sanitize
+ * command does not indicate completion of the sanitize operation.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+int nvme_mi_admin_sanitize_nvm(nvme_mi_ctrl_t ctrl,
+ struct nvme_sanitize_nvm_args *args);
+
+#endif /* _LIBNVME_MI_MI_H */
diff --git a/src/nvme/nbft.c b/src/nvme/nbft.c
new file mode 100644
index 0000000..f2ffc21
--- /dev/null
+++ b/src/nvme/nbft.c
@@ -0,0 +1,754 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021-2022, Dell Inc. or its subsidiaries. All Rights Reserved.
+ *
+ * Authors: Stuart Hayes <Stuart_Hayes@Dell.com>
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <arpa/inet.h>
+#include <ccan/endian/endian.h>
+
+#include "private.h"
+#include "nbft.h"
+#include "log.h"
+
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+static __u8 csum(const __u8 *buffer, ssize_t length)
+{
+ int n;
+ __u8 sum = 0;
+
+ for (n = 0; n < length; n++)
+ sum = (__u8)(sum + ((__u8 *)buffer)[n]);
+ return sum;
+}
+
+static void format_ip_addr(char *buf, size_t buflen, __u8 *addr)
+{
+ struct in6_addr addr_ipv6;
+
+ memcpy(&addr_ipv6, addr, sizeof(addr_ipv6));
+ if (IN6_IS_ADDR_V4MAPPED(&addr_ipv6))
+ /* ipv4 */
+ inet_ntop(AF_INET, &addr_ipv6.s6_addr32[3], buf, buflen);
+ else
+ /* ipv6 */
+ inet_ntop(AF_INET6, &addr_ipv6, buf, buflen);
+}
+
+static bool in_heap(struct nbft_header *header, struct nbft_heap_obj obj)
+{
+ if (le16_to_cpu(obj.length) == 0)
+ return true;
+ if (le32_to_cpu(obj.offset) < le32_to_cpu(header->heap_offset))
+ return false;
+ if (le32_to_cpu(obj.offset) >
+ le32_to_cpu(header->heap_offset) + le32_to_cpu(header->heap_length))
+ return false;
+ if (le32_to_cpu(obj.offset) + le16_to_cpu(obj.length) >
+ le32_to_cpu(header->heap_offset) + le32_to_cpu(header->heap_length))
+ return false;
+ return true;
+}
+
+/*
+ * Return transport_type string (NBFT Table 2)
+ */
+static char *trtype_to_string(__u8 transport_type)
+{
+ switch (transport_type) {
+ case 3:
+ return "tcp";
+ default:
+ return "invalid";
+ }
+}
+
+#define verify(condition, message) \
+ do { \
+ if (!(condition)) { \
+ nvme_msg(NULL, LOG_DEBUG, "file %s: " message "\n", \
+ nbft->filename); \
+ return -EINVAL; \
+ } \
+ } while (0)
+
+static int __get_heap_obj(struct nbft_header *header, const char *filename,
+ const char *descriptorname, const char *fieldname,
+ struct nbft_heap_obj obj, bool is_string,
+ char **output)
+{
+ if (le16_to_cpu(obj.length) == 0)
+ return -ENOENT;
+
+ if (!in_heap(header, obj)) {
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: field '%s' in descriptor '%s' has invalid offset or length\n",
+ filename, fieldname, descriptorname);
+ return -EINVAL;
+ }
+
+ /* check that string is zero terminated correctly */
+ *output = (char *)header + le32_to_cpu(obj.offset);
+
+ if (is_string) {
+ if (strnlen(*output, le16_to_cpu(obj.length) + 1) < le16_to_cpu(obj.length))
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: string '%s' in descriptor '%s' is shorter (%zd) than specified length (%d)\n",
+ filename, fieldname, descriptorname,
+ strnlen(*output, le16_to_cpu(obj.length) + 1),
+ le16_to_cpu(obj.length));
+ else if (strnlen(*output, le16_to_cpu(obj.length) + 1) >
+ le16_to_cpu(obj.length)) {
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: string '%s' in descriptor '%s' is not zero terminated\n",
+ filename, fieldname, descriptorname);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+#define get_heap_obj(descriptor, obj, is_string, output) \
+ __get_heap_obj(header, nbft->filename, \
+ stringify(descriptor), stringify(obj), \
+ descriptor->obj, is_string, \
+ output)
+
+static struct nbft_info_discovery *discovery_from_index(struct nbft_info *nbft, int i)
+{
+ struct nbft_info_discovery **d;
+
+ for (d = nbft->discovery_list; d && *d; d++) {
+ if ((*d)->index == i)
+ return *d;
+ }
+ return NULL;
+}
+
+static struct nbft_info_hfi *hfi_from_index(struct nbft_info *nbft, int i)
+{
+ struct nbft_info_hfi **h;
+
+ for (h = nbft->hfi_list; h && *h; h++) {
+ if ((*h)->index == i)
+ return *h;
+ }
+ return NULL;
+}
+
+static struct nbft_info_security *security_from_index(struct nbft_info *nbft, int i)
+{
+ struct nbft_info_security **s;
+
+ for (s = nbft->security_list; s && *s; s++) {
+ if ((*s)->index == i)
+ return *s;
+ }
+ return NULL;
+}
+
+static int read_ssns_exended_info(struct nbft_info *nbft,
+ struct nbft_info_subsystem_ns *ssns,
+ struct nbft_ssns_ext_info *raw_ssns_ei)
+{
+ struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft;
+
+ verify(raw_ssns_ei->structure_id == NBFT_DESC_SSNS_EXT_INFO,
+ "invalid ID in SSNS extended info descriptor");
+ verify(raw_ssns_ei->version == 1,
+ "invalid version in SSNS extended info descriptor");
+ verify(le16_to_cpu(raw_ssns_ei->ssns_index) == ssns->index,
+ "SSNS index doesn't match extended info descriptor index");
+
+ if (!(le32_to_cpu(raw_ssns_ei->flags) & NBFT_SSNS_EXT_INFO_VALID))
+ return -EINVAL;
+
+ if (le32_to_cpu(raw_ssns_ei->flags) & NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ)
+ ssns->asqsz = le16_to_cpu(raw_ssns_ei->asqsz);
+ ssns->controller_id = le16_to_cpu(raw_ssns_ei->cntlid);
+ get_heap_obj(raw_ssns_ei, dhcp_root_path_str_obj, 1, &ssns->dhcp_root_path_string);
+
+ return 0;
+}
+
+static int read_ssns(struct nbft_info *nbft,
+ struct nbft_ssns *raw_ssns,
+ struct nbft_info_subsystem_ns **s)
+{
+ struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft;
+ struct nbft_info_subsystem_ns *ssns;
+ __u8 *ss_hfi_indexes = NULL;
+ __u8 *tmp = NULL;
+ int i, ret;
+
+ if (!(le16_to_cpu(raw_ssns->flags) & NBFT_SSNS_VALID))
+ return -EINVAL;
+
+ verify(raw_ssns->structure_id == NBFT_DESC_SSNS,
+ "invalid ID in SSNS descriptor");
+
+ /* verify transport type */
+ verify(raw_ssns->trtype == NBFT_TRTYPE_TCP,
+ "invalid transport type in SSNS descriptor");
+
+ ssns = calloc(1, sizeof(*ssns));
+ if (!ssns)
+ return -ENOMEM;
+
+ ssns->index = le16_to_cpu(raw_ssns->index);
+ strncpy(ssns->transport, trtype_to_string(raw_ssns->trtype), sizeof(ssns->transport));
+
+ /* transport specific flags */
+ if (raw_ssns->trtype == NBFT_TRTYPE_TCP) {
+ if (le16_to_cpu(raw_ssns->trflags) & NBFT_SSNS_PDU_HEADER_DIGEST)
+ ssns->pdu_header_digest_required = true;
+ if (le16_to_cpu(raw_ssns->trflags) & NBFT_SSNS_DATA_DIGEST)
+ ssns->data_digest_required = true;
+ }
+
+ /* primary discovery controller */
+ if (raw_ssns->primary_discovery_ctrl_index) {
+ ssns->discovery = discovery_from_index(nbft,
+ raw_ssns->primary_discovery_ctrl_index);
+ if (!ssns->discovery)
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: namespace %d discovery controller not found\n",
+ nbft->filename, ssns->index);
+ }
+
+ /* subsystem transport address */
+ ret = get_heap_obj(raw_ssns, subsys_traddr_obj, 0, (char **)&tmp);
+ if (ret)
+ goto fail;
+
+ format_ip_addr(ssns->traddr, sizeof(ssns->traddr), tmp);
+
+ /* subsystem transport service identifier */
+ ret = get_heap_obj(raw_ssns, subsys_trsvcid_obj, 1, &ssns->trsvcid);
+ if (ret)
+ goto fail;
+
+ /* subsystem port ID */
+ ssns->subsys_port_id = le16_to_cpu(raw_ssns->subsys_port_id);
+
+ /* NSID, NID type, & NID */
+ ssns->nsid = le32_to_cpu(raw_ssns->nsid);
+ ssns->nid_type = raw_ssns->nidt;
+ ssns->nid = raw_ssns->nid;
+
+ /* security profile */
+ if (raw_ssns->security_desc_index) {
+ ssns->security = security_from_index(nbft, raw_ssns->security_desc_index);
+ if (!ssns->security)
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: namespace %d security controller not found\n",
+ nbft->filename, ssns->index);
+ }
+
+ /* HFI descriptors */
+ ret = get_heap_obj(raw_ssns, secondary_hfi_assoc_obj, 0, (char **)&ss_hfi_indexes);
+ if (ret)
+ goto fail;
+
+ ssns->hfis = calloc(le16_to_cpu(raw_ssns->secondary_hfi_assoc_obj.length) + 2,
+ sizeof(*ssns->hfis));
+ if (!ssns->hfis) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ ssns->hfis[0] = hfi_from_index(nbft, raw_ssns->primary_hfi_desc_index);
+ if (!ssns->hfis[0]) {
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: SSNS %d: HFI %d not found\n",
+ nbft->filename, ssns->index, raw_ssns->primary_hfi_desc_index);
+ ret = -EINVAL;
+ goto fail;
+ }
+ ssns->num_hfis = 1;
+ for (i = 0; i < le16_to_cpu(raw_ssns->secondary_hfi_assoc_obj.length); i++) {
+ bool duplicate = false;
+ int j;
+
+ for (j = 0; j < i; j++) {
+ if (ss_hfi_indexes[i] == ss_hfi_indexes[j]) {
+ duplicate = true;
+ break;
+ }
+ }
+
+ if (!duplicate &&
+ ss_hfi_indexes[i] == raw_ssns->primary_hfi_desc_index)
+ duplicate = true;
+
+ if (duplicate) {
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: SSNS %d skipping duplicate HFI index %d\n",
+ nbft->filename, ssns->index, ss_hfi_indexes[i]);
+ continue;
+ }
+
+ ssns->hfis[i + 1] = hfi_from_index(nbft, ss_hfi_indexes[i]);
+ if (ss_hfi_indexes[i] && !ssns->hfis[i + 1])
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: SSNS %d HFI %d not found\n",
+ nbft->filename, ssns->index, ss_hfi_indexes[i]);
+ else
+ ssns->num_hfis++;
+ }
+
+ /* SSNS NQN */
+ ret = get_heap_obj(raw_ssns, subsys_ns_nqn_obj, 1, &ssns->subsys_nqn);
+ if (ret)
+ goto fail;
+
+ /* SSNS extended info */
+ if (le16_to_cpu(raw_ssns->flags) & NBFT_SSNS_EXTENDED_INFO_IN_USE) {
+ struct nbft_ssns_ext_info *ssns_extended_info;
+
+ if (!get_heap_obj(raw_ssns, ssns_extended_info_desc_obj, 0,
+ (char **)&ssns_extended_info))
+ read_ssns_exended_info(nbft, ssns, ssns_extended_info);
+ }
+
+ *s = ssns;
+ return 0;
+
+fail:
+ free(ssns);
+ return ret;
+}
+
+static int read_hfi_info_tcp(struct nbft_info *nbft,
+ struct nbft_hfi_info_tcp *raw_hfi_info_tcp,
+ struct nbft_info_hfi *hfi)
+{
+ struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft;
+
+ if ((raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_VALID) == 0)
+ return -EINVAL;
+
+ verify(raw_hfi_info_tcp->structure_id == NBFT_DESC_HFI_TRINFO,
+ "invalid ID in HFI transport descriptor");
+ verify(raw_hfi_info_tcp->version == 1,
+ "invalid version in HFI transport descriptor");
+ if (le16_to_cpu(raw_hfi_info_tcp->hfi_index) != hfi->index)
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: HFI descriptor index %d does not match index in HFI transport descriptor\n",
+ nbft->filename, hfi->index);
+
+ hfi->tcp_info.pci_sbdf = le32_to_cpu(raw_hfi_info_tcp->pci_sbdf);
+ memcpy(hfi->tcp_info.mac_addr, raw_hfi_info_tcp->mac_addr,
+ sizeof(raw_hfi_info_tcp->mac_addr));
+ hfi->tcp_info.vlan = le16_to_cpu(raw_hfi_info_tcp->vlan);
+ hfi->tcp_info.ip_origin = raw_hfi_info_tcp->ip_origin;
+ format_ip_addr(hfi->tcp_info.ipaddr, sizeof(hfi->tcp_info.ipaddr),
+ raw_hfi_info_tcp->ip_address);
+ hfi->tcp_info.subnet_mask_prefix = raw_hfi_info_tcp->subnet_mask_prefix;
+ format_ip_addr(hfi->tcp_info.gateway_ipaddr, sizeof(hfi->tcp_info.ipaddr),
+ raw_hfi_info_tcp->ip_gateway);
+ hfi->tcp_info.route_metric = le16_to_cpu(raw_hfi_info_tcp->route_metric);
+ format_ip_addr(hfi->tcp_info.primary_dns_ipaddr,
+ sizeof(hfi->tcp_info.primary_dns_ipaddr),
+ raw_hfi_info_tcp->primary_dns);
+ format_ip_addr(hfi->tcp_info.secondary_dns_ipaddr,
+ sizeof(hfi->tcp_info.secondary_dns_ipaddr),
+ raw_hfi_info_tcp->secondary_dns);
+ if (raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_DHCP_OVERRIDE) {
+ hfi->tcp_info.dhcp_override = true;
+ format_ip_addr(hfi->tcp_info.dhcp_server_ipaddr,
+ sizeof(hfi->tcp_info.dhcp_server_ipaddr),
+ raw_hfi_info_tcp->dhcp_server);
+ }
+ get_heap_obj(raw_hfi_info_tcp, host_name_obj, 1, &hfi->tcp_info.host_name);
+ if (raw_hfi_info_tcp->flags & NBFT_HFI_INFO_TCP_GLOBAL_ROUTE)
+ hfi->tcp_info.this_hfi_is_default_route = true;
+
+ return 0;
+}
+
+static int read_hfi(struct nbft_info *nbft,
+ struct nbft_hfi *raw_hfi,
+ struct nbft_info_hfi **h)
+{
+ int ret;
+ struct nbft_info_hfi *hfi;
+ struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft;
+
+ if (!(raw_hfi->flags & NBFT_HFI_VALID))
+ return -EINVAL;
+
+ verify(raw_hfi->structure_id == NBFT_DESC_HFI,
+ "invalid ID in HFI descriptor");
+
+ hfi = calloc(1, sizeof(struct nbft_info_hfi));
+ if (!hfi)
+ return -ENOMEM;
+
+ hfi->index = raw_hfi->index;
+
+ /*
+ * read HFI transport descriptor for this HFI
+ */
+ if (raw_hfi->trtype == NBFT_TRTYPE_TCP) {
+ /* TCP */
+ struct nbft_hfi_info_tcp *raw_hfi_info_tcp;
+
+ strncpy(hfi->transport, trtype_to_string(raw_hfi->trtype),
+ sizeof(hfi->transport));
+
+ ret = get_heap_obj(raw_hfi, trinfo_obj, 0, (char **)&raw_hfi_info_tcp);
+ if (ret)
+ goto fail;
+
+ ret = read_hfi_info_tcp(nbft, raw_hfi_info_tcp, hfi);
+ if (ret)
+ goto fail;
+ } else {
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: invalid transport type %d\n",
+ nbft->filename, raw_hfi->trtype);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ *h = hfi;
+ return 0;
+
+fail:
+ free(hfi);
+ return ret;
+}
+
+static int read_discovery(struct nbft_info *nbft,
+ struct nbft_discovery *raw_discovery,
+ struct nbft_info_discovery **d)
+{
+ struct nbft_info_discovery *discovery = NULL;
+ struct nbft_header *header = (struct nbft_header *)nbft->raw_nbft;
+ int r = -EINVAL;
+
+ if (!(raw_discovery->flags & NBFT_DISCOVERY_VALID))
+ goto error;
+
+ verify(raw_discovery->structure_id == NBFT_DESC_DISCOVERY,
+ "invalid ID in discovery descriptor");
+
+ discovery = calloc(1, sizeof(struct nbft_info_discovery));
+ if (!discovery) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ discovery->index = raw_discovery->index;
+
+ if (get_heap_obj(raw_discovery, discovery_ctrl_addr_obj, 1, &discovery->uri))
+ goto error;
+
+ if (get_heap_obj(raw_discovery, discovery_ctrl_nqn_obj, 1, &discovery->nqn))
+ goto error;
+
+ discovery->hfi = hfi_from_index(nbft, raw_discovery->hfi_index);
+ if (raw_discovery->hfi_index && !discovery->hfi)
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: discovery %d HFI not found\n",
+ nbft->filename, discovery->index);
+
+ discovery->security = security_from_index(nbft, raw_discovery->sec_index);
+ if (raw_discovery->sec_index && !discovery->security)
+ nvme_msg(NULL, LOG_DEBUG,
+ "file %s: discovery %d security descriptor not found\n",
+ nbft->filename, discovery->index);
+
+ *d = discovery;
+ r = 0;
+
+error:
+ if (r)
+ free(discovery);
+ return r;
+}
+
+static int read_security(struct nbft_info *nbft,
+ struct nbft_security *raw_security,
+ struct nbft_info_security **s)
+{
+ return -EINVAL;
+}
+
+static void read_hfi_descriptors(struct nbft_info *nbft, int num_hfi,
+ struct nbft_hfi *raw_hfi_array, int hfi_len)
+{
+ int i, cnt;
+
+ nbft->hfi_list = calloc(num_hfi + 1, sizeof(struct nbft_info_hfi));
+ for (i = 0, cnt = 0; i < num_hfi; i++) {
+ if (read_hfi(nbft, &raw_hfi_array[i], &nbft->hfi_list[cnt]) == 0)
+ cnt++;
+ }
+}
+
+static void read_security_descriptors(struct nbft_info *nbft, int num_sec,
+ struct nbft_security *raw_sec_array, int sec_len)
+{
+ int i, cnt;
+
+ nbft->security_list = calloc(num_sec + 1, sizeof(struct nbft_info_security));
+ for (i = 0, cnt = 0; i < num_sec; i++) {
+ if (read_security(nbft, &raw_sec_array[i], &nbft->security_list[cnt]) == 0)
+ cnt++;
+ }
+}
+
+static void read_discovery_descriptors(struct nbft_info *nbft, int num_disc,
+ struct nbft_discovery *raw_disc_array, int disc_len)
+{
+ int i, cnt;
+
+ nbft->discovery_list = calloc(num_disc + 1, sizeof(struct nbft_info_discovery));
+ for (i = 0, cnt = 0; i < num_disc; i++) {
+ if (read_discovery(nbft, &raw_disc_array[i], &nbft->discovery_list[cnt]) == 0)
+ cnt++;
+ }
+}
+
+static void read_ssns_descriptors(struct nbft_info *nbft, int num_ssns,
+ struct nbft_ssns *raw_ssns_array, int ssns_len)
+{
+ int i, cnt;
+
+ nbft->subsystem_ns_list = calloc(num_ssns + 1, sizeof(struct nbft_info_subsystem_ns));
+ for (i = 0, cnt = 0; i < num_ssns; i++) {
+ if (read_ssns(nbft, &raw_ssns_array[i], &nbft->subsystem_ns_list[cnt]) == 0)
+ cnt++;
+ }
+}
+
+/**
+ * parse_raw_nbft - parses raw ACPI NBFT table and fill in abstracted nbft_info structure
+ * @nbft: nbft_info struct containing only raw_nbft and raw_nbft_size
+ *
+ * Returns 0 on success, errno otherwise.
+ */
+static int parse_raw_nbft(struct nbft_info *nbft)
+{
+ __u8 *raw_nbft = nbft->raw_nbft;
+ int raw_nbft_size = nbft->raw_nbft_size;
+
+ struct nbft_header *header;
+ struct nbft_control *control;
+ struct nbft_host *host;
+
+ verify(raw_nbft_size >= sizeof(struct nbft_header) + sizeof(struct nbft_control),
+ "table is too short");
+ verify(csum(raw_nbft, raw_nbft_size) == 0, "invalid checksum");
+
+ /*
+ * header
+ */
+ header = (struct nbft_header *)raw_nbft;
+
+ verify(strncmp(header->signature, NBFT_HEADER_SIG, 4) == 0, "invalid signature");
+ verify(le32_to_cpu(header->length) <= raw_nbft_size, "length in header exceeds table length");
+ verify(header->major_revision == 1, "unsupported major revision");
+ verify(header->minor_revision == 0, "unsupported minor revision");
+ verify(le32_to_cpu(header->heap_length) + le32_to_cpu(header->heap_offset) <=
+ le32_to_cpu(header->length), "heap exceeds table length");
+
+ /*
+ * control
+ */
+ control = (struct nbft_control *)(raw_nbft + sizeof(struct nbft_header));
+
+ if ((control->flags & NBFT_CONTROL_VALID) == 0)
+ return 0;
+ verify(control->structure_id == NBFT_DESC_CONTROL,
+ "invalid ID in control structure");
+
+ /*
+ * host
+ */
+ verify(le32_to_cpu(control->hdesc.offset) + sizeof(struct nbft_host) <=
+ le32_to_cpu(header->length) &&
+ le32_to_cpu(control->hdesc.offset) >= sizeof(struct nbft_host),
+ "host descriptor offset/length is invalid");
+ host = (struct nbft_host *)(raw_nbft + le32_to_cpu(control->hdesc.offset));
+
+ verify(host->flags & NBFT_HOST_VALID, "host descriptor valid flag not set");
+ verify(host->structure_id == NBFT_DESC_HOST, "invalid ID in HOST descriptor");
+ nbft->host.id = (unsigned char *) &(host->host_id);
+ if (get_heap_obj(host, host_nqn_obj, 1, &nbft->host.nqn) != 0)
+ return -EINVAL;
+ nbft->host.host_id_configured = host->flags & NBFT_HOST_HOSTID_CONFIGURED;
+ nbft->host.host_nqn_configured = host->flags & NBFT_HOST_HOSTNQN_CONFIGURED;
+
+ /*
+ * HFI
+ */
+ if (control->num_hfi > 0) {
+ struct nbft_hfi *raw_hfi_array;
+
+ verify(le32_to_cpu(control->hfio) + sizeof(struct nbft_hfi) *
+ control->num_hfi <= le32_to_cpu(header->length),
+ "invalid hfi descriptor list offset");
+ raw_hfi_array = (struct nbft_hfi *)(raw_nbft + le32_to_cpu(control->hfio));
+ read_hfi_descriptors(nbft, control->num_hfi, raw_hfi_array,
+ le16_to_cpu(control->hfil));
+ }
+
+ /*
+ * security
+ */
+ if (control->num_sec > 0) {
+ struct nbft_security *raw_security_array;
+
+ verify(le32_to_cpu(control->seco) + le16_to_cpu(control->secl) *
+ control->num_sec <= le32_to_cpu(header->length),
+ "invalid security profile desciptor list offset");
+ raw_security_array = (struct nbft_security *)(raw_nbft +
+ le32_to_cpu(control->seco));
+ read_security_descriptors(nbft, control->num_sec,
+ raw_security_array,
+ le16_to_cpu(control->secl));
+ }
+
+ /*
+ * discovery
+ */
+ if (control->num_disc > 0) {
+ struct nbft_discovery *raw_discovery_array;
+
+ verify(le32_to_cpu(control->disco) + le16_to_cpu(control->discl) *
+ control->num_disc <= le32_to_cpu(header->length),
+ "invalid discovery profile descriptor list offset");
+ raw_discovery_array = (struct nbft_discovery *)(raw_nbft +
+ le32_to_cpu(control->disco));
+ read_discovery_descriptors(nbft, control->num_disc, raw_discovery_array,
+ le16_to_cpu(control->discl));
+ }
+
+ /*
+ * subsystem namespace
+ */
+ if (control->num_ssns > 0) {
+ struct nbft_ssns *raw_ssns_array;
+
+ verify(le32_to_cpu(control->ssnso) + le16_to_cpu(control->ssnsl) *
+ control->num_ssns <= le32_to_cpu(header->length),
+ "invalid subsystem namespace descriptor list offset");
+ raw_ssns_array = (struct nbft_ssns *)(raw_nbft +
+ le32_to_cpu(control->ssnso));
+ read_ssns_descriptors(nbft, control->num_ssns, raw_ssns_array,
+ le16_to_cpu(control->ssnsl));
+ }
+
+ return 0;
+}
+
+void nvme_nbft_free(struct nbft_info *nbft)
+{
+ struct nbft_info_hfi **hfi;
+ struct nbft_info_security **sec;
+ struct nbft_info_discovery **disc;
+ struct nbft_info_subsystem_ns **ns;
+
+ for (hfi = nbft->hfi_list; hfi && *hfi; hfi++)
+ free(*hfi);
+ free(nbft->hfi_list);
+ for (disc = nbft->discovery_list; disc && *disc; disc++)
+ free(*disc);
+ free(nbft->discovery_list);
+ for (sec = nbft->security_list; sec && *sec; sec++)
+ free(*sec);
+ free(nbft->security_list);
+ for (ns = nbft->subsystem_ns_list; ns && *ns; ns++) {
+ free((*ns)->hfis);
+ free(*ns);
+ }
+ free(nbft->subsystem_ns_list);
+ free(nbft->raw_nbft);
+ free(nbft->filename);
+ free(nbft);
+}
+
+int nvme_nbft_read(struct nbft_info **nbft, const char *filename)
+{
+ __u8 *raw_nbft = NULL;
+ size_t raw_nbft_size;
+ FILE *raw_nbft_fp = NULL;
+ int i;
+
+ /*
+ * read in raw nbft file
+ */
+ raw_nbft_fp = fopen(filename, "rb");
+ if (raw_nbft_fp == NULL) {
+ nvme_msg(NULL, LOG_ERR, "Failed to open %s: %s\n",
+ filename, strerror(errno));
+ errno = EINVAL;
+ return 1;
+ }
+
+ i = fseek(raw_nbft_fp, 0L, SEEK_END);
+ if (i) {
+ nvme_msg(NULL, LOG_ERR, "Failed to read from %s: %s\n",
+ filename, strerror(errno));
+ fclose(raw_nbft_fp);
+ errno = EINVAL;
+ return 1;
+ }
+
+ raw_nbft_size = ftell(raw_nbft_fp);
+ rewind(raw_nbft_fp);
+
+ raw_nbft = malloc(raw_nbft_size);
+ if (!raw_nbft) {
+ nvme_msg(NULL, LOG_ERR, "Failed to allocate memory for NBFT table");
+ fclose(raw_nbft_fp);
+ errno = ENOMEM;
+ return 1;
+ }
+
+ i = fread(raw_nbft, sizeof(*raw_nbft), raw_nbft_size, raw_nbft_fp);
+ if (i != raw_nbft_size) {
+ nvme_msg(NULL, LOG_ERR, "Failed to read from %s: %s\n",
+ filename, strerror(errno));
+ fclose(raw_nbft_fp);
+ free(raw_nbft);
+ errno = EINVAL;
+ return 1;
+ }
+ fclose(raw_nbft_fp);
+
+ /*
+ * alloc new struct nbft_info, add raw nbft & filename to it, and add it to the list
+ */
+ *nbft = calloc(1, sizeof(struct nbft_info));
+ if (!*nbft) {
+ nvme_msg(NULL, LOG_ERR, "Could not allocate memory for NBFT\n");
+ free(raw_nbft);
+ errno = ENOMEM;
+ return 1;
+ }
+
+ (*nbft)->filename = strdup(filename);
+ (*nbft)->raw_nbft = raw_nbft;
+ (*nbft)->raw_nbft_size = raw_nbft_size;
+
+ if (parse_raw_nbft(*nbft)) {
+ nvme_msg(NULL, LOG_ERR, "Failed to parse %s\n", filename);
+ nvme_nbft_free(*nbft);
+ errno = EINVAL;
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/nvme/nbft.h b/src/nvme/nbft.h
new file mode 100644
index 0000000..6012e16
--- /dev/null
+++ b/src/nvme/nbft.h
@@ -0,0 +1,1238 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021-2022, Dell Inc. or its subsidiaries. All Rights Reserved.
+ *
+ * Authors: Stuart Hayes <Stuart_Hayes@Dell.com>
+ *
+ */
+#ifndef _NBFT_H
+#define _NBFT_H
+
+#include <sys/types.h>
+#include "util.h"
+
+/*
+ * ACPI NBFT table structures (TP8012 Boot Specification rev. 1.0)
+ */
+
+/**
+ * enum nbft_desc_type - NBFT Elements - Descriptor Types (Figure 5)
+ * @NBFT_DESC_HEADER: Header: an ACPI structure header with some additional
+ * NBFT specific info.
+ * @NBFT_DESC_CONTROL: Control Descriptor: indicates the location of host,
+ * HFI, SSNS, security, and discovery descriptors.
+ * @NBFT_DESC_HOST: Host Descriptor: host information.
+ * @NBFT_DESC_HFI: HFI Descriptor: an indexable table of HFI Descriptors,
+ * one for each fabric interface on the host.
+ * @NBFT_DESC_SSNS: Subsystem Namespace Descriptor: an indexable table
+ * of SSNS Descriptors.
+ * @NBFT_DESC_SECURITY: Security Descriptor: an indexable table of Security
+ * descriptors.
+ * @NBFT_DESC_DISCOVERY: Discovery Descriptor: an indexable table of Discovery
+ * Descriptors.
+ * @NBFT_DESC_HFI_TRINFO: HFI Transport Descriptor: indicated by an HFI Descriptor,
+ * corresponds to a specific transport for a single HFI.
+ * @NBFT_DESC_RESERVED_8: Reserved.
+ * @NBFT_DESC_SSNS_EXT_INFO: SSNS Extended Info Descriptor: indicated by an SSNS
+ * Descriptor if required.
+ */
+enum nbft_desc_type {
+ NBFT_DESC_HEADER = 0,
+ NBFT_DESC_CONTROL = 1,
+ NBFT_DESC_HOST = 2,
+ NBFT_DESC_HFI = 3,
+ NBFT_DESC_SSNS = 4,
+ NBFT_DESC_SECURITY = 5,
+ NBFT_DESC_DISCOVERY = 6,
+ NBFT_DESC_HFI_TRINFO = 7,
+ NBFT_DESC_RESERVED_8 = 8,
+ NBFT_DESC_SSNS_EXT_INFO = 9,
+};
+
+/**
+ * enum nbft_trtype - NBFT Interface Transport Types (Figure 7)
+ * @NBFT_TRTYPE_TCP: NVMe/TCP (802.3 + TCP/IP). String Designator "tcp".
+ */
+enum nbft_trtype {
+ NBFT_TRTYPE_TCP = 3,
+};
+
+#define NBFT_HEADER_SIG "NBFT"
+
+/**
+ * struct nbft_heap_obj - NBFT Header Driver Signature
+ * @offset: Offset in bytes of the heap object, if any, from byte offset 0h
+ * of the NBFT Table Header.
+ * @length: Length in bytes of the heap object, if any.
+ */
+struct nbft_heap_obj {
+ __le32 offset;
+ __le16 length;
+} __attribute__((packed));
+
+/**
+ * struct nbft_header - NBFT Table - Header (Figure 8)
+ * @signature: Signature: An ASCII string representation of the table
+ * identifier. This field shall be set to the value 4E424654h
+ * (i.e. "NBFT", see #NBFT_HEADER_SIG).
+ * @length: Length: The length of the table, in bytes, including the
+ * header, starting from offset 0h. This field is used to record
+ * the size of the entire table.
+ * @major_revision: Major Revision: The major revision of the structure
+ * corresponding to the Signature field. Larger major revision
+ * numbers should not be assumed backward compatible to lower
+ * major revision numbers with the same signature.
+ * @checksum: Checksum: The entire table, including the Checksum field,
+ * shall sum to 0h to be considered valid.
+ * @oem_id: OEMID shall be populated by the NBFT driver writer by
+ * an OEM-supplied string that identifies the OEM. All
+ * trailing bytes shall be NULL.
+ * @oem_table_id: OEM Table ID: This field shall be populated by the NBFT
+ * driver writer with an OEM-supplied string that the OEM
+ * uses to identify the particular data table. This field is
+ * particularly useful when defining a definition block to
+ * distinguish definition block functions. The OEM assigns
+ * each dissimilar table a new OEM Table ID.
+ * @oem_revision: OEM Revision: An OEM-supplied revision number. Larger
+ * numbers are assumed to be newer revisions.
+ * @creator_id: Creator ID: Vendor ID of utility that created the table.
+ * For instance, this may be the ID for the ASL Compiler.
+ * @creator_revision: Creator Revision: Revision of utility that created the
+ * table. For instance, this may be the ID for the ASL Compiler.
+ * @heap_offset: Heap Offset (HO): This field indicates the offset in bytes
+ * of the heap, if any, from byte offset 0h of the NBFT
+ * Table Header.
+ * @heap_length: Heap Length (HL): The length of the heap, if any.
+ * @driver_dev_path_sig: Driver Signature Heap Object Reference: This field indicates
+ * the offset in bytes of a heap object containing the Driver
+ * Signature, if any, from byte offset 0h of the NBFT Table
+ * Header.
+ * @minor_revision: Minor Revision: The minor revision of the structure
+ * corresponding to the Signature field. If the major revision
+ * numbers are the same, any minor revision number differences
+ * shall be backwards compatible with the same signature.
+ * @reserved: Reserved.
+ */
+struct nbft_header {
+ char signature[4];
+ __le32 length;
+ __u8 major_revision;
+ __u8 checksum;
+ char oem_id[6];
+ char oem_table_id[8];
+ __le32 oem_revision;
+ __le32 creator_id;
+ __le32 creator_revision;
+ __le32 heap_offset;
+ __le32 heap_length;
+ struct nbft_heap_obj driver_dev_path_sig;
+ __u8 minor_revision;
+ __u8 reserved[13];
+};
+
+/**
+ * struct nbft_control - NBFT Table - Control Descriptor (Figure 8)
+ * @structure_id: Structure ID: This field specifies the element (refer to
+ * &enum nbft_desc_type). This field shall be set to 1h (i.e.,
+ * Control, #NBFT_DESC_CONTROL).
+ * @major_revision: Major Revision: The major revision of the structure corresponding
+ * to the Signature field. Larger major revision numbers should
+ * not be assumed backward compatible to lower major revision
+ * numbers with the same signature.
+ * @minor_revision: Minor Revision: The minor revision of the structure corresponding
+ * to the signature field. If the major revision numbers are
+ * the same, any minor revision number differences shall be backwards
+ * compatible with the same signature.
+ * @reserved1: Reserved.
+ * @csl: Control Structure Length (CSL): This field indicates the length
+ * in bytes of the Control Descriptor.
+ * @flags: Flags, see &enum nbft_control_flags.
+ * @reserved2: Reserved.
+ * @hdesc: Host Descriptor (HDESC): This field indicates the location
+ * and length of the Host Descriptor (see &struct nbft_host).
+ * @hsv: Host Descriptor Version (HSV): This field indicates the version
+ * of the Host Descriptor.
+ * @reserved3: Reserved.
+ * @hfio: HFI Descriptor List Offset (HFIO): If this field is set to
+ * a non-zero value, then this field indicates the offset in bytes
+ * of the HFI Descriptor List, if any, from byte offset 0h of the
+ * NBFT Table Header. If the @num_hfi field is cleared to 0h,
+ * then this field is reserved.
+ * @hfil: HFI Descriptor Length (HFIL): This field indicates the length
+ * in bytes of each HFI Descriptor, if any. If the @num_hfi field
+ * is cleared to 0h, then this field is reserved.
+ * @hfiv: HFI Descriptor Version (HFIV): This field indicates the version
+ * of each HFI Descriptor.
+ * @num_hfi: Number of Host Fabric Interface Descriptors (NumHFI): This field
+ * indicates the number of HFI Descriptors (see &struct nbft_hfi)
+ * in the HFI Descriptor List, if any. If no interfaces have been
+ * configured, then this field shall be cleared to 0h.
+ * @ssnso: SSNS Descriptor List Offset (SSNSO):: This field indicates
+ * the offset in bytes of the SSNS Descriptor List, if any, from
+ * byte offset 0h of the NBFT Table Header. If the @num_ssns field
+ * is cleared to 0h, then this field is reserved.
+ * @ssnsl: SSNS Descriptor Length (SSNSL): This field indicates the length
+ * in bytes of each SSNS Descriptor, if any. If the @num_ssns
+ * field is cleared to 0h, then this field is reserved.
+ * @ssnsv: SSNS Descriptor Version (SSNSV): This field indicates the version
+ * of the SSNS Descriptor.
+ * @num_ssns: Number of Subsystem and Namespace Descriptors (NumSSNS): This
+ * field indicates the number of Subsystem Namespace (SSNS)
+ * Descriptors (see &struct nbft_ssns) in the SSNS Descriptor List,
+ * if any.
+ * @seco: Security Profile Descriptor List Offset (SECO): This field
+ * indicates the offset in bytes of the Security Profile Descriptor
+ * List, if any, from byte offset 0h of the NBFT Table Header.
+ * If the @num_sec field is cleared to 0h, then this field
+ * is reserved.
+ * @secl: Security Profile Descriptor Length (SECL): This field indicates
+ * the length in bytes of each Security Profile Descriptor, if any.
+ * If the @num_sec field is cleared to 0h, then this field
+ * is reserved.
+ * @secv: Security Profile Descriptor Version (SECV): This field indicates
+ * the version of the Security Profile Descriptor.
+ * @num_sec: Number of Security Profile Descriptors (NumSec): This field
+ * indicates the number of Security Profile Descriptors
+ * (see &struct nbft_security), if any, in the Security Profile
+ * Descriptor List.
+ * @disco: Discovery Descriptor Offset (DISCO): This field indicates
+ * the offset in bytes of the Discovery Descriptor List, if any,
+ * from byte offset 0h of the NBFT Table Header. If the @num_disc
+ * field is cleared to 0h, then this field is reserved.
+ * @discl: Discovery Descriptor Length (DISCL): This field indicates
+ * the length in bytes of each Discovery Descriptor, if any.
+ * If the @num_disc field is cleared to 0h, then this field
+ * is reserved.
+ * @discv: Discovery Descriptor Version (DISCV): This field indicates
+ * the version of the Discovery Descriptor.
+ * @num_disc: Number of Discovery Descriptors (NumDisc): This field indicates
+ * the number of Discovery Descriptors (see &struct nbft_discovery),
+ * if any, in the Discovery Descriptor List, if any.
+ * @reserved4: Reserved.
+ */
+struct nbft_control {
+ __u8 structure_id;
+ __u8 major_revision;
+ __u8 minor_revision;
+ __u8 reserved1;
+ __le16 csl;
+ __u8 flags;
+ __u8 reserved2;
+ struct nbft_heap_obj hdesc;
+ __u8 hsv;
+ __u8 reserved3;
+ __le32 hfio;
+ __le16 hfil;
+ __u8 hfiv;
+ __u8 num_hfi;
+ __le32 ssnso;
+ __le16 ssnsl;
+ __u8 ssnsv;
+ __u8 num_ssns;
+ __le32 seco;
+ __le16 secl;
+ __u8 secv;
+ __u8 num_sec;
+ __le32 disco;
+ __le16 discl;
+ __u8 discv;
+ __u8 num_disc;
+ __u8 reserved4[16];
+};
+
+/**
+ * enum nbft_control_flags - Control Descriptor Flags
+ * @NBFT_CONTROL_VALID: Block Valid: indicates that the structure is valid.
+ */
+enum nbft_control_flags {
+ NBFT_CONTROL_VALID = 1 << 0,
+};
+
+/**
+ * struct nbft_host - Host Descriptor (Figure 9)
+ * @structure_id: Structure ID: This field shall be set to 2h (i.e.,
+ * Host Descriptor; #NBFT_DESC_HOST).
+ * @flags: Host Flags, see &enum nbft_host_flags.
+ * @host_id: Host ID: This field shall be set to the Host Identifier. This
+ * field shall not be empty if the NBFT and NVMe Boot are supported
+ * by the Platform.
+ * @host_nqn_obj: Host NQN Heap Object Reference: this field indicates a heap
+ * object containing a Host NQN. This object shall not be empty
+ * if the NBFT and NVMe Boot are supported by the Platform.
+ * @reserved: Reserved.
+ */
+struct nbft_host {
+ __u8 structure_id;
+ __u8 flags;
+ __u8 host_id[16];
+ struct nbft_heap_obj host_nqn_obj;
+ __u8 reserved[8];
+};
+
+/**
+ * enum nbft_host_flags - Host Flags
+ * @NBFT_HOST_VALID: Descriptor Valid: If set to 1h, then this
+ * descriptor is valid. If cleared to 0h, then
+ * this descriptor is reserved.
+ * @NBFT_HOST_HOSTID_CONFIGURED: HostID Configured: If set to 1h, then the
+ * Host ID field contains an administratively-configured
+ * value. If cleared to 0h, then the Host ID
+ * field contains a driver default value.
+ * @NBFT_HOST_HOSTNQN_CONFIGURED: Host NQN Configured: If set to 1h, then the
+ * Host NQN indicated by the Host NQN Heap Object
+ * Reference field (&struct nbft_host.host_nqn)
+ * contains an administratively-configured value.
+ * If cleared to 0h, then the Host NQN indicated
+ * by the Host NQN Offset field contains a driver
+ * default value.
+ * @NBFT_HOST_PRIMARY_ADMIN_MASK: Mask to get Primary Administrative Host Descriptor:
+ * indicates whether the Host Descriptor in this
+ * NBFT was selected as the primary NBFT for
+ * administrative purposes of platform identity
+ * as a hint to the OS. If multiple NBFT tables
+ * are present, only one NBFT should be administratively
+ * selected. There is no enforcement mechanism
+ * for this to be coordinated between multiple NBFT
+ * tables, but this field should be set to Selected
+ * (#NBFT_HOST_PRIMARY_ADMIN_SELECTED) if
+ * more than one NBFT is present.
+ * @NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED: Not Indicated by Driver: The driver that created
+ * this NBFT provided no administrative priority
+ * hint for this NBFT.
+ * @NBFT_HOST_PRIMARY_ADMIN_UNSELECTED: Unselected: The driver that created this NBFT
+ * explicitly indicated that this NBFT should
+ * not be prioritized over any other NBFT.
+ * @NBFT_HOST_PRIMARY_ADMIN_SELECTED: Selected: The driver that created this NBFT
+ * explicitly indicated that this NBFT should
+ * be prioritized over any other NBFT.
+ */
+enum nbft_host_flags {
+ NBFT_HOST_VALID = 1 << 0,
+ NBFT_HOST_HOSTID_CONFIGURED = 1 << 1,
+ NBFT_HOST_HOSTNQN_CONFIGURED = 1 << 2,
+ NBFT_HOST_PRIMARY_ADMIN_MASK = 0x18,
+ NBFT_HOST_PRIMARY_ADMIN_NOT_INDICATED = 0x00,
+ NBFT_HOST_PRIMARY_ADMIN_UNSELECTED = 0x08,
+ NBFT_HOST_PRIMARY_ADMIN_SELECTED = 0x10,
+};
+
+/**
+ * struct nbft_hfi - Host Fabric Interface (HFI) Descriptor (Figure 11)
+ * @structure_id: Structure ID: This field shall be set to 3h (i.e., Host Fabric
+ * Interface Descriptor; #NBFT_DESC_HFI).
+ * @index: HFI Descriptor Index: This field indicates the number of this
+ * HFI Descriptor in the Host Fabric Interface Descriptor List.
+ * @flags: HFI Descriptor Flags, see &enum nbft_hfi_flags.
+ * @trtype: HFI Transport Type, see &enum nbft_trtype.
+ * @reserved1: Reserved.
+ * @trinfo_obj: HFI Transport Info Descriptor Heap Object Reference: If this
+ * field is set to a non-zero value, then this field indicates
+ * the location and size of a heap object containing
+ * a HFI Transport Info.
+ * @reserved2: Reserved.
+ */
+struct nbft_hfi {
+ __u8 structure_id;
+ __u8 index;
+ __u8 flags;
+ __u8 trtype;
+ __u8 reserved1[12];
+ struct nbft_heap_obj trinfo_obj;
+ __u8 reserved2[10];
+};
+
+/**
+ * enum nbft_hfi_flags - HFI Descriptor Flags
+ * @NBFT_HFI_VALID: Descriptor Valid: If set to 1h, then this descriptor is valid.
+ * If cleared to 0h, then this descriptor is reserved.
+ */
+enum nbft_hfi_flags {
+ NBFT_HFI_VALID = 1 << 0,
+};
+
+/**
+ * struct nbft_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP (Figure 13)
+ * @structure_id: Structure ID: This field shall be set to 7h (i.e.,
+ * HFI Transport Info; #NBFT_DESC_HFI_TRINFO).
+ * @version: Version: This field shall be set to 1h.
+ * @trtype: HFI Transport Type, see &enum nbft_trtype: This field
+ * shall be set to 03h (i.e., NVMe/TCP; #NBFT_TRTYPE_TCP).
+ * @trinfo_version: Transport Info Version: Implementations compliant to this
+ * specification shall set this field to 1h.
+ * @hfi_index: HFI Descriptor Index: The value of the HFI Descriptor Index
+ * field of the HFI Descriptor (see &struct nbft_hfi.index)
+ * whose HFI Transport Info Descriptor Heap Object Reference
+ * field indicates this HFI Transport Info Descriptor.
+ * @flags: HFI Transport Flags, see &enum nbft_hfi_info_tcp_flags.
+ * @pci_sbdf: PCI Express Routing ID for the HFI Transport Function:
+ * This field indicates the PCI Express Routing ID as specified
+ * in the PCI Express Base Specification.
+ * @mac_addr: MAC Address: The MAC address of this HFI, in EUI-48TM format,
+ * as defined in the IEEE Guidelines for Use of Extended Unique
+ * Identifiers. This field shall be set to a non-zero value.
+ * @vlan: VLAN: If this field is set to a non-zero value, then this
+ * field contains the VLAN identifier if the VLAN associated
+ * with this HFI, as defined in IEEE 802.1q-2018. If no VLAN
+ * is associated with this HFI, then this field shall be cleared
+ * to 0h.
+ * @ip_origin: IP Origin: If this field is set to a non-zero value, then
+ * this field indicates the source of Ethernet L3 configuration
+ * information used by the driver for this interface. Valid
+ * values are defined in the Win 32 API: NL_PREFIX_ORIGIN
+ * enumeration specification. This field should be cleared
+ * to 0h if the IP Origin field is unused by driver.
+ * @ip_address: IP Address: This field indicates the IPv4 or IPv6 address
+ * of this HFI. This field shall be set to a non-zero value.
+ * @subnet_mask_prefix: Subnet Mask Prefix: This field indicates the IPv4 or IPv6
+ * subnet mask in CIDR routing prefix notation.
+ * @ip_gateway: IP Gateway: If this field is set to a non-zero value, this
+ * field indicates the IPv4 or IPv6 address of the IP gateway
+ * for this HFI. If this field is cleared to 0h, then
+ * no IP gateway is specified.
+ * @reserved1: Reserved.
+ * @route_metric: Route Metric: If this field is set to a non-zero value,
+ * this field indicates the cost value for the route indicated
+ * by this HF. This field contains the value utilized by the
+ * pre-OS driver when chosing among all available routes. Lower
+ * values relate to higher priority. Refer to IETF RFC 4249.
+ * If the pre-OS driver supports routing and did not configure
+ * a specific route metric for this interface, then the pre-OS
+ * driver should set this value to 500. If the pre-OS driver
+ * does not support routing, then this field should be cleared
+ * to 0h.
+ * @primary_dns: Primary DNS: If this field is set to a non-zero value,
+ * this field indicates the IPv4 or IPv6 address of the
+ * Primary DNS server for this HFI, if any, from byte offset
+ * 0h of the NBFT Table Header. If this field is cleared to 0h,
+ * then no Primary DNS is specified.
+ * @secondary_dns: Secondary DNS: If this field is set to a non-zero value,
+ * this field indicates the IPv4 or IPv6 address of
+ * the Secondary DNS server for this HFI, if any, from byte
+ * offset 0h of the NBFT Table Header. If this field is
+ * cleared to 0h, then no Secondary DNS is specified.
+ * @dhcp_server: DHCP Server: If the DHCP Override bit is set to 1h, then
+ * this field indicates the IPv4 or IPv6 address of the DHCP
+ * server used to assign this HFI address. If that bit is
+ * cleared to 0h, then this field is reserved.
+ * @host_name_obj: Host Name Heap Object Reference: If this field is set
+ * to a non-zero value, then this field indicates the location
+ * and size of a heap object containing a Host Name string.
+ * @reserved2: Reserved.
+ */
+struct nbft_hfi_info_tcp {
+ __u8 structure_id;
+ __u8 version;
+ __u8 trtype;
+ __u8 trinfo_version;
+ __le16 hfi_index;
+ __u8 flags;
+ __le32 pci_sbdf;
+ __u8 mac_addr[6];
+ __le16 vlan;
+ __u8 ip_origin;
+ __u8 ip_address[16];
+ __u8 subnet_mask_prefix;
+ __u8 ip_gateway[16];
+ __u8 reserved1;
+ __le16 route_metric;
+ __u8 primary_dns[16];
+ __u8 secondary_dns[16];
+ __u8 dhcp_server[16];
+ struct nbft_heap_obj host_name_obj;
+ __u8 reserved2[18];
+} __attribute__((packed));
+
+/**
+ * enum nbft_hfi_info_tcp_flags - HFI Transport Flags
+ * @NBFT_HFI_INFO_TCP_VALID: Descriptor Valid: if set to 1h, then this
+ * descriptor is valid. If cleared to 0h, then
+ * this descriptor is reserved.
+ * @NBFT_HFI_INFO_TCP_GLOBAL_ROUTE: Global Route vs. Link Local Override Flag:
+ * if set to 1h, then the BIOS utilized this
+ * interface described by HFI to be the default
+ * route with highest priority. If cleared to 0h,
+ * then routes are local to their own scope.
+ * @NBFT_HFI_INFO_TCP_DHCP_OVERRIDE: DHCP Override: if set to 1, then HFI information
+ * was populated by consuming the DHCP on this
+ * interface. If cleared to 0h, then the HFI
+ * information was set administratively by
+ * a configuration interface to the driver and
+ * pre-OS envrionment.
+ */
+enum nbft_hfi_info_tcp_flags {
+ NBFT_HFI_INFO_TCP_VALID = 1 << 0,
+ NBFT_HFI_INFO_TCP_GLOBAL_ROUTE = 1 << 1,
+ NBFT_HFI_INFO_TCP_DHCP_OVERRIDE = 1 << 2,
+};
+
+/**
+ * struct nbft_ssns - Subsystem Namespace (SSNS) Descriptor (Figure 15)
+ * @structure_id: Structure ID: This field shall be set to 4h
+ * (i.e., SSNS; #NBFT_DESC_SSNS).
+ * @index: SSNS Descriptor Index: This field indicates the number
+ * of this Subsystem Namespace Descriptor in the
+ * Subsystem Namespace Descriptor List.
+ * @flags: SSNS Flags, see &enum nbft_ssns_flags.
+ * @trtype: Transport Type, see &enum nbft_trtype.
+ * @trflags: Transport Specific Flags, see &enum nbft_ssns_trflags.
+ * @primary_discovery_ctrl_index: Primary Discovery Controller Index: The Discovery
+ * Descriptor Index field of the Discovery Descriptor
+ * (see &struct nbft_discovery) that is associated with
+ * this SSNS Descriptor. If a Discovery controller was
+ * used to establish this record this value shall
+ * be set to a non-zero value. If this namespace was
+ * associated with multiple Discovery controllers,
+ * those Discovery controllers shall have records
+ * in the Discovery Descriptor to facilitate multi-path
+ * rediscovery as required. If no Discovery controller
+ * was utilized to inform this namespace record,
+ * this field shall be cleared to 0h.
+ * @reserved1: Reserved.
+ * @subsys_traddr_obj: Subsystem Transport Address Heap Object Reference:
+ * This field indicates the location and size of a heap
+ * object containing the Subsystem Transport Address.
+ * For IP based transports types, shall be an IP Address.
+ * @subsys_trsvcid_obj: Subsystem Transport Service Identifier Heap Object Reference:
+ * This field indicates the location and size of a heap
+ * object containing an array of bytes indicating
+ * the Subsystem Transport Service Identifier.
+ * See &enum nbft_trtype.
+ * @subsys_port_id: Subsystem Port ID: Port in the NVM subsystem
+ * associated with this transport address used by
+ * the pre-OS driver.
+ * @nsid: Namespace ID: This field indicates the namespace
+ * identifier (NSID) of the namespace indicated by
+ * this descriptor. This field shall be cleared to 0h
+ * if not specified by the user. If this value is cleared
+ * to 0h, then consumers of the NBFT shall rely
+ * on the NID.
+ * @nidt: Namespace Identifier Type (NIDT): This field
+ * contains the value of the Namespace Identifier Type (NIDT)
+ * field in the Namespace Identification Descriptor
+ * for the namespace indicated by this descriptor.
+ * If a namespace supports multiple NIDT entries
+ * for uniqueness, the order of preference is NIDT field
+ * value of 3h (i.e., UUID) before 2h (i.e., NSGUID),
+ * and 2h before 1h (i.e., EUI-64).
+ * @nid: Namespace Identifier (NID): This field contains
+ * the value of the Namespace Identifier (NID) field
+ * in the Namespace Identification Descriptor for
+ * the namespace indicated by this descriptor.
+ * @security_desc_index: Security Profile Descriptor Index: If the Use Security
+ * Flag bit in the SSNS Flags field is set to 1h, then
+ * this field indicates the value of the Security Profile
+ * Descriptor Index field of the Security Profile
+ * Descriptor (see &struct nbft_security) associated
+ * with this namespace. If the Use Security Flag bit
+ * is cleared to 0h, then no Security Profile Descriptor
+ * is associated with this namespace and this field
+ * is reserved.
+ * @primary_hfi_desc_index: Primary HFI Descriptor Index: This field indicates
+ * the value of the HFI Descriptor Index field of the
+ * HFI Descriptor (see &struct nbft_hfi) for the
+ * interface associated with this namespace. If multiple
+ * HFIs are associated with this record, subsequent
+ * interfaces should be populated in the Secondary
+ * HFI Associations field.
+ * @reserved2: Reserved.
+ * @secondary_hfi_assoc_obj: Secondary HFI Associations Heap Object Reference:
+ * If this field is set to a non-zero value, then
+ * this field indicates an array of bytes, in which
+ * each byte contains the value of the HFI Descriptor
+ * Index field of an HFI Descriptor in the HFI Descriptor
+ * List. If this field is cleared to 0h, then no
+ * secondary HFI associations are specified.
+ * @subsys_ns_nqn_obj: Subsystem and Namespace NQN Heap Object Reference:
+ * This field indicates the location and size of
+ * a heap object containing the Subsystem and Namespace NQN.
+ * @ssns_extended_info_desc_obj: SSNS Extended Information Descriptor Heap Object
+ * Reference: If the SSNS Extended Info In-use Flag
+ * bit is set to 1h, then this field indicates the
+ * offset in bytes of a heap object containing an
+ * SSNS Extended Information Descriptor
+ * (see &struct nbft_ssns_ext_info) heap object
+ * from byte offset 0h of the NBFT Table Header.
+ * If the SSNS Extended Info In-use Flag bit is cleared
+ * to 0h, then this field is reserved.
+ * @reserved3: Reserved.
+ */
+struct nbft_ssns {
+ __u8 structure_id;
+ __le16 index;
+ __le16 flags;
+ __u8 trtype;
+ __le16 trflags;
+ __u8 primary_discovery_ctrl_index;
+ __u8 reserved1;
+ struct nbft_heap_obj subsys_traddr_obj;
+ struct nbft_heap_obj subsys_trsvcid_obj;
+ __le16 subsys_port_id;
+ __le32 nsid;
+ __u8 nidt;
+ __u8 nid[16];
+ __u8 security_desc_index;
+ __u8 primary_hfi_desc_index;
+ __u8 reserved2;
+ struct nbft_heap_obj secondary_hfi_assoc_obj;
+ struct nbft_heap_obj subsys_ns_nqn_obj;
+ struct nbft_heap_obj ssns_extended_info_desc_obj;
+ __u8 reserved3[62];
+} __attribute__((packed));
+
+/**
+ * enum nbft_ssns_flags - Subsystem and Namespace Specific Flags Field (Figure 16)
+ * @NBFT_SSNS_VALID: Descriptor Valid: If set to 1h, then this descriptor
+ * is valid. If cleared to 0h, then this descriptor
+ * is not valid. A host that supports NVMe-oF Boot,
+ * but does not currently have a remote Subsystem
+ * and Namespace assigned may clear this bit to 0h.
+ * @NBFT_SSNS_NON_BOOTABLE_ENTRY: Non-bootable Entry Flag: If set to 1h, this flag
+ * indicates that this SSNS Descriptor contains
+ * a namespace of administrative purpose to the boot
+ * process, but the pre-OS may not have established
+ * connectivity to or evaluated the contents of this
+ * Descriptor. Such namespaces may contain supplemental
+ * data deemed relevant by the Administrator as part
+ * of the pre-OS to OS hand off. This may include
+ * properties such as a UEFI device path that may
+ * not have been created for this namespace. This means
+ * an OS runtime may still require the contents
+ * of such a namespace to complete later stages
+ * of boot. If cleared to 0h, then this namespace did
+ * not have any special administrative intent.
+ * @NBFT_SSNS_USE_SECURITY_FIELD: Use Security Flag: If set to 1h, then there is
+ * a Security Profile Descriptor associated with this
+ * SSNS record and the Security Profile Descriptor Index
+ * field is valid. If cleared to 0h, then there is
+ * no Security Profile Descriptor associated with this
+ * SSNS record and the Security Profile Descriptor Index
+ * field is not valid.
+ * @NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE: DHCP Root-Path Override Flag: If set to 1h, then
+ * this SSNS descriptor was populated by consuming
+ * the DHCP Root-Path on this interface. If cleared
+ * to 0h, then the DHCP Root-Path was not used
+ * in populating the SSNS descriptor.
+ * @NBFT_SSNS_EXTENDED_INFO_IN_USE: SSNS Extended Info In-use Flag: If set to 1h,
+ * then the SSNS Extended Information Offset field
+ * and the SSNS Extended Information Length field
+ * are valid. This flag, if set to 1h, indicates
+ * that a Subsystem and Namespace Extended Information
+ * Descriptor corresponding to this descriptor is present.
+ * @NBFT_SSNS_SEPARATE_DISCOVERY_CTRL: Separate Discovery Controller Flag: If set to 1h,
+ * then the Discovery controller associated with
+ * this volume is on a different transport address
+ * than the specified in the Subsystem Transport
+ * Address Heap Object Reference. If cleared to 0h,
+ * then the Discovery controller is the same as the
+ * Subsystem Transport Address Heap Object Reference.
+ * @NBFT_SSNS_DISCOVERED_NAMESPACE: Discovered Namespace Flag: If set to 1h, then
+ * this namespace was acquired through discovery.
+ * If cleared to 0h, then this namespace was
+ * explicitly configured in the system.
+ * @NBFT_SSNS_UNAVAIL_NAMESPACE_MASK: Mask to get Unavailable Namespace Flag: This
+ * field indicates the availability of the namespace
+ * at a specific point in time. Such use is only
+ * a hint and its use does not guarantee the availability
+ * of that referenced namespace at any future point in time.
+ * @NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND: Not Indicated by Driver: No information is provided.
+ * @NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL: Available: A referenced namespace described by this
+ * flag was previously accessible by the pre-OS driver.
+ * @NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL: Unavailable: This namespace was administratively
+ * configured but unattempted, unavailable or
+ * inaccessible when establishing connectivity
+ * by the pre-OS driver.
+ */
+enum nbft_ssns_flags {
+ NBFT_SSNS_VALID = 1 << 0,
+ NBFT_SSNS_NON_BOOTABLE_ENTRY = 1 << 1,
+ NBFT_SSNS_USE_SECURITY_FIELD = 1 << 2,
+ NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE = 1 << 3,
+ NBFT_SSNS_EXTENDED_INFO_IN_USE = 1 << 4,
+ NBFT_SSNS_SEPARATE_DISCOVERY_CTRL = 1 << 5,
+ NBFT_SSNS_DISCOVERED_NAMESPACE = 1 << 6,
+ NBFT_SSNS_UNAVAIL_NAMESPACE_MASK = 0x0180,
+ NBFT_SSNS_UNAVAIL_NAMESPACE_NOTIND = 0x0000,
+ NBFT_SSNS_UNAVAIL_NAMESPACE_AVAIL = 0x0080,
+ NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL = 0x0100,
+};
+
+/**
+ * enum nbft_ssns_trflags - SSNS Transport Specific Flags Field (Figure 17)
+ * @NBFT_SSNS_TRFLAG_VALID: Transport Specific Flags in Use: If set to 1h, then
+ * this descriptor is valid. If cleared to 0h, then
+ * this descriptor is not valid.
+ * @NBFT_SSNS_PDU_HEADER_DIGEST: PDU Header Digest (HDGST) Flag: If set to 1h, then
+ * the host or administrator required the connection
+ * described by this Subsystem and Namespace Descriptor
+ * to use the NVM Header Digest Enabled. A consumer
+ * of this information should attempt to use NVM Header
+ * Digest when recreating this connection if enabled.
+ * If cleared to 0h, then the host or administrator
+ * did not require the connection described by this
+ * Subsystem and Namespace Descriptor to use the
+ * NVM Header Digest Enabled.
+ * @NBFT_SSNS_DATA_DIGEST: Data Digest (DDGST) Flag: If set to 1h, then
+ * the host or administrator required the connection
+ * described by this Subsystem and Namespace Descriptor
+ * to use the NVM Data Digest Enabled. If cleared
+ * to 0h, then the host or administrator did not
+ * require the connection described by this Subsystem
+ * and Namespace Descriptor to use the NVM Data Digest
+ * Enabled. A consumer of this field should attempt
+ * to use NVM Data Digest when recreating this
+ * connection if enabled.
+ */
+enum nbft_ssns_trflags {
+ NBFT_SSNS_TRFLAG_VALID = 1 << 0,
+ NBFT_SSNS_PDU_HEADER_DIGEST = 1 << 1,
+ NBFT_SSNS_DATA_DIGEST = 1 << 2,
+};
+
+/**
+ * struct nbft_ssns_ext_info - Subsystem and Namespace Extended Information
+ * Descriptor (Figure 19)
+ * @structure_id: Structure ID: This field shall be set to 9h
+ * (i.e., SSNS Extended Info; #NBFT_DESC_SSNS_EXT_INFO).
+ * @version: Version: This field shall be set to 1h.
+ * @ssns_index: SSNS Descriptor Index: This field indicates the value
+ * of the SSNS Descriptor Index field of the Subsystem
+ * and Namespace Descriptor (see &struct nbft_ssns) whose
+ * SSNS Extended Information Descriptor Heap Object
+ * Reference field indicates this descriptor.
+ * @flags: Flags, see &enum nbft_ssns_ext_info_flags.
+ * @cntlid: Controller ID: The controller identifier of the first
+ * controller associated with the Admin Queue by the driver.
+ * If a controller identifier is not administratively
+ * specified or direct configuration is not supported
+ * by the driver, then this field shall be cleared to 0h.
+ * @asqsz: Admin Submission Queue Size (ASQSZ): The Admin Submission
+ * Queue Size utilized for the respective SSNS by the driver.
+ * @dhcp_root_path_str_obj: DHCP Root Path String Heap Object Reference: If the
+ * SSNS DHCP Root Path Override (#NBFT_SSNS_DHCP_ROOT_PATH_OVERRIDE)
+ * flag bit is set to 1h, then this field indicates
+ * the offset in bytes of a heap object containing
+ * an DHCP Root Path String used by the driver. If the
+ * SNSS DHCP Root Path Override flag bit is cleared to 0h,
+ * then this field is reserved.
+ */
+struct nbft_ssns_ext_info {
+ __u8 structure_id;
+ __u8 version;
+ __le16 ssns_index;
+ __le32 flags;
+ __le16 cntlid;
+ __le16 asqsz;
+ struct nbft_heap_obj dhcp_root_path_str_obj;
+} __attribute__((packed));
+
+/**
+ * enum nbft_ssns_ext_info_flags - Subsystem and Namespace Extended Information
+ * Descriptor Flags
+ * @NBFT_SSNS_EXT_INFO_VALID: Descriptor Valid: If set to 1h, then this descriptor
+ * is valid. If cleared to 0h, then this descriptor
+ * is reserved.
+ * @NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ: Administrative ASQSZ: If set to 1h, then the value
+ * of the ASQSZ field was provided by administrative
+ * configuration for this SSNS record. If cleared
+ * to 0h, then the value of the ASQSZ field was
+ * either obtained by discovery or assumed
+ * by the driver.
+ */
+enum nbft_ssns_ext_info_flags {
+ NBFT_SSNS_EXT_INFO_VALID = 1 << 0,
+ NBFT_SSNS_EXT_INFO_ADMIN_ASQSZ = 1 << 1,
+};
+
+/**
+ * struct nbft_security - Security Profile Descriptor (Figure 21)
+ * @structure_id: Structure ID: This field shall be set to 5h
+ * (i.e., Security; #NBFT_DESC_SECURITY).
+ * @index: Security Profile Descriptor Index: This field indicates
+ * the number of this Security Profile Descriptor in the
+ * Security Profile Descriptor List.
+ * @flags: Security Profile Descriptor Flags, see &enum nbft_security_flags.
+ * @secret_type: Secret Type, see &enum nbft_security_secret_type.
+ * @reserved1: Reserved.
+ * @sec_chan_alg_obj: Secure Channel Algorithm Heap Object Reference: If the
+ * Security Policy List field is set to 1h, then this field
+ * indicates the location and size of a heap object containing
+ * a list of secure channel algorithms. The list is an array
+ * of bytes and the values are defined in the Security Type
+ * (SECTYPE) field in the Transport Specific Address Subtype
+ * Definition in the NVMe TCP Transport Specification.
+ * If the Security Policy List field is cleared to 0h, then
+ * this field is reserved.
+ * @auth_proto_obj: Authentication Protocols Heap Object Reference: If the
+ * Authentication Policy List field is set to 1h, then this
+ * field indicates the location and size of a heap object
+ * containing a list of authentication protocol identifiers.
+ * If the Authentication Policy List field is cleared to 0h,
+ * then this field is reserved.
+ * @cipher_suite_obj: Cipher Suite Offset Heap Object Reference: If the Cipher
+ * Suites Restricted by Policy bit is set to 1h, then this
+ * field indicates the location and size of a heap object
+ * containing a list of cipher suite identifiers. The list,
+ * if any, is an array of bytes and the values are defined
+ * in the IANA TLS Parameters Registry. If the Cipher Suites
+ * Restricted by Policy bit is cleared to 0h, then this field
+ * is reserved.
+ * @dh_grp_obj: DH Groups Heap Object Reference: If the Authentication DH Groups
+ * Restricted by Policy List bit is set to 1h, then this field
+ * indicates the location and size of a heap object containing
+ * a list of DH-HMAC-CHAP Diffie-Hellman (DH) group identifiers.
+ * If the Authentication DH Groups Restricted by Policy List
+ * bit is cleared to 0h, then this field is reserved.
+ * @sec_hash_func_obj: Secure Hash Functions Offset Heap Object Reference: If the
+ * Secure Hash Functions Policy List bit is set to 1h, then
+ * this field indicates the offset in bytes of a heap object
+ * containing a list of DH-HMAC-CHAP hash function identifiers.
+ * The list is an array of bytes and the values are defined
+ * in the NVM Express Base Specification. If the Secure Hash
+ * Functions Policy List bit is cleared to 0h, then this
+ * field is reserved.
+ * @sec_keypath_obj: Secret Keypath Offset Heap Object Reference: if this field
+ * is set to a non-zero value, then this field indicates
+ * the location and size of a heap object containing a URI.
+ * The type of the URI is specified in the Secret Type field.
+ * If this field is cleared to 0h, then this field is reserved.
+ * @reserved2: Reserved.
+ */
+struct nbft_security {
+ __u8 structure_id;
+ __u8 index;
+ __le16 flags;
+ __u8 secret_type;
+ __u8 reserved1;
+ struct nbft_heap_obj sec_chan_alg_obj;
+ struct nbft_heap_obj auth_proto_obj;
+ struct nbft_heap_obj cipher_suite_obj;
+ struct nbft_heap_obj dh_grp_obj;
+ struct nbft_heap_obj sec_hash_func_obj;
+ struct nbft_heap_obj sec_keypath_obj;
+ __u8 reserved2[22];
+};
+
+/**
+ * enum nbft_security_flags - Security Profile Descriptor Flags (Figure 22)
+ * @NBFT_SECURITY_VALID: Descriptor Valid: If set to 1h, then
+ * this descriptor is valid. If cleared
+ * to 0h, then this descriptor is not valid.
+ * @NBFT_SECURITY_IN_BAND_AUTH_MASK: Mask to get the In-Band Authentication
+ * Required field.
+ * @NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED: In-band authentication is not supported
+ * by the NVM subsystem.
+ * @NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED: In-band authentication is supported by
+ * the NVM subsystem and is not required.
+ * @NBFT_SECURITY_IN_BAND_AUTH_REQUIRED: In-band authentication is supported by
+ * the NVM subsystem and is required.
+ * @NBFT_SECURITY_AUTH_POLICY_LIST_MASK: Mask to get the Authentication Policy List
+ * flag: This field indicates whether
+ * authentication protocols were indicated
+ * by policy from driver defaults or
+ * administrative configuration.
+ * @NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED: Authentication Protocols Heap Object Reference
+ * field Offset and Length are reserved.
+ * @NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER: Authentication Protocols Offset field and
+ * the Authentication Protocols Length field
+ * indicate a list of authentication protocols
+ * used by the driver.
+ * @NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN: Authentication Protocols Offset field and
+ * the Authentication Protocols Length field
+ * indicate a list of authentication protocols
+ * that were administratively set and used
+ * by the driver.
+ * @NBFT_SECURITY_SEC_CHAN_NEG_MASK: Mask to get the Secure Channel Negotiation
+ * Required flag: This field indicates whether
+ * secure channel negotiation (e.g. TLS)
+ * is required.
+ * @NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED: Secure channel negotiation is not supported
+ * by the NVM subsystem.
+ * @NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED: Secure channel negotiation is supported
+ * by the NVM subsystem and is not required.
+ * @NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED: Secure channel negotiation is supported
+ * by the NVM subsystem and is required.
+ * @NBFT_SECURITY_SEC_POLICY_LIST_MASK: Mask to get the Security Policy List flag:
+ * This field indicates whether secure channel
+ * protocols were indicated by policy from driver
+ * defaults or administrative configuration.
+ * @NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED: The Offset field and Length field in the
+ * Secure Channel Algorithm Heap Object Reference
+ * field are reserved.
+ * @NBFT_SECURITY_SEC_POLICY_LIST_DRIVER: The Heap Object specified by the Secure Channel
+ * Algorithm Heap Object Reference field indicates
+ * a list of authentication protocols used
+ * by the driver.
+ * @NBFT_SECURITY_SEC_POLICY_LIST_ADMIN: The Heap Object specified by the Secure Channel
+ * Algorithm Heap Object Reference field indicates
+ * a list of authentication protocols that were
+ * administratively set and used by the driver.
+ * @NBFT_SECURITY_CIPHER_RESTRICTED: Cipher Suites Restricted by Policy: If set to 1h,
+ * then the Cipher Suite Offset field and the
+ * Ciper Suite Length field indicate a list
+ * of supported cipher suites by the driver.
+ * If cleared to 0h, then the Cipher Suite Offset
+ * field and the Cipher Suite Length field
+ * are reserved.
+ * @NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED: Authentication DH Groups Restricted
+ * by Policy List: If set to 1h, then connections
+ * shall use one of the authentication DH groups
+ * in the Authentication DH Groups List is required.
+ * If cleared to 0h, then no Authentication DH Groups
+ * List is indicated and use of an authentication
+ * DH Group is not required.
+ * @NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST: Secure Hash Functions Policy List: If set to 1h,
+ * then connections shall use one of the secure
+ * hash functions in the Secure Hash Functions
+ * Policy List is required. If cleared to 0h,
+ * then no Secure Hash Functions Policy
+ * List is indicated and use of a secure
+ * hash function is not required.
+ */
+enum nbft_security_flags {
+ NBFT_SECURITY_VALID = 1 << 0,
+ NBFT_SECURITY_IN_BAND_AUTH_MASK = 0x0006,
+ NBFT_SECURITY_IN_BAND_AUTH_NOT_SUPPORTED = 0x0000,
+ NBFT_SECURITY_IN_BAND_AUTH_NOT_REQUIRED = 0x0002,
+ NBFT_SECURITY_IN_BAND_AUTH_REQUIRED = 0x0004,
+ NBFT_SECURITY_AUTH_POLICY_LIST_MASK = 0x0018,
+ NBFT_SECURITY_AUTH_POLICY_LIST_NOT_SUPPORTED = 0x0000,
+ NBFT_SECURITY_AUTH_POLICY_LIST_DRIVER = 0x0008,
+ NBFT_SECURITY_AUTH_POLICY_LIST_ADMIN = 0x0010,
+ NBFT_SECURITY_SEC_CHAN_NEG_MASK = 0x0060,
+ NBFT_SECURITY_SEC_CHAN_NEG_NOT_SUPPORTED = 0x0000,
+ NBFT_SECURITY_SEC_CHAN_NEG_NOT_REQUIRED = 0x0020,
+ NBFT_SECURITY_SEC_CHAN_NEG_REQUIRED = 0x0040,
+ NBFT_SECURITY_SEC_POLICY_LIST_MASK = 0x0180,
+ NBFT_SECURITY_SEC_POLICY_LIST_NOT_SUPPORTED = 0x0000,
+ NBFT_SECURITY_SEC_POLICY_LIST_DRIVER = 0x0080,
+ NBFT_SECURITY_SEC_POLICY_LIST_ADMIN = 0x0100,
+ NBFT_SECURITY_CIPHER_RESTRICTED = 1 << 9,
+ NBFT_SECURITY_AUTH_DH_GROUPS_RESTRICTED = 1 << 10,
+ NBFT_SECURITY_SEC_HASH_FUNC_POLICY_LIST = 1 << 11,
+};
+
+/**
+ * enum nbft_security_secret_type - Security Profile Descriptor Secret Type
+ * @NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI: Redfish Host Interface URI:
+ * If set to 1h, then the Secret Keypath
+ * Object Reference is a URI pointing
+ * to a Redfish Key Collection Object
+ * that contains the PSK.
+ */
+enum nbft_security_secret_type {
+ NBFT_SECURITY_SECRET_REDFISH_HOST_IFACE_URI = 1 << 1,
+};
+
+/**
+ * struct nbft_discovery - Discovery Descriptor (Figure 24)
+ * @structure_id: Structure ID: This field shall be set to 6h
+ * (i.e., Discovery Descriptor; #NBFT_DESC_DISCOVERY).
+ * @flags: Discovery Descriptor Flags, see &enum nbft_discovery_flags.
+ * @index: Discovery Descriptor Index: This field indicates
+ * the number of this Discovery Descriptor in
+ * the Discovery Descriptor List.
+ * @hfi_index: HFI Descriptor Index: This field indicates the value
+ * of the HFI Descriptor Index field of the HFI Descriptor
+ * associated with this Discovery Descriptor. If multiple
+ * HFIs share a common Discovery controller, there shall
+ * be multiple Discovery Descriptor entries with one per HFI.
+ * @sec_index: Security Profile Descriptor Index: This field indicates
+ * the value of the Security Profile Descriptor Index
+ * field of the Security Descriptor associated with
+ * this Discovery Descriptor.
+ * @reserved1: Reserved.
+ * @discovery_ctrl_addr_obj: Discovery Controller Address Heap Object Reference:
+ * This field indicates the location and size of a heap
+ * object containing a URI which indicates an NVMe Discovery
+ * controller associated with this Discovery Descriptor.
+ * If this field is cleared to 0h, then no URI is specified.
+ * @discovery_ctrl_nqn_obj: Discovery Controller NQN Heap Object Reference:
+ * If set to a non-zero value, this field indicates
+ * the location and size of a heap object containing
+ * an NVMe Discovery controller NQN. If the NVMe Discovery
+ * controller referenced by this record requires secure
+ * authentication with a well known Subsystem NQN, this
+ * field indicates the unique NQN for that NVMe Discovery
+ * controller. This record is involved formatted as an NQN
+ * string. If this field is cleared to 0h, then this
+ * field is reserved and the OS shall use the well
+ * known discovery NQN for this record.
+ * @reserved2: Reserved.
+ */
+struct nbft_discovery {
+ __u8 structure_id;
+ __u8 flags;
+ __u8 index;
+ __u8 hfi_index;
+ __u8 sec_index;
+ __u8 reserved1;
+ struct nbft_heap_obj discovery_ctrl_addr_obj;
+ struct nbft_heap_obj discovery_ctrl_nqn_obj;
+ __u8 reserved2[14];
+};
+
+/**
+ * enum nbft_discovery_flags - Discovery Descriptor Flags
+ * @NBFT_DISCOVERY_VALID: Descriptor Valid: if set to 1h, then this descriptor
+ * is valid. If cleared to 0h, then this descriptor
+ * is reserved.
+ */
+enum nbft_discovery_flags {
+ NBFT_DISCOVERY_VALID = 1 << 0,
+};
+
+/*
+ * End of NBFT ACPI table definitions
+ */
+
+
+/*
+ * Convenient NBFT table parser ('nbft_info' prefix)
+ */
+
+/**
+ * enum nbft_info_primary_admin_host_flag - Primary Administrative Host Descriptor Flags
+ * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED: Not Indicated by Driver: The driver
+ * that created this NBFT provided no
+ * administrative priority hint for
+ * this NBFT.
+ * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED: Unselected: The driver that created
+ * this NBFT explicitly indicated that
+ * this NBFT should not be prioritized
+ * over any other NBFT.
+ * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED: Selected: The driver that created
+ * this NBFT explicitly indicated that
+ * this NBFT should be prioritized over
+ * any other NBFT.
+ * @NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED: Reserved.
+ */
+enum nbft_info_primary_admin_host_flag {
+ NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED,
+ NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED,
+ NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED,
+ NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED,
+};
+
+/**
+ * struct nbft_info_host - Host Descriptor
+ * @id: Host ID (raw UUID, length = 16 bytes).
+ * @nqn: Host NQN.
+ * @host_id_configured: HostID Configured Flag: value of True indicates that @id
+ * contains administratively-configured value, or driver
+ * default value if False.
+ * @host_nqn_configured: Host NQN Configured Flag: value of True indicates that
+ * @nqn contains administratively-configured value,
+ * or driver default value if False.
+ * @primary: Primary Administrative Host Descriptor, see
+ * &enum nbft_info_primary_admin_host_flag.
+ */
+struct nbft_info_host {
+ unsigned char *id;
+ char *nqn;
+ bool host_id_configured;
+ bool host_nqn_configured;
+ enum nbft_info_primary_admin_host_flag primary;
+};
+
+/**
+ * struct nbft_info_hfi_info_tcp - HFI Transport Info Descriptor - NVMe/TCP
+ * @pci_sbdf: PCI Express Routing ID for the HFI Transport Function.
+ * @mac_addr: MAC Address: The MAC address of this HFI,
+ * in EUI-48TM format.
+ * @vlan: The VLAN identifier if the VLAN is associated with
+ * this HFI, as defined in IEEE 802.1q-2018 or zeroes
+ * if no VLAN is associated with this HFI.
+ * @ip_origin: The source of Ethernet L3 configuration information
+ * used by the driver or 0 if not used.
+ * @ipaddr: The IPv4 or IPv6 address of this HFI.
+ * @subnet_mask_prefix: The IPv4 or IPv6 subnet mask in CIDR routing prefix
+ * notation.
+ * @gateway_ipaddr: The IPv4 or IPv6 address of the IP gateway for this
+ * HFI or zeroes if no IP gateway is specified.
+ * @route_metric: The cost value for the route indicated by this HFI.
+ * @primary_dns_ipaddr: The IPv4 or IPv6 address of the Primary DNS server
+ * for this HFI.
+ * @secondary_dns_ipaddr: The IPv4 or IPv6 address of the Secondary DNS server
+ * for this HFI.
+ * @dhcp_server_ipaddr: The IPv4 or IPv6 address of the DHCP server used
+ * to assign this HFI address.
+ * @host_name: The Host Name string.
+ * @this_hfi_is_default_route: If True, then the BIOS utilized this interface
+ * described by HFI to be the default route with highest
+ * priority. If False, then routes are local to their
+ * own scope.
+ * @dhcp_override: If True, then HFI information was populated
+ * by consuming the DHCP on this interface. If False,
+ * then the HFI information was set administratively
+ * by a configuration interface to the driver and
+ * pre-OS envrionment.
+ */
+struct nbft_info_hfi_info_tcp {
+ __u32 pci_sbdf;
+ __u8 mac_addr[6];
+ __u16 vlan;
+ __u8 ip_origin;
+ char ipaddr[40];
+ __u8 subnet_mask_prefix;
+ char gateway_ipaddr[40];
+ __u16 route_metric;
+ char primary_dns_ipaddr[40];
+ char secondary_dns_ipaddr[40];
+ char dhcp_server_ipaddr[40];
+ char *host_name;
+ bool this_hfi_is_default_route;
+ bool dhcp_override;
+};
+
+/**
+ * struct nbft_info_hfi - Host Fabric Interface (HFI) Descriptor
+ * @index: HFI Descriptor Index: indicates the number of this HFI Descriptor
+ * in the Host Fabric Interface Descriptor List.
+ * @transport: Transport Type string (e.g. 'tcp').
+ * @tcp_info: The HFI Transport Info Descriptor, see &struct nbft_info_hfi_info_tcp.
+ */
+struct nbft_info_hfi {
+ int index;
+ char transport[8];
+ struct nbft_info_hfi_info_tcp tcp_info;
+};
+
+/**
+ * struct nbft_info_discovery - Discovery Descriptor
+ * @index: The number of this Discovery Descriptor in the Discovery
+ * Descriptor List.
+ * @security: The Security Profile Descriptor, see &struct nbft_info_security.
+ * @hfi: The HFI Descriptor associated with this Discovery Descriptor.
+ * See &struct nbft_info_hfi.
+ * @uri: A URI which indicates an NVMe Discovery controller associated
+ * with this Discovery Descriptor.
+ * @nqn: An NVMe Discovery controller NQN.
+ */
+struct nbft_info_discovery {
+ int index;
+ struct nbft_info_security *security;
+ struct nbft_info_hfi *hfi;
+ char *uri;
+ char *nqn;
+};
+
+/**
+ * struct nbft_info_security - Security Profile Descriptor
+ * @index: The number of this Security Profile Descriptor in the Security
+ * Profile Descriptor List.
+ */
+struct nbft_info_security {
+ int index;
+ /* TODO add fields */
+};
+
+/**
+ * enum nbft_info_nid_type - Namespace Identifier Type (NIDT)
+ * @NBFT_INFO_NID_TYPE_NONE: No identifier available.
+ * @NBFT_INFO_NID_TYPE_EUI64: The EUI-64 identifier.
+ * @NBFT_INFO_NID_TYPE_NGUID: The NSGUID identifier.
+ * @NBFT_INFO_NID_TYPE_NS_UUID: The UUID identifier.
+ */
+enum nbft_info_nid_type {
+ NBFT_INFO_NID_TYPE_NONE = 0,
+ NBFT_INFO_NID_TYPE_EUI64 = 1,
+ NBFT_INFO_NID_TYPE_NGUID = 2,
+ NBFT_INFO_NID_TYPE_NS_UUID = 3,
+};
+
+/**
+ * struct nbft_info_subsystem_ns - Subsystem Namespace (SSNS) info
+ * @index: SSNS Descriptor Index in the descriptor list.
+ * @discovery: Primary Discovery Controller associated with
+ * this SSNS Descriptor.
+ * @security: Security Profile Descriptor associated with
+ * this namespace.
+ * @num_hfis: Number of HFIs.
+ * @hfis: List of HFIs associated with this namespace.
+ * Includes the primary HFI at the first position
+ * and all secondary HFIs. This array is null-terminated.
+ * @transport: Transport Type string (e.g. 'tcp').
+ * @traddr: Subsystem Transport Address.
+ * @trsvcid: Subsystem Transport Service Identifier.
+ * @subsys_port_id: The Subsystem Port ID.
+ * @nsid: The Namespace ID of this descriptor or when @nid
+ * should be used instead.
+ * @nid_type: Namespace Identifier Type, see &enum nbft_info_nid_type.
+ * @nid: The Namespace Identifier value.
+ * @subsys_nqn: Subsystem and Namespace NQN.
+ * @pdu_header_digest_required: PDU Header Digest (HDGST) Flag: the use of NVM Header
+ * Digest Enabled is required.
+ * @data_digest_required: Data Digest (DDGST) Flag: the use of NVM Data Digest
+ * Enabled is required.
+ * @controller_id: Controller ID (SSNS Extended Information Descriptor):
+ * The controller ID associated with the Admin Queue
+ * or 0 if not supported.
+ * @asqsz: Admin Submission Queue Size (SSNS Extended Information
+ * Descriptor) or 0 if not supported.
+ * @dhcp_root_path_string: DHCP Root Path Override string (SSNS Extended
+ * Information Descriptor).
+ */
+struct nbft_info_subsystem_ns {
+ int index;
+ struct nbft_info_discovery *discovery;
+ struct nbft_info_security *security;
+ int num_hfis;
+ struct nbft_info_hfi **hfis;
+ char transport[8];
+ char traddr[40];
+ char *trsvcid;
+ __u16 subsys_port_id;
+ __u32 nsid;
+ enum nbft_info_nid_type nid_type;
+ __u8 *nid;
+ char *subsys_nqn;
+ bool pdu_header_digest_required;
+ bool data_digest_required;
+ int controller_id;
+ int asqsz;
+ char *dhcp_root_path_string;
+};
+
+/**
+ * struct nbft_info - The parsed NBFT table data.
+ * @filename: Path to the NBFT table.
+ * @raw_nbft: The original NBFT table contents.
+ * @raw_nbft_size: Size of @raw_nbft.
+ * @host: The Host Descriptor (should match other NBFTs).
+ * @hfi_list: The HFI Descriptor List (null-terminated array).
+ * @security_list: The Security Profile Descriptor List (null-terminated array).
+ * @discovery_list: The Discovery Descriptor List (null-terminated array).
+ * @subsystem_ns_list: The SSNS Descriptor List (null-terminated array).
+ */
+struct nbft_info {
+ char *filename;
+ __u8 *raw_nbft;
+ ssize_t raw_nbft_size;
+ struct nbft_info_host host;
+ struct nbft_info_hfi **hfi_list;
+ struct nbft_info_security **security_list;
+ struct nbft_info_discovery **discovery_list;
+ struct nbft_info_subsystem_ns **subsystem_ns_list;
+};
+
+/**
+ * nvme_nbft_read() - Read and parse contents of an ACPI NBFT table
+ *
+ * @nbft: Parsed NBFT table data.
+ * @filename: Filename of the raw NBFT table to read.
+ *
+ * Read and parse the specified NBFT file into a struct nbft_info.
+ * Free with nvme_nbft_free().
+ *
+ * Return: 0 on success, errno otherwise.
+ */
+int nvme_nbft_read(struct nbft_info **nbft, const char *filename);
+
+/**
+ * nvme_nbft_free() - Free the struct nbft_info and its contents
+ * @nbft: Parsed NBFT table data.
+ */
+void nvme_nbft_free(struct nbft_info *nbft);
+
+#endif
diff --git a/src/nvme/no-json.c b/src/nvme/no-json.c
new file mode 100644
index 0000000..171144e
--- /dev/null
+++ b/src/nvme/no-json.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2023 SUSE Software Solutions
+ *
+ * Authors: Daniel Wagner <dwagner@suse.de>
+ */
+
+#include "tree.h"
+
+#include <errno.h>
+
+int json_read_config(nvme_root_t r, const char *config_file)
+{
+ return -ENOTSUP;
+}
+
+int json_update_config(nvme_root_t r, const char *config_file)
+{
+ return -ENOTSUP;
+}
+
+int json_dump_tree(nvme_root_t r)
+{
+ return -ENOTSUP;
+}
diff --git a/src/nvme/private.h b/src/nvme/private.h
new file mode 100644
index 0000000..3505802
--- /dev/null
+++ b/src/nvme/private.h
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021 SUSE Software Solutions
+ *
+ * Authors: Hannes Reinecke <hare@suse.de>
+ */
+
+#ifndef _LIBNVME_PRIVATE_H
+#define _LIBNVME_PRIVATE_H
+
+#include <ccan/list/list.h>
+#include <poll.h>
+#include <sys/socket.h>
+
+#include "fabrics.h"
+#include "mi.h"
+
+
+char *nvme_ctrl_sysfs_dir(void);
+char *nvme_subsys_sysfs_dir(void);
+char *nvme_ns_sysfs_dir(void);
+
+struct nvme_path {
+ struct list_node entry;
+ struct list_node nentry;
+
+ struct nvme_ctrl *c;
+ struct nvme_ns *n;
+
+ char *name;
+ char *sysfs_dir;
+ char *ana_state;
+ int grpid;
+};
+
+struct nvme_ns {
+ struct list_node entry;
+ struct list_head paths;
+
+ struct nvme_subsystem *s;
+ struct nvme_ctrl *c;
+
+ int fd;
+ __u32 nsid;
+ char *name;
+ char *generic_name;
+ char *sysfs_dir;
+
+ int lba_shift;
+ int lba_size;
+ int meta_size;
+ uint64_t lba_count;
+ uint64_t lba_util;
+
+ uint8_t eui64[8];
+ uint8_t nguid[16];
+ unsigned char uuid[NVME_UUID_LEN];
+ enum nvme_csi csi;
+};
+
+struct nvme_ctrl {
+ struct list_node entry;
+ struct list_head paths;
+ struct list_head namespaces;
+ struct nvme_subsystem *s;
+
+ int fd;
+ char *name;
+ char *sysfs_dir;
+ char *address;
+ char *firmware;
+ char *model;
+ char *state;
+ char *numa_node;
+ char *queue_count;
+ char *serial;
+ char *sqsize;
+ char *transport;
+ char *subsysnqn;
+ char *traddr;
+ char *trsvcid;
+ char *dhchap_key;
+ char *dhchap_ctrl_key;
+ char *cntrltype;
+ char *dctype;
+ char *phy_slot;
+ bool discovery_ctrl;
+ bool unique_discovery_ctrl;
+ bool discovered;
+ bool persistent;
+ struct nvme_fabrics_config cfg;
+};
+
+struct nvme_subsystem {
+ struct list_node entry;
+ struct list_head ctrls;
+ struct list_head namespaces;
+ struct nvme_host *h;
+
+ char *name;
+ char *sysfs_dir;
+ char *subsysnqn;
+ char *model;
+ char *serial;
+ char *firmware;
+ char *subsystype;
+ char *application;
+ char *iopolicy;
+};
+
+struct nvme_host {
+ struct list_node entry;
+ struct list_head subsystems;
+ struct nvme_root *r;
+
+ char *hostnqn;
+ char *hostid;
+ char *dhchap_key;
+ char *hostsymname;
+ bool pdc_enabled;
+ bool pdc_enabled_valid; /* set if pdc_enabled doesn't have an undefined
+ * value */
+};
+
+struct nvme_fabric_options {
+ bool cntlid;
+ bool concat;
+ bool ctrl_loss_tmo;
+ bool data_digest;
+ bool dhchap_ctrl_secret;
+ bool dhchap_secret;
+ bool disable_sqflow;
+ bool discovery;
+ bool duplicate_connect;
+ bool fast_io_fail_tmo;
+ bool hdr_digest;
+ bool host_iface;
+ bool host_traddr;
+ bool hostid;
+ bool hostnqn;
+ bool instance;
+ bool keep_alive_tmo;
+ bool keyring;
+ bool nqn;
+ bool nr_io_queues;
+ bool nr_poll_queues;
+ bool nr_write_queues;
+ bool queue_size;
+ bool reconnect_delay;
+ bool tls;
+ bool tls_key;
+ bool tos;
+ bool traddr;
+ bool transport;
+ bool trsvcid;
+};
+
+struct nvme_root {
+ char *config_file;
+ char *application;
+ struct list_head hosts;
+ struct list_head endpoints; /* MI endpoints */
+ FILE *fp;
+ int log_level;
+ bool log_pid;
+ bool log_timestamp;
+ bool modified;
+ bool mi_probe_enabled;
+ struct nvme_fabric_options *options;
+};
+
+int nvme_set_attr(const char *dir, const char *attr, const char *value);
+
+int json_read_config(nvme_root_t r, const char *config_file);
+
+int json_update_config(nvme_root_t r, const char *config_file);
+
+int json_dump_tree(nvme_root_t r);
+
+nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
+ const char *traddr, const char *host_traddr,
+ const char *host_iface, const char *trsvcid,
+ const char *subsysnqn, nvme_ctrl_t p);
+
+void *__nvme_alloc(size_t len);
+
+#if (LOG_FUNCNAME == 1)
+#define __nvme_log_func __func__
+#else
+#define __nvme_log_func NULL
+#endif
+
+void __attribute__((format(printf, 4, 5)))
+__nvme_msg(nvme_root_t r, int lvl, const char *func, const char *format, ...);
+
+#define nvme_msg(r, lvl, format, ...) \
+ do { \
+ if ((lvl) <= MAX_LOGLEVEL) \
+ __nvme_msg(r, lvl, __nvme_log_func, \
+ format, ##__VA_ARGS__); \
+ } while (0)
+
+#define root_from_ctrl(c) ((c)->s && (c)->s->h ? (c)->s->h->r : NULL)
+#define root_from_ns(n) ((n)->s && (n)->s->h ? (n)->s->h->r : \
+ (n)->c && (n)->c->s && (n)->c->s->h ? (n)->c->s->h->r : \
+ NULL)
+
+/* mi internal headers */
+
+/* internal transport API */
+struct nvme_mi_req {
+ struct nvme_mi_msg_hdr *hdr;
+ size_t hdr_len;
+ void *data;
+ size_t data_len;
+ __u32 mic;
+};
+
+struct nvme_mi_resp {
+ struct nvme_mi_msg_hdr *hdr;
+ size_t hdr_len;
+ void *data;
+ size_t data_len;
+ __u32 mic;
+};
+
+struct nvme_mi_transport {
+ const char *name;
+ bool mic_enabled;
+ int (*submit)(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp);
+ void (*close)(struct nvme_mi_ep *ep);
+ int (*desc_ep)(struct nvme_mi_ep *ep, char *buf, size_t len);
+ int (*check_timeout)(struct nvme_mi_ep *ep, unsigned int timeout);
+};
+
+/* quirks */
+
+/* Set a minimum time between receiving a response from one command and
+ * sending the next request. Some devices may ignore new commands sent too soon
+ * after the previous request, so manually insert a delay
+ */
+#define NVME_QUIRK_MIN_INTER_COMMAND_TIME (1 << 0)
+
+struct nvme_mi_ep {
+ struct nvme_root *root;
+ const struct nvme_mi_transport *transport;
+ void *transport_data;
+ struct list_node root_entry;
+ struct list_head controllers;
+ bool controllers_scanned;
+ unsigned int timeout;
+ unsigned int mprt_max;
+ unsigned long quirks;
+
+ /* inter-command delay, for NVME_QUIRK_MIN_INTER_COMMAND_TIME */
+ unsigned int inter_command_us;
+ struct timespec last_resp_time;
+ bool last_resp_time_valid;
+};
+
+struct nvme_mi_ctrl {
+ struct nvme_mi_ep *ep;
+ __u16 id;
+ struct list_node ep_entry;
+};
+
+struct nvme_mi_ep *nvme_mi_init_ep(struct nvme_root *root);
+void nvme_mi_ep_probe(struct nvme_mi_ep *ep);
+
+/* for tests, we need to calculate the correct MICs */
+__u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len);
+
+/* we have a facility to mock MCTP socket operations in the mi-mctp transport,
+ * using this ops type. This should only be used for test, and isn't exposed
+ * in the shared lib */;
+struct mctp_ioc_tag_ctl;
+struct __mi_mctp_socket_ops {
+ int (*socket)(int, int, int);
+ ssize_t (*sendmsg)(int, const struct msghdr *, int);
+ ssize_t (*recvmsg)(int, struct msghdr *, int);
+ int (*poll)(struct pollfd *, nfds_t, int);
+ int (*ioctl_tag)(int, unsigned long, struct mctp_ioc_tag_ctl *);
+};
+void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops);
+
+#endif /* _LIBNVME_PRIVATE_H */
diff --git a/src/nvme/tree.c b/src/nvme/tree.c
new file mode 100644
index 0000000..344f8bc
--- /dev/null
+++ b/src/nvme/tree.c
@@ -0,0 +1,2713 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+#include <dirent.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ccan/endian/endian.h>
+#include <ccan/list/list.h>
+
+#include "cleanup.h"
+#include "ioctl.h"
+#include "linux.h"
+#include "filters.h"
+#include "tree.h"
+#include "filters.h"
+#include "util.h"
+#include "fabrics.h"
+#include "log.h"
+#include "private.h"
+
+/**
+ * struct candidate_args - Used to look for a controller matching these parameters
+ * @transport: Transport type: loop, fc, rdma, tcp
+ * @traddr: Transport address (destination address)
+ * @trsvcid: Transport service ID
+ * @subsysnqn: Subsystem NQN
+ * @host_traddr: Host transport address (source address)
+ * @host_iface: Host interface for connection (tcp only)
+ * @iface_list: Interface list (tcp only)
+ * @addreq: Address comparison function (for traddr, host-traddr)
+ * @well_known_nqn: Set to "true" when @subsysnqn is the well-known NQN
+ */
+struct candidate_args {
+ const char *transport;
+ const char *traddr;
+ const char *trsvcid;
+ const char *subsysnqn;
+ const char *host_traddr;
+ const char *host_iface;
+ struct ifaddrs *iface_list;
+ bool (*addreq)(const char *, const char *);
+ bool well_known_nqn;
+};
+typedef bool (*ctrl_match_t)(struct nvme_ctrl *c, struct candidate_args *candidate);
+
+#define PATH_SYSFS_SLOTS "/sys/bus/pci/slots"
+
+static char *nvme_slots_sysfs_dir(void)
+{
+ char *basepath = getenv("LIBNVME_SYSFS_PATH");
+ char *str;
+
+ if (!basepath)
+ return strdup(PATH_SYSFS_SLOTS);
+
+ if (!asprintf(&str, "%s" PATH_SYSFS_SLOTS, basepath))
+ return NULL;
+
+ return str;
+}
+
+static struct nvme_host *default_host;
+
+static void __nvme_free_host(nvme_host_t h);
+static void __nvme_free_ctrl(nvme_ctrl_t c);
+static int nvme_subsystem_scan_namespace(nvme_root_t r,
+ struct nvme_subsystem *s, char *name,
+ nvme_scan_filter_t f, void *f_args);
+static int nvme_init_subsystem(nvme_subsystem_t s, const char *name);
+static int nvme_scan_subsystem(nvme_root_t r, const char *name,
+ nvme_scan_filter_t f, void *f_args);
+static int nvme_ctrl_scan_namespace(nvme_root_t r, struct nvme_ctrl *c,
+ char *name);
+static int nvme_ctrl_scan_path(nvme_root_t r, struct nvme_ctrl *c, char *name);
+
+/**
+ * Compare two C strings and handle NULL pointers gracefully.
+ * Return true if both pointers are equal (including both set to NULL).
+ * Return false if one and only one of the two pointers is NULL.
+ * Perform string comparisong only if both pointers are not NULL and
+ * return true if both strings are the same, false otherwise.
+ */
+static bool streq0(const char *s1, const char *s2)
+{
+ if (s1 == s2)
+ return true;
+ if (!s1 || !s2)
+ return false;
+ return !strcmp(s1, s2);
+}
+
+/**
+ * Same as streq0() but ignore the case of the characters.
+ */
+static bool streqcase0(const char *s1, const char *s2)
+{
+ if (s1 == s2)
+ return true;
+ if (!s1 || !s2)
+ return false;
+ return !strcasecmp(s1, s2);
+}
+
+struct dirents {
+ struct dirent **ents;
+ int num;
+};
+
+static void cleanup_dirents(struct dirents *ents)
+{
+ while (ents->num > 0)
+ free(ents->ents[--ents->num]);
+ free(ents->ents);
+}
+
+#define _cleanup_dirents_ __cleanup__(cleanup_dirents)
+
+nvme_host_t nvme_default_host(nvme_root_t r)
+{
+ struct nvme_host *h;
+ _cleanup_free_ char *hostnqn = NULL;
+ _cleanup_free_ char *hostid = NULL;
+
+ hostnqn = nvmf_hostnqn_from_file();
+ if (!hostnqn)
+ hostnqn = nvmf_hostnqn_generate();
+ hostid = nvmf_hostid_from_file();
+
+ h = nvme_lookup_host(r, hostnqn, hostid);
+
+ nvme_host_set_hostsymname(h, NULL);
+
+ default_host = h;
+ return h;
+}
+
+int nvme_scan_topology(struct nvme_root *r, nvme_scan_filter_t f, void *f_args)
+{
+ _cleanup_dirents_ struct dirents subsys = {}, ctrls = {};
+ int i, ret;
+
+ if (!r)
+ return 0;
+
+ ctrls.num = nvme_scan_ctrls(&ctrls.ents);
+ if (ctrls.num < 0) {
+ nvme_msg(r, LOG_DEBUG, "failed to scan ctrls: %s\n",
+ strerror(errno));
+ return ctrls.num;
+ }
+
+ for (i = 0; i < ctrls.num; i++) {
+ nvme_ctrl_t c = nvme_scan_ctrl(r, ctrls.ents[i]->d_name);
+ if (!c) {
+ nvme_msg(r, LOG_DEBUG, "failed to scan ctrl %s: %s\n",
+ ctrls.ents[i]->d_name, strerror(errno));
+ continue;
+ }
+ if ((f) && !f(NULL, c, NULL, f_args)) {
+ nvme_msg(r, LOG_DEBUG, "filter out controller %s\n",
+ ctrls.ents[i]->d_name);
+ nvme_free_ctrl(c);
+ }
+ }
+
+ subsys.num = nvme_scan_subsystems(&subsys.ents);
+ if (subsys.num < 0) {
+ nvme_msg(r, LOG_DEBUG, "failed to scan subsystems: %s\n",
+ strerror(errno));
+ return subsys.num;
+ }
+
+ for (i = 0; i < subsys.num; i++) {
+ ret = nvme_scan_subsystem(
+ r, subsys.ents[i]->d_name, f, f_args);
+ if (ret < 0) {
+ nvme_msg(r, LOG_DEBUG,
+ "failed to scan subsystem %s: %s\n",
+ subsys.ents[i]->d_name, strerror(errno));
+ }
+ }
+
+ return 0;
+}
+
+nvme_root_t nvme_create_root(FILE *fp, int log_level)
+{
+ struct nvme_root *r = calloc(1, sizeof(*r));
+
+ if (!r) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ r->log_level = log_level;
+ r->fp = stderr;
+ if (fp)
+ r->fp = fp;
+ list_head_init(&r->hosts);
+ list_head_init(&r->endpoints);
+ nvme_set_root(r);
+ return r;
+}
+
+int nvme_read_config(nvme_root_t r, const char *config_file)
+{
+ int err = -1;
+
+ if (!r || !config_file) {
+ errno = ENODEV;
+ return err;
+ }
+
+ r->config_file = strdup(config_file);
+ if (!r->config_file) {
+ errno = ENOMEM;
+ return err;
+ }
+ err = json_read_config(r, config_file);
+ /*
+ * The json configuration file is optional,
+ * so ignore errors when opening the file.
+ */
+ if (err < 0 && errno != EPROTO)
+ err = 0;
+
+ return err;
+}
+
+nvme_root_t nvme_scan(const char *config_file)
+{
+ nvme_root_t r = nvme_create_root(NULL, DEFAULT_LOGLEVEL);
+
+ nvme_scan_topology(r, NULL, NULL);
+ nvme_read_config(r, config_file);
+ return r;
+}
+
+int nvme_update_config(nvme_root_t r)
+{
+ if (!r->modified || !r->config_file)
+ return 0;
+
+ return json_update_config(r, r->config_file);
+}
+
+int nvme_dump_config(nvme_root_t r)
+{
+ return json_update_config(r, NULL);
+}
+
+int nvme_dump_tree(nvme_root_t r)
+{
+ return json_dump_tree(r);
+}
+
+const char *nvme_root_get_application(nvme_root_t r)
+{
+ return r->application;
+}
+
+void nvme_root_set_application(nvme_root_t r, const char *a)
+{
+ if (r->application) {
+ free(r->application);
+ r->application = NULL;
+ }
+ if (a)
+ r->application = strdup(a);
+}
+
+nvme_host_t nvme_first_host(nvme_root_t r)
+{
+ return list_top(&r->hosts, struct nvme_host, entry);
+}
+
+nvme_host_t nvme_next_host(nvme_root_t r, nvme_host_t h)
+{
+ return h ? list_next(&r->hosts, h, entry) : NULL;
+}
+
+nvme_root_t nvme_host_get_root(nvme_host_t h)
+{
+ return h->r;
+}
+
+const char *nvme_host_get_hostnqn(nvme_host_t h)
+{
+ return h->hostnqn;
+}
+
+const char *nvme_host_get_hostid(nvme_host_t h)
+{
+ return h->hostid;
+}
+
+const char *nvme_host_get_hostsymname(nvme_host_t h)
+{
+ return h->hostsymname;
+}
+
+void nvme_host_set_hostsymname(nvme_host_t h, const char *hostsymname)
+{
+ if (h->hostsymname) {
+ free(h->hostsymname);
+ h->hostsymname = NULL;
+ }
+ if (hostsymname)
+ h->hostsymname = strdup(hostsymname);
+}
+
+const char *nvme_host_get_dhchap_key(nvme_host_t h)
+{
+ return h->dhchap_key;
+}
+
+void nvme_host_set_dhchap_key(nvme_host_t h, const char *key)
+{
+ if (h->dhchap_key) {
+ free(h->dhchap_key);
+ h->dhchap_key = NULL;
+ }
+ if (key)
+ h->dhchap_key = strdup(key);
+}
+
+void nvme_host_set_pdc_enabled(nvme_host_t h, bool enabled)
+{
+ h->pdc_enabled_valid = true;
+ h->pdc_enabled = enabled;
+}
+
+bool nvme_host_is_pdc_enabled(nvme_host_t h, bool fallback)
+{
+ if (h->pdc_enabled_valid)
+ return h->pdc_enabled;
+ return fallback;
+}
+
+nvme_subsystem_t nvme_first_subsystem(nvme_host_t h)
+{
+ return list_top(&h->subsystems, struct nvme_subsystem, entry);
+}
+
+nvme_subsystem_t nvme_next_subsystem(nvme_host_t h, nvme_subsystem_t s)
+{
+ return s ? list_next(&h->subsystems, s, entry) : NULL;
+}
+
+void nvme_refresh_topology(nvme_root_t r)
+{
+ struct nvme_host *h, *_h;
+
+ nvme_for_each_host_safe(r, h, _h)
+ __nvme_free_host(h);
+ nvme_scan_topology(r, NULL, NULL);
+}
+
+void nvme_free_tree(nvme_root_t r)
+{
+ struct nvme_host *h, *_h;
+
+ free(r->options);
+ nvme_for_each_host_safe(r, h, _h)
+ __nvme_free_host(h);
+ if (r->config_file)
+ free(r->config_file);
+ if (r->application)
+ free(r->application);
+ nvme_set_root(NULL);
+ free(r);
+}
+
+void nvme_root_release_fds(nvme_root_t r)
+{
+ struct nvme_host *h, *_h;
+
+ nvme_for_each_host_safe(r, h, _h)
+ nvme_host_release_fds(h);
+}
+
+const char *nvme_subsystem_get_nqn(nvme_subsystem_t s)
+{
+ return s->subsysnqn;
+}
+
+const char *nvme_subsystem_get_sysfs_dir(nvme_subsystem_t s)
+{
+ return s->sysfs_dir;
+}
+
+const char *nvme_subsystem_get_name(nvme_subsystem_t s)
+{
+ return s->name;
+}
+
+const char *nvme_subsystem_get_type(nvme_subsystem_t s)
+{
+ return s->subsystype;
+}
+
+const char *nvme_subsystem_get_application(nvme_subsystem_t s)
+{
+ return s->application;
+}
+
+void nvme_subsystem_set_application(nvme_subsystem_t s, const char *a)
+{
+ if (s->application) {
+ free(s->application);
+ s->application = NULL;
+ }
+ if (a)
+ s->application = strdup(a);
+}
+
+const char *nvme_subsystem_get_iopolicy(nvme_subsystem_t s)
+{
+ return s->iopolicy;
+}
+
+nvme_ctrl_t nvme_subsystem_first_ctrl(nvme_subsystem_t s)
+{
+ return list_top(&s->ctrls, struct nvme_ctrl, entry);
+}
+
+nvme_ctrl_t nvme_subsystem_next_ctrl(nvme_subsystem_t s, nvme_ctrl_t c)
+{
+ return c ? list_next(&s->ctrls, c, entry) : NULL;
+}
+
+nvme_host_t nvme_subsystem_get_host(nvme_subsystem_t s)
+{
+ return s->h;
+}
+
+nvme_ns_t nvme_subsystem_first_ns(nvme_subsystem_t s)
+{
+ return list_top(&s->namespaces, struct nvme_ns, entry);
+}
+
+nvme_ns_t nvme_subsystem_next_ns(nvme_subsystem_t s, nvme_ns_t n)
+{
+ return n ? list_next(&s->namespaces, n, entry) : NULL;
+}
+
+nvme_path_t nvme_namespace_first_path(nvme_ns_t ns)
+{
+ return list_top(&ns->paths, struct nvme_path, nentry);
+}
+
+nvme_path_t nvme_namespace_next_path(nvme_ns_t ns, nvme_path_t p)
+{
+ return p ? list_next(&ns->paths, p, nentry) : NULL;
+}
+
+static void __nvme_free_ns(struct nvme_ns *n)
+{
+ list_del_init(&n->entry);
+ nvme_ns_release_fd(n);
+ free(n->generic_name);
+ free(n->name);
+ free(n->sysfs_dir);
+ free(n);
+}
+
+/* Stub for SWIG */
+void nvme_free_ns(struct nvme_ns *n)
+{
+ __nvme_free_ns(n);
+}
+
+static void __nvme_free_subsystem(struct nvme_subsystem *s)
+{
+ struct nvme_ctrl *c, *_c;
+ struct nvme_ns *n, *_n;
+
+ list_del_init(&s->entry);
+ nvme_subsystem_for_each_ctrl_safe(s, c, _c)
+ __nvme_free_ctrl(c);
+
+ nvme_subsystem_for_each_ns_safe(s, n, _n)
+ __nvme_free_ns(n);
+
+ if (s->name)
+ free(s->name);
+ free(s->sysfs_dir);
+ free(s->subsysnqn);
+ if (s->model)
+ free(s->model);
+ if (s->serial)
+ free(s->serial);
+ if (s->firmware)
+ free(s->firmware);
+ if (s->subsystype)
+ free(s->subsystype);
+ if (s->application)
+ free(s->application);
+ if (s->iopolicy)
+ free(s->iopolicy);
+ free(s);
+}
+
+void nvme_subsystem_release_fds(struct nvme_subsystem *s)
+{
+ struct nvme_ctrl *c, *_c;
+ struct nvme_ns *n, *_n;
+
+ nvme_subsystem_for_each_ctrl_safe(s, c, _c)
+ nvme_ctrl_release_fd(c);
+
+ nvme_subsystem_for_each_ns_safe(s, n, _n)
+ nvme_ns_release_fd(n);
+}
+
+/*
+ * Stub for SWIG
+ */
+void nvme_free_subsystem(nvme_subsystem_t s)
+{
+}
+
+struct nvme_subsystem *nvme_alloc_subsystem(struct nvme_host *h,
+ const char *name,
+ const char *subsysnqn)
+{
+ struct nvme_subsystem *s;
+
+ s = calloc(1, sizeof(*s));
+ if (!s)
+ return NULL;
+
+ s->h = h;
+ s->subsysnqn = strdup(subsysnqn);
+ if (name)
+ nvme_init_subsystem(s, name);
+ list_head_init(&s->ctrls);
+ list_head_init(&s->namespaces);
+ list_node_init(&s->entry);
+ list_add(&h->subsystems, &s->entry);
+ h->r->modified = true;
+ return s;
+}
+
+struct nvme_subsystem *nvme_lookup_subsystem(struct nvme_host *h,
+ const char *name,
+ const char *subsysnqn)
+{
+ struct nvme_subsystem *s;
+
+ nvme_for_each_subsystem(h, s) {
+ if (subsysnqn && s->subsysnqn &&
+ strcmp(s->subsysnqn, subsysnqn))
+ continue;
+ if (name && s->name &&
+ strcmp(s->name, name))
+ continue;
+ if (h->r->application) {
+ if (!s->application)
+ continue;
+ if (strcmp(h->r->application, s->application))
+ continue;
+ }
+ return s;
+ }
+ return nvme_alloc_subsystem(h, name, subsysnqn);
+}
+
+static void __nvme_free_host(struct nvme_host *h)
+{
+ struct nvme_subsystem *s, *_s;
+
+ list_del_init(&h->entry);
+ nvme_for_each_subsystem_safe(h, s, _s)
+ __nvme_free_subsystem(s);
+ free(h->hostnqn);
+ if (h->hostid)
+ free(h->hostid);
+ if (h->dhchap_key)
+ free(h->dhchap_key);
+ nvme_host_set_hostsymname(h, NULL);
+ h->r->modified = true;
+ free(h);
+}
+
+void nvme_host_release_fds(struct nvme_host *h)
+{
+ struct nvme_subsystem *s, *_s;
+
+ nvme_for_each_subsystem_safe(h, s, _s)
+ nvme_subsystem_release_fds(s);
+}
+
+/* Stub for SWIG */
+void nvme_free_host(struct nvme_host *h)
+{
+ __nvme_free_host(h);
+}
+
+struct nvme_host *nvme_lookup_host(nvme_root_t r, const char *hostnqn,
+ const char *hostid)
+{
+ struct nvme_host *h;
+
+ if (!hostnqn)
+ return NULL;
+ nvme_for_each_host(r, h) {
+ if (strcmp(h->hostnqn, hostnqn))
+ continue;
+ if (hostid && (!h->hostid ||
+ strcmp(h->hostid, hostid)))
+ continue;
+ return h;
+ }
+ h = calloc(1,sizeof(*h));
+ if (!h)
+ return NULL;
+ h->hostnqn = strdup(hostnqn);
+ if (hostid)
+ h->hostid = strdup(hostid);
+ list_head_init(&h->subsystems);
+ list_node_init(&h->entry);
+ h->r = r;
+ list_add(&r->hosts, &h->entry);
+ r->modified = true;
+
+ return h;
+}
+
+static int nvme_subsystem_scan_namespaces(nvme_root_t r, nvme_subsystem_t s,
+ nvme_scan_filter_t f, void *f_args)
+{
+ _cleanup_dirents_ struct dirents namespaces = {};
+ int i, ret;
+
+ namespaces.num = nvme_scan_subsystem_namespaces(s, &namespaces.ents);
+ if (namespaces.num < 0) {
+ nvme_msg(r, LOG_DEBUG,
+ "failed to scan namespaces for subsys %s: %s\n",
+ s->subsysnqn, strerror(errno));
+ return namespaces.num;
+ }
+
+ for (i = 0; i < namespaces.num; i++) {
+ ret = nvme_subsystem_scan_namespace(r, s,
+ namespaces.ents[i]->d_name, f, f_args);
+ if (ret < 0)
+ nvme_msg(r, LOG_DEBUG,
+ "failed to scan namespace %s: %s\n",
+ namespaces.ents[i]->d_name, strerror(errno));
+ }
+
+ return 0;
+}
+
+static int nvme_init_subsystem(nvme_subsystem_t s, const char *name)
+{
+ _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir();
+ char *path;
+
+ if (asprintf(&path, "%s/%s", subsys_dir, name) < 0)
+ return -1;
+
+ s->model = nvme_get_attr(path, "model");
+ if (!s->model)
+ s->model = strdup("undefined");
+ s->serial = nvme_get_attr(path, "serial");
+ s->firmware = nvme_get_attr(path, "firmware_rev");
+ s->subsystype = nvme_get_attr(path, "subsystype");
+ if (!s->subsystype) {
+ if (!strcmp(s->subsysnqn, NVME_DISC_SUBSYS_NAME))
+ s->subsystype = strdup("discovery");
+ else
+ s->subsystype = strdup("nvm");
+ }
+ s->name = strdup(name);
+ s->sysfs_dir = (char *)path;
+ if (s->h->r->application)
+ s->application = strdup(s->h->r->application);
+ s->iopolicy = nvme_get_attr(path, "iopolicy");
+
+ return 0;
+}
+
+static bool __nvme_scan_subsystem(struct nvme_root *r, nvme_subsystem_t s,
+ nvme_scan_filter_t f, void *f_args)
+{
+ if (f && !f(s, NULL, NULL, f_args)) {
+ nvme_msg(r, LOG_DEBUG, "filter out subsystem %s\n", s->name);
+ __nvme_free_subsystem(s);
+ return false;
+ }
+ nvme_subsystem_scan_namespaces(r, s, f, f_args);
+ return true;
+}
+
+static int nvme_scan_subsystem(struct nvme_root *r, const char *name,
+ nvme_scan_filter_t f, void *f_args)
+{
+ struct nvme_subsystem *s = NULL, *_s;
+ _cleanup_free_ char *path = NULL, *subsysnqn = NULL;
+ _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir();
+ nvme_host_t h = NULL;
+ int ret;
+
+ nvme_msg(r, LOG_DEBUG, "scan subsystem %s\n", name);
+ ret = asprintf(&path, "%s/%s", subsys_dir, name);
+ if (ret < 0)
+ return ret;
+
+ subsysnqn = nvme_get_attr(path, "subsysnqn");
+ if (!subsysnqn) {
+ errno = ENODEV;
+ return -1;
+ }
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, _s) {
+ /*
+ * We are always called after nvme_scan_ctrl(),
+ * so any subsystem we're interested at _must_
+ * have a name.
+ */
+ if (!_s->name)
+ continue;
+ if (strcmp(_s->name, name))
+ continue;
+ if (!__nvme_scan_subsystem(r, _s, f, f_args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ s = _s;
+ }
+ }
+ if (!s) {
+ /*
+ * Subsystem with non-matching controller. odd.
+ * Create a subsystem with the default host
+ * and hope for the best.
+ */
+ nvme_msg(r, LOG_DEBUG, "creating detached subsystem '%s'\n",
+ name);
+ h = nvme_default_host(r);
+ s = nvme_alloc_subsystem(h, name, subsysnqn);
+ if (!s) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (!__nvme_scan_subsystem(r, s, f, f_args)) {
+ errno = EINVAL;
+ return -1;
+ }
+ } else if (strcmp(s->subsysnqn, subsysnqn)) {
+ nvme_msg(r, LOG_DEBUG, "NQN mismatch for subsystem '%s'\n",
+ name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+nvme_ctrl_t nvme_path_get_ctrl(nvme_path_t p)
+{
+ return p->c;
+}
+
+nvme_ns_t nvme_path_get_ns(nvme_path_t p)
+{
+ return p->n;
+}
+
+const char *nvme_path_get_sysfs_dir(nvme_path_t p)
+{
+ return p->sysfs_dir;
+}
+
+const char *nvme_path_get_name(nvme_path_t p)
+{
+ return p->name;
+}
+
+const char *nvme_path_get_ana_state(nvme_path_t p)
+{
+ return p->ana_state;
+}
+
+void nvme_free_path(struct nvme_path *p)
+{
+ list_del_init(&p->entry);
+ list_del_init(&p->nentry);
+ free(p->name);
+ free(p->sysfs_dir);
+ free(p->ana_state);
+ free(p);
+}
+
+static void nvme_subsystem_set_path_ns(nvme_subsystem_t s, nvme_path_t p)
+{
+ char n_name[32] = { };
+ int i, c, nsid, ret;
+ nvme_ns_t n;
+
+ ret = sscanf(nvme_path_get_name(p), "nvme%dc%dn%d", &i, &c, &nsid);
+ if (ret != 3)
+ return;
+
+ sprintf(n_name, "nvme%dn%d", i, nsid);
+ nvme_subsystem_for_each_ns(s, n) {
+ if (!strcmp(n_name, nvme_ns_get_name(n))) {
+ list_add(&n->paths, &p->nentry);
+ p->n = n;
+ }
+ }
+}
+
+static int nvme_ctrl_scan_path(nvme_root_t r, struct nvme_ctrl *c, char *name)
+{
+ struct nvme_path *p;
+ _cleanup_free_ char *path = NULL, *grpid = NULL;
+ int ret;
+
+ nvme_msg(r, LOG_DEBUG, "scan controller %s path %s\n",
+ c->name, name);
+ if (!c->s) {
+ errno = ENXIO;
+ return -1;
+ }
+ ret = asprintf(&path, "%s/%s", c->sysfs_dir, name);
+ if (ret < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ p = calloc(1, sizeof(*p));
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ p->c = c;
+ p->name = strdup(name);
+ p->sysfs_dir = path;
+ path = NULL;
+ p->ana_state = nvme_get_path_attr(p, "ana_state");
+ if (!p->ana_state)
+ p->ana_state = strdup("optimized");
+
+ grpid = nvme_get_path_attr(p, "ana_grpid");
+ if (grpid) {
+ sscanf(grpid, "%d", &p->grpid);
+ }
+
+ list_node_init(&p->nentry);
+ nvme_subsystem_set_path_ns(c->s, p);
+ list_node_init(&p->entry);
+ list_add(&c->paths, &p->entry);
+ return 0;
+}
+
+int nvme_ctrl_get_fd(nvme_ctrl_t c)
+{
+ if (c->fd < 0) {
+ c->fd = nvme_open(c->name);
+ if (c->fd < 0)
+ nvme_msg(root_from_ctrl(c), LOG_ERR,
+ "Failed to open ctrl %s, errno %d\n",
+ c->name, errno);
+ }
+ return c->fd;
+}
+
+void nvme_ctrl_release_fd(nvme_ctrl_t c)
+{
+ if (c->fd < 0)
+ return;
+
+ close(c->fd);
+ c->fd = -1;
+}
+
+nvme_subsystem_t nvme_ctrl_get_subsystem(nvme_ctrl_t c)
+{
+ return c->s;
+}
+
+const char *nvme_ctrl_get_name(nvme_ctrl_t c)
+{
+ return c->name;
+}
+
+const char *nvme_ctrl_get_sysfs_dir(nvme_ctrl_t c)
+{
+ return c->sysfs_dir;
+}
+
+const char *nvme_ctrl_get_subsysnqn(nvme_ctrl_t c)
+{
+ return c->s ? c->s->subsysnqn : c->subsysnqn;
+}
+
+const char *nvme_ctrl_get_address(nvme_ctrl_t c)
+{
+ return c->address ? c->address : "";
+}
+
+char *nvme_ctrl_get_src_addr(nvme_ctrl_t c, char *src_addr, size_t src_addr_len)
+{
+ size_t l;
+ char *p;
+
+ if (!c->address)
+ return NULL;
+
+ p = strstr(c->address, "src_addr=");
+ if (!p)
+ return NULL;
+
+ p += strlen("src_addr=");
+ l = strcspn(p, ",%"); /* % to eliminate IPv6 scope (if present) */
+ if (l >= src_addr_len) {
+ nvme_msg(root_from_ctrl(c), LOG_ERR,
+ "Buffer for src_addr is too small (%zu must be > %zu)\n",
+ src_addr_len, l);
+ return NULL;
+ }
+
+ strncpy(src_addr, p, l);
+ src_addr[l] = '\0';
+ return src_addr;
+}
+
+const char *nvme_ctrl_get_phy_slot(nvme_ctrl_t c)
+{
+ return c->phy_slot ? c->phy_slot : "";
+}
+
+const char *nvme_ctrl_get_firmware(nvme_ctrl_t c)
+{
+ return c->firmware;
+}
+
+const char *nvme_ctrl_get_model(nvme_ctrl_t c)
+{
+ return c->model;
+}
+
+const char *nvme_ctrl_get_state(nvme_ctrl_t c)
+{
+ char *state = c->state;
+
+ c->state = nvme_get_ctrl_attr(c, "state");
+ if (state)
+ free(state);
+ return c->state;
+}
+
+const char *nvme_ctrl_get_numa_node(nvme_ctrl_t c)
+{
+ return c->numa_node;
+}
+
+const char *nvme_ctrl_get_queue_count(nvme_ctrl_t c)
+{
+ return c->queue_count;
+}
+
+const char *nvme_ctrl_get_serial(nvme_ctrl_t c)
+{
+ return c->serial;
+}
+
+const char *nvme_ctrl_get_sqsize(nvme_ctrl_t c)
+{
+ return c->sqsize;
+}
+
+const char *nvme_ctrl_get_transport(nvme_ctrl_t c)
+{
+ return c->transport;
+}
+
+const char *nvme_ctrl_get_traddr(nvme_ctrl_t c)
+{
+ return c->traddr;
+}
+
+const char *nvme_ctrl_get_trsvcid(nvme_ctrl_t c)
+{
+ return c->trsvcid;
+}
+
+const char *nvme_ctrl_get_host_traddr(nvme_ctrl_t c)
+{
+ return c->cfg.host_traddr;
+}
+
+const char *nvme_ctrl_get_host_iface(nvme_ctrl_t c)
+{
+ return c->cfg.host_iface;
+}
+
+struct nvme_fabrics_config *nvme_ctrl_get_config(nvme_ctrl_t c)
+{
+ return &c->cfg;
+}
+
+const char *nvme_ctrl_get_dhchap_host_key(nvme_ctrl_t c)
+{
+ return c->dhchap_key;
+}
+
+void nvme_ctrl_set_dhchap_host_key(nvme_ctrl_t c, const char *key)
+{
+ if (c->dhchap_key) {
+ free(c->dhchap_key);
+ c->dhchap_key = NULL;
+ }
+ if (key)
+ c->dhchap_key = strdup(key);
+}
+
+const char *nvme_ctrl_get_dhchap_key(nvme_ctrl_t c)
+{
+ return c->dhchap_ctrl_key;
+}
+
+void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key)
+{
+ if (c->dhchap_ctrl_key) {
+ free(c->dhchap_ctrl_key);
+ c->dhchap_ctrl_key = NULL;
+ }
+ if (key)
+ c->dhchap_ctrl_key = strdup(key);
+}
+
+void nvme_ctrl_set_discovered(nvme_ctrl_t c, bool discovered)
+{
+ c->discovered = discovered;
+}
+
+bool nvme_ctrl_is_discovered(nvme_ctrl_t c)
+{
+ return c->discovered;
+}
+
+void nvme_ctrl_set_persistent(nvme_ctrl_t c, bool persistent)
+{
+ c->persistent = persistent;
+}
+
+bool nvme_ctrl_is_persistent(nvme_ctrl_t c)
+{
+ return c->persistent;
+}
+
+void nvme_ctrl_set_discovery_ctrl(nvme_ctrl_t c, bool discovery)
+{
+ c->discovery_ctrl = discovery;
+}
+
+bool nvme_ctrl_is_discovery_ctrl(nvme_ctrl_t c)
+{
+ return c->discovery_ctrl;
+}
+
+void nvme_ctrl_set_unique_discovery_ctrl(nvme_ctrl_t c, bool unique)
+{
+ c->unique_discovery_ctrl = unique;
+}
+
+bool nvme_ctrl_is_unique_discovery_ctrl(nvme_ctrl_t c)
+{
+ return c->unique_discovery_ctrl;
+}
+
+int nvme_ctrl_identify(nvme_ctrl_t c, struct nvme_id_ctrl *id)
+{
+ return nvme_identify_ctrl(nvme_ctrl_get_fd(c), id);
+}
+
+nvme_ns_t nvme_ctrl_first_ns(nvme_ctrl_t c)
+{
+ return list_top(&c->namespaces, struct nvme_ns, entry);
+}
+
+nvme_ns_t nvme_ctrl_next_ns(nvme_ctrl_t c, nvme_ns_t n)
+{
+ return n ? list_next(&c->namespaces, n, entry) : NULL;
+}
+
+nvme_path_t nvme_ctrl_first_path(nvme_ctrl_t c)
+{
+ return list_top(&c->paths, struct nvme_path, entry);
+}
+
+nvme_path_t nvme_ctrl_next_path(nvme_ctrl_t c, nvme_path_t p)
+{
+ return p ? list_next(&c->paths, p, entry) : NULL;
+}
+
+#define FREE_CTRL_ATTR(a) \
+ do { if (a) { free(a); (a) = NULL; } } while (0)
+void nvme_deconfigure_ctrl(nvme_ctrl_t c)
+{
+ nvme_ctrl_release_fd(c);
+ FREE_CTRL_ATTR(c->name);
+ FREE_CTRL_ATTR(c->sysfs_dir);
+ FREE_CTRL_ATTR(c->firmware);
+ FREE_CTRL_ATTR(c->model);
+ FREE_CTRL_ATTR(c->state);
+ FREE_CTRL_ATTR(c->numa_node);
+ FREE_CTRL_ATTR(c->queue_count);
+ FREE_CTRL_ATTR(c->serial);
+ FREE_CTRL_ATTR(c->sqsize);
+ FREE_CTRL_ATTR(c->dhchap_key);
+ FREE_CTRL_ATTR(c->dhchap_ctrl_key);
+ FREE_CTRL_ATTR(c->address);
+ FREE_CTRL_ATTR(c->dctype);
+ FREE_CTRL_ATTR(c->cntrltype);
+ FREE_CTRL_ATTR(c->phy_slot);
+}
+
+int nvme_disconnect_ctrl(nvme_ctrl_t c)
+{
+ nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL;
+ int ret;
+
+ ret = nvme_set_attr(nvme_ctrl_get_sysfs_dir(c),
+ "delete_controller", "1");
+ if (ret < 0) {
+ nvme_msg(r, LOG_ERR, "%s: failed to disconnect, error %d\n",
+ c->name, errno);
+ return ret;
+ }
+ nvme_msg(r, LOG_INFO, "%s: %s disconnected\n", c->name, c->subsysnqn);
+ nvme_deconfigure_ctrl(c);
+ return 0;
+}
+
+void nvme_unlink_ctrl(nvme_ctrl_t c)
+{
+ list_del_init(&c->entry);
+ c->s = NULL;
+}
+
+static void __nvme_free_ctrl(nvme_ctrl_t c)
+{
+ struct nvme_path *p, *_p;
+ struct nvme_ns *n, *_n;
+
+ nvme_unlink_ctrl(c);
+
+ nvme_ctrl_for_each_path_safe(c, p, _p)
+ nvme_free_path(p);
+
+ nvme_ctrl_for_each_ns_safe(c, n, _n)
+ __nvme_free_ns(n);
+
+ nvme_deconfigure_ctrl(c);
+
+ FREE_CTRL_ATTR(c->transport);
+ FREE_CTRL_ATTR(c->subsysnqn);
+ FREE_CTRL_ATTR(c->traddr);
+ FREE_CTRL_ATTR(c->cfg.host_traddr);
+ FREE_CTRL_ATTR(c->cfg.host_iface);
+ FREE_CTRL_ATTR(c->trsvcid);
+ free(c);
+}
+
+void nvme_free_ctrl(nvme_ctrl_t c)
+{
+ __nvme_free_ctrl(c);
+}
+
+static bool traddr_is_hostname(const char *transport, const char *traddr)
+{
+ char addrstr[NVMF_TRADDR_SIZE];
+
+ if (!traddr || !transport)
+ return false;
+ if (!strcmp(traddr, "none"))
+ return false;
+ if (strcmp(transport, "tcp") &&
+ strcmp(transport, "rdma"))
+ return false;
+ if (inet_pton(AF_INET, traddr, addrstr) > 0 ||
+ inet_pton(AF_INET6, traddr, addrstr) > 0)
+ return false;
+ return true;
+}
+
+struct nvme_ctrl *nvme_create_ctrl(nvme_root_t r,
+ const char *subsysnqn, const char *transport,
+ const char *traddr, const char *host_traddr,
+ const char *host_iface, const char *trsvcid)
+{
+ struct nvme_ctrl *c;
+
+ if (!transport) {
+ nvme_msg(r, LOG_ERR, "No transport specified\n");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (strncmp(transport, "loop", 4) &&
+ strncmp(transport, "pcie", 4) && !traddr) {
+ nvme_msg(r, LOG_ERR, "No transport address for '%s'\n",
+ transport);
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!subsysnqn) {
+ nvme_msg(r, LOG_ERR, "No subsystem NQN specified\n");
+ errno = EINVAL;
+ return NULL;
+ }
+ c = calloc(1, sizeof(*c));
+ if (!c) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ c->fd = -1;
+ nvmf_default_config(&c->cfg);
+ list_head_init(&c->namespaces);
+ list_head_init(&c->paths);
+ list_node_init(&c->entry);
+ c->transport = strdup(transport);
+ c->subsysnqn = strdup(subsysnqn);
+ if (traddr)
+ c->traddr = strdup(traddr);
+ if (host_traddr) {
+ if (traddr_is_hostname(transport, host_traddr))
+ c->cfg.host_traddr = hostname2traddr(r, host_traddr);
+ if (!c->cfg.host_traddr)
+ c->cfg.host_traddr = strdup(host_traddr);
+ }
+ if (host_iface)
+ c->cfg.host_iface = strdup(host_iface);
+ if (trsvcid)
+ c->trsvcid = strdup(trsvcid);
+
+ return c;
+}
+
+/**
+ * _tcp_ctrl_match_host_traddr_no_src_addr() - Match host_traddr w/o src_addr
+ * @c: An existing controller instance
+ * @candidate: Candidate ctrl we're trying to match with @c.
+ *
+ * On kernels prior to 6.1 (i.e. src_addr is not available), try to match
+ * a candidate controller's host_traddr to that of an existing controller.
+ *
+ * This function takes an optimistic approach. In doubt, it will declare a
+ * match and return true.
+ *
+ * Return: true if @c->host_traddr matches @candidate->host_traddr. false otherwise.
+ */
+static bool _tcp_ctrl_match_host_traddr_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate)
+{
+ if (c->cfg.host_traddr)
+ return candidate->addreq(candidate->host_traddr, c->cfg.host_traddr);
+
+ /* If c->cfg.host_traddr is NULL, then the controller (c)
+ * uses the interface's primary address as the source
+ * address. If c->cfg.host_iface is defined we can
+ * determine the primary address associated with that
+ * interface and compare that to the candidate->host_traddr.
+ */
+ if (c->cfg.host_iface)
+ return nvme_iface_primary_addr_matches(candidate->iface_list,
+ c->cfg.host_iface,
+ candidate->host_traddr);
+
+ /* If both c->cfg.host_traddr and c->cfg.host_iface are
+ * NULL, we don't have enough information to make a
+ * 100% positive match. Regardless, let's be optimistic
+ * and assume that we have a match.
+ */
+ nvme_msg(root_from_ctrl(c), LOG_DEBUG,
+ "Not enough data, but assume %s matches candidate's host_traddr: %s\n",
+ nvme_ctrl_get_name(c), candidate->host_traddr);
+
+ return true;
+}
+
+/**
+ * _tcp_ctrl_match_host_iface_no_src_addr() - Match host_iface w/o src_addr
+ * @c: An existing controller instance
+ * @candidate: Candidate ctrl we're trying to match with @c.
+ *
+ * On kernels prior to 6.1 (i.e. src_addr is not available), try to match
+ * a candidate controller's host_iface to that of an existing controller.
+ *
+ * This function takes an optimistic approach. In doubt, it will declare a
+ * match and return true.
+ *
+ * Return: true if @c->host_iface matches @candidate->host_iface. false otherwise.
+ */
+static bool _tcp_ctrl_match_host_iface_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate)
+{
+ if (c->cfg.host_iface)
+ return streq0(candidate->host_iface, c->cfg.host_iface);
+
+ /* If c->cfg.host_traddr is not NULL we can infer the controller's (c)
+ * interface from it and compare it to the candidate->host_iface.
+ */
+ if (c->cfg.host_traddr) {
+ const char *c_host_iface;
+
+ c_host_iface = nvme_iface_matching_addr(candidate->iface_list, c->cfg.host_traddr);
+ return streq0(candidate->host_iface, c_host_iface);
+ }
+
+ /* If both c->cfg.host_traddr and c->cfg.host_iface are
+ * NULL, we don't have enough information to make a
+ * 100% positive match. Regardless, let's be optimistic
+ * and assume that we have a match.
+ */
+ nvme_msg(root_from_ctrl(c), LOG_DEBUG,
+ "Not enough data, but assume %s matches candidate's host_iface: %s\n",
+ nvme_ctrl_get_name(c), candidate->host_iface);
+
+ return true;
+}
+
+/**
+ * _tcp_opt_params_match_no_src_addr() - Match optional host_traddr/host_iface w/o src_addr
+ * @c: An existing controller instance
+ * @candidate: Candidate ctrl we're trying to match with @c.
+ *
+ * Before kernel 6.1, the src_addr was not reported by the kernel which makes
+ * it hard to match a candidate's host_traddr and host_iface to an existing
+ * controller if that controller was created without specifying the
+ * host_traddr and/or host_iface. This function tries its best in the absense
+ * of a src_addr to match @c to @candidate. This may not be 100% accurate.
+ * Only the src_addr can provide 100% accuracy.
+ *
+ * This function takes an optimistic approach. In doubt, it will declare a
+ * match and return true.
+ *
+ * Return: true if @c matches @candidate. false otherwise.
+ */
+static bool _tcp_opt_params_match_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate)
+{
+ /* Check host_traddr only if candidate is interested */
+ if (candidate->host_traddr) {
+ if (!_tcp_ctrl_match_host_traddr_no_src_addr(c, candidate))
+ return false;
+ }
+
+ /* Check host_iface only if candidate is interested */
+ if (candidate->host_iface) {
+ if (!_tcp_ctrl_match_host_iface_no_src_addr(c, candidate))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * _tcp_opt_params_match() - Match optional host_traddr/host_iface
+ * @c: An existing controller instance
+ * @candidate: Candidate ctrl we're trying to match with @c.
+ *
+ * The host_traddr and host_iface are optional for TCP. When they are not
+ * specified, the kernel looks up the destination IP address (traddr) in the
+ * routing table to determine the best interface for the connection. The
+ * kernel then retrieves the primary IP address assigned to that interface
+ * and uses that as the connection’s source address.
+ *
+ * An interface’s primary address is the default source address used for
+ * all connections made on that interface unless host-traddr is used to
+ * override the default. Kernel-selected interfaces and/or source addresses
+ * are hidden from user-space applications unless the kernel makes that
+ * information available through the "src_addr" attribute in the
+ * sysfs (kernel 6.1 or later).
+ *
+ * Sometimes, an application may force the interface by specifying the
+ * "host-iface" or may force a different source address (instead of the
+ * primary address) by providing the "host-traddr".
+ *
+ * If the candidate specifies the host_traddr and/or host_iface but they
+ * do not match the existing controller's host_traddr and/or host_iface
+ * (they could be NULL), we may still be able to find a match by taking
+ * the existing controller's src_addr into consideration since that
+ * parameter identifies the actual source address of the connection and
+ * therefore can be used to infer the interface of the connection. However,
+ * the src_addr can only be read from the nvme device's sysfs "address"
+ * attribute starting with kernel 6.1 (or kernels that backported the
+ * src_addr patch).
+ *
+ * For legacy kernels that do not provide the src_addr we must use a
+ * different algorithm to match the host_traddr and host_iface, but
+ * it's not 100% accurate.
+ *
+ * Return: true if @c matches @candidate. false otherwise.
+ */
+static bool _tcp_opt_params_match(struct nvme_ctrl *c, struct candidate_args *candidate)
+{
+ char *src_addr, buffer[INET6_ADDRSTRLEN];
+
+ /* Check if src_addr is available (kernel 6.1 or later) */
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (!src_addr)
+ return _tcp_opt_params_match_no_src_addr(c, candidate);
+
+ /* Check host_traddr only if candidate is interested */
+ if (candidate->host_traddr &&
+ !candidate->addreq(candidate->host_traddr, src_addr))
+ return false;
+
+ /* Check host_iface only if candidate is interested */
+ if (candidate->host_iface &&
+ !streq0(candidate->host_iface,
+ nvme_iface_matching_addr(candidate->iface_list, src_addr)))
+ return false;
+
+ return true;
+}
+
+/**
+ * _tcp_match_ctrl() - Check if controller matches candidate (TCP only)
+ * @c: An existing controller instance
+ * @candidate: Candidate ctrl we're trying to match with @c.
+ *
+ * We want to determine if an existing controller can be re-used
+ * for the candidate controller we're trying to instantiate.
+ *
+ * For TCP, we do not have a match if the candidate's transport, traddr,
+ * trsvcid are not identical to those of the the existing controller.
+ * These 3 parameters are mandatory for a match.
+ *
+ * The host_traddr and host_iface are optional. When the candidate does
+ * not specify them (both NULL), we can ignore them. Otherwise, we must
+ * employ advanced investigation techniques to determine if there's a match.
+ *
+ * Return: true if a match is found, false otherwise.
+ */
+static bool _tcp_match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate)
+{
+ if (!streq0(c->transport, candidate->transport))
+ return false;
+
+ if (!streq0(c->trsvcid, candidate->trsvcid))
+ return false;
+
+ if (!candidate->addreq(c->traddr, candidate->traddr))
+ return false;
+
+ if (candidate->well_known_nqn && !nvme_ctrl_is_discovery_ctrl(c))
+ return false;
+
+ if (candidate->subsysnqn && !streq0(c->subsysnqn, candidate->subsysnqn))
+ return false;
+
+ /* Check host_traddr / host_iface only if candidate is interested */
+ if ((candidate->host_iface || candidate->host_traddr) &&
+ !_tcp_opt_params_match(c, candidate))
+ return false;
+
+ return true;
+}
+
+/**
+ * _match_ctrl() - Check if controller matches candidate (non TCP transport)
+ * @c: An existing controller instance
+ * @candidate: Candidate ctrl we're trying to match with @c.
+ *
+ * We want to determine if an existing controller can be re-used
+ * for the candidate controller we're trying to instantiate. This function
+ * is used for all transports except TCP.
+ *
+ * Return: true if a match is found, false otherwise.
+ */
+static bool _match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate)
+{
+ if (!streq0(c->transport, candidate->transport))
+ return false;
+
+ if (candidate->traddr && c->traddr &&
+ !candidate->addreq(c->traddr, candidate->traddr))
+ return false;
+
+ if (candidate->host_traddr && c->cfg.host_traddr &&
+ !candidate->addreq(c->cfg.host_traddr, candidate->host_traddr))
+ return false;
+
+ if (candidate->host_iface && c->cfg.host_iface &&
+ !streq0(c->cfg.host_iface, candidate->host_iface))
+ return false;
+
+ if (candidate->trsvcid && c->trsvcid &&
+ !streq0(c->trsvcid, candidate->trsvcid))
+ return false;
+
+ if (candidate->well_known_nqn && !nvme_ctrl_is_discovery_ctrl(c))
+ return false;
+
+ if (candidate->subsysnqn && !streq0(c->subsysnqn, candidate->subsysnqn))
+ return false;
+
+ return true;
+}
+/**
+ * _candidate_init() - Init candidate and get the matching function
+ *
+ * @candidate: Candidate struct to initialize
+ * @transport: Transport name
+ * @traddr: Transport address
+ * @trsvcid: Transport service identifier
+ * @subsysnqn: Subsystem NQN
+ * @host_traddr: Host transport address
+ * @host_iface: Host interface name
+ * @host_iface: Host interface name
+ *
+ * The function _candidate_free() must be called to release resources once
+ * the candidate object is not longer required.
+ *
+ * Return: The matching function to use when comparing an existing
+ * controller to the candidate controller.
+ */
+static ctrl_match_t _candidate_init(struct candidate_args *candidate,
+ const char *transport,
+ const char *traddr,
+ const char *trsvcid,
+ const char *subsysnqn,
+ const char *host_traddr,
+ const char *host_iface)
+{
+ memset(candidate, 0, sizeof(*candidate));
+
+ candidate->traddr = traddr;
+ candidate->trsvcid = trsvcid;
+ candidate->transport = transport;
+ candidate->subsysnqn = subsysnqn;
+ candidate->host_iface = host_iface;
+ candidate->host_traddr = host_traddr;
+
+ if (streq0(subsysnqn, NVME_DISC_SUBSYS_NAME)) {
+ /* Since TP8013, the NQN of discovery controllers can be the
+ * well-known NQN (i.e. nqn.2014-08.org.nvmexpress.discovery) or
+ * a unique NQN. A DC created using the well-known NQN may later
+ * display a unique NQN when looked up in the sysfs. Therefore,
+ * ignore (i.e. set to NULL) the well-known NQN when looking for
+ * a match.
+ */
+ candidate->subsysnqn = NULL;
+ candidate->well_known_nqn = true;
+ }
+
+ if (streq0(transport, "tcp")) {
+ /* For TCP we may need to access the interface map.
+ * Let's retrieve and cache the map.
+ */
+ if (getifaddrs(&candidate->iface_list) == -1)
+ candidate->iface_list = NULL;
+
+ candidate->addreq = nvme_ipaddrs_eq;
+ return _tcp_match_ctrl;
+ }
+
+ if (streq0(transport, "rdma")) {
+ candidate->addreq = nvme_ipaddrs_eq;
+ return _match_ctrl;
+ }
+
+ /* All other transport types */
+ candidate->addreq = streqcase0;
+ return _match_ctrl;
+}
+
+/**
+ * _candidate_free() - Release resources allocated by _candidate_init()
+ *
+ * @candidate: data to free.
+ */
+static void _candidate_free(struct candidate_args *candidate)
+{
+ freeifaddrs(candidate->iface_list); /* This is NULL-safe */
+}
+
+#define _cleanup_candidate_ __cleanup__(_candidate_free)
+
+nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
+ const char *traddr, const char *host_traddr,
+ const char *host_iface, const char *trsvcid,
+ const char *subsysnqn, nvme_ctrl_t p)
+{
+ struct nvme_ctrl *c, *matching_c = NULL;
+ _cleanup_candidate_ struct candidate_args candidate;
+ ctrl_match_t ctrl_match;
+
+ /* Init candidate and get the matching function to use */
+ ctrl_match = _candidate_init(&candidate, transport, traddr, trsvcid,
+ subsysnqn, host_traddr, host_iface);
+
+ c = p ? nvme_subsystem_next_ctrl(s, p) : nvme_subsystem_first_ctrl(s);
+ for (; c != NULL; c = nvme_subsystem_next_ctrl(s, c)) {
+ if (ctrl_match(c, &candidate)) {
+ matching_c = c;
+ break;
+ }
+ }
+
+ return matching_c;
+}
+
+bool nvme_ctrl_config_match(struct nvme_ctrl *c, const char *transport,
+ const char *traddr, const char *trsvcid,
+ const char *subsysnqn, const char *host_traddr,
+ const char *host_iface)
+{
+ ctrl_match_t ctrl_match;
+ _cleanup_candidate_ struct candidate_args candidate;
+
+ /* Init candidate and get the matching function to use */
+ ctrl_match = _candidate_init(&candidate, transport, traddr, trsvcid,
+ subsysnqn, host_traddr, host_iface);
+
+ return ctrl_match(c, &candidate);
+}
+
+nvme_ctrl_t nvme_ctrl_find(nvme_subsystem_t s, const char *transport,
+ const char *traddr, const char *trsvcid,
+ const char *subsysnqn, const char *host_traddr,
+ const char *host_iface)
+{
+ return __nvme_lookup_ctrl(s, transport, traddr, host_traddr, host_iface,
+ trsvcid, subsysnqn, NULL/*p*/);
+}
+
+nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
+ const char *traddr, const char *host_traddr,
+ const char *host_iface, const char *trsvcid,
+ nvme_ctrl_t p)
+{
+ nvme_root_t r;
+ struct nvme_ctrl *c;
+
+ if (!s || !transport)
+ return NULL;
+
+ c = __nvme_lookup_ctrl(s, transport, traddr, host_traddr,
+ host_iface, trsvcid, NULL, p);
+ if (c)
+ return c;
+
+ r = s->h ? s->h->r : NULL;
+ c = nvme_create_ctrl(r, s->subsysnqn, transport, traddr,
+ host_traddr, host_iface, trsvcid);
+ if (c) {
+ c->s = s;
+ list_add(&s->ctrls, &c->entry);
+ s->h->r->modified = true;
+ }
+ return c;
+}
+
+static int nvme_ctrl_scan_paths(nvme_root_t r, struct nvme_ctrl *c)
+{
+ _cleanup_dirents_ struct dirents paths = {};
+ int i;
+
+ paths.num = nvme_scan_ctrl_namespace_paths(c, &paths.ents);
+ if (paths.num < 0)
+ return paths.num;
+
+ for (i = 0; i < paths.num; i++)
+ nvme_ctrl_scan_path(r, c, paths.ents[i]->d_name);
+
+ return 0;
+}
+
+static int nvme_ctrl_scan_namespaces(nvme_root_t r, struct nvme_ctrl *c)
+{
+ _cleanup_dirents_ struct dirents namespaces = {};
+ int i;
+
+ namespaces.num = nvme_scan_ctrl_namespaces(c, &namespaces.ents);
+ for (i = 0; i < namespaces.num; i++)
+ nvme_ctrl_scan_namespace(r, c, namespaces.ents[i]->d_name);
+
+ return 0;
+}
+
+static char *nvme_ctrl_lookup_subsystem_name(nvme_root_t r,
+ const char *ctrl_name)
+{
+ _cleanup_free_ char *subsys_dir = nvme_subsys_sysfs_dir();
+ _cleanup_dirents_ struct dirents subsys = {};
+ int i;
+
+ subsys.num = nvme_scan_subsystems(&subsys.ents);
+ if (subsys.num < 0)
+ return NULL;
+ for (i = 0; i < subsys.num; i++) {
+ struct stat st;
+ _cleanup_free_ char *path = NULL;
+
+ if (asprintf(&path, "%s/%s/%s", subsys_dir,
+ subsys.ents[i]->d_name, ctrl_name) < 0) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ nvme_msg(r, LOG_DEBUG, "lookup subsystem %s\n", path);
+ if (stat(path, &st) < 0) {
+ continue;
+ }
+ return strdup(subsys.ents[i]->d_name);
+ }
+ return NULL;
+}
+
+static char *nvme_ctrl_lookup_phy_slot(nvme_root_t r, const char *address)
+{
+ _cleanup_free_ char *slots_sysfs_dir = nvme_slots_sysfs_dir();
+ _cleanup_free_ char *target_addr = NULL;
+ _cleanup_dir_ DIR *slots_dir = NULL;
+ int ret;
+ struct dirent *entry;
+
+ if (!address)
+ return NULL;
+
+ slots_dir = opendir(slots_sysfs_dir);
+ if (!slots_dir) {
+ nvme_msg(r, LOG_WARNING, "failed to open slots dir %s\n",
+ slots_sysfs_dir);
+ return NULL;
+ }
+
+ target_addr = strndup(address, 10);
+ while (!(entry = readdir(slots_dir))) {
+ if (entry->d_type == DT_DIR &&
+ strncmp(entry->d_name, ".", 1) != 0 &&
+ strncmp(entry->d_name, "..", 2) != 0) {
+ _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *addr = NULL;
+
+ ret = asprintf(&path, "%s/%s",
+ slots_sysfs_dir, entry->d_name);
+ if (ret < 0) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ addr = nvme_get_attr(path, "address");
+ if (strcmp(addr, target_addr) == 0)
+ return strdup(entry->d_name);
+ }
+ }
+ return NULL;
+}
+
+static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path,
+ const char *name)
+{
+ DIR *d;
+ char *host_key;
+
+ d = opendir(path);
+ if (!d) {
+ nvme_msg(r, LOG_ERR, "Failed to open ctrl dir %s, error %d\n",
+ path, errno);
+ errno = ENODEV;
+ return -1;
+ }
+ closedir(d);
+
+ c->fd = -1;
+ c->name = strdup(name);
+ c->sysfs_dir = (char *)path;
+ c->firmware = nvme_get_ctrl_attr(c, "firmware_rev");
+ c->model = nvme_get_ctrl_attr(c, "model");
+ c->state = nvme_get_ctrl_attr(c, "state");
+ c->numa_node = nvme_get_ctrl_attr(c, "numa_node");
+ c->queue_count = nvme_get_ctrl_attr(c, "queue_count");
+ c->serial = nvme_get_ctrl_attr(c, "serial");
+ c->sqsize = nvme_get_ctrl_attr(c, "sqsize");
+ host_key = nvme_get_ctrl_attr(c, "dhchap_secret");
+ if (host_key && c->s && c->s->h && c->s->h->dhchap_key &&
+ (!strcmp(c->s->h->dhchap_key, host_key) ||
+ !strcmp("none", host_key))) {
+ free(host_key);
+ host_key = NULL;
+ }
+ if (host_key)
+ c->dhchap_key = host_key;
+ c->dhchap_ctrl_key = nvme_get_ctrl_attr(c, "dhchap_ctrl_secret");
+ if (c->dhchap_ctrl_key && !strcmp(c->dhchap_ctrl_key, "none")) {
+ free(c->dhchap_ctrl_key);
+ c->dhchap_ctrl_key = NULL;
+ }
+ c->cntrltype = nvme_get_ctrl_attr(c, "cntrltype");
+ c->dctype = nvme_get_ctrl_attr(c, "dctype");
+ c->phy_slot = nvme_ctrl_lookup_phy_slot(r, c->address);
+
+ errno = 0; /* cleanup after nvme_get_ctrl_attr() */
+ return 0;
+}
+
+int nvme_init_ctrl(nvme_host_t h, nvme_ctrl_t c, int instance)
+{
+ _cleanup_free_ char *ctrl_dir = nvme_ctrl_sysfs_dir();
+ _cleanup_free_ char *subsys_name = NULL;
+ _cleanup_free_ char *name = NULL;
+ nvme_subsystem_t s;
+ char *path;
+ int ret;
+
+ ret = asprintf(&name, "nvme%d", instance);
+ if (ret < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ret = asprintf(&path, "%s/nvme%d", ctrl_dir, instance);
+ if (ret < 0) {
+ errno = ENOMEM;
+ return ret;
+ }
+
+ ret = nvme_configure_ctrl(h->r, c, path, name);
+ if (ret < 0) {
+ free(path);
+ return ret;
+ }
+
+ c->address = nvme_get_attr(path, "address");
+ if (!c->address && strcmp(c->transport, "loop")) {
+ errno = ENVME_CONNECT_INVAL_TR;
+ return -1;
+ }
+
+ subsys_name = nvme_ctrl_lookup_subsystem_name(h->r, name);
+ if (!subsys_name) {
+ nvme_msg(h->r, LOG_ERR,
+ "Failed to lookup subsystem name for %s\n",
+ c->name);
+ errno = ENVME_CONNECT_LOOKUP_SUBSYS_NAME;
+ return -1;
+ }
+ s = nvme_lookup_subsystem(h, subsys_name, c->subsysnqn);
+ if (!s) {
+ errno = ENVME_CONNECT_LOOKUP_SUBSYS;
+ return -1;
+ }
+ if (s->subsystype && !strcmp(s->subsystype, "discovery"))
+ c->discovery_ctrl = true;
+ c->s = s;
+ list_add(&s->ctrls, &c->entry);
+ return ret;
+}
+
+static nvme_ctrl_t nvme_ctrl_alloc(nvme_root_t r, nvme_subsystem_t s,
+ const char *path, const char *name)
+{
+ nvme_ctrl_t c, p;
+ _cleanup_free_ char *addr = NULL, *address = NULL;
+ char *a, *e;
+ _cleanup_free_ char *transport = NULL;
+ char *traddr = NULL, *trsvcid = NULL;
+ char *host_traddr = NULL, *host_iface = NULL;
+ int ret;
+
+ transport = nvme_get_attr(path, "transport");
+ if (!transport) {
+ errno = ENXIO;
+ return NULL;
+ }
+ /* Parse 'address' string into components */
+ addr = nvme_get_attr(path, "address");
+ if (!addr) {
+ _cleanup_free_ char *rpath = NULL;
+ char *p = NULL, *_a = NULL;
+
+ /* loop transport might not have an address */
+ if (!strcmp(transport, "loop"))
+ goto skip_address;
+
+ /* Older kernel don't support pcie transport addresses */
+ if (strcmp(transport, "pcie")) {
+ errno = ENXIO;
+ return NULL;
+ }
+ /* Figure out the PCI address from the attribute path */
+ rpath = realpath(path, NULL);
+ if (!rpath) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ a = strtok_r(rpath, "/", &e);
+ while(a && strlen(a)) {
+ if (_a)
+ p = _a;
+ _a = a;
+ if (!strncmp(a, "nvme", 4))
+ break;
+ a = strtok_r(NULL, "/", &e);
+ }
+ if (p)
+ addr = strdup(p);
+ } else if (!strcmp(transport, "pcie")) {
+ /* The 'address' string is the transport address */
+ traddr = addr;
+ } else {
+ address = strdup(addr);
+ a = strtok_r(address, ",", &e);
+ while (a && strlen(a)) {
+ if (!strncmp(a, "traddr=", 7))
+ traddr = a + 7;
+ else if (!strncmp(a, "trsvcid=", 8))
+ trsvcid = a + 8;
+ else if (!strncmp(a, "host_traddr=", 12))
+ host_traddr = a + 12;
+ else if (!strncmp(a, "host_iface=", 11))
+ host_iface = a + 11;
+ a = strtok_r(NULL, ",", &e);
+ }
+ }
+skip_address:
+ p = NULL;
+ do {
+ c = nvme_lookup_ctrl(s, transport, traddr,
+ host_traddr, host_iface, trsvcid, p);
+ if (c) {
+ if (!c->name)
+ break;
+ if (!strcmp(c->name, name)) {
+ nvme_msg(r, LOG_DEBUG,
+ "found existing ctrl %s\n", c->name);
+ break;
+ }
+ nvme_msg(r, LOG_DEBUG, "skipping ctrl %s\n", c->name);
+ p = c;
+ }
+ } while (c);
+ if (!c)
+ c = p;
+ if (!c && !p) {
+ nvme_msg(r, LOG_ERR, "failed to lookup ctrl\n");
+ errno = ENODEV;
+ return NULL;
+ }
+ c->address = addr;
+ addr = NULL;
+ if (s->subsystype && !strcmp(s->subsystype, "discovery"))
+ c->discovery_ctrl = true;
+ ret = nvme_configure_ctrl(r, c, path, name);
+ return (ret < 0) ? NULL : c;
+}
+
+nvme_ctrl_t nvme_scan_ctrl(nvme_root_t r, const char *name)
+{
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *hostnqn = NULL, *hostid = NULL;
+ _cleanup_free_ char *subsysnqn = NULL, *subsysname = NULL;
+ _cleanup_free_ char *ctrl_dir = nvme_ctrl_sysfs_dir();
+ int ret;
+
+ nvme_msg(r, LOG_DEBUG, "scan controller %s\n", name);
+ ret = asprintf(&path, "%s/%s", ctrl_dir, name);
+ if (ret < 0) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ hostnqn = nvme_get_attr(path, "hostnqn");
+ hostid = nvme_get_attr(path, "hostid");
+ h = nvme_lookup_host(r, hostnqn, hostid);
+ if (h) {
+ if (h->dhchap_key)
+ free(h->dhchap_key);
+ h->dhchap_key = nvme_get_attr(path, "dhchap_secret");
+ if (h->dhchap_key && !strcmp(h->dhchap_key, "none")) {
+ free(h->dhchap_key);
+ h->dhchap_key = NULL;
+ }
+ }
+ if (!h) {
+ h = nvme_default_host(r);
+ if (!h) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ subsysnqn = nvme_get_attr(path, "subsysnqn");
+ if (!subsysnqn) {
+ errno = ENXIO;
+ return NULL;
+ }
+ subsysname = nvme_ctrl_lookup_subsystem_name(r, name);
+ if (!subsysname) {
+ nvme_msg(r, LOG_DEBUG,
+ "failed to lookup subsystem for controller %s\n",
+ name);
+ errno = ENXIO;
+ return NULL;
+ }
+ s = nvme_lookup_subsystem(h, subsysname, subsysnqn);
+
+ if (!s) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ c = nvme_ctrl_alloc(r, s, path, name);
+ if (!c)
+ return NULL;
+
+ path = NULL;
+ nvme_ctrl_scan_namespaces(r, c);
+ nvme_ctrl_scan_paths(r, c);
+ return c;
+}
+
+void nvme_rescan_ctrl(struct nvme_ctrl *c)
+{
+ nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL;
+ if (!c->s)
+ return;
+ nvme_ctrl_scan_namespaces(r, c);
+ nvme_ctrl_scan_paths(r, c);
+ nvme_subsystem_scan_namespaces(r, c->s, NULL, NULL);
+}
+
+static int nvme_bytes_to_lba(nvme_ns_t n, off_t offset, size_t count,
+ __u64 *lba, __u16 *nlb)
+{
+ int bs;
+
+ bs = nvme_ns_get_lba_size(n);
+ if (!count || offset & (bs - 1) || count & (bs - 1)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *lba = offset >> n->lba_shift;
+ *nlb = (count >> n->lba_shift) - 1;
+ return 0;
+}
+
+int nvme_ns_get_fd(nvme_ns_t n)
+{
+ if (n->fd < 0) {
+ n->fd = nvme_open(n->name);
+ if (n->fd < 0)
+ nvme_msg(root_from_ns(n), LOG_ERR,
+ "Failed to open ns %s, errno %d\n",
+ n->name, errno);
+ }
+
+ return n->fd;
+}
+
+void nvme_ns_release_fd(nvme_ns_t n)
+{
+ if (n->fd < 0)
+ return;
+
+ close(n->fd);
+ n->fd = -1;
+}
+
+nvme_subsystem_t nvme_ns_get_subsystem(nvme_ns_t n)
+{
+ return n->s;
+}
+
+nvme_ctrl_t nvme_ns_get_ctrl(nvme_ns_t n)
+{
+ return n->c;
+}
+
+int nvme_ns_get_nsid(nvme_ns_t n)
+{
+ return n->nsid;
+}
+
+const char *nvme_ns_get_sysfs_dir(nvme_ns_t n)
+{
+ return n->sysfs_dir;
+}
+
+const char *nvme_ns_get_name(nvme_ns_t n)
+{
+ return n->name;
+}
+
+const char *nvme_ns_get_generic_name(nvme_ns_t n)
+{
+ return n->generic_name;
+}
+
+const char *nvme_ns_get_model(nvme_ns_t n)
+{
+ return n->c ? n->c->model : n->s->model;
+}
+
+const char *nvme_ns_get_serial(nvme_ns_t n)
+{
+ return n->c ? n->c->serial : n->s->serial;
+}
+
+const char *nvme_ns_get_firmware(nvme_ns_t n)
+{
+ return n->c ? n->c->firmware : n->s->firmware;
+}
+
+int nvme_ns_get_lba_size(nvme_ns_t n)
+{
+ return n->lba_size;
+}
+
+int nvme_ns_get_meta_size(nvme_ns_t n)
+{
+ return n->meta_size;
+}
+
+uint64_t nvme_ns_get_lba_count(nvme_ns_t n)
+{
+ return n->lba_count;
+}
+
+uint64_t nvme_ns_get_lba_util(nvme_ns_t n)
+{
+ return n->lba_util;
+}
+
+enum nvme_csi nvme_ns_get_csi(nvme_ns_t n)
+{
+ return n->csi;
+}
+
+const uint8_t *nvme_ns_get_eui64(nvme_ns_t n)
+{
+ return n->eui64;
+}
+
+const uint8_t *nvme_ns_get_nguid(nvme_ns_t n)
+{
+ return n->nguid;
+}
+
+void nvme_ns_get_uuid(nvme_ns_t n, unsigned char out[NVME_UUID_LEN])
+{
+ memcpy(out, n->uuid, NVME_UUID_LEN);
+}
+
+int nvme_ns_identify(nvme_ns_t n, struct nvme_id_ns *ns)
+{
+ return nvme_identify_ns(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), ns);
+}
+
+int nvme_ns_identify_descs(nvme_ns_t n, struct nvme_ns_id_desc *descs)
+{
+ return nvme_identify_ns_descs(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), descs);
+}
+
+int nvme_ns_verify(nvme_ns_t n, off_t offset, size_t count)
+{
+ struct nvme_io_args args = {
+ .args_size = sizeof(args),
+ .fd = nvme_ns_get_fd(n),
+ .nsid = nvme_ns_get_nsid(n),
+ .control = 0,
+ .dsm = 0,
+ .dspec = 0,
+ .reftag = 0,
+ .apptag = 0,
+ .appmask = 0,
+ .storage_tag = 0,
+ .data_len = 0,
+ .data = NULL,
+ .metadata_len = 0,
+ .metadata = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb))
+ return -1;
+
+ return nvme_verify(&args);
+}
+
+int nvme_ns_write_uncorrectable(nvme_ns_t n, off_t offset, size_t count)
+{
+ struct nvme_io_args args = {
+ .args_size = sizeof(args),
+ .fd = nvme_ns_get_fd(n),
+ .nsid = nvme_ns_get_nsid(n),
+ .control = 0,
+ .dsm = 0,
+ .dspec = 0,
+ .reftag = 0,
+ .apptag = 0,
+ .appmask = 0,
+ .storage_tag = 0,
+ .data_len = 0,
+ .data = NULL,
+ .metadata_len = 0,
+ .metadata = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb))
+ return -1;
+
+ return nvme_write_uncorrectable(&args);
+}
+
+int nvme_ns_write_zeros(nvme_ns_t n, off_t offset, size_t count)
+{
+ struct nvme_io_args args = {
+ .args_size = sizeof(args),
+ .fd = nvme_ns_get_fd(n),
+ .nsid = nvme_ns_get_nsid(n),
+ .control = 0,
+ .dsm = 0,
+ .dspec = 0,
+ .reftag = 0,
+ .apptag = 0,
+ .appmask = 0,
+ .storage_tag = 0,
+ .data_len = 0,
+ .data = NULL,
+ .metadata_len = 0,
+ .metadata = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb))
+ return -1;
+
+ return nvme_write_zeros(&args);
+}
+
+int nvme_ns_write(nvme_ns_t n, void *buf, off_t offset, size_t count)
+{
+ struct nvme_io_args args = {
+ .args_size = sizeof(args),
+ .fd = nvme_ns_get_fd(n),
+ .nsid = nvme_ns_get_nsid(n),
+ .control = 0,
+ .dsm = 0,
+ .dspec = 0,
+ .reftag = 0,
+ .apptag = 0,
+ .appmask = 0,
+ .storage_tag = 0,
+ .data_len = count,
+ .data = buf,
+ .metadata_len = 0,
+ .metadata = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb))
+ return -1;
+
+ return nvme_write(&args);
+}
+
+int nvme_ns_read(nvme_ns_t n, void *buf, off_t offset, size_t count)
+{
+ struct nvme_io_args args = {
+ .args_size = sizeof(args),
+ .fd = nvme_ns_get_fd(n),
+ .nsid = nvme_ns_get_nsid(n),
+ .control = 0,
+ .dsm = 0,
+ .dspec = 0,
+ .reftag = 0,
+ .apptag = 0,
+ .appmask = 0,
+ .storage_tag = 0,
+ .data_len = count,
+ .data = buf,
+ .metadata_len = 0,
+ .metadata = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb))
+ return -1;
+
+ return nvme_read(&args);
+}
+
+int nvme_ns_compare(nvme_ns_t n, void *buf, off_t offset, size_t count)
+{
+ struct nvme_io_args args = {
+ .args_size = sizeof(args),
+ .fd = nvme_ns_get_fd(n),
+ .nsid = nvme_ns_get_nsid(n),
+ .control = 0,
+ .dsm = 0,
+ .dspec = 0,
+ .reftag = 0,
+ .apptag = 0,
+ .appmask = 0,
+ .storage_tag = 0,
+ .data_len = count,
+ .data = buf,
+ .metadata_len = 0,
+ .metadata = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ if (nvme_bytes_to_lba(n, offset, count, &args.slba, &args.nlb))
+ return -1;
+
+ return nvme_compare(&args);
+}
+
+int nvme_ns_flush(nvme_ns_t n)
+{
+ return nvme_flush(nvme_ns_get_fd(n), nvme_ns_get_nsid(n));
+}
+
+static int nvme_strtou64(const char *str, void *res)
+{
+ char *endptr;
+ __u64 v;
+
+ errno = 0;
+ v = strtoull(str, &endptr, 0);
+
+ if (errno != 0)
+ return -errno;
+
+ if (endptr == str) {
+ /* no digits found */
+ return -EINVAL;
+ }
+
+ *(__u64 *)res = v;
+ return 0;
+}
+
+static int nvme_strtou32(const char *str, void *res)
+{
+ char *endptr;
+ __u32 v;
+
+ errno = 0;
+ v = strtol(str, &endptr, 0);
+
+ if (errno != 0)
+ return -errno;
+
+ if (endptr == str) {
+ /* no digits found */
+ return -EINVAL;
+ }
+
+ *(__u32 *)res = v;
+ return 0;
+}
+
+static int nvme_strtoi(const char *str, void *res)
+{
+ char *endptr;
+ int v;
+
+ errno = 0;
+ v = strtol(str, &endptr, 0);
+
+ if (errno != 0)
+ return -errno;
+
+ if (endptr == str) {
+ /* no digits found */
+ return -EINVAL;
+ }
+
+ *(int *)res = v;
+ return 0;
+}
+
+static int nvme_strtoeuid(const char *str, void *res)
+{
+ memcpy(res, str, 8);
+ return 0;
+}
+
+static int nvme_strtouuid(const char *str, void *res)
+{
+ memcpy(res, str, NVME_UUID_LEN);
+ return 0;
+}
+
+struct sysfs_attr_table {
+ void *var;
+ int (*parse)(const char *str, void *res);
+ bool mandatory;
+ const char *name;
+};
+
+#define GETSHIFT(x) (__builtin_ffsll(x) - 1)
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static int parse_attrs(const char *path, struct sysfs_attr_table *tbl, int size)
+{
+ char *str;
+ int ret, i;
+
+ for (i = 0; i < size; i++) {
+ struct sysfs_attr_table *e = &tbl[i];
+
+ str = nvme_get_attr(path, e->name);
+ if (!str) {
+ if (!e->mandatory)
+ continue;
+ return -ENOENT;
+ }
+ ret = e->parse(str, e->var);
+ free(str);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nvme_ns_init(const char *path, struct nvme_ns *ns)
+{
+ _cleanup_free_ char *attr = NULL;
+ struct stat sb;
+ int ret;
+
+ struct sysfs_attr_table base[] = {
+ { &ns->nsid, nvme_strtou32, true, "nsid" },
+ { &ns->lba_count, nvme_strtou64, true, "size" },
+ { &ns->lba_size, nvme_strtou64, true, "queue/logical_block_size" },
+ { ns->eui64, nvme_strtoeuid, false, "eui" },
+ { ns->nguid, nvme_strtouuid, false, "nguid" },
+ { ns->uuid, nvme_strtouuid, false, "uuid" }
+ };
+
+ ret = parse_attrs(path, base, ARRAY_SIZE(base));
+ if (ret)
+ return ret;
+
+ ns->lba_shift = GETSHIFT(ns->lba_size);
+
+ if (asprintf(&attr, "%s/csi", path) < 0)
+ return -errno;
+ ret = stat(attr, &sb);
+ if (ret == 0) {
+ /* only available on kernels >= 6.8 */
+ struct sysfs_attr_table ext[] = {
+ { &ns->csi, nvme_strtoi, true, "csi" },
+ { &ns->lba_util, nvme_strtou64, true, "nuse" },
+ { &ns->meta_size, nvme_strtoi, true, "metadata_bytes"},
+
+ };
+
+ ret = parse_attrs(path, ext, ARRAY_SIZE(ext));
+ if (ret)
+ return ret;
+ } else {
+ struct nvme_id_ns *id;
+ uint8_t flbas;
+
+ id = __nvme_alloc(sizeof(*ns));
+ if (!id)
+ return -ENOMEM;
+
+ ret = nvme_ns_identify(ns, id);
+ if (ret)
+ return ret;
+
+ nvme_id_ns_flbas_to_lbaf_inuse(id->flbas, &flbas);
+ ns->lba_count = le64_to_cpu(id->nsze);
+ ns->lba_util = le64_to_cpu(id->nuse);
+ ns->meta_size = le16_to_cpu(id->lbaf[flbas].ms);
+ }
+
+ return 0;
+}
+
+static void nvme_ns_set_generic_name(struct nvme_ns *n, const char *name)
+{
+ char generic_name[PATH_MAX];
+ int instance, head_instance;
+ int ret;
+
+ ret = sscanf(name, "nvme%dn%d", &instance, &head_instance);
+ if (ret != 2)
+ return;
+
+ sprintf(generic_name, "ng%dn%d", instance, head_instance);
+ n->generic_name = strdup(generic_name);
+}
+
+static nvme_ns_t nvme_ns_open(const char *sys_path, const char *name)
+{
+ struct nvme_ns *n;
+
+ n = calloc(1, sizeof(*n));
+ if (!n) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ n->fd = -1;
+ n->name = strdup(name);
+
+ nvme_ns_set_generic_name(n, name);
+
+ if (nvme_ns_init(sys_path, n) != 0)
+ goto free_ns;
+
+ list_head_init(&n->paths);
+ list_node_init(&n->entry);
+
+ nvme_ns_release_fd(n); /* Do not leak fds */
+ return n;
+
+free_ns:
+ free(n->generic_name);
+ free(n->name);
+ free(n);
+ return NULL;
+}
+
+static inline bool nvme_ns_is_generic(const char *name)
+{
+ int instance, head_instance;
+
+ if (sscanf(name, "ng%dn%d", &instance, &head_instance) != 2)
+ return false;
+ return true;
+}
+
+static char *nvme_ns_generic_to_blkdev(const char *generic)
+{
+
+ int instance, head_instance;
+ char blkdev[PATH_MAX];
+
+ if (!nvme_ns_is_generic(generic))
+ return strdup(generic);
+
+ sscanf(generic, "ng%dn%d", &instance, &head_instance);
+ sprintf(blkdev, "nvme%dn%d", instance, head_instance);
+
+ return strdup(blkdev);
+}
+
+static struct nvme_ns *__nvme_scan_namespace(const char *sysfs_dir, const char *name)
+{
+ struct nvme_ns *n;
+ _cleanup_free_ char *path = NULL;
+ int ret;
+ _cleanup_free_ char *blkdev = NULL;
+
+ blkdev = nvme_ns_generic_to_blkdev(name);
+ if (!blkdev) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret = asprintf(&path, "%s/%s", sysfs_dir, blkdev);
+ if (ret < 0) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ n = nvme_ns_open(path, blkdev);
+ if (!n)
+ return NULL;
+
+ n->sysfs_dir = path;
+ path = NULL;
+
+ return n;
+}
+
+nvme_ns_t nvme_scan_namespace(const char *name)
+{
+ _cleanup_free_ char *ns_dir = nvme_ns_sysfs_dir();
+
+ return __nvme_scan_namespace(ns_dir, name);
+}
+
+static int nvme_ctrl_scan_namespace(nvme_root_t r, struct nvme_ctrl *c,
+ char *name)
+{
+ struct nvme_ns *n, *_n, *__n;
+
+ nvme_msg(r, LOG_DEBUG, "scan controller %s namespace %s\n",
+ c->name, name);
+ if (!c->s) {
+ nvme_msg(r, LOG_DEBUG, "no subsystem for %s\n", name);
+ errno = EINVAL;
+ return -1;
+ }
+ n = __nvme_scan_namespace(c->sysfs_dir, name);
+ if (!n) {
+ nvme_msg(r, LOG_DEBUG, "failed to scan namespace %s\n", name);
+ return -1;
+ }
+ nvme_ctrl_for_each_ns_safe(c, _n, __n) {
+ if (strcmp(n->name, _n->name))
+ continue;
+ __nvme_free_ns(_n);
+ }
+ n->s = c->s;
+ n->c = c;
+ list_add(&c->namespaces, &n->entry);
+ return 0;
+}
+
+static void nvme_subsystem_set_ns_path(nvme_subsystem_t s, nvme_ns_t n)
+{
+ nvme_ctrl_t c;
+ nvme_path_t p;
+ int ns_ctrl, ns_nsid, ret;
+
+ ret = sscanf(nvme_ns_get_name(n), "nvme%dn%d", &ns_ctrl, &ns_nsid);
+ if (ret != 2)
+ return;
+
+ nvme_subsystem_for_each_ctrl(s, c) {
+ nvme_ctrl_for_each_path(c, p) {
+ int p_subsys, p_ctrl, p_nsid;
+
+ ret = sscanf(nvme_path_get_name(p), "nvme%dc%dn%d",
+ &p_subsys, &p_ctrl, &p_nsid);
+ if (ret != 3)
+ continue;
+ if (ns_ctrl == p_subsys && ns_nsid == p_nsid) {
+ list_add(&n->paths, &p->nentry);
+ p->n = n;
+ }
+ }
+ }
+}
+
+static int nvme_subsystem_scan_namespace(nvme_root_t r, nvme_subsystem_t s,
+ char *name, nvme_scan_filter_t f, void *f_args)
+{
+ struct nvme_ns *n, *_n, *__n;
+
+ nvme_msg(r, LOG_DEBUG, "scan subsystem %s namespace %s\n",
+ s->name, name);
+ n = __nvme_scan_namespace(s->sysfs_dir, name);
+ if (!n) {
+ nvme_msg(r, LOG_DEBUG, "failed to scan namespace %s\n", name);
+ return -1;
+ }
+ if (f && !f(NULL, NULL, n, f_args)) {
+ nvme_msg(r, LOG_DEBUG, "filter out namespace %s\n", name);
+ __nvme_free_ns(n);
+ return 0;
+ }
+ nvme_subsystem_for_each_ns_safe(s, _n, __n) {
+ struct nvme_path *p, *_p;
+
+ if (strcmp(n->name, _n->name))
+ continue;
+ /* Detach paths */
+ nvme_namespace_for_each_path_safe(_n, p, _p) {
+ list_del_init(&p->nentry);
+ p->n = NULL;
+ }
+ list_head_init(&_n->paths);
+ __nvme_free_ns(_n);
+ }
+ n->s = s;
+ list_add(&s->namespaces, &n->entry);
+ nvme_subsystem_set_ns_path(s, n);
+ return 0;
+}
+
+struct nvme_ns *nvme_subsystem_lookup_namespace(struct nvme_subsystem *s,
+ __u32 nsid)
+{
+ struct nvme_ns *n;
+
+ nvme_subsystem_for_each_ns(s, n) {
+ if (nvme_ns_get_nsid(n) == nsid)
+ return n;
+ }
+ return NULL;
+}
diff --git a/src/nvme/tree.h b/src/nvme/tree.h
new file mode 100644
index 0000000..a30e8eb
--- /dev/null
+++ b/src/nvme/tree.h
@@ -0,0 +1,1454 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+
+#ifndef _LIBNVME_TREE_H
+#define _LIBNVME_TREE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "ioctl.h"
+#include "util.h"
+
+/**
+ * DOC: tree.h
+ *
+ * libnvme tree object interface
+ */
+
+typedef struct nvme_ns *nvme_ns_t;
+typedef struct nvme_path *nvme_path_t;
+typedef struct nvme_ctrl *nvme_ctrl_t;
+typedef struct nvme_subsystem *nvme_subsystem_t;
+typedef struct nvme_host *nvme_host_t;
+typedef struct nvme_root *nvme_root_t;
+
+typedef bool (*nvme_scan_filter_t)(nvme_subsystem_t, nvme_ctrl_t,
+ nvme_ns_t, void *);
+
+/**
+ * nvme_create_root() - Initialize root object
+ * @fp: File descriptor for logging messages
+ * @log_level: Logging level to use
+ *
+ * Return: Initialized &nvme_root_t object
+ */
+nvme_root_t nvme_create_root(FILE *fp, int log_level);
+
+/**
+ * nvme_root_set_application - Specify managing application
+ * @r: &nvme_root_t object
+ * @a: Application string
+ *
+ * Sets the managing application string for @r.
+ */
+void nvme_root_set_application(nvme_root_t r, const char *a);
+
+/**
+ * nvme_root_get_application - Get managing application
+ * @r: &nvme_root_t object
+ *
+ * Returns the managing application string for @r or NULL if not set.
+ */
+const char *nvme_root_get_application(nvme_root_t r);
+
+/**
+ * nvme_root_release_fds - Close all opened file descriptors in the tree
+ * @r: &nvme_root_t object
+ *
+ * Controller and Namespace objects cache the file descriptors
+ * of opened nvme devices. This API can be used to close and
+ * clear all cached fds in the tree.
+ *
+ */
+void nvme_root_release_fds(nvme_root_t r);
+
+/**
+ * nvme_free_tree() - Free root object
+ * @r: &nvme_root_t object
+ *
+ * Free an &nvme_root_t object and all attached objects
+ */
+void nvme_free_tree(nvme_root_t r);
+
+/**
+ * nvme_first_host() - Start host iterator
+ * @r: &nvme_root_t object
+ *
+ * Return: First &nvme_host_t object in an iterator
+ */
+nvme_host_t nvme_first_host(nvme_root_t r);
+
+/**
+ * nvme_next_host() - Next host iterator
+ * @r: &nvme_root_t object
+ * @h: Previous &nvme_host_t iterator
+ *
+ * Return: Next &nvme_host_t object in an iterator
+ */
+nvme_host_t nvme_next_host(nvme_root_t r, nvme_host_t h);
+
+/**
+ * nvme_host_get_root() - Returns nvme_root_t object
+ * @h: &nvme_host_t object
+ *
+ * Return: &nvme_root_t object from @h
+ */
+nvme_root_t nvme_host_get_root(nvme_host_t h);
+
+/**
+ * nvme_lookup_host() - Lookup nvme_host_t object
+ * @r: &nvme_root_t object
+ * @hostnqn: Host NQN
+ * @hostid: Host ID
+ *
+ * Lookup a nvme_host_t object based on @hostnqn and @hostid
+ * or create one if not found.
+ *
+ * Return: &nvme_host_t object
+ */
+nvme_host_t nvme_lookup_host(nvme_root_t r, const char *hostnqn,
+ const char *hostid);
+
+/**
+ * nvme_host_get_dhchap_key() - Return host key
+ * @h: Host for which the key should be returned
+ *
+ * Return: DH-HMAC-CHAP host key or NULL if not set
+ */
+const char *nvme_host_get_dhchap_key(nvme_host_t h);
+
+/**
+ * nvme_host_set_dhchap_key() - set host key
+ * @h: Host for which the key should be set
+ * @key: DH-HMAC-CHAP Key to set or NULL to clear existing key
+ */
+void nvme_host_set_dhchap_key(nvme_host_t h, const char *key);
+
+/**
+ * nvme_host_set_pdc_enabled() - Set Persistent Discovery Controller flag
+ * @h: Host for which the falg should be set
+ * @enabled: The bool to set the enabled flag
+ *
+ * When nvme_host_set_pdc_enabled() is not used to set the PDC flag,
+ * nvme_host_is_pdc_enabled() will return the default value which was
+ * passed into the function and not the undefined flag value.
+ */
+void nvme_host_set_pdc_enabled(nvme_host_t h, bool enabled);
+
+/**
+ * nvme_host_is_pdc_enabled() - Is Persistenct Discovery Controller enabled
+ * @h: Host which to check if PDC is enabled
+ * @fallback: The fallback default value of the flag when
+ * @nvme_host_set_pdc_enabled has not be used
+ * to set the flag.
+ *
+ * Return: true if PDC is enabled for @h, else false
+ */
+bool nvme_host_is_pdc_enabled(nvme_host_t h, bool fallback);
+
+/**
+ * nvme_default_host() - Initializes the default host
+ * @r: &nvme_root_t object
+ *
+ * Initializes the default host object based on the values in
+ * /etc/nvme/hostnqn and /etc/nvme/hostid and attaches it to @r.
+ *
+ * Return: &nvme_host_t object
+ */
+nvme_host_t nvme_default_host(nvme_root_t r);
+
+/**
+ * nvme_first_subsystem() - Start subsystem iterator
+ * @h: &nvme_host_t object
+ *
+ * Return: first &nvme_subsystem_t object in an iterator
+ */
+nvme_subsystem_t nvme_first_subsystem(nvme_host_t h);
+
+/**
+ * nvme_next_subsystem() - Next subsystem iterator
+ * @h: &nvme_host_t object
+ * @s: Previous &nvme_subsystem_t iterator
+ *
+ * Return: next &nvme_subsystem_t object in an iterator
+ */
+nvme_subsystem_t nvme_next_subsystem(nvme_host_t h, nvme_subsystem_t s);
+
+/**
+ * nvme_lookup_subsystem() - Lookup nvme_subsystem_t object
+ * @h: &nvme_host_t object
+ * @name: Name of the subsystem (may be NULL)
+ * @subsysnqn: Subsystem NQN
+ *
+ * Lookup a &nvme_subsystem_t object in @h base on @name (if present)
+ * and @subsysnqn or create one if not found.
+ *
+ * Return: nvme_subsystem_t object
+ */
+nvme_subsystem_t nvme_lookup_subsystem(struct nvme_host *h,
+ const char *name,
+ const char *subsysnqn);
+
+/**
+ * nvme_free_subsystem() - Free a subsystem
+ * @s: subsystem
+ *
+ * Frees @s and all related objects.
+ */
+void nvme_free_subsystem(struct nvme_subsystem *s);
+
+/**
+ * nvme_subsystem_get_host() - Returns nvme_host_t object
+ * @s: subsystem
+ *
+ * Return: &nvme_host_t object from @s
+ */
+nvme_host_t nvme_subsystem_get_host(nvme_subsystem_t s);
+
+/**
+ * nvme_ctrl_first_ns() - Start namespace iterator
+ * @c: Controller instance
+ *
+ * Return: First &nvme_ns_t object of an @c iterator
+ */
+nvme_ns_t nvme_ctrl_first_ns(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_next_ns() - Next namespace iterator
+ * @c: Controller instance
+ * @n: Previous nvme_ns_t iterator
+ *
+ * Return: Next nvme_ns_t object of an @c iterator
+ */
+nvme_ns_t nvme_ctrl_next_ns(nvme_ctrl_t c, nvme_ns_t n);
+
+/**
+ * nvme_ctrl_first_path() - Start path iterator
+ * @c: Controller instance
+ *
+ * Return: First &nvme_path_t object of an @c iterator
+ */
+nvme_path_t nvme_ctrl_first_path(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_next_path() - Next path iterator
+ * @c: Controller instance
+ * @p: Previous &nvme_path_t object of an @c iterator
+ *
+ * Return: Next &nvme_path_t object of an @c iterator
+ */
+nvme_path_t nvme_ctrl_next_path(nvme_ctrl_t c, nvme_path_t p);
+
+/**
+ * nvme_subsystem_first_ctrl() - First ctrl iterator
+ * @s: &nvme_subsystem_t object
+ *
+ * Return: First controller of an @s iterator
+ */
+nvme_ctrl_t nvme_subsystem_first_ctrl(nvme_subsystem_t s);
+
+/**
+ * nvme_subsystem_next_ctrl() - Next ctrl iterator
+ * @s: &nvme_subsystem_t object
+ * @c: Previous controller instance of an @s iterator
+ *
+ * Return: Next controller of an @s iterator
+ */
+nvme_ctrl_t nvme_subsystem_next_ctrl(nvme_subsystem_t s, nvme_ctrl_t c);
+
+/**
+ * nvme_namespace_first_path() - Start path iterator
+ * @ns: Namespace instance
+ *
+ * Return: First &nvme_path_t object of an @ns iterator
+ */
+nvme_path_t nvme_namespace_first_path(nvme_ns_t ns);
+
+/**
+ * nvme_namespace_next_path() - Next path iterator
+ * @ns: Namespace instance
+ * @p: Previous &nvme_path_t object of an @ns iterator
+ *
+ * Return: Next &nvme_path_t object of an @ns iterator
+ */
+nvme_path_t nvme_namespace_next_path(nvme_ns_t ns, nvme_path_t p);
+
+/**
+ * nvme_lookup_ctrl() - Lookup nvme_ctrl_t object
+ * @s: &nvme_subsystem_t object
+ * @transport: Transport name
+ * @traddr: Transport address
+ * @host_traddr: Host transport address
+ * @host_iface: Host interface name
+ * @trsvcid: Transport service identifier
+ * @p: Previous controller instance
+ *
+ * Lookup a controller in @s based on @transport, @traddr,
+ * @host_traddr, @host_iface, and @trsvcid. @transport must be specified,
+ * other fields may be required depending on the transport. A new
+ * object is created if none is found. If @p is specified the lookup
+ * will start at @p instead of the first controller.
+ *
+ * Return: Controller instance
+ */
+nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
+ const char *traddr, const char *host_traddr,
+ const char *host_iface, const char *trsvcid,
+ nvme_ctrl_t p);
+
+/**
+ * nvme_ctrl_find() - Locate an existing controller
+ * @s: &nvme_subsystem_t object
+ * @transport: Transport name
+ * @traddr: Transport address
+ * @trsvcid: Transport service identifier
+ * @subsysnqn: Subsystem NQN
+ * @host_traddr: Host transport address
+ * @host_iface: Host interface name
+ *
+ * Lookup a controller in @s based on @transport, @traddr, @trsvcid,
+ * @subsysnqn, @host_traddr, and @host_iface. @transport must be specified,
+ * other fields may be required depending on the transport. Parameters set
+ * to NULL will be ignored.
+ *
+ * Unlike nvme_lookup_ctrl(), this function does not create a new object if
+ * an existing controller cannot be found.
+ *
+ * Return: Controller instance on success, NULL otherwise.
+ */
+nvme_ctrl_t nvme_ctrl_find(nvme_subsystem_t s, const char *transport,
+ const char *traddr, const char *trsvcid,
+ const char *subsysnqn, const char *host_traddr,
+ const char *host_iface);
+
+/**
+ * nvme_ctrl_config_match() - Check if ctrl @c matches config params
+ * @c: An existing controller instance
+ * @transport: Transport name
+ * @traddr: Transport address
+ * @trsvcid: Transport service identifier
+ * @subsysnqn: Subsystem NQN
+ * @host_traddr: Host transport address
+ * @host_iface: Host interface name
+ *
+ * Check that controller @c matches parameters: @transport, @traddr,
+ * @trsvcid, @subsysnqn, @host_traddr, and @host_iface. Parameters set
+ * to NULL will be ignored.
+ *
+ * Return: true if there's a match, false otherwise.
+ */
+bool nvme_ctrl_config_match(struct nvme_ctrl *c, const char *transport,
+ const char *traddr, const char *trsvcid,
+ const char *subsysnqn, const char *host_traddr,
+ const char *host_iface);
+
+/**
+ * nvme_create_ctrl() - Allocate an unconnected NVMe controller
+ * @r: NVMe root element
+ * @subsysnqn: Subsystem NQN
+ * @transport: Transport type
+ * @traddr: Transport address
+ * @host_traddr: Host transport address
+ * @host_iface: Host interface name
+ * @trsvcid: Transport service ID
+ *
+ * Creates an unconnected controller to be used for nvme_add_ctrl().
+ *
+ * Return: Controller instance
+ */
+nvme_ctrl_t nvme_create_ctrl(nvme_root_t r,
+ const char *subsysnqn, const char *transport,
+ const char *traddr, const char *host_traddr,
+ const char *host_iface, const char *trsvcid);
+
+
+/**
+ * nvme_subsystem_first_ns() - Start namespace iterator
+ * @s: &nvme_subsystem_t object
+ *
+ * Return: First &nvme_ns_t object of an @s iterator
+ */
+nvme_ns_t nvme_subsystem_first_ns(nvme_subsystem_t s);
+
+/**
+ * nvme_subsystem_next_ns() - Next namespace iterator
+ * @s: &nvme_subsystem_t object
+ * @n: Previous &nvme_ns_t iterator
+ *
+ * Return: Next &nvme_ns_t object of an @s iterator
+ */
+nvme_ns_t nvme_subsystem_next_ns(nvme_subsystem_t s, nvme_ns_t n);
+
+/**
+ * nvme_for_each_host_safe() - Traverse host list
+ * @r: &nvme_root_t object
+ * @h: &nvme_host_t object
+ * @_h: Temporary &nvme_host_t object
+ */
+#define nvme_for_each_host_safe(r, h, _h) \
+ for (h = nvme_first_host(r), \
+ _h = nvme_next_host(r, h); \
+ h != NULL; \
+ h = _h, _h = nvme_next_host(r, h))
+
+/**
+ * nvme_for_each_host() - Traverse host list
+ * @r: &nvme_root_t object
+ * @h: &nvme_host_t object
+ */
+#define nvme_for_each_host(r, h) \
+ for (h = nvme_first_host(r); h != NULL; \
+ h = nvme_next_host(r, h))
+
+/**
+ * nvme_for_each_subsystem_safe() - Traverse subsystems
+ * @h: &nvme_host_t object
+ * @s: &nvme_subsystem_t object
+ * @_s: Temporary &nvme_subsystem_t object
+ */
+#define nvme_for_each_subsystem_safe(h, s, _s) \
+ for (s = nvme_first_subsystem(h), \
+ _s = nvme_next_subsystem(h, s); \
+ s != NULL; \
+ s = _s, _s = nvme_next_subsystem(h, s))
+
+/**
+ * nvme_for_each_subsystem() - Traverse subsystems
+ * @h: &nvme_host_t object
+ * @s: &nvme_subsystem_t object
+ */
+#define nvme_for_each_subsystem(h, s) \
+ for (s = nvme_first_subsystem(h); s != NULL; \
+ s = nvme_next_subsystem(h, s))
+
+/**
+ * nvme_subsystem_for_each_ctrl_safe() - Traverse controllers
+ * @s: &nvme_subsystem_t object
+ * @c: Controller instance
+ * @_c: A &nvme_ctrl_t_node to use as temporary storage
+ */
+#define nvme_subsystem_for_each_ctrl_safe(s, c, _c) \
+ for (c = nvme_subsystem_first_ctrl(s), \
+ _c = nvme_subsystem_next_ctrl(s, c); \
+ c != NULL; \
+ c = _c, _c = nvme_subsystem_next_ctrl(s, c))
+
+/**
+ * nvme_subsystem_for_each_ctrl() - Traverse controllers
+ * @s: &nvme_subsystem_t object
+ * @c: Controller instance
+ */
+#define nvme_subsystem_for_each_ctrl(s, c) \
+ for (c = nvme_subsystem_first_ctrl(s); c != NULL; \
+ c = nvme_subsystem_next_ctrl(s, c))
+
+/**
+ * nvme_ctrl_for_each_ns_safe() - Traverse namespaces
+ * @c: Controller instance
+ * @n: &nvme_ns_t object
+ * @_n: A &nvme_ns_t_node to use as temporary storage
+ */
+#define nvme_ctrl_for_each_ns_safe(c, n, _n) \
+ for (n = nvme_ctrl_first_ns(c), \
+ _n = nvme_ctrl_next_ns(c, n); \
+ n != NULL; \
+ n = _n, _n = nvme_ctrl_next_ns(c, n))
+
+/**
+ * nvme_ctrl_for_each_ns() - Traverse namespaces
+ * @c: Controller instance
+ * @n: &nvme_ns_t object
+ */
+#define nvme_ctrl_for_each_ns(c, n) \
+ for (n = nvme_ctrl_first_ns(c); n != NULL; \
+ n = nvme_ctrl_next_ns(c, n))
+
+/**
+ * nvme_ctrl_for_each_path_safe() - Traverse paths
+ * @c: Controller instance
+ * @p: &nvme_path_t object
+ * @_p: A &nvme_path_t_node to use as temporary storage
+ */
+#define nvme_ctrl_for_each_path_safe(c, p, _p) \
+ for (p = nvme_ctrl_first_path(c), \
+ _p = nvme_ctrl_next_path(c, p); \
+ p != NULL; \
+ p = _p, _p = nvme_ctrl_next_path(c, p))
+
+/**
+ * nvme_ctrl_for_each_path() - Traverse paths
+ * @c: Controller instance
+ * @p: &nvme_path_t object
+ */
+#define nvme_ctrl_for_each_path(c, p) \
+ for (p = nvme_ctrl_first_path(c); p != NULL; \
+ p = nvme_ctrl_next_path(c, p))
+
+/**
+ * nvme_subsystem_for_each_ns_safe() - Traverse namespaces
+ * @s: &nvme_subsystem_t object
+ * @n: &nvme_ns_t object
+ * @_n: A &nvme_ns_t_node to use as temporary storage
+ */
+#define nvme_subsystem_for_each_ns_safe(s, n, _n) \
+ for (n = nvme_subsystem_first_ns(s), \
+ _n = nvme_subsystem_next_ns(s, n); \
+ n != NULL; \
+ n = _n, _n = nvme_subsystem_next_ns(s, n))
+
+/**
+ * nvme_subsystem_for_each_ns() - Traverse namespaces
+ * @s: &nvme_subsystem_t object
+ * @n: &nvme_ns_t object
+ */
+#define nvme_subsystem_for_each_ns(s, n) \
+ for (n = nvme_subsystem_first_ns(s); n != NULL; \
+ n = nvme_subsystem_next_ns(s, n))
+
+/**
+ * nvme_namespace_for_each_path_safe() - Traverse paths
+ * @n: Namespace instance
+ * @p: &nvme_path_t object
+ * @_p: A &nvme_path_t_node to use as temporary storage
+ */
+#define nvme_namespace_for_each_path_safe(n, p, _p) \
+ for (p = nvme_namespace_first_path(n), \
+ _p = nvme_namespace_next_path(n, p); \
+ p != NULL; \
+ p = _p, _p = nvme_namespace_next_path(n, p))
+
+/**
+ * nvme_namespace_for_each_path() - Traverse paths
+ * @n: Namespace instance
+ * @p: &nvme_path_t object
+ */
+#define nvme_namespace_for_each_path(n, p) \
+ for (p = nvme_namespace_first_path(n); p != NULL; \
+ p = nvme_namespace_next_path(n, p))
+
+/**
+ * nvme_ns_get_fd() - Get associated file descriptor
+ * @n: Namespace instance
+ *
+ * libnvme will open() the file (if not already opened) and keep
+ * an internal copy of the file descriptor. Following calls to
+ * this API retrieve the internal cached copy of the file
+ * descriptor. The file will remain opened and the fd will
+ * remain cached until the ns object is deleted or
+ * nvme_ns_release_fd() is called.
+ *
+ * Return: File descriptor associated with @n or -1
+ */
+int nvme_ns_get_fd(nvme_ns_t n);
+
+/**
+ * nvme_ns_release_fd() - Close fd and clear fd from ns object
+ * @n: Namespace instance
+ *
+ */
+void nvme_ns_release_fd(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_nsid() - NSID of a namespace
+ * @n: Namespace instance
+ *
+ * Return: NSID of @n
+ */
+int nvme_ns_get_nsid(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_lba_size() - LBA size of a namespace
+ * @n: Namespace instance
+ *
+ * Return: LBA size of @n
+ */
+int nvme_ns_get_lba_size(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_meta_size() - Metadata size of a namespace
+ * @n: Namespace instance
+ *
+ * Return: Metadata size of @n
+ */
+int nvme_ns_get_meta_size(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_lba_count() - LBA count of a namespace
+ * @n: Namespace instance
+ *
+ * Return: LBA count of @n
+ */
+uint64_t nvme_ns_get_lba_count(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_lba_util() - LBA utilization of a namespace
+ * @n: Namespace instance
+ *
+ * Return: LBA utilization of @n
+ */
+uint64_t nvme_ns_get_lba_util(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_csi() - Command set identifier of a namespace
+ * @n: Namespace instance
+ *
+ * Return: The namespace's command set identifier in use
+ */
+enum nvme_csi nvme_ns_get_csi(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_eui64() - 64-bit eui of a namespace
+ * @n: Namespace instance
+ *
+ * Return: A pointer to the 64-bit eui
+ */
+const uint8_t *nvme_ns_get_eui64(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_nguid() - 128-bit nguid of a namespace
+ * @n: Namespace instance
+ *
+ * Return: A pointer to the 128-bit nguid
+ */
+const uint8_t *nvme_ns_get_nguid(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_uuid() - UUID of a namespace
+ * @n: Namespace instance
+ * @out: buffer for the UUID
+ *
+ * Copies the namespace's uuid into @out
+ */
+void nvme_ns_get_uuid(nvme_ns_t n, unsigned char out[NVME_UUID_LEN]);
+
+/**
+ * nvme_ns_get_sysfs_dir() - sysfs directory of a namespace
+ * @n: Namespace instance
+ *
+ * Return: sysfs directory name of @n
+ */
+const char *nvme_ns_get_sysfs_dir(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_name() - sysfs name of a namespace
+ * @n: Namespace instance
+ *
+ * Return: sysfs name of @n
+ */
+const char *nvme_ns_get_name(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_generic_name() - Returns name of generic namespace chardev.
+ * @n: Namespace instance
+ *
+ * Return: Name of generic namespace chardev
+ */
+const char *nvme_ns_get_generic_name(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_firmware() - Firmware string of a namespace
+ * @n: Namespace instance
+ *
+ * Return: Firmware string of @n
+ */
+const char *nvme_ns_get_firmware(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_serial() - Serial number of a namespace
+ * @n: Namespace instance
+ *
+ * Return: Serial number string of @n
+ */
+const char *nvme_ns_get_serial(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_model() - Model of a namespace
+ * @n: Namespace instance
+ *
+ * Return: Model string of @n
+ */
+const char *nvme_ns_get_model(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_subsystem() - &nvme_subsystem_t of a namespace
+ * @n: Namespace instance
+ *
+ * Return: nvme_subsystem_t object of @n
+ */
+nvme_subsystem_t nvme_ns_get_subsystem(nvme_ns_t n);
+
+/**
+ * nvme_ns_get_ctrl() - &nvme_ctrl_t of a namespace
+ * @n: Namespace instance
+ *
+ * nvme_ctrl_t object may be NULL for a multipathed namespace
+ *
+ * Return: nvme_ctrl_t object of @n if present
+ */
+nvme_ctrl_t nvme_ns_get_ctrl(nvme_ns_t n);
+
+/**
+ * nvme_free_ns() - Free a namespace object
+ * @n: Namespace instance
+ */
+void nvme_free_ns(struct nvme_ns *n);
+
+/**
+ * nvme_ns_read() - Read from a namespace
+ * @n: Namespace instance
+ * @buf: Buffer into which the data will be transferred
+ * @offset: LBA offset of @n
+ * @count: Number of sectors in @buf
+ *
+ * Return: Number of sectors read or -1 on error.
+ */
+int nvme_ns_read(nvme_ns_t n, void *buf, off_t offset, size_t count);
+
+/**
+ * nvme_ns_write() - Write to a namespace
+ * @n: Namespace instance
+ * @buf: Buffer with data to be written
+ * @offset: LBA offset of @n
+ * @count: Number of sectors in @buf
+ *
+ * Return: Number of sectors written or -1 on error
+ */
+int nvme_ns_write(nvme_ns_t n, void *buf, off_t offset, size_t count);
+
+/**
+ * nvme_ns_verify() - Verify data on a namespace
+ * @n: Namespace instance
+ * @offset: LBA offset of @n
+ * @count: Number of sectors to be verified
+ *
+ * Return: Number of sectors verified
+ */
+int nvme_ns_verify(nvme_ns_t n, off_t offset, size_t count);
+
+/**
+ * nvme_ns_compare() - Compare data on a namespace
+ * @n: Namespace instance
+ * @buf: Buffer with data to be compared
+ * @offset: LBA offset of @n
+ * @count: Number of sectors in @buf
+ *
+ * Return: Number of sectors compared
+ */
+int nvme_ns_compare(nvme_ns_t n, void *buf, off_t offset, size_t count);
+
+/**
+ * nvme_ns_write_zeros() - Write zeros to a namespace
+ * @n: Namespace instance
+ * @offset: LBA offset in @n
+ * @count: Number of sectors to be written
+ *
+ * Return: Number of sectors written
+ */
+int nvme_ns_write_zeros(nvme_ns_t n, off_t offset, size_t count);
+
+/**
+ * nvme_ns_write_uncorrectable() - Issus a 'write uncorrectable' command
+ * @n: Namespace instance
+ * @offset: LBA offset in @n
+ * @count: Number of sectors to be written
+ *
+ * Return: Number of sectors written
+ */
+int nvme_ns_write_uncorrectable(nvme_ns_t n, off_t offset, size_t count);
+
+/**
+ * nvme_ns_flush() - Flush data to a namespace
+ * @n: Namespace instance
+ *
+ * Return: 0 on success, -1 on error.
+ */
+int nvme_ns_flush(nvme_ns_t n);
+
+/**
+ * nvme_ns_identify() - Issue an 'identify namespace' command
+ * @n: Namespace instance
+ * @ns: &nvme_id_ns buffer
+ *
+ * Writes the data returned by the 'identify namespace' command
+ * into @ns.
+ *
+ * Return: 0 on success, -1 on error.
+ */
+int nvme_ns_identify(nvme_ns_t n, struct nvme_id_ns *ns);
+
+/**
+ * nvme_ns_identify_descs() - Issue an 'identify descriptors' command
+ * @n: Namespace instance
+ * @descs: List of identify descriptors
+ *
+ * Writes the data returned by the 'identify descriptors' command
+ * into @descs.
+ *
+ * Return: 0 on success, -1 on error.
+ */
+int nvme_ns_identify_descs(nvme_ns_t n, struct nvme_ns_id_desc *descs);
+
+/**
+ * nvme_path_get_name() - sysfs name of an &nvme_path_t object
+ * @p: &nvme_path_t object
+ *
+ * Return: sysfs name of @p
+ */
+const char *nvme_path_get_name(nvme_path_t p);
+
+/**
+ * nvme_path_get_sysfs_dir() - sysfs directory of an nvme_path_t object
+ * @p: &nvme_path_t object
+ *
+ * Return: sysfs directory of @p
+ */
+const char *nvme_path_get_sysfs_dir(nvme_path_t p);
+
+/**
+ * nvme_path_get_ana_state() - ANA state of an nvme_path_t object
+ * @p: &nvme_path_t object
+ *
+ * Return: ANA (Asynchronous Namespace Access) state of @p
+ */
+const char *nvme_path_get_ana_state(nvme_path_t p);
+
+/**
+ * nvme_path_get_ctrl() - Parent controller of an nvme_path_t object
+ * @p: &nvme_path_t object
+ *
+ * Return: Parent controller if present
+ */
+nvme_ctrl_t nvme_path_get_ctrl(nvme_path_t p);
+
+/**
+ * nvme_path_get_ns() - Parent namespace of an nvme_path_t object
+ * @p: &nvme_path_t object
+ *
+ * Return: Parent namespace if present
+ */
+nvme_ns_t nvme_path_get_ns(nvme_path_t p);
+
+/**
+ * nvme_ctrl_get_fd() - Get associated file descriptor
+ * @c: Controller instance
+ *
+ * libnvme will open() the file (if not already opened) and keep
+ * an internal copy of the file descriptor. Following calls to
+ * this API retrieve the internal cached copy of the file
+ * descriptor. The file will remain opened and the fd will
+ * remain cached until the controller object is deleted or
+ * nvme_ctrl_release_fd() is called.
+ *
+ * Return: File descriptor associated with @c or -1
+ */
+int nvme_ctrl_get_fd(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_release_fd() - Close fd and clear fd from controller object
+ * @c: Controller instance
+ *
+ */
+void nvme_ctrl_release_fd(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_name() - sysfs name of a controller
+ * @c: Controller instance
+ *
+ * Return: sysfs name of @c
+ */
+const char *nvme_ctrl_get_name(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_sysfs_dir() - sysfs directory of a controller
+ * @c: Controller instance
+ *
+ * Return: sysfs directory name of @c
+ */
+const char *nvme_ctrl_get_sysfs_dir(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_address() - Address string of a controller
+ * @c: Controller instance
+ *
+ * Return: NVMe-over-Fabrics address string of @c or empty string
+ * of no address is present.
+ */
+const char *nvme_ctrl_get_address(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_src_addr() - Extract src_addr from the c->address string
+ * @c: Controller instance
+ * @src_addr: Where to copy the src_addr. Size must be at least INET6_ADDRSTRLEN.
+ * @src_addr_len: Length of the buffer @src_addr.
+ *
+ * Return: Pointer to @src_addr on success. NULL on failure to extract the src_addr.
+ */
+char *nvme_ctrl_get_src_addr(nvme_ctrl_t c, char *src_addr, size_t src_addr_len);
+
+/**
+ * nvme_ctrl_get_phy_slot() - PCI physical slot number of a controller
+ * @c: Controller instance
+ *
+ * Return: PCI physical slot number of @c or empty string if slot
+ * number is not present.
+ */
+const char *nvme_ctrl_get_phy_slot(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_firmware() - Firmware string of a controller
+ * @c: Controller instance
+ *
+ * Return: Firmware string of @c
+ */
+const char *nvme_ctrl_get_firmware(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_model() - Model of a controller
+ * @c: Controller instance
+ *
+ * Return: Model string of @c
+ */
+const char *nvme_ctrl_get_model(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_state() - Running state of a controller
+ * @c: Controller instance
+ *
+ * Return: String indicating the running state of @c
+ */
+const char *nvme_ctrl_get_state(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_numa_node() - NUMA node of a controller
+ * @c: Controller instance
+ *
+ * Return: String indicating the NUMA node
+ */
+const char *nvme_ctrl_get_numa_node(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_queue_count() - Queue count of a controller
+ * @c: Controller instance
+ *
+ * Return: Queue count of @c
+ */
+const char *nvme_ctrl_get_queue_count(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_serial() - Serial number of a controller
+ * @c: Controller instance
+ *
+ * Return: Serial number string of @c
+ */
+const char *nvme_ctrl_get_serial(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_sqsize() - SQ size of a controller
+ * @c: Controller instance
+ *
+ * Return: SQ size (as string) of @c
+ */
+const char *nvme_ctrl_get_sqsize(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_transport() - Transport type of a controller
+ * @c: Controller instance
+ *
+ * Return: Transport type of @c
+ */
+const char *nvme_ctrl_get_transport(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_subsysnqn() - Subsystem NQN of a controller
+ * @c: Controller instance
+ *
+ * Return: Subsystem NQN of @c
+ */
+const char *nvme_ctrl_get_subsysnqn(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_subsystem() - Parent subsystem of a controller
+ * @c: Controller instance
+ *
+ * Return: Parent nvme_subsystem_t object
+ */
+nvme_subsystem_t nvme_ctrl_get_subsystem(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_traddr() - Transport address of a controller
+ * @c: Controller instance
+ *
+ * Return: Transport address of @c
+ */
+const char *nvme_ctrl_get_traddr(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_trsvcid() - Transport service identifier of a controller
+ * @c: Controller instance
+ *
+ * Return: Transport service identifier of @c (if present)
+ */
+const char *nvme_ctrl_get_trsvcid(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_host_traddr() - Host transport address of a controller
+ * @c: Controller instance
+ *
+ * Return: Host transport address of @c (if present)
+ */
+const char *nvme_ctrl_get_host_traddr(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_host_iface() - Host interface name of a controller
+ * @c: Controller instance
+ *
+ * Return: Host interface name of @c (if present)
+ */
+const char *nvme_ctrl_get_host_iface(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_get_dhchap_host_key() - Return host key
+ * @c: Controller to be checked
+ *
+ * Return: DH-HMAC-CHAP host key or NULL if not set
+ */
+const char *nvme_ctrl_get_dhchap_host_key(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_dhchap_host_key() - Set host key
+ * @c: Host for which the key should be set
+ * @key: DH-HMAC-CHAP Key to set or NULL to clear existing key
+ */
+void nvme_ctrl_set_dhchap_host_key(nvme_ctrl_t c, const char *key);
+
+/**
+ * nvme_ctrl_get_dhchap_key() - Return controller key
+ * @c: Controller for which the key should be set
+ *
+ * Return: DH-HMAC-CHAP controller key or NULL if not set
+ */
+const char *nvme_ctrl_get_dhchap_key(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_dhchap_key() - Set controller key
+ * @c: Controller for which the key should be set
+ * @key: DH-HMAC-CHAP Key to set or NULL to clear existing key
+ */
+void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key);
+
+/**
+ * nvme_ctrl_get_config() - Fabrics configuration of a controller
+ * @c: Controller instance
+ *
+ * Return: Fabrics configuration of @c
+ */
+struct nvme_fabrics_config *nvme_ctrl_get_config(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_discovered() - Set the 'discovered' flag
+ * @c: nvme_ctrl_t object
+ * @discovered: Value of the 'discovered' flag
+ *
+ * Set the 'discovered' flag of @c to @discovered
+ */
+void nvme_ctrl_set_discovered(nvme_ctrl_t c, bool discovered);
+
+/**
+ * nvme_ctrl_is_discovered() - Returns the value of the 'discovered' flag
+ * @c: Controller instance
+ *
+ * Return: Value of the 'discovered' flag of @c
+ */
+bool nvme_ctrl_is_discovered(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_persistent() - Set the 'persistent' flag
+ * @c: Controller instance
+ * @persistent: value of the 'persistent' flag
+ *
+ * Set the 'persistent' flag of @c to @persistent
+ */
+void nvme_ctrl_set_persistent(nvme_ctrl_t c, bool persistent);
+
+/**
+ * nvme_ctrl_is_persistent() - Returns the value of the 'persistent' flag
+ * @c: Controller instance
+ *
+ * Return: Value of the 'persistent' flag of @c
+ */
+bool nvme_ctrl_is_persistent(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_discovery_ctrl() - Set the 'discovery_ctrl' flag
+ * @c: Controller to be modified
+ * @discovery: value of the discovery_ctrl flag
+ *
+ * Sets the 'discovery_ctrl' flag in @c to specify whether
+ * @c connects to a discovery subsystem.
+ *
+ */
+void nvme_ctrl_set_discovery_ctrl(nvme_ctrl_t c, bool discovery);
+
+/**
+ * nvme_ctrl_is_discovery_ctrl() - Check the 'discovery_ctrl' flag
+ * @c: Controller to be checked
+ *
+ * Returns the value of the 'discovery_ctrl' flag which specifies whether
+ * @c connects to a discovery subsystem.
+ *
+ * Return: Value of the 'discover_ctrl' flag
+ */
+bool nvme_ctrl_is_discovery_ctrl(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_unique_discovery_ctrl() - Set the 'unique_discovery_ctrl' flag
+ * @c: Controller to be modified
+ * @unique: value of the unique_disc_ctrl flag
+ *
+ * Sets the 'unique_discovery_ctrl' flag in @c to specify wheter
+ * @c is a unique discovery controller
+ *
+ */
+void nvme_ctrl_set_unique_discovery_ctrl(nvme_ctrl_t c, bool unique);
+
+/**
+ * nvme_ctrl_is_unique_discovery_ctrl() - Check the 'unique_discovery_ctrl' flag
+ * @c: Controller to be checked
+ *
+ * Return: Value of the 'unique_discovery_ctrl' flag
+ */
+bool nvme_ctrl_is_unique_discovery_ctrl(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_identify() - Issues an 'identify controller' command
+ * @c: Controller instance
+ * @id: Identify controller data structure
+ *
+ * Issues an 'identify controller' command to @c and copies the
+ * data into @id.
+ *
+ * Return: 0 on success or -1 on failure.
+ */
+int nvme_ctrl_identify(nvme_ctrl_t c, struct nvme_id_ctrl *id);
+
+/**
+ * nvme_disconnect_ctrl() - Disconnect a controller
+ * @c: Controller instance
+ *
+ * Issues a 'disconnect' fabrics command to @c
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int nvme_disconnect_ctrl(nvme_ctrl_t c);
+
+/**
+ * nvme_scan_ctrl() - Scan on a controller
+ * @r: nvme_root_t object
+ * @name: Name of the controller
+ *
+ * Scans a controller with sysfs name @name and add it to @r.
+ *
+ * Return: nvme_ctrl_t object
+ */
+nvme_ctrl_t nvme_scan_ctrl(nvme_root_t r, const char *name);
+
+/**
+ * nvme_rescan_ctrl() - Rescan an existing controller
+ * @c: Controller instance
+ */
+void nvme_rescan_ctrl(nvme_ctrl_t c);
+
+/**
+ * nvme_init_ctrl() - Initialize nvme_ctrl_t object for an existing controller.
+ * @h: nvme_host_t object
+ * @c: nvme_ctrl_t object
+ * @instance: Instance number (e.g. 1 for nvme1)
+ *
+ * Return: The ioctl() return code. Typically 0 on success.
+ */
+int nvme_init_ctrl(nvme_host_t h, nvme_ctrl_t c, int instance);
+
+/**
+ * nvme_free_ctrl() - Free controller
+ * @c: Controller instance
+ */
+void nvme_free_ctrl(struct nvme_ctrl *c);
+
+/**
+ * nvme_unlink_ctrl() - Unlink controller
+ * @c: Controller instance
+ */
+void nvme_unlink_ctrl(struct nvme_ctrl *c);
+
+/**
+ * nvme_subsystem_get_nqn() - Retrieve NQN from subsystem
+ * @s: nvme_subsystem_t object
+ *
+ * Return: NQN of subsystem
+ */
+const char *nvme_subsystem_get_nqn(nvme_subsystem_t s);
+
+/**
+ * nvme_subsystem_get_sysfs_dir() - sysfs directory of an nvme_subsystem_t object
+ * @s: nvme_subsystem_t object
+ *
+ * Return: sysfs directory name of @s
+ */
+const char *nvme_subsystem_get_sysfs_dir(nvme_subsystem_t s);
+
+/**
+ * nvme_subsystem_get_name() - sysfs name of an nvme_subsystem_t object
+ * @s: nvme_subsystem_t object
+ *
+ * Return: sysfs name of @s
+ */
+const char *nvme_subsystem_get_name(nvme_subsystem_t s);
+
+/**
+ * nvme_subsystem_get_type() - Returns the type of a subsystem
+ * @s: nvme_subsystem_t object
+ *
+ * Returns the subsystem type of @s.
+ *
+ * Return: 'nvm' or 'discovery'
+ */
+const char *nvme_subsystem_get_type(nvme_subsystem_t s);
+
+/**
+ * nvme_subsystem_get_application() - Return the application string
+ * @s: nvme_subsystem_t object
+ *
+ * Return: Managing application string or NULL if not set.
+ */
+const char *nvme_subsystem_get_application(nvme_subsystem_t s);
+
+/**
+ * nvme_subsystem_set_application() - Set the application string
+ * @s: nvme_subsystem_t object
+ * @a: application string
+ *
+ * Sets the managing application string for @s.
+ */
+void nvme_subsystem_set_application(nvme_subsystem_t s, const char *a);
+
+/**
+ * nvme_subsystem_get_iopolicy() - Return the IO policy of subsytem
+ * @s: nvme_subsystem_t object
+ *
+ * Return: IO policy used by current subsystem
+ */
+const char *nvme_subsystem_get_iopolicy(nvme_subsystem_t s);
+
+/**
+ * nvme_scan_topology() - Scan NVMe topology and apply filter
+ * @r: nvme_root_t object
+ * @f: filter to apply
+ * @f_args: user-specified argument to @f
+ *
+ * Scans the NVMe topology and filters out the resulting elements
+ * by applying @f.
+ *
+ * Return: Number of elements scanned
+ */
+int nvme_scan_topology(nvme_root_t r, nvme_scan_filter_t f, void *f_args);
+
+/**
+ * nvme_host_get_hostnqn() - Host NQN of an nvme_host_t object
+ * @h: nvme_host_t object
+ *
+ * Return: Host NQN of @h
+ */
+const char *nvme_host_get_hostnqn(nvme_host_t h);
+
+/**
+ * nvme_host_get_hostid() - Host ID of an nvme_host_t object
+ * @h: nvme_host_t object
+ *
+ * Return: Host ID of @h
+ */
+const char *nvme_host_get_hostid(nvme_host_t h);
+
+/**
+ * nvme_host_release_fds() - Close all opened file descriptors under host
+ * @h: nvme_host_t object
+ *
+ * Controller and Namespace objects cache the file descriptors
+ * of opened nvme devices. This API can be used to close and
+ * clear all cached fds under this host.
+ */
+void nvme_host_release_fds(struct nvme_host *h);
+
+/**
+ * nvme_free_host() - Free nvme_host_t object
+ * @h: nvme_host_t object
+ */
+void nvme_free_host(nvme_host_t h);
+
+/**
+ * nvme_scan() - Scan NVMe topology
+ * @config_file: Configuration file
+ *
+ * Return: nvme_root_t object of found elements
+ */
+nvme_root_t nvme_scan(const char *config_file);
+
+/**
+ * nvme_read_config() - Read NVMe JSON configuration file
+ * @r: nvme_root_t object
+ * @config_file: JSON configuration file
+ *
+ * Read in the contents of @config_file and merge them with
+ * the elements in @r.
+ *
+ * Returns: 0 on success, -1 on failure with errno set.
+ */
+int nvme_read_config(nvme_root_t r, const char *config_file);
+
+/**
+ * nvme_refresh_topology() - Refresh nvme_root_t object contents
+ * @r: nvme_root_t object
+ *
+ * Removes all elements in @r and rescans the existing topology.
+ */
+void nvme_refresh_topology(nvme_root_t r);
+
+/**
+ * nvme_update_config() - Update JSON configuration
+ * @r: nvme_root_t object
+ *
+ * Updates the JSON configuration file with the contents of @r.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int nvme_update_config(nvme_root_t r);
+
+/**
+ * nvme_dump_config() - Print the JSON configuration
+ * @r: nvme_root_t object
+ *
+ * Prints the current contents of the JSON configuration
+ * file to stdout.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int nvme_dump_config(nvme_root_t r);
+
+/**
+ * nvme_dump_tree() - Dump internal object tree
+ * @r: nvme_root_t object
+ *
+ * Prints the internal object tree in JSON format
+ * to stdout.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+int nvme_dump_tree(nvme_root_t r);
+
+/**
+ * nvme_get_attr() - Read sysfs attribute
+ * @d: sysfs directory
+ * @attr: sysfs attribute name
+ *
+ * Return: String with the contents of @attr or %NULL in case of an empty value
+ * or in case of an error (indicated by non-zero errno code).
+ */
+char *nvme_get_attr(const char *d, const char *attr);
+
+/**
+ * nvme_get_subsys_attr() - Read subsystem sysfs attribute
+ * @s: nvme_subsystem_t object
+ * @attr: sysfs attribute name
+ *
+ * Return: String with the contents of @attr or %NULL in case of an empty value
+ * or in case of an error (indicated by non-zero errno code).
+ */
+char *nvme_get_subsys_attr(nvme_subsystem_t s, const char *attr);
+
+/**
+ * nvme_get_ctrl_attr() - Read controller sysfs attribute
+ * @c: Controller instance
+ * @attr: sysfs attribute name
+ *
+ * Return: String with the contents of @attr or %NULL in case of an empty value
+ * or in case of an error (indicated by non-zero errno code).
+ */
+char *nvme_get_ctrl_attr(nvme_ctrl_t c, const char *attr);
+
+/**
+ * nvme_get_ns_attr() - Read namespace sysfs attribute
+ * @n: nvme_ns_t object
+ * @attr: sysfs attribute name
+ *
+ * Return: String with the contents of @attr or %NULL in case of an empty value
+ * or in case of an error (indicated by non-zero errno code).
+ */
+char *nvme_get_ns_attr(nvme_ns_t n, const char *attr);
+
+/**
+ * nvme_subsystem_lookup_namespace() - lookup namespace by NSID
+ * @s: nvme_subsystem_t object
+ * @nsid: Namespace id
+ *
+ * Return: nvme_ns_t of the namespace with id @nsid in subsystem @s
+ */
+nvme_ns_t nvme_subsystem_lookup_namespace(struct nvme_subsystem *s,
+ __u32 nsid);
+
+/**
+ * nvme_subsystem_release_fds() - Close all opened fds under subsystem
+ * @s: nvme_subsystem_t object
+ *
+ * Controller and Namespace objects cache the file descriptors
+ * of opened nvme devices. This API can be used to close and
+ * clear all cached fds under this subsystem.
+ *
+ */
+void nvme_subsystem_release_fds(struct nvme_subsystem *s);
+
+
+/**
+ * nvme_get_path_attr() - Read path sysfs attribute
+ * @p: nvme_path_t object
+ * @attr: sysfs attribute name
+ *
+ * Return: String with the contents of @attr or %NULL in case of an empty value
+ * or in case of an error (indicated by non-zero errno code).
+ */
+char *nvme_get_path_attr(nvme_path_t p, const char *attr);
+
+/**
+ * nvme_scan_namespace() - scan namespace based on sysfs name
+ * @name: sysfs name of the namespace to scan
+ *
+ * Return: nvme_ns_t object or NULL if not found.
+ */
+nvme_ns_t nvme_scan_namespace(const char *name);
+
+/**
+ * nvme_host_get_hostsymname() - Get the host's symbolic name
+ * @h: Host for which the symbolic name should be returned.
+ *
+ * Return: The symbolic name or NULL if a symbolic name hasn't been
+ * configure.
+ */
+const char *nvme_host_get_hostsymname(nvme_host_t h);
+
+/**
+ * nvme_host_set_hostsymname() - Set the host's symbolic name
+ * @h: Host for which the symbolic name should be set.
+ * @hostsymname: Symbolic name
+ */
+void nvme_host_set_hostsymname(nvme_host_t h, const char *hostsymname);
+
+#endif /* _LIBNVME_TREE_H */
diff --git a/src/nvme/types.h b/src/nvme/types.h
new file mode 100644
index 0000000..fe79b6e
--- /dev/null
+++ b/src/nvme/types.h
@@ -0,0 +1,7996 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+
+#ifndef _LIBNVME_TYPES_H
+#define _LIBNVME_TYPES_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <linux/types.h>
+
+/**
+ * DOC: types.h
+ *
+ * NVMe standard definitions
+ */
+
+/**
+ * NVME_GET() - extract field from complex value
+ * @value: The original value of a complex field
+ * @name: The name of the sub-field within an nvme value
+ *
+ * By convention, this library defines _SHIFT and _MASK such that mask can be
+ * applied after the shift to isolate a specific set of bits that decode to a
+ * sub-field.
+ *
+ * Returns: The 'name' field from 'value'
+ */
+#define NVME_GET(value, name) \
+ (((value) >> NVME_##name##_SHIFT) & NVME_##name##_MASK)
+
+/**
+ * NVME_SET() - set field into complex value
+ * @value: The value to be set in its completed position
+ * @name: The name of the sub-field within an nvme value
+ *
+ * Returns: The 'name' field from 'value'
+ */
+#define NVME_SET(value, name) \
+ (((__u32)(value) & NVME_##name##_MASK) << NVME_##name##_SHIFT)
+
+/**
+ * enum nvme_constants - A place to stash various constant nvme values
+ * @NVME_NSID_ALL: A broadcast value that is used to specify all
+ * namespaces
+ * @NVME_NSID_NONE: The invalid namespace id, for when the nsid
+ * parameter is not used in a command
+ * @NVME_UUID_NONE: Use to omit a uuid command parameter
+ * @NVME_CNTLID_NONE: Use to omit a cntlid command parameter
+ * @NVME_CNSSPECID_NONE: Use to omit a cns_specific_id command parameter
+ * @NVME_LOG_LSP_NONE: Use to omit a log lsp command parameter
+ * @NVME_LOG_LSI_NONE: Use to omit a log lsi command parameter
+ * @NVME_LOG_LPO_NONE: Use to omit a log lpo command parameter
+ * @NVME_IDENTIFY_DATA_SIZE: The transfer size for nvme identify commands
+ * @NVME_LOG_SUPPORTED_LOG_PAGES_MAX: The largest possible index in the supported
+ * log pages log.
+ * @NVME_ID_NVMSET_LIST_MAX: The largest possible nvmset index in identify
+ * nvmeset
+ * @NVME_ID_UUID_LIST_MAX: The largest possible uuid index in identify
+ * uuid list
+ * @NVME_ID_CTRL_LIST_MAX: The largest possible controller index in
+ * identify controller list
+ * @NVME_ID_NS_LIST_MAX: The largest possible namespace index in
+ * identify namespace list
+ * @NVME_ID_SECONDARY_CTRL_MAX: The largest possible secondary controller index
+ * in identify secondary controller
+ * @NVME_ID_DOMAIN_LIST_MAX: The largest possible domain index in the
+ * in domain list
+ * @NVME_ID_ENDURANCE_GROUP_LIST_MAX: The largest possible endurance group
+ * index in the endurance group list
+ * @NVME_ID_ND_DESCRIPTOR_MAX: The largest possible namespace granularity
+ * index in the namespace granularity descriptor
+ * list
+ * @NVME_FEAT_LBA_RANGE_MAX: The largest possible LBA range index in feature
+ * lba range type
+ * @NVME_LOG_ST_MAX_RESULTS: The largest possible self test result index in the
+ * device self test log
+ * @NVME_LOG_FID_SUPPORTED_EFFECTS_MAX: The largest possible FID index in the
+ * feature identifiers effects log.
+ * @NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX: The largest possible MI Command index
+ * in the MI Command effects log.
+ * @NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED: The reserved space in the MI Command
+ * effects log.
+ * @NVME_LOG_TELEM_BLOCK_SIZE: Specification defined size of Telemetry Data Blocks
+ * @NVME_DSM_MAX_RANGES: The largest possible range index in a data-set
+ * management command
+ * @NVME_NQN_LENGTH: Max length for NVMe Qualified Name
+ * @NVMF_TRADDR_SIZE: Max Transport Address size
+ * @NVMF_TSAS_SIZE: Max Transport Specific Address Subtype size
+ * @NVME_ZNS_CHANGED_ZONES_MAX: Max number of zones in the changed zones log
+ * page
+ */
+enum nvme_constants {
+ NVME_NSID_ALL = 0xffffffff,
+ NVME_NSID_NONE = 0,
+ NVME_UUID_NONE = 0,
+ NVME_CNTLID_NONE = 0,
+ NVME_CNSSPECID_NONE = 0,
+ NVME_LOG_LSP_NONE = 0,
+ NVME_LOG_LSI_NONE = 0,
+ NVME_LOG_LPO_NONE = 0,
+ NVME_IDENTIFY_DATA_SIZE = 4096,
+ NVME_LOG_SUPPORTED_LOG_PAGES_MAX = 256,
+ NVME_ID_NVMSET_LIST_MAX = 31,
+ NVME_ID_UUID_LIST_MAX = 127,
+ NVME_ID_CTRL_LIST_MAX = 2047,
+ NVME_ID_NS_LIST_MAX = 1024,
+ NVME_ID_SECONDARY_CTRL_MAX = 127,
+ NVME_ID_DOMAIN_LIST_MAX = 31,
+ NVME_ID_ENDURANCE_GROUP_LIST_MAX = 2047,
+ NVME_ID_ND_DESCRIPTOR_MAX = 16,
+ NVME_FEAT_LBA_RANGE_MAX = 64,
+ NVME_LOG_ST_MAX_RESULTS = 20,
+ NVME_LOG_TELEM_BLOCK_SIZE = 512,
+ NVME_LOG_FID_SUPPORTED_EFFECTS_MAX = 256,
+ NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX = 256,
+ NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED = 768,
+ NVME_DSM_MAX_RANGES = 256,
+ NVME_NQN_LENGTH = 256,
+ NVMF_TRADDR_SIZE = 256,
+ NVMF_TSAS_SIZE = 256,
+ NVME_ZNS_CHANGED_ZONES_MAX = 511,
+};
+
+/**
+ * enum nvme_csi - Defined command set indicators
+ * @NVME_CSI_NVM: NVM Command Set Indicator
+ * @NVME_CSI_KV: Key Value Command Set
+ * @NVME_CSI_ZNS: Zoned Namespace Command Set
+ */
+enum nvme_csi {
+ NVME_CSI_NVM = 0,
+ NVME_CSI_KV = 1,
+ NVME_CSI_ZNS = 2,
+};
+
+/**
+ * enum nvme_register_offsets - controller registers for all transports. This
+ * is the layout of BAR0/1 for PCIe, and
+ * properties for fabrics.
+ * @NVME_REG_CAP: Controller Capabilities
+ * @NVME_REG_VS: Version
+ * @NVME_REG_INTMS: Interrupt Mask Set
+ * @NVME_REG_INTMC: Interrupt Mask Clear
+ * @NVME_REG_CC: Controller Configuration
+ * @NVME_REG_CSTS: Controller Status
+ * @NVME_REG_NSSR: NVM Subsystem Reset
+ * @NVME_REG_AQA: Admin Queue Attributes
+ * @NVME_REG_ASQ: Admin SQ Base Address
+ * @NVME_REG_ACQ: Admin CQ Base Address
+ * @NVME_REG_CMBLOC: Controller Memory Buffer Location
+ * @NVME_REG_CMBSZ: Controller Memory Buffer Size
+ * @NVME_REG_BPINFO: Boot Partition Information
+ * @NVME_REG_BPRSEL: Boot Partition Read Select
+ * @NVME_REG_BPMBL: Boot Partition Memory Buffer Location
+ * @NVME_REG_CMBMSC: Controller Memory Buffer Memory Space Control
+ * @NVME_REG_CMBSTS: Controller Memory Buffer Status
+ * @NVME_REG_CMBEBS: Controller Memory Buffer Elasticity Buffer Size
+ * @NVME_REG_CMBSWTP: Controller Memory Buffer Sustained Write Throughput
+ * @NVME_REG_NSSD: NVM Subsystem Shutdown
+ * @NVME_REG_CRTO: Controller Ready Timeouts
+ * @NVME_REG_PMRCAP: Persistent Memory Capabilities
+ * @NVME_REG_PMRCTL: Persistent Memory Region Control
+ * @NVME_REG_PMRSTS: Persistent Memory Region Status
+ * @NVME_REG_PMREBS: Persistent Memory Region Elasticity Buffer Size
+ * @NVME_REG_PMRSWTP: Memory Region Sustained Write Throughput
+ * @NVME_REG_PMRMSCL: Persistent Memory Region Controller Memory Space Control Lower
+ * @NVME_REG_PMRMSCU: Persistent Memory Region Controller Memory Space Control Upper
+ */
+enum nvme_register_offsets {
+ NVME_REG_CAP = 0x0000,
+ NVME_REG_VS = 0x0008,
+ NVME_REG_INTMS = 0x000c,
+ NVME_REG_INTMC = 0x0010,
+ NVME_REG_CC = 0x0014,
+ NVME_REG_CSTS = 0x001c,
+ NVME_REG_NSSR = 0x0020,
+ NVME_REG_AQA = 0x0024,
+ NVME_REG_ASQ = 0x0028,
+ NVME_REG_ACQ = 0x0030,
+ NVME_REG_CMBLOC = 0x0038,
+ NVME_REG_CMBSZ = 0x003c,
+ NVME_REG_BPINFO = 0x0040,
+ NVME_REG_BPRSEL = 0x0044,
+ NVME_REG_BPMBL = 0x0048,
+ NVME_REG_CMBMSC = 0x0050,
+ NVME_REG_CMBSTS = 0x0058,
+ NVME_REG_CMBEBS = 0x005c,
+ NVME_REG_CMBSWTP = 0x0060,
+ NVME_REG_NSSD = 0x0064,
+ NVME_REG_CRTO = 0x0068,
+ NVME_REG_PMRCAP = 0x0e00,
+ NVME_REG_PMRCTL = 0x0e04,
+ NVME_REG_PMRSTS = 0x0e08,
+ NVME_REG_PMREBS = 0x0e0c,
+ NVME_REG_PMRSWTP = 0x0e10,
+ NVME_REG_PMRMSCL = 0x0e14,
+ NVME_REG_PMRMSCU = 0x0e18,
+};
+
+/**
+ * nvme_is_64bit_reg() - Checks if offset of the controller register is a know
+ * 64bit value.
+ * @offset: Offset of controller register field in bytes
+ *
+ * This function does not care about transport so that the offset is not going
+ * to be checked inside of this function for the unsupported fields in a
+ * specific transport. For example, BPMBL(Boot Partition Memory Buffer
+ * Location) register is not supported by fabrics, but it can be checked here.
+ *
+ * Returns: true if given offset is 64bit register, otherwise it returns false.
+ */
+static inline bool nvme_is_64bit_reg(__u32 offset)
+{
+ switch (offset) {
+ case NVME_REG_CAP:
+ case NVME_REG_ASQ:
+ case NVME_REG_ACQ:
+ case NVME_REG_BPMBL:
+ case NVME_REG_CMBMSC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+enum nvme_cap {
+ NVME_CAP_MQES_SHIFT = 0,
+ NVME_CAP_CQR_SHIFT = 16,
+ NVME_CAP_AMS_SHIFT = 17,
+ NVME_CAP_TO_SHIFT = 24,
+ NVME_CAP_DSTRD_SHIFT = 32,
+ NVME_CAP_NSSRC_SHIFT = 36,
+ NVME_CAP_CSS_SHIFT = 37,
+ NVME_CAP_BPS_SHIFT = 45,
+ NVME_CAP_MPSMIN_SHIFT = 48,
+ NVME_CAP_MPSMAX_SHIFT = 52,
+ NVME_CAP_PMRS_SHIFT = 56,
+ NVME_CAP_CMBS_SHIFT = 57,
+ NVME_CAP_CRMS_SHIFT = 59,
+ NVME_CAP_MQES_MASK = 0xffff,
+ NVME_CAP_CQR_MASK = 0x1,
+ NVME_CAP_AMS_MASK = 0x3,
+ NVME_CAP_TO_MASK = 0xff,
+ NVME_CAP_DSTRD_MASK = 0xf,
+ NVME_CAP_NSSRC_MASK = 0x1,
+ NVME_CAP_CSS_MASK = 0xff,
+ NVME_CAP_BPS_MASK = 0x1,
+ NVME_CAP_MPSMIN_MASK = 0xf,
+ NVME_CAP_MPSMAX_MASK = 0xf,
+ NVME_CAP_PMRS_MASK = 0x1,
+ NVME_CAP_CMBS_MASK = 0x1,
+ NVME_CAP_CRMS_MASK = 0x3,
+ NVME_CAP_AMS_WRR = 1 << 0,
+ NVME_CAP_AMS_VS = 1 << 1,
+ NVME_CAP_CSS_NVM = 1 << 0,
+ NVME_CAP_CSS_CSI = 1 << 6,
+ NVME_CAP_CSS_ADMIN = 1 << 7,
+ NVME_CAP_CRWMS = 1 << 0,
+ NVME_CAP_CRIMS = 1 << 1,
+};
+
+#define NVME_CAP_MQES(cap) NVME_GET(cap, CAP_MQES)
+#define NVME_CAP_CQR(cap) NVME_GET(cap, CAP_CQR)
+#define NVME_CAP_AMS(cap) NVME_GET(cap, CAP_AMS)
+#define NVME_CAP_TO(cap) NVME_GET(cap, CAP_TO)
+#define NVME_CAP_DSTRD(cap) NVME_GET(cap, CAP_DSTRD)
+#define NVME_CAP_NSSRC(cap) NVME_GET(cap, CAP_NSSRC)
+#define NVME_CAP_CSS(cap) NVME_GET(cap, CAP_CSS)
+#define NVME_CAP_BPS(cap) NVME_GET(cap, CAP_BPS)
+#define NVME_CAP_MPSMIN(cap) NVME_GET(cap, CAP_MPSMIN)
+#define NVME_CAP_MPSMAX(cap) NVME_GET(cap, CAP_MPSMAX)
+#define NVME_CAP_PMRS(cap) NVME_GET(cap, CAP_PMRS)
+#define NVME_CAP_CMBS(cap) NVME_GET(cap, CAP_CMBS)
+#define NVME_CAP_CRMS(cap) NVME_GET(cap, CAP_CRMS)
+
+enum nvme_vs {
+ NVME_VS_TER_SHIFT = 0,
+ NVME_VS_MNR_SHIFT = 8,
+ NVME_VS_MJR_SHIFT = 16,
+ NVME_VS_TER_MASK = 0xff,
+ NVME_VS_MNR_MASK = 0xff,
+ NVME_VS_MJR_MASK = 0xffff,
+};
+
+#define NVME_VS_TER(vs) NVME_GET(vs, VS_TER)
+#define NVME_VS_MNR(vs) NVME_GET(vs, VS_MNR)
+#define NVME_VS_MJR(vs) NVME_GET(vs, VS_MJR)
+
+#define NVME_MAJOR(ver) NVME_VS_MJR(ver)
+#define NVME_MINOR(ver) NVME_VS_MNR(ver)
+#define NVME_TERTIARY(ver) NVME_VS_TER(ver)
+
+enum nvme_cc {
+ NVME_CC_EN_SHIFT = 0,
+ NVME_CC_CSS_SHIFT = 4,
+ NVME_CC_MPS_SHIFT = 7,
+ NVME_CC_AMS_SHIFT = 11,
+ NVME_CC_SHN_SHIFT = 14,
+ NVME_CC_IOSQES_SHIFT = 16,
+ NVME_CC_IOCQES_SHIFT = 20,
+ NVME_CC_CRIME_SHIFT = 24,
+ NVME_CC_EN_MASK = 0x1,
+ NVME_CC_CSS_MASK = 0x7,
+ NVME_CC_MPS_MASK = 0xf,
+ NVME_CC_AMS_MASK = 0x7,
+ NVME_CC_SHN_MASK = 0x3,
+ NVME_CC_CRIME_MASK = 0x1,
+ NVME_CC_IOSQES_MASK = 0xf,
+ NVME_CC_IOCQES_MASK = 0xf,
+ NVME_CC_CSS_NVM = 0,
+ NVME_CC_CSS_CSI = 6,
+ NVME_CC_CSS_ADMIN = 7,
+ NVME_CC_AMS_RR = 0,
+ NVME_CC_AMS_WRRU = 1,
+ NVME_CC_AMS_VS = 7,
+ NVME_CC_SHN_NONE = 0,
+ NVME_CC_SHN_NORMAL = 1,
+ NVME_CC_SHN_ABRUPT = 2,
+ NVME_CC_CRWME = 0,
+ NVME_CC_CRIME = 1,
+};
+
+#define NVME_CC_EN(cc) NVME_GET(cc, CC_EN)
+#define NVME_CC_CSS(cc) NVME_GET(cc, CC_CSS)
+#define NVME_CC_MPS(cc) NVME_GET(cc, CC_MPS)
+#define NVME_CC_AMS(cc) NVME_GET(cc, CC_AMS)
+#define NVME_CC_SHN(cc) NVME_GET(cc, CC_SHN)
+#define NVME_CC_IOSQES(cc) NVME_GET(cc, CC_IOSQES)
+#define NVME_CC_IOCQES(cc) NVME_GET(cc, CC_IOCQES)
+#define NVME_CC_CRIME(cc) NVME_GET(cc, CC_CRIME)
+
+enum nvme_csts {
+ NVME_CSTS_RDY_SHIFT = 0,
+ NVME_CSTS_CFS_SHIFT = 1,
+ NVME_CSTS_SHST_SHIFT = 2,
+ NVME_CSTS_NSSRO_SHIFT = 4,
+ NVME_CSTS_PP_SHIFT = 5,
+ NVME_CSTS_RDY_MASK = 0x1,
+ NVME_CSTS_CFS_MASK = 0x1,
+ NVME_CSTS_SHN_MASK = 0x3,
+ NVME_CSTS_NSSRO_MASK = 0x1,
+ NVME_CSTS_PP_MASK = 0x1,
+ NVME_CSTS_SHST_NORMAL = 0,
+ NVME_CSTS_SHST_OCCUR = 1,
+ NVME_CSTS_SHST_CMPLT = 2,
+ NVME_CSTS_SHST_MASK = 3,
+};
+
+#define NVME_CSTS_RDY(csts) NVME_GET(csts, CSTS_RDY)
+#define NVME_CSTS_CFS(csts) NVME_GET(csts, CSTS_CFS)
+#define NVME_CSTS_SHST(csts) NVME_GET(csts, CSTS_SHST)
+#define NVME_CSTS_NSSRO(csts) NVME_GET(csts, CSTS_NSSRO)
+#define NVME_CSTS_PP(csts) NVME_GET(csts, CSTS_PP)
+
+enum nvme_aqa {
+ NVME_AQA_ASQS_SHIFT = 0,
+ NVME_AQA_ACQS_SHIFT = 16,
+ NVME_AQA_ASQS_MASK = 0xfff,
+ NVME_AQA_ACQS_MASK = 0xfff,
+};
+
+#define NVME_AQA_ASQS(aqa) NVME_GET(aqa, AQA_ASQS)
+#define NVME_AQA_ACQS(aqa) NVME_GET(aqa, AQA_ACQS)
+
+enum nvme_cmbloc {
+ NVME_CMBLOC_BIR_SHIFT = 0,
+ NVME_CMBLOC_CQMMS_SHIFT = 3,
+ NVME_CMBLOC_CQPDS_SHIFT = 4,
+ NVME_CMBLOC_CDPLMS_SHIFT = 5,
+ NVME_CMBLOC_CDPCILS_SHIFT = 6,
+ NVME_CMBLOC_CDMMMS_SHIFT = 7,
+ NVME_CMBLOC_CQDA_SHIFT = 8,
+ NVME_CMBLOC_OFST_SHIFT = 12,
+ NVME_CMBLOC_BIR_MASK = 0x7,
+ NVME_CMBLOC_CQMMS_MASK = 0x1,
+ NVME_CMBLOC_CQPDS_MASK = 0x1,
+ NVME_CMBLOC_CDPLMS_MASK = 0x1,
+ NVME_CMBLOC_CDPCILS_MASK = 0x1,
+ NVME_CMBLOC_CDMMMS_MASK = 0x1,
+ NVME_CMBLOC_CQDA_MASK = 0x1,
+ NVME_CMBLOC_OFST_MASK = 0xfffff,
+};
+
+#define NVME_CMBLOC_BIR(cmbloc) NVME_GET(cmbloc, CMBLOC_BIR)
+#define NVME_CMBLOC_CQMMS(cmbloc) NVME_GET(cmbloc, CMBLOC_CQMMS)
+#define NVME_CMBLOC_CQPDS(cmbloc) NVME_GET(cmbloc, CMBLOC_CQPDS)
+#define NVME_CMBLOC_CDPLMS(cmbloc) NVME_GET(cmbloc, CMBLOC_CDPLMS)
+#define NVME_CMBLOC_CDPCILS(cmbloc) NVME_GET(cmbloc, CMBLOC_CDPCILS)
+#define NVME_CMBLOC_CDMMMS(cmbloc) NVME_GET(cmbloc, CMBLOC_CDMMMS)
+#define NVME_CMBLOC_CQDA(cmbloc) NVME_GET(cmbloc, CMBLOC_CQDA)
+#define NVME_CMBLOC_OFST(cmbloc) NVME_GET(cmbloc, CMBLOC_OFST)
+
+enum nvme_cmbsz {
+ NVME_CMBSZ_SQS_SHIFT = 0,
+ NVME_CMBSZ_CQS_SHIFT = 1,
+ NVME_CMBSZ_LISTS_SHIFT = 2,
+ NVME_CMBSZ_RDS_SHIFT = 3,
+ NVME_CMBSZ_WDS_SHIFT = 4,
+ NVME_CMBSZ_SZU_SHIFT = 8,
+ NVME_CMBSZ_SZ_SHIFT = 12,
+ NVME_CMBSZ_SQS_MASK = 0x1,
+ NVME_CMBSZ_CQS_MASK = 0x1,
+ NVME_CMBSZ_LISTS_MASK = 0x1,
+ NVME_CMBSZ_RDS_MASK = 0x1,
+ NVME_CMBSZ_WDS_MASK = 0x1,
+ NVME_CMBSZ_SZU_MASK = 0xf,
+ NVME_CMBSZ_SZ_MASK = 0xfffff,
+ NVME_CMBSZ_SZU_4K = 0,
+ NVME_CMBSZ_SZU_64K = 1,
+ NVME_CMBSZ_SZU_1M = 2,
+ NVME_CMBSZ_SZU_16M = 3,
+ NVME_CMBSZ_SZU_256M = 4,
+ NVME_CMBSZ_SZU_4G = 5,
+ NVME_CMBSZ_SZU_64G = 6,
+};
+
+#define NVME_CMBSZ_SQS(cmbsz) NVME_GET(cmbsz, CMBSZ_SQS)
+#define NVME_CMBSZ_CQS(cmbsz) NVME_GET(cmbsz, CMBSZ_CQS)
+#define NVME_CMBSZ_LISTS(cmbsz) NVME_GET(cmbsz, CMBSZ_LISTS)
+#define NVME_CMBSZ_RDS(cmbsz) NVME_GET(cmbsz, CMBSZ_RDS)
+#define NVME_CMBSZ_WDS(cmbsz) NVME_GET(cmbsz, CMBSZ_WDS)
+#define NVME_CMBSZ_SZU(cmbsz) NVME_GET(cmbsz, CMBSZ_SZU)
+#define NVME_CMBSZ_SZ(cmbsz) NVME_GET(cmbsz, CMBSZ_SZ)
+
+/**
+ * nvme_cmb_size() - Calculate size of the controller memory buffer
+ * @cmbsz: Value from controller register %NVME_REG_CMBSZ
+ *
+ * Returns: size of controller memory buffer in bytes
+ */
+static inline __u64 nvme_cmb_size(__u32 cmbsz)
+{
+ return ((__u64)NVME_CMBSZ_SZ(cmbsz)) *
+ (1ULL << (12 + 4 * NVME_CMBSZ_SZU(cmbsz)));
+}
+
+enum nvme_bpinfo {
+ NVME_BPINFO_BPSZ_SHIFT = 0,
+ NVME_BPINFO_BRS_SHIFT = 24,
+ NVME_BPINFO_ABPID_SHIFT = 31,
+ NVME_BPINFO_BPSZ_MASK = 0x7fff,
+ NVME_BPINFO_BRS_MASK = 0x3,
+ NVME_BPINFO_ABPID_MASK = 0x1,
+ NVME_BPINFO_BRS_NONE = 0,
+ NVME_BPINFO_BRS_READ_IN_PROGRESS = 1,
+ NVME_BPINFO_BRS_READ_SUCCESS = 2,
+ NVME_BPINFO_BRS_READ_ERROR = 3,
+};
+
+#define NVME_BPINFO_BPSZ(bpinfo) NVME_GET(bpinfo, BPINFO_BPSZ)
+#define NVME_BPINFO_BRS(bpinfo) NVME_GET(bpinfo, BPINFO_BRS)
+#define NVME_BPINFO_ABPID(bpinfo) NVME_GET(bpinfo, BPINFO_ABPID)
+
+enum nvme_bprsel {
+ NVME_BPRSEL_BPRSZ_SHIFT = 0,
+ NVME_BPRSEL_BPROF_SHIFT = 10,
+ NVME_BPRSEL_BPID_SHIFT = 31,
+ NVME_BPRSEL_BPRSZ_MASK = 0x3ff,
+ NVME_BPRSEL_BPROF_MASK = 0xfff,
+ NVME_BPRSEL_BPID_MASK = 0x1,
+};
+
+#define NVME_BPRSEL_BPRSZ(bprsel) NVME_GET(bprsel, BPRSEL_BPRSZ)
+#define NVME_BPRSEL_BPROF(bprsel) NVME_GET(bprsel, BPRSEL_BPROF)
+#define NVME_BPRSEL_BPID(bprsel) NVME_GET(bprsel, BPRSEL_BPID)
+
+enum nvme_cmbmsc {
+ NVME_CMBMSC_CRE_SHIFT = 0,
+ NVME_CMBMSC_CMSE_SHIFT = 1,
+ NVME_CMBMSC_CBA_SHIFT = 12,
+ NVME_CMBMSC_CRE_MASK = 0x1,
+ NVME_CMBMSC_CMSE_MASK = 0x1,
+};
+static const __u64 NVME_CMBMSC_CBA_MASK = 0xfffffffffffffull;
+
+#define NVME_CMBMSC_CRE(cmbmsc) NVME_GET(cmbmsc, CMBMSC_CRE)
+#define NVME_CMBMSC_CMSE(cmbmsc) NVME_GET(cmbmsc, CMBMSC_CMSE)
+#define NVME_CMBMSC_CBA(cmbmsc) NVME_GET(cmbmsc, CMBMSC_CBA)
+
+enum nvme_cmbsts {
+ NVME_CMBSTS_CBAI_SHIFT = 0,
+ NVME_CMBSTS_CBAI_MASK = 0x1,
+};
+
+#define NVME_CMBSTS_CBAI(cmbsts) NVME_GET(cmbsts, CMBSTS_CBAI)
+
+enum nvme_crto {
+ NVME_CRTO_CRIMT_SHIFT = 16,
+ NVME_CRTO_CRIMT_MASK = 0xffff0000,
+ NVME_CRTO_CRWMT_SHIFT = 0,
+ NVME_CRTO_CRWMT_MASK = 0x0000ffff,
+};
+
+#define NVME_CRTO_CRIMT(crto) NVME_GET(crto, CRTO_CRIMT)
+#define NVME_CRTO_CRWMT(crto) NVME_GET(crto, CRTO_CRWMT)
+
+enum nvme_pmrcap {
+ NVME_PMRCAP_RDS_SHIFT = 3,
+ NVME_PMRCAP_WDS_SHIFT = 4,
+ NVME_PMRCAP_BIR_SHIFT = 5,
+ NVME_PMRCAP_PMRTU_SHIFT = 8,
+ NVME_PMRCAP_PMRWMB_SHIFT = 10,
+ NVME_PMRCAP_PMRTO_SHIFT = 16,
+ NVME_PMRCAP_CMSS_SHIFT = 24,
+ NVME_PMRCAP_RDS_MASK = 0x1,
+ NVME_PMRCAP_WDS_MASK = 0x1,
+ NVME_PMRCAP_BIR_MASK = 0x7,
+ NVME_PMRCAP_PMRTU_MASK = 0x3,
+ NVME_PMRCAP_PMRWMB_MASK = 0xf,
+ NVME_PMRCAP_PMRTO_MASK = 0xff,
+ NVME_PMRCAP_CMSS_MASK = 0x1,
+ NVME_PMRCAP_PMRTU_500MS = 0,
+ NVME_PMRCAP_PMRTU_60S = 1,
+};
+
+#define NVME_PMRCAP_RDS(pmrcap) NVME_GET(pmrcap, PMRCAP_RDS)
+#define NVME_PMRCAP_WDS(pmrcap) NVME_GET(pmrcap, PMRCAP_WDS)
+#define NVME_PMRCAP_BIR(pmrcap) NVME_GET(pmrcap, PMRCAP_BIR)
+#define NVME_PMRCAP_PMRTU(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRTU)
+#define NVME_PMRCAP_PMRWMB(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRWMB)
+#define NVME_PMRCAP_PMRTO(pmrcap) NVME_GET(pmrcap, PMRCAP_PMRTO)
+#define NVME_PMRCAP_CMSS(pmrcap) NVME_GET(pmrcap, PMRCAP_CMSS)
+
+enum nvme_pmrctl {
+ NVME_PMRCTL_EN_SHIFT = 0,
+ NVME_PMRCTL_EN_MASK = 0x1,
+};
+
+#define NVME_PMRCTL_EN(pmrctl) NVME_GET(pmrctl, PMRCTL_EN)
+
+enum nvme_pmrsts {
+ NVME_PMRSTS_ERR_SHIFT = 0,
+ NVME_PMRSTS_NRDY_SHIFT = 8,
+ NVME_PMRSTS_HSTS_SHIFT = 9,
+ NVME_PMRSTS_CBAI_SHIFT = 12,
+ NVME_PMRSTS_ERR_MASK = 0xff,
+ NVME_PMRSTS_NRDY_MASK = 0x1,
+ NVME_PMRSTS_HSTS_MASK = 0x7,
+ NVME_PMRSTS_CBAI_MASK = 0x1,
+};
+
+#define NVME_PMRSTS_ERR(pmrsts) NVME_GET(pmrsts, PMRSTS_ERR)
+#define NVME_PMRSTS_NRDY(pmrsts) NVME_GET(pmrsts, PMRSTS_NRDY)
+#define NVME_PMRSTS_HSTS(pmrsts) NVME_GET(pmrsts, PMRSTS_HSTS)
+#define NVME_PMRSTS_CBAI(pmrsts) NVME_GET(pmrsts, PMRSTS_CBAI)
+
+enum nvme_pmrebs {
+ NVME_PMREBS_PMRSZU_SHIFT = 0,
+ NVME_PMREBS_RBB_SHIFT = 4,
+ NVME_PMREBS_PMRWBZ_SHIFT = 8,
+ NVME_PMREBS_PMRSZU_MASK = 0xf,
+ NVME_PMREBS_RBB_MASK = 0x1,
+ NVME_PMREBS_PMRWBZ_MASK = 0xffffff,
+ NVME_PMREBS_PMRSZU_B = 0,
+ NVME_PMREBS_PMRSZU_1K = 1,
+ NVME_PMREBS_PMRSZU_1M = 2,
+ NVME_PMREBS_PMRSZU_1G = 3,
+};
+
+#define NVME_PMREBS_PMRSZU(pmrebs) NVME_GET(pmrebs, PMREBS_PMRSZU)
+#define NVME_PMREBS_RBB(pmrebs) NVME_GET(pmrebs, PMREBS_RBB)
+#define NVME_PMREBS_PMRWBZ(pmrebs) NVME_GET(pmrebs, PMREBS_PMRWBZ)
+
+/**
+ * nvme_pmr_size() - Calculate size of persistent memory region elasticity
+ * buffer
+ * @pmrebs: Value from controller register %NVME_REG_PMREBS
+ *
+ * Returns: size of controller persistent memory buffer in bytes
+ */
+static inline __u64 nvme_pmr_size(__u32 pmrebs)
+{
+ return ((__u64)NVME_PMREBS_PMRWBZ(pmrebs)) *
+ (1ULL << (10 * NVME_PMREBS_PMRSZU(pmrebs)));
+}
+
+enum nvme_pmrswtp {
+ NVME_PMRSWTP_PMRSWTU_SHIFT = 0,
+ NVME_PMRSWTP_PMRSWTV_SHIFT = 8,
+ NVME_PMRSWTP_PMRSWTU_MASK = 0xf,
+ NVME_PMRSWTP_PMRSWTV_MASK = 0xffffff,
+ NVME_PMRSWTP_PMRSWTU_BPS = 0,
+ NVME_PMRSWTP_PMRSWTU_KBPS = 1,
+ NVME_PMRSWTP_PMRSWTU_MBPS = 2,
+ NVME_PMRSWTP_PMRSWTU_GBPS = 3,
+};
+
+#define NVME_PMRSWTP_PMRSWTU(pmrswtp) NVME_GET(pmrswtp, PMRSWTP_PMRSWTU)
+#define NVME_PMRSWTP_PMRSWTV(pmrswtp) NVME_GET(pmrswtp, PMRSWTP_PMRSWTU)
+
+/**
+ * nvme_pmr_throughput() - Calculate throughput of persistent memory buffer
+ * @pmrswtp: Value from controller register %NVME_REG_PMRSWTP
+ *
+ * Returns: throughput of controller persistent memory buffer in bytes/second
+ */
+static inline __u64 nvme_pmr_throughput(__u32 pmrswtp)
+{
+ return ((__u64)NVME_PMRSWTP_PMRSWTV(pmrswtp)) *
+ (1ULL << (10 * NVME_PMRSWTP_PMRSWTU(pmrswtp)));
+}
+
+enum nvme_pmrmsc {
+ NVME_PMRMSC_CMSE_SHIFT = 1,
+ NVME_PMRMSC_CBA_SHIFT = 12,
+ NVME_PMRMSC_CMSE_MASK = 0x1,
+};
+static const __u64 NVME_PMRMSC_CBA_MASK = 0xfffffffffffffull;
+
+#define NVME_PMRMSC_CMSE(pmrmsc) NVME_GET(pmrmsc, PMRMSC_CMSE)
+#define NVME_PMRMSC_CBA(pmrmsc) NVME_GET(pmrmsc, PMRMSC_CBA)
+
+enum nvme_flbas {
+ NVME_FLBAS_LOWER_SHIFT = 0,
+ NVME_FLBAS_META_EXT_SHIFT = 4,
+ NVME_FLBAS_HIGHER_SHIFT = 5,
+ NVME_FLBAS_LOWER_MASK = 0xf,
+ NVME_FLBAS_META_EXT_MASK = 0x1,
+ NVME_FLBAS_HIGHER_MASK = 0x3,
+};
+
+#define NVME_FLBAS_LOWER(flbas) NVME_GET(flbas, FLBAS_LOWER)
+#define NVME_FLBAS_META_EXT(flbas) NVME_GET(flbas, FLBAS_META_EXT)
+#define NVME_FLBAS_HIGHER(flbas) NVME_GET(flbas, FLBAS_HIGHER)
+
+/**
+ * enum nvme_psd_flags - Possible flag values in nvme power state descriptor
+ * @NVME_PSD_FLAGS_MXPS: Indicates the scale for the Maximum Power
+ * field. If this bit is cleared, then the scale of the
+ * Maximum Power field is in 0.01 Watts. If this bit is
+ * set, then the scale of the Maximum Power field is in
+ * 0.0001 Watts.
+ * @NVME_PSD_FLAGS_NOPS: Indicates whether the controller processes I/O
+ * commands in this power state. If this bit is cleared,
+ * then the controller processes I/O commands in this
+ * power state. If this bit is set, then the controller
+ * does not process I/O commands in this power state.
+ */
+enum nvme_psd_flags {
+ NVME_PSD_FLAGS_MXPS = 1 << 0,
+ NVME_PSD_FLAGS_NOPS = 1 << 1,
+};
+
+/**
+ * enum nvme_psd_ps - Known values for &struct nvme_psd %ips and %aps. Use with
+ * nvme_psd_power_scale() to extract the power scale field
+ * to match this enum.
+ * @NVME_PSD_PS_NOT_REPORTED: Not reported
+ * @NVME_PSD_PS_100_MICRO_WATT: 0.0001 watt scale
+ * @NVME_PSD_PS_10_MILLI_WATT: 0.01 watt scale
+ */
+enum nvme_psd_ps {
+ NVME_PSD_PS_NOT_REPORTED = 0,
+ NVME_PSD_PS_100_MICRO_WATT = 1,
+ NVME_PSD_PS_10_MILLI_WATT = 2,
+};
+
+/**
+ * nvme_psd_power_scale() - power scale occupies the upper 3 bits
+ * @ps: power scale value
+ *
+ * Returns: power scale value
+ */
+static inline unsigned int nvme_psd_power_scale(__u8 ps)
+{
+ return ps >> 6;
+}
+
+/**
+ * enum nvme_psd_workload - Specifies a workload hint in the Power Management
+ * Feature (see &struct nvme_psd.apw) to inform the
+ * NVM subsystem or indicate the conditions for the
+ * active power level.
+ * @NVME_PSD_WORKLOAD_NP: The workload is unknown or not provided.
+ * @NVME_PSD_WORKLOAD_1: Extended Idle Period with a Burst of Random Write
+ * consists of five minutes of idle followed by
+ * thirty-two random write commands of size 1 MiB
+ * submitted to a single controller while all other
+ * controllers in the NVM subsystem are idle, and then
+ * thirty (30) seconds of idle.
+ * @NVME_PSD_WORKLOAD_2: Heavy Sequential Writes consists of 80,000
+ * sequential write commands of size 128 KiB submitted to
+ * a single controller while all other controllers in the
+ * NVM subsystem are idle. The submission queue(s)
+ * should be sufficiently large allowing the host to
+ * ensure there are multiple commands pending at all
+ * times during the workload.
+ */
+enum nvme_psd_workload {
+ NVME_PSD_WORKLOAD_NP = 0,
+ NVME_PSD_WORKLOAD_1 = 1,
+ NVME_PSD_WORKLOAD_2 = 2,
+};
+
+/**
+ * struct nvme_id_psd - Power Management data structure
+ * @mp: Maximum Power indicates the sustained maximum power consumed by the
+ * NVM subsystem in this power state. The power in Watts is equal to
+ * the value in this field multiplied by the scale specified in the Max
+ * Power Scale bit (see &enum nvme_psd_flags). A value of 0 indicates
+ * Maximum Power is not reported.
+ * @rsvd2: Reserved
+ * @flags: Additional decoding flags, see &enum nvme_psd_flags.
+ * @enlat: Entry Latency indicates the maximum latency in microseconds
+ * associated with entering this power state. A value of 0 indicates
+ * Entry Latency is not reported.
+ * @exlat: Exit Latency indicates the maximum latency in microseconds
+ * associated with exiting this power state. A value of 0 indicates
+ * Exit Latency is not reported.
+ * @rrt: Relative Read Throughput indicates the read throughput rank
+ * associated with this power state relative to others. The value in
+ * this is less than the number of supported power states.
+ * @rrl: Relative Read Latency indicates the read latency rank associated
+ * with this power state relative to others. The value in this field is
+ * less than the number of supported power states.
+ * @rwt: Relative Write Throughput indicates write throughput rank associated
+ * with this power state relative to others. The value in this field is
+ * less than the number of supported power states
+ * @rwl: Relative Write Latency indicates the write latency rank associated
+ * with this power state relative to others. The value in this field is
+ * less than the number of supported power states
+ * @idlp: Idle Power indicates the typical power consumed by the NVM
+ * subsystem over 30 seconds in this power state when idle.
+ * @ips: Idle Power Scale indicates the scale for &struct nvme_id_psd.idlp,
+ * see &enum nvme_psd_ps for decoding this field.
+ * @rsvd19: Reserved
+ * @actp: Active Power indicates the largest average power consumed by the
+ * NVM subsystem over a 10 second period in this power state with
+ * the workload indicated in the Active Power Workload field.
+ * @apws: Bits 7-6: Active Power Scale(APS) indicates the scale for the &struct
+ * nvme_id_psd.actp, see &enum nvme_psd_ps for decoding this value.
+ * Bits 2-0: Active Power Workload(APW) indicates the workload
+ * used to calculate maximum power for this power state.
+ * See &enum nvme_psd_workload for decoding this field.
+ * @rsvd23: Reserved
+ */
+struct nvme_id_psd {
+ __le16 mp;
+ __u8 rsvd2;
+ __u8 flags;
+ __le32 enlat;
+ __le32 exlat;
+ __u8 rrt;
+ __u8 rrl;
+ __u8 rwt;
+ __u8 rwl;
+ __le16 idlp;
+ __u8 ips;
+ __u8 rsvd19;
+ __le16 actp;
+ __u8 apws;
+ __u8 rsvd23[9];
+};
+
+/**
+ * struct nvme_id_ctrl - Identify Controller data structure
+ * @vid: PCI Vendor ID, the company vendor identifier that is assigned by
+ * the PCI SIG.
+ * @ssvid: PCI Subsystem Vendor ID, the company vendor identifier that is
+ * assigned by the PCI SIG for the subsystem.
+ * @sn: Serial Number in ASCII
+ * @mn: Model Number in ASCII
+ * @fr: Firmware Revision in ASCII, the currently active firmware
+ * revision for the NVM subsystem
+ * @rab: Recommended Arbitration Burst, reported as a power of two
+ * @ieee: IEEE assigned Organization Unique Identifier
+ * @cmic: Controller Multipath IO and Namespace Sharing Capabilities of
+ * the controller and NVM subsystem. See &enum nvme_id_ctrl_cmic.
+ * @mdts: Max Data Transfer Size is the largest data transfer size. The
+ * host should not submit a command that exceeds this maximum data
+ * transfer size. The value is in units of the minimum memory page
+ * size (CAP.MPSMIN) and is reported as a power of two
+ * @cntlid: Controller ID, the NVM subsystem unique controller identifier
+ * associated with the controller.
+ * @ver: Version, this field contains the value reported in the Version
+ * register, or property (see &enum nvme_registers %NVME_REG_VS).
+ * @rtd3r: RTD3 Resume Latency, the expected latency in microseconds to resume
+ * from Runtime D3
+ * @rtd3e: RTD3 Exit Latency, the typical latency in microseconds to enter
+ * Runtime D3.
+ * @oaes: Optional Async Events Supported, see @enum nvme_id_ctrl_oaes.
+ * @ctratt: Controller Attributes, see @enum nvme_id_ctrl_ctratt.
+ * @rrls: Read Recovery Levels. If a bit is set, then the corresponding
+ * Read Recovery Level is supported. If a bit is cleared, then the
+ * corresponding Read Recovery Level is not supported.
+ * @rsvd102: Reserved
+ * @cntrltype: Controller Type, see &enum nvme_id_ctrl_cntrltype
+ * @fguid: FRU GUID, a 128-bit value that is globally unique for a given
+ * Field Replaceable Unit
+ * @crdt1: Controller Retry Delay time in 100 millisecond units if CQE CRD
+ * field is 1
+ * @crdt2: Controller Retry Delay time in 100 millisecond units if CQE CRD
+ * field is 2
+ * @crdt3: Controller Retry Delay time in 100 millisecond units if CQE CRD
+ * field is 3
+ * @rsvd134: Reserved
+ * @nvmsr: NVM Subsystem Report, see &enum nvme_id_ctrl_nvmsr
+ * @vwci: VPD Write Cycle Information, see &enum nvme_id_ctrl_vwci
+ * @mec: Management Endpoint Capabilities, see &enum nvme_id_ctrl_mec
+ * @oacs: Optional Admin Command Support,the optional Admin commands and
+ * features supported by the controller, see &enum nvme_id_ctrl_oacs.
+ * @acl: Abort Command Limit, the maximum number of concurrently
+ * executing Abort commands supported by the controller. This is a
+ * 0's based value.
+ * @aerl: Async Event Request Limit, the maximum number of concurrently
+ * outstanding Asynchronous Event Request commands supported by the
+ * controller This is a 0's based value.
+ * @frmw: Firmware Updates indicates capabilities regarding firmware
+ * updates. See &enum nvme_id_ctrl_frmw.
+ * @lpa: Log Page Attributes, see &enum nvme_id_ctrl_lpa.
+ * @elpe: Error Log Page Entries, the maximum number of Error Information
+ * log entries that are stored by the controller. This field is a
+ * 0's based value.
+ * @npss: Number of Power States Supported, the number of NVM Express
+ * power states supported by the controller, indicating the number
+ * of valid entries in &struct nvme_id_ctrl.psd. This is a 0's
+ * based value.
+ * @avscc: Admin Vendor Specific Command Configuration, see
+ * &enum nvme_id_ctrl_avscc.
+ * @apsta: Autonomous Power State Transition Attributes, see
+ * &enum nvme_id_ctrl_apsta.
+ * @wctemp: Warning Composite Temperature Threshold indicates
+ * the minimum Composite Temperature field value (see &struct
+ * nvme_smart_log.critical_comp_time) that indicates an overheating
+ * condition during which controller operation continues.
+ * @cctemp: Critical Composite Temperature Threshold, field indicates the
+ * minimum Composite Temperature field value (see &struct
+ * nvme_smart_log.critical_comp_time) that indicates a critical
+ * overheating condition.
+ * @mtfa: Maximum Time for Firmware Activation indicates the maximum time
+ * the controller temporarily stops processing commands to activate
+ * the firmware image, specified in 100 millisecond units. This
+ * field is always valid if the controller supports firmware
+ * activation without a reset.
+ * @hmpre: Host Memory Buffer Preferred Size indicates the preferred size
+ * that the host is requested to allocate for the Host Memory
+ * Buffer feature in 4 KiB units.
+ * @hmmin: Host Memory Buffer Minimum Size indicates the minimum size that
+ * the host is requested to allocate for the Host Memory Buffer
+ * feature in 4 KiB units.
+ * @tnvmcap: Total NVM Capacity, the total NVM capacity in the NVM subsystem.
+ * The value is in bytes.
+ * @unvmcap: Unallocated NVM Capacity, the unallocated NVM capacity in the
+ * NVM subsystem. The value is in bytes.
+ * @rpmbs: Replay Protected Memory Block Support, see
+ * &enum nvme_id_ctrl_rpmbs.
+ * @edstt: Extended Device Self-test Time, if Device Self-test command is
+ * supported (see &struct nvme_id_ctrl.oacs, %NVME_CTRL_OACS_SELF_TEST),
+ * then this field indicates the nominal amount of time in one
+ * minute units that the controller takes to complete an extended
+ * device self-test operation when in power state 0.
+ * @dsto: Device Self-test Options, see &enum nvme_id_ctrl_dsto.
+ * @fwug: Firmware Update Granularity indicates the granularity and
+ * alignment requirement of the firmware image being updated by the
+ * Firmware Image Download command. The value is reported in 4 KiB
+ * units. A value of 0h indicates no information on granularity is
+ * provided. A value of FFh indicates no restriction
+ * @kas: Keep Alive Support indicates the granularity of the Keep Alive
+ * Timer in 100 millisecond units.
+ * @hctma: Host Controlled Thermal Management Attributes, see
+ * &enum nvme_id_ctrl_hctm.
+ * @mntmt: Minimum Thermal Management Temperature indicates the minimum
+ * temperature, in degrees Kelvin, that the host may request in the
+ * Thermal Management Temperature 1 field and Thermal Management
+ * Temperature 2 field of a Set Features command with the Feature
+ * Identifier field set to %NVME_FEAT_FID_HCTM.
+ * @mxtmt: Maximum Thermal Management Temperature indicates the maximum
+ * temperature, in degrees Kelvin, that the host may request in the
+ * Thermal Management Temperature 1 field and Thermal Management
+ * Temperature 2 field of the Set Features command with the Feature
+ * Identifier set to %NVME_FEAT_FID_HCTM.
+ * @sanicap: Sanitize Capabilities, see &enum nvme_id_ctrl_sanicap
+ * @hmminds: Host Memory Buffer Minimum Descriptor Entry Size indicates the
+ * minimum usable size of a Host Memory Buffer Descriptor Entry in
+ * 4 KiB units.
+ * @hmmaxd: Host Memory Maximum Descriptors Entries indicates the number of
+ * usable Host Memory Buffer Descriptor Entries.
+ * @nsetidmax: NVM Set Identifier Maximum, defines the maximum value of a valid
+ * NVM Set Identifier for any controller in the NVM subsystem.
+ * @endgidmax: Endurance Group Identifier Maximum, defines the maximum value of
+ * a valid Endurance Group Identifier for any controller in the NVM
+ * subsystem.
+ * @anatt: ANA Transition Time indicates the maximum amount of time, in
+ * seconds, for a transition between ANA states or the maximum
+ * amount of time, in seconds, that the controller reports the ANA
+ * change state.
+ * @anacap: Asymmetric Namespace Access Capabilities, see
+ * &enum nvme_id_ctrl_anacap.
+ * @anagrpmax: ANA Group Identifier Maximum indicates the maximum value of a
+ * valid ANA Group Identifier for any controller in the NVM
+ * subsystem.
+ * @nanagrpid: Number of ANA Group Identifiers indicates the number of ANA
+ * groups supported by this controller.
+ * @pels: Persistent Event Log Size indicates the maximum reportable size
+ * for the Persistent Event Log.
+ * @domainid: Domain Identifier indicates the identifier of the domain
+ * that contains this controller.
+ * @rsvd358: Reserved
+ * @megcap: Max Endurance Group Capacity indicates the maximum capacity
+ * of a single Endurance Group.
+ * @rsvd384: Reserved
+ * @sqes: Submission Queue Entry Size, see &enum nvme_id_ctrl_sqes.
+ * @cqes: Completion Queue Entry Size, see &enum nvme_id_ctrl_cqes.
+ * @maxcmd: Maximum Outstanding Commands indicates the maximum number of
+ * commands that the controller processes at one time for a
+ * particular queue.
+ * @nn: Number of Namespaces indicates the maximum value of a valid
+ * nsid for the NVM subsystem. If the MNAN (&struct nvme_id_ctrl.mnan
+ * field is cleared to 0h, then this field also indicates the
+ * maximum number of namespaces supported by the NVM subsystem.
+ * @oncs: Optional NVM Command Support, see &enum nvme_id_ctrl_oncs.
+ * @fuses: Fused Operation Support, see &enum nvme_id_ctrl_fuses.
+ * @fna: Format NVM Attributes, see &enum nvme_id_ctrl_fna.
+ * @vwc: Volatile Write Cache, see &enum nvme_id_ctrl_vwc.
+ * @awun: Atomic Write Unit Normal indicates the size of the write
+ * operation guaranteed to be written atomically to the NVM across
+ * all namespaces with any supported namespace format during normal
+ * operation. This field is specified in logical blocks and is a
+ * 0's based value.
+ * @awupf: Atomic Write Unit Power Fail indicates the size of the write
+ * operation guaranteed to be written atomically to the NVM across
+ * all namespaces with any supported namespace format during a
+ * power fail or error condition. This field is specified in
+ * logical blocks and is a 0’s based value.
+ * @icsvscc: NVM Vendor Specific Command Configuration, see
+ * &enum nvme_id_ctrl_nvscc.
+ * @nwpc: Namespace Write Protection Capabilities, see
+ * &enum nvme_id_ctrl_nwpc.
+ * @acwu: Atomic Compare & Write Unit indicates the size of the write
+ * operation guaranteed to be written atomically to the NVM across
+ * all namespaces with any supported namespace format for a Compare
+ * and Write fused operation. This field is specified in logical
+ * blocks and is a 0’s based value.
+ * @ocfs: Optional Copy Formats Supported, each bit n means controller
+ * supports Copy Format n.
+ * @sgls: SGL Support, see &enum nvme_id_ctrl_sgls
+ * @mnan: Maximum Number of Allowed Namespaces indicates the maximum
+ * number of namespaces supported by the NVM subsystem.
+ * @maxdna: Maximum Domain Namespace Attachments indicates the maximum
+ * of the sum of the number of namespaces attached to each I/O
+ * controller in the Domain.
+ * @maxcna: Maximum I/O Controller Namespace Attachments indicates the
+ * maximum number of namespaces that are allowed to be attached to
+ * this I/O controller.
+ * @oaqd: Optimal Aggregated Queue Depth indicates the recommended maximum
+ * total number of outstanding I/O commands across all I/O queues
+ * on the controller for optimal operation.
+ * @rsvd568: Reserved
+ * @subnqn: NVM Subsystem NVMe Qualified Name, UTF-8 null terminated string
+ * @rsvd1024: Reserved
+ * @ioccsz: I/O Queue Command Capsule Supported Size, defines the maximum
+ * I/O command capsule size in 16 byte units.
+ * @iorcsz: I/O Queue Response Capsule Supported Size, defines the maximum
+ * I/O response capsule size in 16 byte units.
+ * @icdoff: In Capsule Data Offset, defines the offset where data starts
+ * within a capsule. This value is applicable to I/O Queues only.
+ * @fcatt: Fabrics Controller Attributes, see &enum nvme_id_ctrl_fcatt.
+ * @msdbd: Maximum SGL Data Block Descriptors indicates the maximum
+ * number of SGL Data Block or Keyed SGL Data Block descriptors
+ * that a host is allowed to place in a capsule. A value of 0h
+ * indicates no limit.
+ * @ofcs: Optional Fabric Commands Support, see &enum nvme_id_ctrl_ofcs.
+ * @dctype: Discovery Controller Type (DCTYPE). This field indicates what
+ * type of Discovery controller the controller is (see enum
+ * nvme_id_ctrl_dctype)
+ * @rsvd1807: Reserved
+ * @psd: Power State Descriptors, see &struct nvme_id_psd.
+ * @vs: Vendor Specific
+ */
+struct nvme_id_ctrl {
+ __le16 vid;
+ __le16 ssvid;
+ char sn[20];
+ char mn[40];
+ char fr[8];
+ __u8 rab;
+ __u8 ieee[3];
+ __u8 cmic;
+ __u8 mdts;
+ __le16 cntlid;
+ __le32 ver;
+ __le32 rtd3r;
+ __le32 rtd3e;
+ __le32 oaes;
+ __le32 ctratt;
+ __le16 rrls;
+ __u8 rsvd102[9];
+ __u8 cntrltype;
+ __u8 fguid[16];
+ __le16 crdt1;
+ __le16 crdt2;
+ __le16 crdt3;
+ __u8 rsvd134[119];
+ __u8 nvmsr;
+ __u8 vwci;
+ __u8 mec;
+ __le16 oacs;
+ __u8 acl;
+ __u8 aerl;
+ __u8 frmw;
+ __u8 lpa;
+ __u8 elpe;
+ __u8 npss;
+ __u8 avscc;
+ __u8 apsta;
+ __le16 wctemp;
+ __le16 cctemp;
+ __le16 mtfa;
+ __le32 hmpre;
+ __le32 hmmin;
+ __u8 tnvmcap[16];
+ __u8 unvmcap[16];
+ __le32 rpmbs;
+ __le16 edstt;
+ __u8 dsto;
+ __u8 fwug;
+ __le16 kas;
+ __le16 hctma;
+ __le16 mntmt;
+ __le16 mxtmt;
+ __le32 sanicap;
+ __le32 hmminds;
+ __le16 hmmaxd;
+ __le16 nsetidmax;
+ __le16 endgidmax;
+ __u8 anatt;
+ __u8 anacap;
+ __le32 anagrpmax;
+ __le32 nanagrpid;
+ __le32 pels;
+ __le16 domainid;
+ __u8 rsvd358[10];
+ __u8 megcap[16];
+ __u8 rsvd384[128];
+ __u8 sqes;
+ __u8 cqes;
+ __le16 maxcmd;
+ __le32 nn;
+ __le16 oncs;
+ __le16 fuses;
+ __u8 fna;
+ __u8 vwc;
+ __le16 awun;
+ __le16 awupf;
+ __u8 icsvscc;
+ __u8 nwpc;
+ __le16 acwu;
+ __le16 ocfs;
+ __le32 sgls;
+ __le32 mnan;
+ __u8 maxdna[16];
+ __le32 maxcna;
+ __le32 oaqd;
+ __u8 rsvd568[200];
+ char subnqn[NVME_NQN_LENGTH];
+ __u8 rsvd1024[768];
+
+ /* Fabrics Only */
+ __le32 ioccsz;
+ __le32 iorcsz;
+ __le16 icdoff;
+ __u8 fcatt;
+ __u8 msdbd;
+ __le16 ofcs;
+ __u8 dctype;
+ __u8 rsvd1807[241];
+
+ struct nvme_id_psd psd[32];
+ __u8 vs[1024];
+};
+
+/**
+ * enum nvme_id_ctrl_cmic - Controller Multipath IO and Namespace Sharing
+ * Capabilities of the controller and NVM subsystem.
+ * @NVME_CTRL_CMIC_MULTI_PORT: If set, then the NVM subsystem may contain
+ * more than one NVM subsystem port, otherwise
+ * the NVM subsystem contains only a single
+ * NVM subsystem port.
+ * @NVME_CTRL_CMIC_MULTI_CTRL: If set, then the NVM subsystem may contain
+ * two or more controllers, otherwise the
+ * NVM subsystem contains only a single
+ * controller. An NVM subsystem that contains
+ * multiple controllers may be used by
+ * multiple hosts, or may provide multiple
+ * paths for a single host.
+ * @NVME_CTRL_CMIC_MULTI_SRIOV: If set, then the controller is associated
+ * with an SR-IOV Virtual Function, otherwise
+ * it is associated with a PCI Function
+ * or a Fabrics connection.
+ * @NVME_CTRL_CMIC_MULTI_ANA_REPORTING: If set, then the NVM subsystem supports
+ * Asymmetric Namespace Access Reporting.
+ */
+enum nvme_id_ctrl_cmic {
+ NVME_CTRL_CMIC_MULTI_PORT = 1 << 0,
+ NVME_CTRL_CMIC_MULTI_CTRL = 1 << 1,
+ NVME_CTRL_CMIC_MULTI_SRIOV = 1 << 2,
+ NVME_CTRL_CMIC_MULTI_ANA_REPORTING = 1 << 3,
+};
+
+/**
+ * enum nvme_id_ctrl_oaes - Optional Asynchronous Events Supported
+ * @NVME_CTRL_OAES_NA: Namespace Attribute Notices event supported
+ * @NVME_CTRL_OAES_FA: Firmware Activation Notices event supported
+ * @NVME_CTRL_OAES_ANA: ANA Change Notices supported
+ * @NVME_CTRL_OAES_PLEA: Predictable Latency Event Aggregate Log
+ * Change Notices event supported
+ * @NVME_CTRL_OAES_LBAS: LBA Status Information Notices event supported
+ * @NVME_CTRL_OAES_EGE: Endurance Group Events Aggregate Log Change
+ * Notices event supported
+ * @NVME_CTRL_OAES_NS: Normal NVM Subsystem Shutdown event supported
+ * @NVME_CTRL_OAES_ZD: Zone Descriptor Change Notifications supported
+ * @NVME_CTRL_OAES_DL: Discover Log Page Change Notifications supported
+ */
+enum nvme_id_ctrl_oaes {
+ NVME_CTRL_OAES_NA = 1 << 8,
+ NVME_CTRL_OAES_FA = 1 << 9,
+ NVME_CTRL_OAES_ANA = 1 << 11,
+ NVME_CTRL_OAES_PLEA = 1 << 12,
+ NVME_CTRL_OAES_LBAS = 1 << 13,
+ NVME_CTRL_OAES_EGE = 1 << 14,
+ NVME_CTRL_OAES_NS = 1 << 15,
+ NVME_CTRL_OAES_ZD = 1 << 27,
+ NVME_CTRL_OAES_DL = 1 << 31,
+};
+
+/**
+ * enum nvme_id_ctrl_ctratt - Controller attributes
+ * @NVME_CTRL_CTRATT_128_ID: 128-bit Host Identifier supported
+ * @NVME_CTRL_CTRATT_NON_OP_PSP: Non-Operational Poser State Permissive Mode
+ * supported
+ * @NVME_CTRL_CTRATT_NVM_SETS: NVM Sets supported
+ * @NVME_CTRL_CTRATT_READ_RECV_LVLS: Read Recovery Levels supported
+ * @NVME_CTRL_CTRATT_ENDURANCE_GROUPS: Endurance Groups supported
+ * @NVME_CTRL_CTRATT_PREDICTABLE_LAT: Predictable Latency Mode supported
+ * @NVME_CTRL_CTRATT_TBKAS: Traffic Based Keep Alive Support
+ * @NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY: Namespace Granularity reporting
+ * supported
+ * @NVME_CTRL_CTRATT_SQ_ASSOCIATIONS: SQ Associations supported
+ * @NVME_CTRL_CTRATT_UUID_LIST: UUID List reporting supported
+ * @NVME_CTRL_CTRATT_MDS: Multi-Domain Subsystem supported
+ * @NVME_CTRL_CTRATT_FIXED_CAP: Fixed Capacity Management supported
+ * @NVME_CTRL_CTRATT_VARIABLE_CAP: Variable Capacity Management supported
+ * @NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS: Delete Endurance Groups supported
+ * @NVME_CTRL_CTRATT_DEL_NVM_SETS: Delete NVM Sets supported
+ * @NVME_CTRL_CTRATT_ELBAS: Extended LBA Formats supported
+ * @NVME_CTRL_CTRATT_FDPS: Flexible Data Placement supported
+ */
+enum nvme_id_ctrl_ctratt {
+ NVME_CTRL_CTRATT_128_ID = 1 << 0,
+ NVME_CTRL_CTRATT_NON_OP_PSP = 1 << 1,
+ NVME_CTRL_CTRATT_NVM_SETS = 1 << 2,
+ NVME_CTRL_CTRATT_READ_RECV_LVLS = 1 << 3,
+ NVME_CTRL_CTRATT_ENDURANCE_GROUPS = 1 << 4,
+ NVME_CTRL_CTRATT_PREDICTABLE_LAT = 1 << 5,
+ NVME_CTRL_CTRATT_TBKAS = 1 << 6,
+ NVME_CTRL_CTRATT_NAMESPACE_GRANULARITY = 1 << 7,
+ NVME_CTRL_CTRATT_SQ_ASSOCIATIONS = 1 << 8,
+ NVME_CTRL_CTRATT_UUID_LIST = 1 << 9,
+ NVME_CTRL_CTRATT_MDS = 1 << 10,
+ NVME_CTRL_CTRATT_FIXED_CAP = 1 << 11,
+ NVME_CTRL_CTRATT_VARIABLE_CAP = 1 << 12,
+ NVME_CTRL_CTRATT_DEL_ENDURANCE_GROUPS = 1 << 13,
+ NVME_CTRL_CTRATT_DEL_NVM_SETS = 1 << 14,
+ NVME_CTRL_CTRATT_ELBAS = 1 << 15,
+ NVME_CTRL_CTRATT_FDPS = 1 << 19,
+};
+
+/**
+ * enum nvme_id_ctrl_cntrltype - Controller types
+ * @NVME_CTRL_CNTRLTYPE_IO: NVM I/O controller
+ * @NVME_CTRL_CNTRLTYPE_DISCOVERY: Discovery controller
+ * @NVME_CTRL_CNTRLTYPE_ADMIN: Admin controller
+ */
+enum nvme_id_ctrl_cntrltype {
+ NVME_CTRL_CNTRLTYPE_IO = 1,
+ NVME_CTRL_CNTRLTYPE_DISCOVERY = 2,
+ NVME_CTRL_CNTRLTYPE_ADMIN = 3,
+};
+
+/**
+ * enum nvme_id_ctrl_dctype - Discovery Controller types
+ * @NVME_CTRL_DCTYPE_NOT_REPORTED: Not reported (I/O, Admin, and pre-TP8010)
+ * @NVME_CTRL_DCTYPE_DDC: Direct Discovery controller
+ * @NVME_CTRL_DCTYPE_CDC: Central Discovery controller
+ */
+enum nvme_id_ctrl_dctype {
+ NVME_CTRL_DCTYPE_NOT_REPORTED = 0,
+ NVME_CTRL_DCTYPE_DDC = 1,
+ NVME_CTRL_DCTYPE_CDC = 2,
+};
+
+/**
+ * enum nvme_id_ctrl_nvmsr - This field reports information associated with the
+ * NVM Subsystem, see &struct nvme_id_ctrl.nvmsr.
+ * @NVME_CTRL_NVMSR_NVMESD: If set, then the NVM Subsystem is part of an NVMe
+ * Storage Device; if cleared, then the NVM Subsystem
+ * is not part of an NVMe Storage Device.
+ * @NVME_CTRL_NVMSR_NVMEE: If set’, then the NVM Subsystem is part of an NVMe
+ * Enclosure; if cleared, then the NVM Subsystem is
+ * not part of an NVMe Enclosure.
+ */
+enum nvme_id_ctrl_nvmsr {
+ NVME_CTRL_NVMSR_NVMESD = 1 << 0,
+ NVME_CTRL_NVMSR_NVMEE = 1 << 1,
+};
+
+/**
+ * enum nvme_id_ctrl_vwci - This field indicates information about remaining
+ * number of times that VPD contents are able to be
+ * updated using the VPD Write command, see &struct
+ * nvme_id_ctrl.vwci.
+ * @NVME_CTRL_VWCI_VWCR: Mask to get value of VPD Write Cycles Remaining. If
+ * the VPD Write Cycle Remaining Valid bit is set, then
+ * this field contains a value indicating the remaining
+ * number of times that VPD contents are able to be
+ * updated using the VPD Write command. If this field is
+ * set to 7Fh, then the remaining number of times that
+ * VPD contents are able to be updated using the VPD
+ * Write command is greater than or equal to 7Fh.
+ * @NVME_CTRL_VWCI_VWCRV: VPD Write Cycle Remaining Valid. If this bit is set,
+ * then the VPD Write Cycle Remaining field is valid. If
+ * this bit is cleared, then the VPD Write Cycles
+ * Remaining field is invalid and cleared to 0h.
+ */
+enum nvme_id_ctrl_vwci {
+ NVME_CTRL_VWCI_VWCR = 0x7f << 0,
+ NVME_CTRL_VWCI_VWCRV = 1 << 7,
+};
+
+/**
+ * enum nvme_id_ctrl_mec - Flags indicating the capabilities of the Management
+ * Endpoint in the Controller, &struct nvme_id_ctrl.mec.
+ * @NVME_CTRL_MEC_SMBUSME: If set, then the NVM Subsystem contains a Management
+ * Endpoint on an SMBus/I2C port.
+ * @NVME_CTRL_MEC_PCIEME: If set, then the NVM Subsystem contains a Management
+ * Endpoint on a PCIe port.
+ */
+enum nvme_id_ctrl_mec {
+ NVME_CTRL_MEC_SMBUSME = 1 << 0,
+ NVME_CTRL_MEC_PCIEME = 1 << 1,
+};
+
+/**
+ * enum nvme_id_ctrl_oacs - Flags indicating the optional Admin commands and
+ * features supported by the controller, see
+ * &struct nvme_id_ctrl.oacs.
+ * @NVME_CTRL_OACS_SECURITY: If set, then the controller supports the
+ * Security Send and Security Receive commands.
+ * @NVME_CTRL_OACS_FORMAT: If set then the controller supports the Format
+ * NVM command.
+ * @NVME_CTRL_OACS_FW: If set, then the controller supports the
+ * Firmware Commit and Firmware Image Download commands.
+ * @NVME_CTRL_OACS_NS_MGMT: If set, then the controller supports the
+ * Namespace Management capability
+ * @NVME_CTRL_OACS_SELF_TEST: If set, then the controller supports the Device
+ * Self-test command.
+ * @NVME_CTRL_OACS_DIRECTIVES: If set, then the controller supports Directives
+ * and the Directive Send and Directive Receive
+ * commands.
+ * @NVME_CTRL_OACS_NVME_MI: If set, then the controller supports the NVMe-MI
+ * Send and NVMe-MI Receive commands.
+ * @NVME_CTRL_OACS_VIRT_MGMT: If set, then the controller supports the
+ * Virtualization Management command.
+ * @NVME_CTRL_OACS_DBBUF_CFG: If set, then the controller supports the
+ * Doorbell Buffer Config command.
+ * @NVME_CTRL_OACS_LBA_STATUS: If set, then the controller supports the Get LBA
+ * Status capability.
+ * @NVME_CTRL_OACS_CMD_FEAT_LD: If set, then the controller supports the command
+ * and feature lockdown capability.
+ */
+enum nvme_id_ctrl_oacs {
+ NVME_CTRL_OACS_SECURITY = 1 << 0,
+ NVME_CTRL_OACS_FORMAT = 1 << 1,
+ NVME_CTRL_OACS_FW = 1 << 2,
+ NVME_CTRL_OACS_NS_MGMT = 1 << 3,
+ NVME_CTRL_OACS_SELF_TEST = 1 << 4,
+ NVME_CTRL_OACS_DIRECTIVES = 1 << 5,
+ NVME_CTRL_OACS_NVME_MI = 1 << 6,
+ NVME_CTRL_OACS_VIRT_MGMT = 1 << 7,
+ NVME_CTRL_OACS_DBBUF_CFG = 1 << 8,
+ NVME_CTRL_OACS_LBA_STATUS = 1 << 9,
+ NVME_CTRL_OACS_CMD_FEAT_LD = 1 << 10,
+};
+
+/**
+ * enum nvme_id_ctrl_frmw - Flags and values indicates capabilities regarding
+ * firmware updates from &struct nvme_id_ctrl.frmw.
+ * @NVME_CTRL_FRMW_1ST_RO: If set, the first firmware slot is readonly
+ * @NVME_CTRL_FRMW_NR_SLOTS: Mask to get the value of the number of
+ * firmware slots that the controller supports.
+ * @NVME_CTRL_FRMW_FW_ACT_NO_RESET: If set, the controller supports firmware
+ * activation without a reset.
+ * @NVME_CTRL_FRMW_MP_UP_DETECTION: If set, the controller is able to detect
+ * overlapping firmware/boot partition
+ * image update.
+ */
+enum nvme_id_ctrl_frmw {
+ NVME_CTRL_FRMW_1ST_RO = 1 << 0,
+ NVME_CTRL_FRMW_NR_SLOTS = 3 << 1,
+ NVME_CTRL_FRMW_FW_ACT_NO_RESET = 1 << 4,
+ NVME_CTRL_FRMW_MP_UP_DETECTION = 1 << 5,
+};
+
+/**
+ * enum nvme_id_ctrl_lpa - Flags indicating optional attributes for log pages
+ * that are accessed via the Get Log Page command.
+ * @NVME_CTRL_LPA_SMART_PER_NS: If set, controller supports SMART/Health log
+ * page on a per namespace basis.
+ * @NVME_CTRL_LPA_CMD_EFFECTS: If Set, the controller supports the commands
+ * supported and effects log page.
+ * @NVME_CTRL_LPA_EXTENDED: If set, the controller supports extended data
+ * for log page command including extended number
+ * of dwords and log page offset fields.
+ * @NVME_CTRL_LPA_TELEMETRY: If set, the controller supports the telemetry
+ * host-initiated and telemetry controller-initiated
+ * log pages and sending telemetry log notices.
+ * @NVME_CTRL_LPA_PERSETENT_EVENT: If set, the controller supports
+ * persistent event log.
+ * @NVME_CTRL_LPA_LI0_LI5_LI12_LI13: If set, the controller supports
+ * - log pages log page.
+ * - returning scope of each command in
+ * commands supported and effects log
+ * page.
+ * - feature identifiers supported and
+ * effects log page.
+ * - NVMe-MI commands supported and
+ * effects log page.
+ * @NVME_CTRL_LPA_DA4_TELEMETRY: If set, the controller supports data
+ * area 4 for telemetry host-initiated and
+ * telemetry.
+ */
+enum nvme_id_ctrl_lpa {
+ NVME_CTRL_LPA_SMART_PER_NS = 1 << 0,
+ NVME_CTRL_LPA_CMD_EFFECTS = 1 << 1,
+ NVME_CTRL_LPA_EXTENDED = 1 << 2,
+ NVME_CTRL_LPA_TELEMETRY = 1 << 3,
+ NVME_CTRL_LPA_PERSETENT_EVENT = 1 << 4,
+ NVME_CTRL_LPA_LI0_LI5_LI12_LI13 = 1 << 5,
+ NVME_CTRL_LPA_DA4_TELEMETRY = 1 << 6,
+};
+
+/**
+ * enum nvme_id_ctrl_avscc - Flags indicating the configuration settings for
+ * Admin Vendor Specific command handling.
+ * @NVME_CTRL_AVSCC_AVS: If set, all Admin Vendor Specific Commands use the
+ * optional vendor specific command format with NDT and
+ * NDM fields.
+ */
+enum nvme_id_ctrl_avscc {
+ NVME_CTRL_AVSCC_AVS = 1 << 0,
+};
+
+/**
+ * enum nvme_id_ctrl_apsta - Flags indicating the attributes of the autonomous
+ * power state transition feature.
+ * @NVME_CTRL_APSTA_APST: If set, then the controller supports autonomous power
+ * state transitions.
+ */
+enum nvme_id_ctrl_apsta {
+ NVME_CTRL_APSTA_APST = 1 << 0,
+};
+
+/**
+ * enum nvme_id_ctrl_rpmbs - This field indicates if the controller supports
+ * one or more Replay Protected Memory Blocks, from
+ * &struct nvme_id_ctrl.rpmbs.
+ * @NVME_CTRL_RPMBS_NR_UNITS: Mask to get the value of the Number of RPMB Units
+ * @NVME_CTRL_RPMBS_AUTH_METHOD: Mask to get the value of the Authentication Method
+ * @NVME_CTRL_RPMBS_TOTAL_SIZE: Mask to get the value of Total Size
+ * @NVME_CTRL_RPMBS_ACCESS_SIZE: Mask to get the value of Access Size
+ */
+enum nvme_id_ctrl_rpmbs {
+ NVME_CTRL_RPMBS_NR_UNITS = 7 << 0,
+ NVME_CTRL_RPMBS_AUTH_METHOD = 7 << 3,
+ NVME_CTRL_RPMBS_TOTAL_SIZE = 0xff << 16,
+ NVME_CTRL_RPMBS_ACCESS_SIZE = 0xff << 24,
+};
+
+/**
+ * enum nvme_id_ctrl_dsto - Flags indicating the optional Device Self-test
+ * command or operation behaviors supported by the
+ * controller or NVM subsystem.
+ * @NVME_CTRL_DSTO_ONE_DST: If set, then the NVM subsystem supports only one
+ * device self-test operation in progress at a time.
+ */
+enum nvme_id_ctrl_dsto {
+ NVME_CTRL_DSTO_ONE_DST = 1 << 0,
+};
+
+/**
+ * enum nvme_id_ctrl_hctm - Flags indicate the attributes of the host
+ * controlled thermal management feature
+ * @NVME_CTRL_HCTMA_HCTM: then the controller supports host controlled thermal
+ * management, and the Set Features command and Get
+ * Features command with the Feature Identifier field
+ * set to %NVME_FEAT_FID_HCTM.
+ */
+enum nvme_id_ctrl_hctm {
+ NVME_CTRL_HCTMA_HCTM = 1 << 0,
+};
+
+/**
+ * enum nvme_id_ctrl_sanicap - Indicates attributes for sanitize operations.
+ * @NVME_CTRL_SANICAP_CES: Crypto Erase Support. If set, then the
+ * controller supports the Crypto Erase sanitize operation.
+ * @NVME_CTRL_SANICAP_BES: Block Erase Support. If set, then the controller
+ * supports the Block Erase sanitize operation.
+ * @NVME_CTRL_SANICAP_OWS: Overwrite Support. If set, then the controller
+ * supports the Overwrite sanitize operation.
+ * @NVME_CTRL_SANICAP_NDI: No-Deallocate Inhibited. If set and the No-
+ * Deallocate Response Mode bit is set, then the
+ * controller deallocates after the sanitize
+ * operation even if the No-Deallocate After
+ * Sanitize bit is set in a Sanitize command.
+ * @NVME_CTRL_SANICAP_NODMMAS: No-Deallocate Modifies Media After Sanitize,
+ * mask to extract value.
+ */
+enum nvme_id_ctrl_sanicap {
+ NVME_CTRL_SANICAP_CES = 1 << 0,
+ NVME_CTRL_SANICAP_BES = 1 << 1,
+ NVME_CTRL_SANICAP_OWS = 1 << 2,
+ NVME_CTRL_SANICAP_NDI = 1 << 29,
+ NVME_CTRL_SANICAP_NODMMAS = 3 << 30,
+};
+
+/**
+ * enum nvme_id_ctrl_anacap - This field indicates the capabilities associated
+ * with Asymmetric Namespace Access Reporting.
+ * @NVME_CTRL_ANACAP_OPT: If set, then the controller is able to
+ * report ANA Optimized state.
+ * @NVME_CTRL_ANACAP_NON_OPT: If set, then the controller is able to
+ * report ANA Non-Optimized state.
+ * @NVME_CTRL_ANACAP_INACCESSIBLE: If set, then the controller is able to
+ * report ANA Inaccessible state.
+ * @NVME_CTRL_ANACAP_PERSISTENT_LOSS: If set, then the controller is able to
+ * report ANA Persistent Loss state.
+ * @NVME_CTRL_ANACAP_CHANGE: If set, then the controller is able to
+ * report ANA Change state.
+ * @NVME_CTRL_ANACAP_GRPID_NO_CHG: If set, then the ANAGRPID field in the
+ * Identify Namespace data structure
+ * (&struct nvme_id_ns.anagrpid), does not
+ * change while the namespace is attached to
+ * any controller.
+ * @NVME_CTRL_ANACAP_GRPID_MGMT: If set, then the controller supports a
+ * non-zero value in the ANAGRPID field of
+ * the Namespace Management command.
+ */
+enum nvme_id_ctrl_anacap {
+ NVME_CTRL_ANACAP_OPT = 1 << 0,
+ NVME_CTRL_ANACAP_NON_OPT = 1 << 1,
+ NVME_CTRL_ANACAP_INACCESSIBLE = 1 << 2,
+ NVME_CTRL_ANACAP_PERSISTENT_LOSS = 1 << 3,
+ NVME_CTRL_ANACAP_CHANGE = 1 << 4,
+ NVME_CTRL_ANACAP_GRPID_NO_CHG = 1 << 6,
+ NVME_CTRL_ANACAP_GRPID_MGMT = 1 << 7,
+};
+
+/**
+ * enum nvme_id_ctrl_sqes - Defines the required and maximum Submission Queue
+ * entry size when using the NVM Command Set.
+ * @NVME_CTRL_SQES_MIN: Mask to get the value of the required Submission Queue
+ * Entry size when using the NVM Command Set.
+ * @NVME_CTRL_SQES_MAX: Mask to get the value of the maximum Submission Queue
+ * entry size when using the NVM Command Set.
+ */
+enum nvme_id_ctrl_sqes {
+ NVME_CTRL_SQES_MIN = 0xf << 0,
+ NVME_CTRL_SQES_MAX = 0xf << 4,
+};
+
+/**
+ * enum nvme_id_ctrl_cqes - Defines the required and maximum Completion Queue
+ * entry size when using the NVM Command Set.
+ * @NVME_CTRL_CQES_MIN: Mask to get the value of the required Completion Queue
+ * Entry size when using the NVM Command Set.
+ * @NVME_CTRL_CQES_MAX: Mask to get the value of the maximum Completion Queue
+ * entry size when using the NVM Command Set.
+ */
+enum nvme_id_ctrl_cqes {
+ NVME_CTRL_CQES_MIN = 0xf << 0,
+ NVME_CTRL_CQES_MAX = 0xf << 4,
+};
+
+/**
+ * enum nvme_id_ctrl_oncs - This field indicates the optional NVM commands and
+ * features supported by the controller.
+ * @NVME_CTRL_ONCS_COMPARE: If set, then the controller supports
+ * the Compare command.
+ * @NVME_CTRL_ONCS_WRITE_UNCORRECTABLE: If set, then the controller supports
+ * the Write Uncorrectable command.
+ * @NVME_CTRL_ONCS_DSM: If set, then the controller supports
+ * the Dataset Management command.
+ * @NVME_CTRL_ONCS_WRITE_ZEROES: If set, then the controller supports
+ * the Write Zeroes command.
+ * @NVME_CTRL_ONCS_SAVE_FEATURES: If set, then the controller supports
+ * the Save field set to a non-zero value
+ * in the Set Features command and the
+ * Select field set to a non-zero value in
+ * the Get Features command.
+ * @NVME_CTRL_ONCS_RESERVATIONS: If set, then the controller supports
+ * reservations.
+ * @NVME_CTRL_ONCS_TIMESTAMP: If set, then the controller supports
+ * the Timestamp feature.
+ * @NVME_CTRL_ONCS_VERIFY: If set, then the controller supports
+ * the Verify command.
+ * @NVME_CTRL_ONCS_COPY: If set, then the controller supports
+ * the copy command.
+ * @NVME_CTRL_ONCS_COPY_SINGLE_ATOMICITY: If set, then the write portion of a
+ * Copy command is performed as a single
+ * write command to which the same
+ * atomicity requirements that apply to
+ * a write command apply.
+ * @NVME_CTRL_ONCS_ALL_FAST_COPY: If set, then all copy operations for
+ * the Copy command are fast copy
+ * operations.
+ */
+enum nvme_id_ctrl_oncs {
+ NVME_CTRL_ONCS_COMPARE = 1 << 0,
+ NVME_CTRL_ONCS_WRITE_UNCORRECTABLE = 1 << 1,
+ NVME_CTRL_ONCS_DSM = 1 << 2,
+ NVME_CTRL_ONCS_WRITE_ZEROES = 1 << 3,
+ NVME_CTRL_ONCS_SAVE_FEATURES = 1 << 4,
+ NVME_CTRL_ONCS_RESERVATIONS = 1 << 5,
+ NVME_CTRL_ONCS_TIMESTAMP = 1 << 6,
+ NVME_CTRL_ONCS_VERIFY = 1 << 7,
+ NVME_CTRL_ONCS_COPY = 1 << 8,
+ NVME_CTRL_ONCS_COPY_SINGLE_ATOMICITY = 1 << 9,
+ NVME_CTRL_ONCS_ALL_FAST_COPY = 1 << 10,
+};
+
+/**
+ * enum nvme_id_ctrl_fuses - This field indicates the fused operations that the
+ * controller supports.
+ * @NVME_CTRL_FUSES_COMPARE_AND_WRITE: If set, then the controller supports the
+ * Compare and Write fused operation.
+ */
+enum nvme_id_ctrl_fuses {
+ NVME_CTRL_FUSES_COMPARE_AND_WRITE = 1 << 0,
+};
+
+/**
+ * enum nvme_id_ctrl_fna - This field indicates attributes for the Format NVM
+ * command.
+ * @NVME_CTRL_FNA_FMT_ALL_NAMESPACES: If set, then all namespaces in an NVM
+ * subsystem shall be configured with the
+ * same attributes and a format (excluding
+ * secure erase) of any namespace results in
+ * a format of all namespaces in an NVM
+ * subsystem. If cleared, then the
+ * controller supports format on a per
+ * namespace basis.
+ * @NVME_CTRL_FNA_SEC_ALL_NAMESPACES: If set, then any secure erase performed
+ * as part of a format operation results in
+ * a secure erase of all namespaces in the
+ * NVM subsystem. If cleared, then any
+ * secure erase performed as part of a
+ * format results in a secure erase of the
+ * particular namespace specified.
+ * @NVME_CTRL_FNA_CRYPTO_ERASE: If set, then cryptographic erase is
+ * supported. If cleared, then cryptographic
+ * erase is not supported.
+ * @NVME_CTRL_FNA_NSID_FFFFFFFF: If set, then format does not support
+ * nsid value set to FFFFFFFFh. If cleared,
+ * format supports nsid value set to
+ * FFFFFFFFh.
+ */
+enum nvme_id_ctrl_fna {
+ NVME_CTRL_FNA_FMT_ALL_NAMESPACES = 1 << 0,
+ NVME_CTRL_FNA_SEC_ALL_NAMESPACES = 1 << 1,
+ NVME_CTRL_FNA_CRYPTO_ERASE = 1 << 2,
+ NVME_CTRL_FNA_NSID_FFFFFFFF = 1 << 3,
+};
+
+/**
+ * enum nvme_id_ctrl_vwc - Volatile write cache
+ * @NVME_CTRL_VWC_PRESENT: If set, indicates a volatile write cache is present.
+ * If a volatile write cache is present, then the host
+ * controls whether the volatile write cache is enabled
+ * with a Set Features command specifying the value
+ * %NVME_FEAT_FID_VOLATILE_WC.
+ * @NVME_CTRL_VWC_FLUSH: Mask to get the value of the flush command behavior.
+ */
+enum nvme_id_ctrl_vwc {
+ NVME_CTRL_VWC_PRESENT = 1 << 0,
+ NVME_CTRL_VWC_FLUSH = 3 << 1,
+};
+
+/**
+ * enum nvme_id_ctrl_nvscc - This field indicates the configuration settings
+ * for NVM Vendor Specific command handling.
+ * @NVME_CTRL_NVSCC_FMT: If set, all NVM Vendor Specific Commands use the
+ * format with NDT and NDM fields.
+ */
+enum nvme_id_ctrl_nvscc {
+ NVME_CTRL_NVSCC_FMT = 1 << 0,
+};
+
+/**
+ * enum nvme_id_ctrl_nwpc - This field indicates the optional namespace write
+ * protection capabilities supported by the
+ * controller.
+ * @NVME_CTRL_NWPC_WRITE_PROTECT: If set, then the controller shall
+ * support the No Write Protect and
+ * Write Protect namespace write
+ * protection states and may support
+ * the Write Protect Until Power
+ * Cycle state and Permanent Write
+ * Protect namespace write
+ * protection states.
+ * @NVME_CTRL_NWPC_WRITE_PROTECT_POWER_CYCLE: If set, then the controller
+ * supports the Write Protect Until
+ * Power Cycle state.
+ * @NVME_CTRL_NWPC_WRITE_PROTECT_PERMANENT: If set, then the controller
+ * supports the Permanent Write
+ * Protect state.
+ */
+enum nvme_id_ctrl_nwpc {
+ NVME_CTRL_NWPC_WRITE_PROTECT = 1 << 0,
+ NVME_CTRL_NWPC_WRITE_PROTECT_POWER_CYCLE= 1 << 1,
+ NVME_CTRL_NWPC_WRITE_PROTECT_PERMANENT = 1 << 2,
+};
+
+/**
+ * enum nvme_id_ctrl_sgls - This field indicates if SGLs are supported for the
+ * NVM Command Set and the particular SGL types supported.
+ * @NVME_CTRL_SGLS_SUPPORTED:
+ * @NVME_CTRL_SGLS_KEYED:
+ * @NVME_CTRL_SGLS_BIT_BUCKET:
+ * @NVME_CTRL_SGLS_MPTR_BYTE_ALIGNED:
+ * @NVME_CTRL_SGLS_OVERSIZE:
+ * @NVME_CTRL_SGLS_MPTR_SGL:
+ * @NVME_CTRL_SGLS_OFFSET:
+ * @NVME_CTRL_SGLS_TPORT:
+ */
+enum nvme_id_ctrl_sgls {
+ NVME_CTRL_SGLS_SUPPORTED = 3 << 0,
+ NVME_CTRL_SGLS_KEYED = 1 << 2,
+ NVME_CTRL_SGLS_BIT_BUCKET = 1 << 16,
+ NVME_CTRL_SGLS_MPTR_BYTE_ALIGNED = 1 << 17,
+ NVME_CTRL_SGLS_OVERSIZE = 1 << 18,
+ NVME_CTRL_SGLS_MPTR_SGL = 1 << 19,
+ NVME_CTRL_SGLS_OFFSET = 1 << 20,
+ NVME_CTRL_SGLS_TPORT = 1 << 21,
+};
+
+/**
+ * enum nvme_id_ctrl_fcatt - This field indicates attributes of the controller
+ * that are specific to NVMe over Fabrics.
+ * @NVME_CTRL_FCATT_DYNAMIC: If cleared, then the NVM subsystem uses a dynamic
+ * controller model. If set, then the NVM subsystem
+ * uses a static controller model.
+ */
+enum nvme_id_ctrl_fcatt {
+ NVME_CTRL_FCATT_DYNAMIC = 1 << 0,
+};
+
+/**
+ * enum nvme_id_ctrl_ofcs - Indicate whether the controller supports optional
+ * fabric commands.
+ * @NVME_CTRL_OFCS_DISCONNECT: If set, then the controller supports the
+ * Disconnect command and deletion of individual
+ * I/O Queues.
+ */
+enum nvme_id_ctrl_ofcs {
+ NVME_CTRL_OFCS_DISCONNECT = 1 << 0,
+};
+
+/**
+ * struct nvme_lbaf - LBA Format Data Structure
+ * @ms: Metadata Size indicates the number of metadata bytes provided per LBA
+ * based on the LBA Data Size indicated.
+ * @ds: LBA Data Size indicates the LBA data size supported, reported as a
+ * power of two.
+ * @rp: Relative Performance, see &enum nvme_lbaf_rp.
+ */
+struct nvme_lbaf {
+ __le16 ms;
+ __u8 ds;
+ __u8 rp;
+};
+
+/**
+ * enum nvme_lbaf_rp - This field indicates the relative performance of the LBA
+ * format indicated relative to other LBA formats supported
+ * by the controller.
+ * @NVME_LBAF_RP_BEST: Best performance
+ * @NVME_LBAF_RP_BETTER: Better performance
+ * @NVME_LBAF_RP_GOOD: Good performance
+ * @NVME_LBAF_RP_DEGRADED: Degraded performance
+ * @NVME_LBAF_RP_MASK: Mask to get the relative performance value from the
+ * field
+ */
+enum nvme_lbaf_rp {
+ NVME_LBAF_RP_BEST = 0,
+ NVME_LBAF_RP_BETTER = 1,
+ NVME_LBAF_RP_GOOD = 2,
+ NVME_LBAF_RP_DEGRADED = 3,
+ NVME_LBAF_RP_MASK = 3,
+};
+
+/**
+ * struct nvme_id_ns - Identify Namespace data structure
+ * @nsze: Namespace Size indicates the total size of the namespace in
+ * logical blocks. The number of logical blocks is based on the
+ * formatted LBA size.
+ * @ncap: Namespace Capacity indicates the maximum number of logical blocks
+ * that may be allocated in the namespace at any point in time. The
+ * number of logical blocks is based on the formatted LBA size.
+ * @nuse: Namespace Utilization indicates the current number of logical
+ * blocks allocated in the namespace. This field is smaller than or
+ * equal to the Namespace Capacity. The number of logical blocks is
+ * based on the formatted LBA size.
+ * @nsfeat: Namespace Features, see &enum nvme_id_nsfeat.
+ * @nlbaf: Number of LBA Formats defines the number of supported LBA data
+ * size and metadata size combinations supported by the namespace
+ * and the highest possible index to &struct nvme_id_ns.lbaf.
+ * @flbas: Formatted LBA Size, see &enum nvme_id_ns_flbas.
+ * @mc: Metadata Capabilities, see &enum nvme_id_ns_mc.
+ * @dpc: End-to-end Data Protection Capabilities, see
+ * &enum nvme_id_ns_dpc.
+ * @dps: End-to-end Data Protection Type Settings, see
+ * &enum nvme_id_ns_dps.
+ * @nmic: Namespace Multi-path I/O and Namespace Sharing Capabilities, see
+ * &enum nvme_id_ns_nmic.
+ * @rescap: Reservation Capabilities, see &enum nvme_id_ns_rescap.
+ * @fpi: Format Progress Indicator, see &enum nvme_nd_ns_fpi.
+ * @dlfeat: Deallocate Logical Block Features, see &enum nvme_id_ns_dlfeat.
+ * @nawun: Namespace Atomic Write Unit Normal indicates the
+ * namespace specific size of the write operation guaranteed to be
+ * written atomically to the NVM during normal operation.
+ * @nawupf: Namespace Atomic Write Unit Power Fail indicates the
+ * namespace specific size of the write operation guaranteed to be
+ * written atomically to the NVM during a power fail or error
+ * condition.
+ * @nacwu: Namespace Atomic Compare & Write Unit indicates the namespace
+ * specific size of the write operation guaranteed to be written
+ * atomically to the NVM for a Compare and Write fused command.
+ * @nabsn: Namespace Atomic Boundary Size Normal indicates the atomic
+ * boundary size for this namespace for the NAWUN value. This field
+ * is specified in logical blocks.
+ * @nabo: Namespace Atomic Boundary Offset indicates the LBA on this
+ * namespace where the first atomic boundary starts.
+ * @nabspf: Namespace Atomic Boundary Size Power Fail indicates the atomic
+ * boundary size for this namespace specific to the Namespace Atomic
+ * Write Unit Power Fail value. This field is specified in logical
+ * blocks.
+ * @noiob: Namespace Optimal I/O Boundary indicates the optimal I/O boundary
+ * for this namespace. This field is specified in logical blocks.
+ * The host should construct Read and Write commands that do not
+ * cross the I/O boundary to achieve optimal performance.
+ * @nvmcap: NVM Capacity indicates the total size of the NVM allocated to
+ * this namespace. The value is in bytes.
+ * @npwg: Namespace Preferred Write Granularity indicates the smallest
+ * recommended write granularity in logical blocks for this
+ * namespace. This is a 0's based value.
+ * @npwa: Namespace Preferred Write Alignment indicates the recommended
+ * write alignment in logical blocks for this namespace. This is a
+ * 0's based value.
+ * @npdg: Namespace Preferred Deallocate Granularity indicates the
+ * recommended granularity in logical blocks for the Dataset
+ * Management command with the Attribute - Deallocate bit.
+ * @npda: Namespace Preferred Deallocate Alignment indicates the
+ * recommended alignment in logical blocks for the Dataset
+ * Management command with the Attribute - Deallocate bit
+ * @nows: Namespace Optimal Write Size indicates the size in logical blocks
+ * for optimal write performance for this namespace. This is a 0's
+ * based value.
+ * @mssrl: Maximum Single Source Range Length indicates the maximum number
+ * of logical blocks that may be specified in each valid Source Range
+ * field of a Copy command.
+ * @mcl: Maximum Copy Length indicates the maximum number of logical
+ * blocks that may be specified in a Copy command.
+ * @msrc: Maximum Source Range Count indicates the maximum number of Source
+ * Range entries that may be used to specify source data in a Copy
+ * command. This is a 0’s based value.
+ * @rsvd81: Reserved
+ * @nulbaf: Number of Unique Capability LBA Formats defines the number of
+ * supported user data size and metadata size combinations supported
+ * by the namespace that may not share the same capabilities. LBA
+ * formats shall be allocated in order and packed sequentially.
+ * @rsvd83: Reserved
+ * @anagrpid: ANA Group Identifier indicates the ANA Group Identifier of the
+ * ANA group of which the namespace is a member.
+ * @rsvd96: Reserved
+ * @nsattr: Namespace Attributes, see &enum nvme_id_ns_attr.
+ * @nvmsetid: NVM Set Identifier indicates the NVM Set with which this
+ * namespace is associated.
+ * @endgid: Endurance Group Identifier indicates the Endurance Group with
+ * which this namespace is associated.
+ * @nguid: Namespace Globally Unique Identifier contains a 128-bit value
+ * that is globally unique and assigned to the namespace when the
+ * namespace is created. This field remains fixed throughout the
+ * life of the namespace and is preserved across namespace and
+ * controller operations
+ * @eui64: IEEE Extended Unique Identifier contains a 64-bit IEEE Extended
+ * Unique Identifier (EUI-64) that is globally unique and assigned
+ * to the namespace when the namespace is created. This field
+ * remains fixed throughout the life of the namespace and is
+ * preserved across namespace and controller operations
+ * @lbaf: LBA Format, see &struct nvme_lbaf.
+ * @vs: Vendor Specific
+ */
+struct nvme_id_ns {
+ __le64 nsze;
+ __le64 ncap;
+ __le64 nuse;
+ __u8 nsfeat;
+ __u8 nlbaf;
+ __u8 flbas;
+ __u8 mc;
+ __u8 dpc;
+ __u8 dps;
+ __u8 nmic;
+ __u8 rescap;
+ __u8 fpi;
+ __u8 dlfeat;
+ __le16 nawun;
+ __le16 nawupf;
+ __le16 nacwu;
+ __le16 nabsn;
+ __le16 nabo;
+ __le16 nabspf;
+ __le16 noiob;
+ __u8 nvmcap[16];
+ __le16 npwg;
+ __le16 npwa;
+ __le16 npdg;
+ __le16 npda;
+ __le16 nows;
+ __le16 mssrl;
+ __le32 mcl;
+ __u8 msrc;
+ __u8 rsvd81;
+ __u8 nulbaf;
+ __u8 rsvd83[9];
+ __le32 anagrpid;
+ __u8 rsvd96[3];
+ __u8 nsattr;
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 nguid[16];
+ __u8 eui64[8];
+ struct nvme_lbaf lbaf[64];
+ __u8 vs[3712];
+};
+
+/**
+ * enum nvme_id_nsfeat - This field defines features of the namespace.
+ * @NVME_NS_FEAT_THIN: If set, indicates that the namespace supports thin
+ * provisioning. Specifically, the Namespace Capacity
+ * reported may be less than the Namespace Size.
+ * @NVME_NS_FEAT_NATOMIC: If set, indicates that the fields NAWUN, NAWUPF, and
+ * NACWU are defined for this namespace and should be
+ * used by the host for this namespace instead of the
+ * AWUN, AWUPF, and ACWU fields in the Identify
+ * Controller data structure.
+ * @NVME_NS_FEAT_DULBE: If set, indicates that the controller supports the
+ * Deallocated or Unwritten Logical Block error for
+ * this namespace.
+ * @NVME_NS_FEAT_ID_REUSE: If set, indicates that the value in the NGUID field
+ * for this namespace, if non- zero, is never reused by
+ * the controller and that the value in the EUI64 field
+ * for this namespace, if non-zero, is never reused by
+ * the controller.
+ * @NVME_NS_FEAT_IO_OPT: If set, indicates that the fields NPWG, NPWA, NPDG,
+ * NPDA, and NOWS are defined for this namespace and
+ * should be used by the host for I/O optimization
+ */
+enum nvme_id_nsfeat {
+ NVME_NS_FEAT_THIN = 1 << 0,
+ NVME_NS_FEAT_NATOMIC = 1 << 1,
+ NVME_NS_FEAT_DULBE = 1 << 2,
+ NVME_NS_FEAT_ID_REUSE = 1 << 3,
+ NVME_NS_FEAT_IO_OPT = 1 << 4,
+};
+
+/**
+ * enum nvme_id_ns_flbas - This field indicates the LBA data size & metadata
+ * size combination that the namespace has been
+ * formatted with
+ * @NVME_NS_FLBAS_LOWER_MASK: Mask to get the index of one of the supported
+ * LBA Formats's least significant
+ * 4bits indicated in
+ * :c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf.
+ * @NVME_NS_FLBAS_META_EXT: Applicable only if format contains metadata. If
+ * this bit is set, indicates that the metadata is
+ * transferred at the end of the data LBA, creating an
+ * extended data LBA. If cleared, indicates that all
+ * of the metadata for a command is transferred as a
+ * separate contiguous buffer of data.
+ * @NVME_NS_FLBAS_HIGHER_MASK: Mask to get the index of one of
+ * the supported LBA Formats's most significant
+ * 2bits indicated in
+ * :c:type:`struct nvme_id_ns <nvme_id_ns>`.lbaf.
+ */
+enum nvme_id_ns_flbas {
+ NVME_NS_FLBAS_LOWER_MASK = 15 << 0,
+ NVME_NS_FLBAS_META_EXT = 1 << 4,
+ NVME_NS_FLBAS_HIGHER_MASK = 3 << 5,
+};
+
+/**
+ * enum nvme_nvm_id_ns_elbaf - This field indicates the extended LBA format
+ * @NVME_NVM_ELBAF_STS_MASK: Mask to get the storage tag size used to determine
+ * the variable-sized storage tag/reference tag fields
+ * @NVME_NVM_ELBAF_PIF_MASK: Mask to get the protection information format for
+ * the extended LBA format.
+ */
+enum nvme_nvm_id_ns_elbaf {
+ NVME_NVM_ELBAF_STS_MASK = 127 << 0,
+ NVME_NVM_ELBAF_PIF_MASK = 3 << 7,
+};
+
+/**
+ * enum nvme_id_ns_mc - This field indicates the capabilities for metadata.
+ * @NVME_NS_MC_EXTENDED: If set, indicates the namespace supports the metadata
+ * being transferred as part of a separate buffer that is
+ * specified in the Metadata Pointer.
+ * @NVME_NS_MC_SEPARATE: If set, indicates that the namespace supports the
+ * metadata being transferred as part of an extended data LBA.
+ */
+enum nvme_id_ns_mc {
+ NVME_NS_MC_EXTENDED = 1 << 0,
+ NVME_NS_MC_SEPARATE = 1 << 1,
+};
+
+/**
+ * enum nvme_id_ns_dpc - This field indicates the capabilities for the
+ * end-to-end data protection feature.
+ * @NVME_NS_DPC_PI_TYPE1: If set, indicates that the namespace supports
+ * Protection Information Type 1.
+ * @NVME_NS_DPC_PI_TYPE2: If set, indicates that the namespace supports
+ * Protection Information Type 2.
+ * @NVME_NS_DPC_PI_TYPE3: If set, indicates that the namespace supports
+ * Protection Information Type 3.
+ * @NVME_NS_DPC_PI_FIRST: If set, indicates that the namespace supports
+ * protection information transferred as the first eight
+ * bytes of metadata.
+ * @NVME_NS_DPC_PI_LAST: If set, indicates that the namespace supports
+ * protection information transferred as the last eight
+ * bytes of metadata.
+ */
+enum nvme_id_ns_dpc {
+ NVME_NS_DPC_PI_TYPE1 = 1 << 0,
+ NVME_NS_DPC_PI_TYPE2 = 1 << 1,
+ NVME_NS_DPC_PI_TYPE3 = 1 << 2,
+ NVME_NS_DPC_PI_FIRST = 1 << 3,
+ NVME_NS_DPC_PI_LAST = 1 << 4,
+};
+
+/**
+ * enum nvme_id_ns_dps - This field indicates the Type settings for the
+ * end-to-end data protection feature.
+ * @NVME_NS_DPS_PI_NONE: Protection information is not enabled
+ * @NVME_NS_DPS_PI_TYPE1: Protection information is enabled, Type 1
+ * @NVME_NS_DPS_PI_TYPE2: Protection information is enabled, Type 2
+ * @NVME_NS_DPS_PI_TYPE3: Protection information is enabled, Type 3
+ * @NVME_NS_DPS_PI_MASK: Mask to get the value of the PI type
+ * @NVME_NS_DPS_PI_FIRST: If set, indicates that the protection information, if
+ * enabled, is transferred as the first eight bytes of
+ * metadata.
+ */
+enum nvme_id_ns_dps {
+ NVME_NS_DPS_PI_NONE = 0,
+ NVME_NS_DPS_PI_TYPE1 = 1,
+ NVME_NS_DPS_PI_TYPE2 = 2,
+ NVME_NS_DPS_PI_TYPE3 = 3,
+ NVME_NS_DPS_PI_MASK = 7 << 0,
+ NVME_NS_DPS_PI_FIRST = 1 << 3,
+};
+
+/**
+ * enum nvme_id_ns_nmic - This field specifies multi-path I/O and namespace
+ * sharing capabilities of the namespace.
+ * @NVME_NS_NMIC_SHARED: If set, then the namespace may be attached to two or
+ * more controllers in the NVM subsystem concurrently
+ */
+enum nvme_id_ns_nmic {
+ NVME_NS_NMIC_SHARED = 1 << 0,
+};
+
+/**
+ * enum nvme_id_ns_rescap - This field indicates the reservation capabilities
+ * of the namespace.
+ * @NVME_NS_RESCAP_PTPL: If set, indicates that the namespace supports the
+ * Persist Through Power Loss capability.
+ * @NVME_NS_RESCAP_WE: If set, indicates that the namespace supports the
+ * Write Exclusive reservation type.
+ * @NVME_NS_RESCAP_EA: If set, indicates that the namespace supports the
+ * Exclusive Access reservation type.
+ * @NVME_NS_RESCAP_WERO: If set, indicates that the namespace supports the
+ * Write Exclusive - Registrants Only reservation type.
+ * @NVME_NS_RESCAP_EARO: If set, indicates that the namespace supports the
+ * Exclusive Access - Registrants Only reservation type.
+ * @NVME_NS_RESCAP_WEAR: If set, indicates that the namespace supports the
+ * Write Exclusive - All Registrants reservation type.
+ * @NVME_NS_RESCAP_EAAR: If set, indicates that the namespace supports the
+ * Exclusive Access - All Registrants reservation type.
+ * @NVME_NS_RESCAP_IEK_13: If set, indicates that Ignore Existing Key is used
+ * as defined in revision 1.3 or later of this specification.
+ */
+enum nvme_id_ns_rescap {
+ NVME_NS_RESCAP_PTPL = 1 << 0,
+ NVME_NS_RESCAP_WE = 1 << 1,
+ NVME_NS_RESCAP_EA = 1 << 2,
+ NVME_NS_RESCAP_WERO = 1 << 3,
+ NVME_NS_RESCAP_EARO = 1 << 4,
+ NVME_NS_RESCAP_WEAR = 1 << 5,
+ NVME_NS_RESCAP_EAAR = 1 << 6,
+ NVME_NS_RESCAP_IEK_13 = 1 << 7,
+};
+
+/**
+ * enum nvme_nd_ns_fpi - If a format operation is in progress, this field
+ * indicates the percentage of the namespace that remains
+ * to be formatted.
+ * @NVME_NS_FPI_REMAINING: Mask to get the format percent remaining value
+ * @NVME_NS_FPI_SUPPORTED: If set, indicates that the namespace supports the
+ * Format Progress Indicator defined for the field.
+ */
+enum nvme_nd_ns_fpi {
+ NVME_NS_FPI_REMAINING = 0x7f << 0,
+ NVME_NS_FPI_SUPPORTED = 1 << 7,
+};
+
+/**
+ * enum nvme_id_ns_dlfeat - This field indicates information about features
+ * that affect deallocating logical blocks for this
+ * namespace.
+ * @NVME_NS_DLFEAT_RB: Mask to get the value of the read behavior
+ * @NVME_NS_DLFEAT_RB_NR: Read behvaior is not reported
+ * @NVME_NS_DLFEAT_RB_ALL_0S: A deallocated logical block returns all bytes
+ * cleared to 0h.
+ * @NVME_NS_DLFEAT_RB_ALL_FS: A deallocated logical block returns all bytes
+ * set to FFh.
+ * @NVME_NS_DLFEAT_WRITE_ZEROES: If set, indicates that the controller supports
+ * the Deallocate bit in the Write Zeroes command
+ * for this namespace.
+ * @NVME_NS_DLFEAT_CRC_GUARD: If set, indicates that the Guard field for
+ * deallocated logical blocks that contain
+ * protection information is set to the CRC for
+ * the value read from the deallocated logical
+ * block and its metadata
+ */
+enum nvme_id_ns_dlfeat {
+ NVME_NS_DLFEAT_RB = 7 << 0,
+ NVME_NS_DLFEAT_RB_NR = 0,
+ NVME_NS_DLFEAT_RB_ALL_0S = 1,
+ NVME_NS_DLFEAT_RB_ALL_FS = 2,
+ NVME_NS_DLFEAT_WRITE_ZEROES = 1 << 3,
+ NVME_NS_DLFEAT_CRC_GUARD = 1 << 4,
+};
+
+/**
+ * enum nvme_id_ns_attr - Specifies attributes of the namespace.
+ * @NVME_NS_NSATTR_WRITE_PROTECTED: If set, then the namespace is currently
+ * write protected and all write access to the
+ * namespace shall fail.
+ */
+enum nvme_id_ns_attr {
+ NVME_NS_NSATTR_WRITE_PROTECTED = 1 << 0
+};
+
+/**
+ * struct nvme_ns_id_desc - Namespace identifier type descriptor
+ * @nidt: Namespace Identifier Type, see &enum nvme_ns_id_desc_nidt
+ * @nidl: Namespace Identifier Length contains the length in bytes of the
+ * &struct nvme_id_ns.nid.
+ * @rsvd: Reserved
+ * @nid: Namespace Identifier contains a value that is globally unique and
+ * assigned to the namespace when the namespace is created. The length
+ * is defined in &struct nvme_id_ns.nidl.
+ */
+struct nvme_ns_id_desc {
+ __u8 nidt;
+ __u8 nidl;
+ __le16 rsvd;
+ __u8 nid[];
+};
+
+/**
+ * enum nvme_ns_id_desc_nidt - Known namespace identifier types
+ * @NVME_NIDT_EUI64: IEEE Extended Unique Identifier, the NID field contains a
+ * copy of the EUI64 field in the struct nvme_id_ns.eui64.
+ * @NVME_NIDT_NGUID: Namespace Globally Unique Identifier, the NID field
+ * contains a copy of the NGUID field in struct nvme_id_ns.nguid.
+ * @NVME_NIDT_UUID: The NID field contains a 128-bit Universally Unique
+ * Identifier (UUID) as specified in RFC 4122.
+ * @NVME_NIDT_CSI: The NID field contains the command set identifier.
+ */
+enum nvme_ns_id_desc_nidt {
+ NVME_NIDT_EUI64 = 1,
+ NVME_NIDT_NGUID = 2,
+ NVME_NIDT_UUID = 3,
+ NVME_NIDT_CSI = 4,
+};
+
+enum nvme_ns_id_desc_nidt_lens {
+ NVME_NIDT_EUI64_LEN = 8,
+ NVME_NIDT_NGUID_LEN = 16,
+ NVME_NIDT_UUID_LEN = 16,
+ NVME_NIDT_CSI_LEN = 1,
+};
+
+/**
+ * struct nvme_nvmset_attr - NVM Set Attributes Entry
+ * @nvmsetid: NVM Set Identifier
+ * @endgid: Endurance Group Identifier
+ * @rsvd4: Reserved
+ * @rr4kt: Random 4 KiB Read Typical indicates the typical
+ * time to complete a 4 KiB random read in 100 nanosecond units
+ * when the NVM Set is in a Predictable Latency Mode Deterministic
+ * Window and there is 1 outstanding command per NVM Set.
+ * @ows: Optimal Write Size
+ * @tnvmsetcap: Total NVM Set Capacity
+ * @unvmsetcap: Unallocated NVM Set Capacity
+ * @rsvd48: Reserved
+ */
+struct nvme_nvmset_attr {
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 rsvd4[4];
+ __le32 rr4kt;
+ __le32 ows;
+ __u8 tnvmsetcap[16];
+ __u8 unvmsetcap[16];
+ __u8 rsvd48[80];
+};
+
+/**
+ * struct nvme_id_nvmset_list - NVM set list
+ * @nid: Nvmset id
+ * @rsvd1: Reserved
+ * @ent: nvmset id list
+ */
+struct nvme_id_nvmset_list {
+ __u8 nid;
+ __u8 rsvd1[127];
+ struct nvme_nvmset_attr ent[NVME_ID_NVMSET_LIST_MAX];
+};
+
+/**
+ * struct nvme_id_independent_id_ns - Identify - I/O Command Set Independent Identify Namespace Data Structure
+ * @nsfeat: common namespace features
+ * @nmic: Namespace Multi-path I/O and Namespace
+ * Sharing Capabilities
+ * @rescap: Reservation Capabilities
+ * @fpi: Format Progress Indicator
+ * @anagrpid: ANA Group Identifier
+ * @nsattr: Namespace Attributes
+ * @rsvd9: reserved
+ * @nvmsetid: NVM Set Identifier
+ * @endgid: Endurance Group Identifier
+ * @nstat: Namespace Status
+ * @rsvd15: reserved
+ */
+struct nvme_id_independent_id_ns {
+ __u8 nsfeat;
+ __u8 nmic;
+ __u8 rescap;
+ __u8 fpi;
+ __le32 anagrpid;
+ __u8 nsattr;
+ __u8 rsvd9;
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 nstat;
+ __u8 rsvd15[4081];
+};
+
+/**
+ * struct nvme_id_ns_granularity_desc - Namespace Granularity Descriptor
+ * @nszegran: Namespace Size Granularity
+ * @ncapgran: Namespace Capacity Granularity
+ */
+struct nvme_id_ns_granularity_desc {
+ __le64 nszegran;
+ __le64 ncapgran;
+};
+
+/**
+ * struct nvme_id_ns_granularity_list - Namespace Granularity List
+ * @attributes: Namespace Granularity Attributes
+ * @num_descriptors: Number of Descriptors
+ * @rsvd5: reserved
+ * @entry: Namespace Granularity Descriptor
+ * @rsvd288: reserved
+ */
+struct nvme_id_ns_granularity_list {
+ __le32 attributes;
+ __u8 num_descriptors;
+ __u8 rsvd5[27];
+ struct nvme_id_ns_granularity_desc entry[NVME_ID_ND_DESCRIPTOR_MAX];
+ __u8 rsvd288[3808];
+};
+
+/**
+ * struct nvme_id_uuid_list_entry - UUID List Entry
+ * @header: UUID Lists Entry Header
+ * @rsvd1: reserved
+ * @uuid: 128-bit Universally Unique Identifier
+ */
+struct nvme_id_uuid_list_entry {
+ __u8 header;
+ __u8 rsvd1[15];
+ __u8 uuid[16];
+};
+
+/**
+ * enum nvme_id_uuid - Identifier Association
+ * @NVME_ID_UUID_HDR_ASSOCIATION_MASK:
+ * @NVME_ID_UUID_ASSOCIATION_NONE:
+ * @NVME_ID_UUID_ASSOCIATION_VENDOR:
+ * @NVME_ID_UUID_ASSOCIATION_SUBSYSTEM_VENDOR:
+ */
+enum nvme_id_uuid {
+ NVME_ID_UUID_HDR_ASSOCIATION_MASK = 0x3,
+ NVME_ID_UUID_ASSOCIATION_NONE = 0,
+ NVME_ID_UUID_ASSOCIATION_VENDOR = 1,
+ NVME_ID_UUID_ASSOCIATION_SUBSYSTEM_VENDOR = 2,
+};
+
+/**
+ * struct nvme_id_uuid_list - UUID list
+ * @rsvd0: reserved
+ * @entry: UUID list entry
+ */
+struct nvme_id_uuid_list {
+ __u8 rsvd0[32];
+ struct nvme_id_uuid_list_entry entry[NVME_ID_UUID_LIST_MAX];
+};
+
+/**
+ * struct nvme_ctrl_list - Controller List
+ * @num: Number of Identifiers
+ * @identifier: NVM subsystem unique controller identifier
+ */
+struct nvme_ctrl_list {
+ __le16 num;
+ __le16 identifier[NVME_ID_CTRL_LIST_MAX];
+};
+
+/**
+ * struct nvme_ns_list - Namespace List
+ * @ns: Namespace Identifier
+ */
+struct nvme_ns_list {
+ __le32 ns[NVME_ID_NS_LIST_MAX];
+};
+
+/**
+ * struct nvme_id_ctrl_nvm - I/O Command Set Specific Identify Controller data structure
+ * @vsl: Verify Size Limit
+ * @wzsl: Write Zeroes Size Limit
+ * @wusl: Write Uncorrectable Size Limit
+ * @dmrl: Dataset Management Ranges Limit
+ * @dmrsl: Dataset Management Range Size Limit
+ * @dmsl: Dataset Management Size Limit
+ * @rsvd16: reserved
+ */
+struct nvme_id_ctrl_nvm {
+ __u8 vsl;
+ __u8 wzsl;
+ __u8 wusl;
+ __u8 dmrl;
+ __le32 dmrsl;
+ __le64 dmsl;
+ __u8 rsvd16[4080];
+};
+
+/**
+ * struct nvme_nvm_id_ns - NVME Command Set I/O Command Set Specific Identify Namespace Data Structure
+ * @lbstm: Logical Block Storage Tag Mask
+ * @pic: Protection Information Capabilities
+ * @rsvd9: Reserved
+ * @elbaf: List of Extended LBA Format Support
+ * @rsvd268: Reserved
+ */
+struct nvme_nvm_id_ns {
+ __le64 lbstm;
+ __u8 pic;
+ __u8 rsvd9[3];
+ __le32 elbaf[64];
+ __u8 rsvd268[3828];
+};
+
+/**
+ * struct nvme_zns_lbafe - LBA Format Extension Data Structure
+ * @zsze: Zone Size
+ * @zdes: Zone Descriptor Extension Size
+ * @rsvd9: reserved
+ */
+struct nvme_zns_lbafe {
+ __le64 zsze;
+ __u8 zdes;
+ __u8 rsvd9[7];
+};
+
+/**
+ * struct nvme_zns_id_ns - Zoned Namespace Command Set Specific Identify Namespace Data Structure
+ * @zoc: Zone Operation Characteristics
+ * @ozcs: Optional Zoned Command Support
+ * @mar: Maximum Active Resources
+ * @mor: Maximum Open Resources
+ * @rrl: Reset Recommended Limit
+ * @frl: Finish Recommended Limit
+ * @rrl1: Reset Recommended Limit 1
+ * @rrl2: Reset Recommended Limit 2
+ * @rrl3: Reset Recommended Limit 3
+ * @frl1: Finish Recommended Limit 1
+ * @frl2: Finish Recommended Limit 2
+ * @frl3: Finish Recommended Limit 3
+ * @numzrwa: Number of ZRWA Resources
+ * @zrwafg: ZRWA Flush Granularity
+ * @zrwasz: ZRWA Size
+ * @zrwacap: ZRWA Capability
+ * @rsvd53: Reserved
+ * @lbafe: LBA Format Extension
+ * @vs: Vendor Specific
+ */
+struct nvme_zns_id_ns {
+ __le16 zoc;
+ __le16 ozcs;
+ __le32 mar;
+ __le32 mor;
+ __le32 rrl;
+ __le32 frl;
+ __le32 rrl1;
+ __le32 rrl2;
+ __le32 rrl3;
+ __le32 frl1;
+ __le32 frl2;
+ __le32 frl3;
+ __le32 numzrwa;
+ __le16 zrwafg;
+ __le16 zrwasz;
+ __u8 zrwacap;
+ __u8 rsvd53[2763];
+ struct nvme_zns_lbafe lbafe[64];
+ __u8 vs[256];
+};
+
+/**
+ * struct nvme_zns_id_ctrl - I/O Command Set Specific Identify Controller Data Structure for the Zoned Namespace Command Set
+ * @zasl: Zone Append Size Limit
+ * @rsvd1: Reserved
+ */
+struct nvme_zns_id_ctrl {
+ __u8 zasl;
+ __u8 rsvd1[4095];
+};
+
+/**
+ * struct nvme_primary_ctrl_cap - Identify - Controller Capabilities Structure
+ * @cntlid: Controller Identifier
+ * @portid: Port Identifier
+ * @crt: Controller Resource Types
+ * @rsvd5: reserved
+ * @vqfrt: VQ Resources Flexible Total
+ * @vqrfa: VQ Resources Flexible Assigned
+ * @vqrfap: VQ Resources Flexible Allocated to Primary
+ * @vqprt: VQ Resources Private Total
+ * @vqfrsm: VQ Resources Flexible Secondary Maximum
+ * @vqgran: VQ Flexible Resource Preferred Granularity
+ * @rsvd48: reserved
+ * @vifrt: VI Resources Flexible Total
+ * @virfa: VI Resources Flexible Assigned
+ * @virfap: VI Resources Flexible Allocated to Primary
+ * @viprt: VI Resources Private Total
+ * @vifrsm: VI Resources Flexible Secondary Maximum
+ * @vigran: VI Flexible Resource Preferred Granularity
+ * @rsvd80: reserved
+ */
+struct nvme_primary_ctrl_cap {
+ __le16 cntlid;
+ __le16 portid;
+ __u8 crt;
+ __u8 rsvd5[27];
+ __le32 vqfrt;
+ __le32 vqrfa;
+ __le16 vqrfap;
+ __le16 vqprt;
+ __le16 vqfrsm;
+ __le16 vqgran;
+ __u8 rsvd48[16];
+ __le32 vifrt;
+ __le32 virfa;
+ __le16 virfap;
+ __le16 viprt;
+ __le16 vifrsm;
+ __le16 vigran;
+ __u8 rsvd80[4016];
+};
+
+/**
+ * struct nvme_secondary_ctrl - Secondary Controller Entry
+ * @scid: Secondary Controller Identifier
+ * @pcid: Primary Controller Identifier
+ * @scs: Secondary Controller State
+ * @rsvd5: Reserved
+ * @vfn: Virtual Function Number
+ * @nvq: Number of VQ Flexible Resources Assigned
+ * @nvi: Number of VI Flexible Resources Assigned
+ * @rsvd14: Reserved
+ */
+struct nvme_secondary_ctrl {
+ __le16 scid;
+ __le16 pcid;
+ __u8 scs;
+ __u8 rsvd5[3];
+ __le16 vfn;
+ __le16 nvq;
+ __le16 nvi;
+ __u8 rsvd14[18];
+};
+
+/**
+ * struct nvme_secondary_ctrl_list - Secondary Controller List
+ * @num: Number of Identifiers
+ * @rsvd: Reserved
+ * @sc_entry: Secondary Controller Entry
+ */
+struct nvme_secondary_ctrl_list {
+ __u8 num;
+ __u8 rsvd[31];
+ struct nvme_secondary_ctrl sc_entry[NVME_ID_SECONDARY_CTRL_MAX];
+};
+
+/**
+ * struct nvme_id_iocs - NVMe Identify IO Command Set data structure
+ * @iocsc: List of supported IO Command Set Combination vectors
+ */
+struct nvme_id_iocs {
+ __le64 iocsc[512];
+};
+
+/**
+ * struct nvme_id_domain_attr - Domain Attributes Entry
+ * @dom_id: Domain Identifier
+ * @rsvd2: Reserved
+ * @dom_cap: Total Domain Capacity
+ * @unalloc_dom_cap: Unallocated Domain Capacity
+ * @max_egrp_dom_cap: Max Endurance Group Domain Capacity
+ * @rsvd64: Reserved
+ */
+struct nvme_id_domain_attr {
+ __le16 dom_id;
+ __u8 rsvd2[14];
+ __u8 dom_cap[16];
+ __u8 unalloc_dom_cap[16];
+ __u8 max_egrp_dom_cap[16];
+ __u8 rsvd64[64];
+};
+
+/**
+ * struct nvme_id_domain_list - Domain List
+ * @num: Number of domain attributes
+ * @rsvd: Reserved
+ * @domain_attr: List of domain attributes
+ */
+struct nvme_id_domain_list {
+ __u8 num;
+ __u8 rsvd[127];
+ struct nvme_id_domain_attr domain_attr[NVME_ID_DOMAIN_LIST_MAX];
+};
+
+/**
+ * struct nvme_id_endurance_group_list - Endurance Group List
+ * @num: Number of Identifiers
+ * @identifier: Endurance Group Identifier
+ */
+struct nvme_id_endurance_group_list {
+ __le16 num;
+ __le16 identifier[NVME_ID_ENDURANCE_GROUP_LIST_MAX];
+};
+
+/**
+ * struct nvme_supported_log_pages - Supported Log Pages - Log
+ * @lid_support: Log Page Identifier Supported
+ *
+ * Supported Log Pages (Log Identifier 00h)
+ */
+struct nvme_supported_log_pages {
+ __le32 lid_support[NVME_LOG_SUPPORTED_LOG_PAGES_MAX];
+};
+
+/**
+ * struct nvme_error_log_page - Error Information Log Entry (Log Identifier 01h)
+ * @error_count: Error Count: a 64-bit incrementing error count,
+ * indicating a unique identifier for this error. The error
+ * count starts at %1h, is incremented for each unique error
+ * log entry, and is retained across power off conditions.
+ * A value of %0h indicates an invalid entry; this value
+ * is used when there are lost entries or when there are
+ * fewer errors than the maximum number of entries the
+ * controller supports. If the value of this field is
+ * %FFFFFFFFh, then the field shall be set to 1h when
+ * incremented (i.e., rolls over to %1h). Prior to NVMe
+ * 1.4, processing of incrementing beyond %FFFFFFFFh is
+ * unspecified.
+ * @sqid: Submission Queue ID: indicates the Submission Queue
+ * Identifier of the command that the error information is
+ * associated with. If the error is not specific to
+ * a particular command, then this field shall be set to
+ * %FFFFh.
+ * @cmdid: Command ID: indicates the Command Identifier of the
+ * command that the error is associated with. If the error
+ * is not specific to a particular command, then this field
+ * shall be set to %FFFFh.
+ * @status_field: Bits 15-1: Status Field: indicates the Status Field for
+ * the command that completed. If the error is not specific
+ * to a particular command, then this field reports the most
+ * applicable status value.
+ * Bit 0: Phase Tag: may indicate the Phase Tag posted for
+ * the command.
+ * @parm_error_location: Parameter Error Location: indicates the byte and bit of
+ * the command parameter that the error is associated with,
+ * if applicable. If the parameter spans multiple bytes or
+ * bits, then the location indicates the first byte and bit
+ * of the parameter.
+ * Bits 10-8: Bit in command that contained the error.
+ * Valid values are 0 to 7.
+ * Bits 7-0: Byte in command that contained the error.
+ * Valid values are 0 to 63.
+ * @lba: LBA: This field indicates the first LBA that experienced
+ * the error condition, if applicable.
+ * @nsid: Namespace: This field indicates the NSID of the namespace
+ * that the error is associated with, if applicable.
+ * @vs: Vendor Specific Information Available: If there is
+ * additional vendor specific error information available,
+ * this field provides the log page identifier associated
+ * with that page. A value of %0h indicates that no additional
+ * information is available. Valid values are in the range
+ * of %80h to %FFh.
+ * @trtype: Transport Type (TRTYPE): indicates the Transport Type of
+ * the transport associated with the error. The values in
+ * this field are the same as the TRTYPE values in the
+ * Discovery Log Page Entry. If the error is not transport
+ * related, this field shall be cleared to %0h. If the error
+ * is transport related, this field shall be set to the type
+ * of the transport - see &enum nvme_trtype.
+ * @csi: Command Set Indicator: This field contains command set
+ * indicator for the command that the error is associated
+ * with.
+ * @opcode: Opcode: This field contains opcode for the command that
+ * the error is associated with.
+ * @cs: Command Specific Information: This field contains command
+ * specific information. If used, the command definition
+ * specifies the information returned.
+ * @trtype_spec_info: Transport Type Specific Information
+ * @rsvd: Reserved: [62:42]
+ * @log_page_version: This field shall be set to 1h. If set, @csi and @opcode
+ * will have valid values.
+ */
+struct nvme_error_log_page {
+ __le64 error_count;
+ __le16 sqid;
+ __le16 cmdid;
+ __le16 status_field;
+ __le16 parm_error_location;
+ __le64 lba;
+ __le32 nsid;
+ __u8 vs;
+ __u8 trtype;
+ __u8 csi;
+ __u8 opcode;
+ __le64 cs;
+ __le16 trtype_spec_info;
+ __u8 rsvd[21];
+ __u8 log_page_version;
+};
+
+enum nvme_err_pel {
+ NVME_ERR_PEL_BYTE_MASK = 0xf,
+ NVME_ERR_PEL_BIT_MASK = 0x70,
+};
+
+/**
+ * struct nvme_smart_log - SMART / Health Information Log (Log Identifier 02h)
+ * @critical_warning: This field indicates critical warnings for the state
+ * of the controller. Critical warnings may result in an
+ * asynchronous event notification to the host. Bits in
+ * this field represent the current associated state and
+ * are not persistent (see &enum nvme_smart_crit).
+ * @temperature: Composite Temperature: Contains a value corresponding
+ * to a temperature in Kelvins that represents the current
+ * composite temperature of the controller and namespace(s)
+ * associated with that controller. The manner in which
+ * this value is computed is implementation specific and
+ * may not represent the actual temperature of any physical
+ * point in the NVM subsystem. Warning and critical
+ * overheating composite temperature threshold values are
+ * reported by the WCTEMP and CCTEMP fields in the Identify
+ * Controller data structure.
+ * @avail_spare: Available Spare: Contains a normalized percentage (0%
+ * to 100%) of the remaining spare capacity available.
+ * @spare_thresh: Available Spare Threshold: When the Available Spare
+ * falls below the threshold indicated in this field, an
+ * asynchronous event completion may occur. The value is
+ * indicated as a normalized percentage (0% to 100%).
+ * The values 101 to 255 are reserved.
+ * @percent_used: Percentage Used: Contains a vendor specific estimate
+ * of the percentage of NVM subsystem life used based on
+ * the actual usage and the manufacturer's prediction of
+ * NVM life. A value of 100 indicates that the estimated
+ * endurance of the NVM in the NVM subsystem has been
+ * consumed, but may not indicate an NVM subsystem failure.
+ * The value is allowed to exceed 100. Percentages greater
+ * than 254 shall be represented as 255. This value shall
+ * be updated once per power-on hour (when the controller
+ * is not in a sleep state).
+ * @endu_grp_crit_warn_sumry: Endurance Group Critical Warning Summary: This field
+ * indicates critical warnings for the state of Endurance
+ * Groups. Bits in this field represent the current associated
+ * state and are not persistent (see &enum nvme_smart_egcw).
+ * @rsvd7: Reserved
+ * @data_units_read: Data Units Read: Contains the number of 512 byte data
+ * units the host has read from the controller; this value
+ * does not include metadata. This value is reported in
+ * thousands (i.e., a value of 1 corresponds to 1000
+ * units of 512 bytes read) and is rounded up (e.g., one
+ * indicates the that number of 512 byte data units read
+ * is from 1 to 1000, three indicates that the number of
+ * 512 byte data units read is from 2001 to 3000). When
+ * the LBA size is a value other than 512 bytes, the
+ * controller shall convert the amount of data read to
+ * 512 byte units. For the NVM command set, logical blocks
+ * read as part of Compare, Read, and Verify operations
+ * shall be included in this value. A value of %0h in
+ * this field indicates that the number of Data Units Read
+ * is not reported.
+ * @data_units_written: Data Units Written: Contains the number of 512 byte
+ * data units the host has written to the controller;
+ * this value does not include metadata. This value is
+ * reported in thousands (i.e., a value of 1 corresponds
+ * to 1000 units of 512 bytes written) and is rounded up
+ * (e.g., one indicates that the number of 512 byte data
+ * units written is from 1 to 1,000, three indicates that
+ * the number of 512 byte data units written is from 2001
+ * to 3000). When the LBA size is a value other than 512
+ * bytes, the controller shall convert the amount of data
+ * written to 512 byte units. For the NVM command set,
+ * logical blocks written as part of Write operations shall
+ * be included in this value. Write Uncorrectable commands
+ * and Write Zeroes commands shall not impact this value.
+ * A value of %0h in this field indicates that the number
+ * of Data Units Written is not reported.
+ * @host_reads: Host Read Commands: Contains the number of read commands
+ * completed by the controller. For the NVM command set,
+ * this value is the sum of the number of Compare commands
+ * and the number of Read commands.
+ * @host_writes: Host Write Commands: Contains the number of write
+ * commands completed by the controller. For the NVM
+ * command set, this is the number of Write commands.
+ * @ctrl_busy_time: Controller Busy Time: Contains the amount of time the
+ * controller is busy with I/O commands. The controller
+ * is busy when there is a command outstanding to an I/O
+ * Queue (specifically, a command was issued via an I/O
+ * Submission Queue Tail doorbell write and the corresponding
+ * completion queue entry has not been posted yet to the
+ * associated I/O Completion Queue). This value is
+ * reported in minutes.
+ * @power_cycles: Power Cycles: Contains the number of power cycles.
+ * @power_on_hours: Power On Hours: Contains the number of power-on hours.
+ * This may not include time that the controller was
+ * powered and in a non-operational power state.
+ * @unsafe_shutdowns: Unsafe Shutdowns: Contains the number of unsafe
+ * shutdowns. This count is incremented when a Shutdown
+ * Notification (CC.SHN) is not received prior to loss of power.
+ * @media_errors: Media and Data Integrity Errors: Contains the number
+ * of occurrences where the controller detected an
+ * unrecovered data integrity error. Errors such as
+ * uncorrectable ECC, CRC checksum failure, or LBA tag
+ * mismatch are included in this field. Errors introduced
+ * as a result of a Write Uncorrectable command may or
+ * may not be included in this field.
+ * @num_err_log_entries: Number of Error Information Log Entries: Contains the
+ * number of Error Information log entries over the life
+ * of the controller.
+ * @warning_temp_time: Warning Composite Temperature Time: Contains the amount
+ * of time in minutes that the controller is operational
+ * and the Composite Temperature is greater than or equal
+ * to the Warning Composite Temperature Threshold (WCTEMP)
+ * field and less than the Critical Composite Temperature
+ * Threshold (CCTEMP) field in the Identify Controller
+ * data structure. If the value of the WCTEMP or CCTEMP
+ * field is %0h, then this field is always cleared to %0h
+ * regardless of the Composite Temperature value.
+ * @critical_comp_time: Critical Composite Temperature Time: Contains the amount
+ * of time in minutes that the controller is operational
+ * and the Composite Temperature is greater than or equal
+ * to the Critical Composite Temperature Threshold (CCTEMP)
+ * field in the Identify Controller data structure. If
+ * the value of the CCTEMP field is %0h, then this field
+ * is always cleared to 0h regardless of the Composite
+ * Temperature value.
+ * @temp_sensor: Temperature Sensor 1-8: Contains the current temperature
+ * in degrees Kelvin reported by temperature sensors 1-8.
+ * The physical point in the NVM subsystem whose temperature
+ * is reported by the temperature sensor and the temperature
+ * accuracy is implementation specific. An implementation
+ * that does not implement the temperature sensor reports
+ * a value of %0h.
+ * @thm_temp1_trans_count: Thermal Management Temperature 1 Transition Count:
+ * Contains the number of times the controller transitioned
+ * to lower power active power states or performed vendor
+ * specific thermal management actions while minimizing
+ * the impact on performance in order to attempt to reduce
+ * the Composite Temperature because of the host controlled
+ * thermal management feature (i.e., the Composite
+ * Temperature rose above the Thermal Management
+ * Temperature 1). This counter shall not wrap once the
+ * value %FFFFFFFFh is reached. A value of %0h, indicates
+ * that this transition has never occurred or this field
+ * is not implemented.
+ * @thm_temp2_trans_count: Thermal Management Temperature 2 Transition Count
+ * @thm_temp1_total_time: Total Time For Thermal Management Temperature 1:
+ * Contains the number of seconds that the controller
+ * had transitioned to lower power active power states or
+ * performed vendor specific thermal management actions
+ * while minimizing the impact on performance in order to
+ * attempt to reduce the Composite Temperature because of
+ * the host controlled thermal management feature. This
+ * counter shall not wrap once the value %FFFFFFFFh is
+ * reached. A value of %0h, indicates that this transition
+ * has never occurred or this field is not implemented.
+ * @thm_temp2_total_time: Total Time For Thermal Management Temperature 2
+ * @rsvd232: Reserved
+ */
+struct nvme_smart_log {
+ __u8 critical_warning;
+ __u8 temperature[2];
+ __u8 avail_spare;
+ __u8 spare_thresh;
+ __u8 percent_used;
+ __u8 endu_grp_crit_warn_sumry;
+ __u8 rsvd7[25];
+ __u8 data_units_read[16];
+ __u8 data_units_written[16];
+ __u8 host_reads[16];
+ __u8 host_writes[16];
+ __u8 ctrl_busy_time[16];
+ __u8 power_cycles[16];
+ __u8 power_on_hours[16];
+ __u8 unsafe_shutdowns[16];
+ __u8 media_errors[16];
+ __u8 num_err_log_entries[16];
+ __le32 warning_temp_time;
+ __le32 critical_comp_time;
+ __le16 temp_sensor[8];
+ __le32 thm_temp1_trans_count;
+ __le32 thm_temp2_trans_count;
+ __le32 thm_temp1_total_time;
+ __le32 thm_temp2_total_time;
+ __u8 rsvd232[280];
+};
+
+/**
+ * enum nvme_smart_crit - Critical Warning
+ * @NVME_SMART_CRIT_SPARE: If set, then the available spare capacity has fallen
+ * below the threshold.
+ * @NVME_SMART_CRIT_TEMPERATURE: If set, then a temperature is either greater
+ * than or equal to an over temperature threshold; or
+ * less than or equal to an under temperature threshold.
+ * @NVME_SMART_CRIT_DEGRADED: If set, then the NVM subsystem reliability has
+ * been degraded due to significant media related errors
+ * or any internal error that degrades NVM subsystem
+ * reliability.
+ * @NVME_SMART_CRIT_MEDIA: If set, then all of the media has been placed in read
+ * only mode. The controller shall not set this bit if
+ * the read-only condition on the media is a result of
+ * a change in the write protection state of a namespace.
+ * @NVME_SMART_CRIT_VOLATILE_MEMORY: If set, then the volatile memory backup
+ * device has failed. This field is only valid if the
+ * controller has a volatile memory backup solution.
+ * @NVME_SMART_CRIT_PMR_RO: If set, then the Persistent Memory Region has become
+ * read-only or unreliable.
+ */
+enum nvme_smart_crit {
+ NVME_SMART_CRIT_SPARE = 1 << 0,
+ NVME_SMART_CRIT_TEMPERATURE = 1 << 1,
+ NVME_SMART_CRIT_DEGRADED = 1 << 2,
+ NVME_SMART_CRIT_MEDIA = 1 << 3,
+ NVME_SMART_CRIT_VOLATILE_MEMORY = 1 << 4,
+ NVME_SMART_CRIT_PMR_RO = 1 << 5,
+};
+
+/**
+ * enum nvme_smart_egcw - Endurance Group Critical Warning Summary
+ * @NVME_SMART_EGCW_SPARE: If set, then the available spare capacity of one or
+ * more Endurance Groups has fallen below the threshold.
+ * @NVME_SMART_EGCW_DEGRADED: If set, then the reliability of one or more
+ * Endurance Groups has been degraded due to significant
+ * media related errors or any internal error that
+ * degrades NVM subsystem reliability.
+ * @NVME_SMART_EGCW_RO: If set, then the namespaces in one or more Endurance
+ * Groups have been placed in read only mode not as
+ * a result of a change in the write protection state
+ * of a namespace.
+ */
+enum nvme_smart_egcw {
+ NVME_SMART_EGCW_SPARE = 1 << 0,
+ NVME_SMART_EGCW_DEGRADED = 1 << 2,
+ NVME_SMART_EGCW_RO = 1 << 3,
+};
+
+/**
+ * struct nvme_firmware_slot - Firmware Slot Information Log
+ * @afi: Active Firmware Info
+ * @rsvd1: Reserved
+ * @frs: Firmware Revision for Slot
+ * @rsvd2: Reserved
+ */
+struct nvme_firmware_slot {
+ __u8 afi;
+ __u8 rsvd1[7];
+ char frs[7][8];
+ __u8 rsvd2[448];
+};
+
+/**
+ * struct nvme_cmd_effects_log - Commands Supported and Effects Log
+ * @acs: Admin Command Supported
+ * @iocs: I/O Command Supported
+ * @rsvd: Reserved
+ */
+struct nvme_cmd_effects_log {
+ __le32 acs[256];
+ __le32 iocs[256];
+ __u8 rsvd[2048];
+};
+
+/**
+ * enum nvme_cmd_effects - Commands Supported and Effects
+ * @NVME_CMD_EFFECTS_CSUPP: Command Supported
+ * @NVME_CMD_EFFECTS_LBCC: Logical Block Content Change
+ * @NVME_CMD_EFFECTS_NCC: Namespace Capability Change
+ * @NVME_CMD_EFFECTS_NIC: Namespace Inventory Change
+ * @NVME_CMD_EFFECTS_CCC: Controller Capability Change
+ * @NVME_CMD_EFFECTS_CSE_MASK: Command Submission and Execution
+ * @NVME_CMD_EFFECTS_UUID_SEL: UUID Selection Supported
+ */
+enum nvme_cmd_effects {
+ NVME_CMD_EFFECTS_CSUPP = 1 << 0,
+ NVME_CMD_EFFECTS_LBCC = 1 << 1,
+ NVME_CMD_EFFECTS_NCC = 1 << 2,
+ NVME_CMD_EFFECTS_NIC = 1 << 3,
+ NVME_CMD_EFFECTS_CCC = 1 << 4,
+ NVME_CMD_EFFECTS_CSE_MASK = 3 << 16,
+ NVME_CMD_EFFECTS_UUID_SEL = 1 << 19,
+};
+
+/**
+ * struct nvme_st_result - Self-test Result
+ * @dsts: Device Self-test Status: Indicates the device self-test code and the
+ * status of the operation (see &enum nvme_status_result and &enum nvme_st_code).
+ * @seg: Segment Number: Iindicates the segment number where the first self-test
+ * failure occurred. If Device Self-test Status (@dsts) is not set to
+ * #NVME_ST_RESULT_KNOWN_SEG_FAIL, then this field should be ignored.
+ * @vdi: Valid Diagnostic Information: Indicates the diagnostic failure
+ * information that is reported. See &enum nvme_st_valid_diag_info.
+ * @rsvd: Reserved
+ * @poh: Power On Hours (POH): Indicates the number of power-on hours at the
+ * time the device self-test operation was completed or aborted. This
+ * does not include time that the controller was powered and in a low
+ * power state condition.
+ * @nsid: Namespace Identifier (NSID): Indicates the namespace that the Failing
+ * LBA occurred on. Valid only when the NSID Valid bit
+ * (#NVME_ST_VALID_DIAG_INFO_NSID) is set in the Valid Diagnostic
+ * Information (@vdi) field.
+ * @flba: Failing LBA: indicates the LBA of the logical block that caused the
+ * test to fail. If the device encountered more than one failed logical
+ * block during the test, then this field only indicates one of those
+ * failed logical blocks. Valid only when the NSID Valid bit
+ * (#NVME_ST_VALID_DIAG_INFO_FLBA) is set in the Valid Diagnostic
+ * Information (@vdi) field.
+ * @sct: Status Code Type: This field may contain additional information related
+ * to errors or conditions. Bits 2:0 may contain additional information
+ * relating to errors or conditions that occurred during the device
+ * self-test operation represented in the same format used in the Status
+ * Code Type field of the completion queue entry (refer to &enum nvme_status_field).
+ * Valid only when the NSID Valid bit (#NVME_ST_VALID_DIAG_INFO_SCT) is
+ * set in the Valid Diagnostic Information (@vdi) field.
+ * @sc: Status Code: This field may contain additional information relating
+ * to errors or conditions that occurred during the device self-test
+ * operation represented in the same format used in the Status Code field
+ * of the completion queue entry. Valid only when the SCT Valid bit
+ * (#NVME_ST_VALID_DIAG_INFO_SC) is set in the Valid Diagnostic
+ * Information (@vdi) field.
+ * @vs: Vendor Specific.
+ */
+struct nvme_st_result {
+ __u8 dsts;
+ __u8 seg;
+ __u8 vdi;
+ __u8 rsvd;
+ __le64 poh;
+ __le32 nsid;
+ __le64 flba;
+ __u8 sct;
+ __u8 sc;
+ __u8 vs[2];
+} __attribute__((packed));
+
+/**
+ * enum nvme_status_result - Result of the device self-test operation
+ * @NVME_ST_RESULT_NO_ERR: Operation completed without error.
+ * @NVME_ST_RESULT_ABORTED: Operation was aborted by a Device Self-test command.
+ * @NVME_ST_RESULT_CLR: Operation was aborted by a Controller Level Reset.
+ * @NVME_ST_RESULT_NS_REMOVED: Operation was aborted due to a removal of
+ * a namespace from the namespace inventory.
+ * @NVME_ST_RESULT_ABORTED_FORMAT: Operation was aborted due to the processing
+ * of a Format NVM command.
+ * @NVME_ST_RESULT_FATAL_ERR: A fatal error or unknown test error occurred
+ * while the controller was executing the device
+ * self-test operation and the operation did
+ * not complete.
+ * @NVME_ST_RESULT_UNKNOWN_SEG_FAIL: Operation completed with a segment that failed
+ * and the segment that failed is not known.
+ * @NVME_ST_RESULT_KNOWN_SEG_FAIL: Operation completed with one or more failed
+ * segments and the first segment that failed
+ * is indicated in the Segment Number field.
+ * @NVME_ST_RESULT_ABORTED_UNKNOWN: Operation was aborted for unknown reason.
+ * @NVME_ST_RESULT_ABORTED_SANITIZE: Operation was aborted due to a sanitize operation.
+ * @NVME_ST_RESULT_NOT_USED: Entry not used (does not contain a test result).
+ * @NVME_ST_RESULT_MASK: Mask to get the status result value from
+ * the &struct nvme_st_result.dsts field.
+ */
+enum nvme_status_result {
+ NVME_ST_RESULT_NO_ERR = 0x0,
+ NVME_ST_RESULT_ABORTED = 0x1,
+ NVME_ST_RESULT_CLR = 0x2,
+ NVME_ST_RESULT_NS_REMOVED = 0x3,
+ NVME_ST_RESULT_ABORTED_FORMAT = 0x4,
+ NVME_ST_RESULT_FATAL_ERR = 0x5,
+ NVME_ST_RESULT_UNKNOWN_SEG_FAIL = 0x6,
+ NVME_ST_RESULT_KNOWN_SEG_FAIL = 0x7,
+ NVME_ST_RESULT_ABORTED_UNKNOWN = 0x8,
+ NVME_ST_RESULT_ABORTED_SANITIZE = 0x9,
+ NVME_ST_RESULT_NOT_USED = 0xf,
+ NVME_ST_RESULT_MASK = 0xf,
+};
+
+/**
+ * enum nvme_st_code - Self-test Code value
+ * @NVME_ST_CODE_RESERVED: Reserved.
+ * @NVME_ST_CODE_SHORT: Short device self-test operation.
+ * @NVME_ST_CODE_EXTENDED: Extended device self-test operation.
+ * @NVME_ST_CODE_VS: Vendor specific.
+ * @NVME_ST_CODE_ABORT: Abort device self-test operation.
+ * @NVME_ST_CODE_SHIFT: Shift amount to get the code value from the
+ * &struct nvme_st_result.dsts field.
+ */
+enum nvme_st_code {
+ NVME_ST_CODE_RESERVED = 0x0,
+ NVME_ST_CODE_SHORT = 0x1,
+ NVME_ST_CODE_EXTENDED = 0x2,
+ NVME_ST_CODE_VS = 0xe,
+ NVME_ST_CODE_ABORT = 0xf,
+ NVME_ST_CODE_SHIFT = 4,
+};
+
+/**
+ * enum nvme_st_curr_op - Current Device Self-Test Operation
+ * @NVME_ST_CURR_OP_NOT_RUNNING: No device self-test operation in progress.
+ * @NVME_ST_CURR_OP_SHORT: Short device self-test operation in progress.
+ * @NVME_ST_CURR_OP_EXTENDED: Extended device self-test operation in progress.
+ * @NVME_ST_CURR_OP_VS: Vendor specific.
+ * @NVME_ST_CURR_OP_RESERVED: Reserved.
+ * @NVME_ST_CURR_OP_MASK: Mask to get the current operation value from the
+ * &struct nvme_self_test_log.current_operation field.
+ * @NVME_ST_CURR_OP_CMPL_MASK: Mask to get the current operation completion value
+ * from the &struct nvme_self_test_log.completion field.
+ */
+enum nvme_st_curr_op {
+ NVME_ST_CURR_OP_NOT_RUNNING = 0x0,
+ NVME_ST_CURR_OP_SHORT = 0x1,
+ NVME_ST_CURR_OP_EXTENDED = 0x2,
+ NVME_ST_CURR_OP_VS = 0xe,
+ NVME_ST_CURR_OP_RESERVED = 0xf,
+ NVME_ST_CURR_OP_MASK = 0xf,
+ NVME_ST_CURR_OP_CMPL_MASK = 0x7f,
+};
+
+/**
+ * enum nvme_st_valid_diag_info - Valid Diagnostic Information
+ * @NVME_ST_VALID_DIAG_INFO_NSID: NSID Valid: if set, then the contents of
+ * the Namespace Identifier field are valid.
+ * @NVME_ST_VALID_DIAG_INFO_FLBA: FLBA Valid: if set, then the contents of
+ * the Failing LBA field are valid.
+ * @NVME_ST_VALID_DIAG_INFO_SCT: SCT Valid: if set, then the contents of
+ * the Status Code Type field are valid.
+ * @NVME_ST_VALID_DIAG_INFO_SC: SC Valid: if set, then the contents of
+ * the Status Code field are valid.
+ */
+enum nvme_st_valid_diag_info {
+ NVME_ST_VALID_DIAG_INFO_NSID = 1 << 0,
+ NVME_ST_VALID_DIAG_INFO_FLBA = 1 << 1,
+ NVME_ST_VALID_DIAG_INFO_SCT = 1 << 2,
+ NVME_ST_VALID_DIAG_INFO_SC = 1 << 3,
+};
+
+/**
+ * struct nvme_self_test_log - Device Self-test (Log Identifier 06h)
+ * @current_operation: Current Device Self-Test Operation: indicates the status
+ * of the current device self-test operation. If a device
+ * self-test operation is in process (i.e., this field is set
+ * to #NVME_ST_CURR_OP_SHORT or #NVME_ST_CURR_OP_EXTENDED),
+ * then the controller shall not set this field to
+ * #NVME_ST_CURR_OP_NOT_RUNNING until a new Self-test Result
+ * Data Structure is created (i.e., if a device self-test
+ * operation completes or is aborted, then the controller
+ * shall create a Self-test Result Data Structure prior to
+ * setting this field to #NVME_ST_CURR_OP_NOT_RUNNING).
+ * See &enum nvme_st_curr_op.
+ * @completion: Current Device Self-Test Completion: indicates the percentage
+ * of the device self-test operation that is complete (e.g.,
+ * a value of 25 indicates that 25% of the device self-test
+ * operation is complete and 75% remains to be tested).
+ * If the @current_operation field is cleared to
+ * #NVME_ST_CURR_OP_NOT_RUNNING (indicating there is no device
+ * self-test operation in progress), then this field is ignored.
+ * @rsvd: Reserved
+ * @result: Self-test Result Data Structures, see &struct nvme_st_result.
+ */
+struct nvme_self_test_log {
+ __u8 current_operation;
+ __u8 completion;
+ __u8 rsvd[2];
+ struct nvme_st_result result[NVME_LOG_ST_MAX_RESULTS];
+} __attribute__((packed));
+
+/**
+ * enum nvme_cmd_get_log_telemetry_host_lsp - Telemetry Host-Initiated log specific field
+ * @NVME_LOG_TELEM_HOST_LSP_RETAIN: Get Telemetry Data Blocks
+ * @NVME_LOG_TELEM_HOST_LSP_CREATE: Create Telemetry Data Blocks
+ */
+enum nvme_cmd_get_log_telemetry_host_lsp {
+ NVME_LOG_TELEM_HOST_LSP_RETAIN = 0,
+ NVME_LOG_TELEM_HOST_LSP_CREATE = 1,
+};
+
+/**
+ * struct nvme_telemetry_log - Retrieve internal data specific to the
+ * manufacturer.
+ * @lpi: Log Identifier, either %NVME_LOG_LID_TELEMETRY_HOST or
+ * %NVME_LOG_LID_TELEMETRY_CTRL
+ * @rsvd1: Reserved
+ * @ieee: IEEE OUI Identifier is the Organization Unique Identifier (OUI)
+ * for the controller vendor that is able to interpret the data.
+ * @dalb1: Telemetry Controller-Initiated Data Area 1 Last Block is
+ * the value of the last block in this area.
+ * @dalb2: Telemetry Controller-Initiated Data Area 1 Last Block is
+ * the value of the last block in this area.
+ * @dalb3: Telemetry Controller-Initiated Data Area 1 Last Block is
+ * the value of the last block in this area.
+ * @rsvd14: Reserved
+ * @dalb4: Telemetry Controller-Initiated Data Area 4 Last Block is
+ * the value of the last block in this area.
+ * @rsvd20: Reserved
+ * @hostdgn: Telemetry Host-Initiated Data Generation Number is a
+ * value that is incremented each time the host initiates a
+ * capture of its internal controller state in the controller .
+ * @ctrlavail: Telemetry Controller-Initiated Data Available, if cleared,
+ * then the controller telemetry log does not contain saved
+ * internal controller state. If this field is set to 1h, the
+ * controller log contains saved internal controller state. If
+ * this field is set to 1h, the data will be latched until the
+ * host releases it by reading the log with RAE cleared.
+ * @ctrldgn: Telemetry Controller-Initiated Data Generation Number is
+ * a value that is incremented each time the controller initiates a
+ * capture of its internal controller state in the controller .
+ * @rsnident: Reason Identifiers a vendor specific identifier that describes
+ * the operating conditions of the controller at the time of
+ * capture.
+ * @data_area: Telemetry data blocks, vendor specific information data.
+ *
+ * This log consists of a header describing the log and zero or more Telemetry
+ * Data Blocks. All Telemetry Data Blocks are %NVME_LOG_TELEM_BLOCK_SIZE, 512
+ * bytes, in size. This log captures the controller’s internal state.
+ */
+struct nvme_telemetry_log {
+ __u8 lpi;
+ __u8 rsvd1[4];
+ __u8 ieee[3];
+ __le16 dalb1;
+ __le16 dalb2;
+ __le16 dalb3;
+ __u8 rsvd14[2];
+ __le32 dalb4;
+ __u8 rsvd20[361];
+ __u8 hostdgn;
+ __u8 ctrlavail;
+ __u8 ctrldgn;
+ __u8 rsnident[128];
+ __u8 data_area[];
+};
+
+/**
+ * struct nvme_endurance_group_log - Endurance Group Information Log
+ * @critical_warning: Critical Warning
+ * @endurance_group_features: Endurance Group Features
+ * @rsvd2: Reserved
+ * @avl_spare: Available Spare
+ * @avl_spare_threshold: Available Spare Threshold
+ * @percent_used: Percentage Used
+ * @domain_identifier: Domain Identifier
+ * @rsvd8: Reserved
+ * @endurance_estimate: Endurance Estimate
+ * @data_units_read: Data Units Read
+ * @data_units_written: Data Units Written
+ * @media_units_written: Media Units Written
+ * @host_read_cmds: Host Read Commands
+ * @host_write_cmds: Host Write Commands
+ * @media_data_integrity_err: Media and Data Integrity Errors
+ * @num_err_info_log_entries: Number of Error Information Log Entries
+ * @total_end_grp_cap: Total Endurance Group Capacity
+ * @unalloc_end_grp_cap: Unallocated Endurance Group Capacity
+ * @rsvd192: Reserved
+ */
+struct nvme_endurance_group_log {
+ __u8 critical_warning;
+ __u8 endurance_group_features;
+ __u8 rsvd2;
+ __u8 avl_spare;
+ __u8 avl_spare_threshold;
+ __u8 percent_used;
+ __le16 domain_identifier;
+ __u8 rsvd8[24];
+ __u8 endurance_estimate[16];
+ __u8 data_units_read[16];
+ __u8 data_units_written[16];
+ __u8 media_units_written[16];
+ __u8 host_read_cmds[16];
+ __u8 host_write_cmds[16];
+ __u8 media_data_integrity_err[16];
+ __u8 num_err_info_log_entries[16];
+ __u8 total_end_grp_cap[16];
+ __u8 unalloc_end_grp_cap[16];
+ __u8 rsvd192[320];
+};
+
+/**
+ * enum nvme_eg_critical_warning_flags - Endurance Group Information Log - Critical Warning
+ * @NVME_EG_CRITICAL_WARNING_SPARE: Available spare capacity of the Endurance Group
+ * has fallen below the threshold
+ * @NVME_EG_CRITICAL_WARNING_DEGRADED: Endurance Group reliability has been degraded
+ * @NVME_EG_CRITICAL_WARNING_READ_ONLY: Endurance Group have been placed in read only
+ * mode
+ */
+enum nvme_eg_critical_warning_flags {
+ NVME_EG_CRITICAL_WARNING_SPARE = 1 << 0,
+ NVME_EG_CRITICAL_WARNING_DEGRADED = 1 << 2,
+ NVME_EG_CRITICAL_WARNING_READ_ONLY = 1 << 3,
+};
+
+/**
+ * struct nvme_aggregate_endurance_group_event - Endurance Group Event Aggregate
+ * @num_entries: Number or entries
+ * @entries: List of entries
+ */
+struct nvme_aggregate_endurance_group_event {
+ __le64 num_entries;
+ __le16 entries[];
+};
+
+/**
+ * struct nvme_nvmset_predictable_lat_log - Predictable Latency Mode - Deterministic Threshold Configuration Data
+ * @status: Status
+ * @rsvd1: Reserved
+ * @event_type: Event Type
+ * @rsvd4: Reserved
+ * @dtwin_rt: DTWIN Reads Typical
+ * @dtwin_wt: DTWIN Writes Typical
+ * @dtwin_tmax: DTWIN Time Maximum
+ * @ndwin_tmin_hi: NDWIN Time Minimum High
+ * @ndwin_tmin_lo: NDWIN Time Minimum Low
+ * @rsvd72: Reserved
+ * @dtwin_re: DTWIN Reads Estimate
+ * @dtwin_we: DTWIN Writes Estimate
+ * @dtwin_te: DTWIN Time Estimate
+ * @rsvd152: Reserved
+ */
+struct nvme_nvmset_predictable_lat_log {
+ __u8 status;
+ __u8 rsvd1;
+ __le16 event_type;
+ __u8 rsvd4[28];
+ __le64 dtwin_rt;
+ __le64 dtwin_wt;
+ __le64 dtwin_tmax;
+ __le64 ndwin_tmin_hi;
+ __le64 ndwin_tmin_lo;
+ __u8 rsvd72[56];
+ __le64 dtwin_re;
+ __le64 dtwin_we;
+ __le64 dtwin_te;
+ __u8 rsvd152[360];
+};
+
+/**
+ * enum nvme_nvmeset_pl_status - Predictable Latency Per NVM Set Log - Status
+ * @NVME_NVMSET_PL_STATUS_DISABLED: Not used (Predictable Latency Mode not enabled)
+ * @NVME_NVMSET_PL_STATUS_DTWIN: Deterministic Window (DTWIN)
+ * @NVME_NVMSET_PL_STATUS_NDWIN: Non-Deterministic Window (NDWIN)
+ */
+enum nvme_nvmeset_pl_status {
+ NVME_NVMSET_PL_STATUS_DISABLED = 0,
+ NVME_NVMSET_PL_STATUS_DTWIN = 1,
+ NVME_NVMSET_PL_STATUS_NDWIN = 2,
+};
+
+/**
+ * enum nvme_nvmset_pl_events - Predictable Latency Per NVM Set Log - Event Type
+ * @NVME_NVMSET_PL_EVENT_DTWIN_READ_WARN: DTWIN Reads Warning
+ * @NVME_NVMSET_PL_EVENT_DTWIN_WRITE_WARN: DTWIN Writes Warning
+ * @NVME_NVMSET_PL_EVENT_DTWIN_TIME_WARN: DTWIN Time Warning
+ * @NVME_NVMSET_PL_EVENT_DTWIN_EXCEEDED: Autonomous transition from DTWIN
+ * to NDWIN due to typical or
+ * maximum value exceeded
+ * @NVME_NVMSET_PL_EVENT_DTWIN_EXCURSION: Autonomous transition from DTWIN
+ * to NDWIN due to Deterministic
+ * Excursion
+ */
+enum nvme_nvmset_pl_events {
+ NVME_NVMSET_PL_EVENT_DTWIN_READ_WARN = 1 << 0,
+ NVME_NVMSET_PL_EVENT_DTWIN_WRITE_WARN = 1 << 1,
+ NVME_NVMSET_PL_EVENT_DTWIN_TIME_WARN = 1 << 2,
+ NVME_NVMSET_PL_EVENT_DTWIN_EXCEEDED = 1 << 14,
+ NVME_NVMSET_PL_EVENT_DTWIN_EXCURSION = 1 << 15,
+};
+
+/**
+ * struct nvme_aggregate_predictable_lat_event - Predictable Latency Event Aggregate Log Page
+ * @num_entries: Number of entries
+ * @entries: Entry list
+ */
+struct nvme_aggregate_predictable_lat_event {
+ __le64 num_entries;
+ __le16 entries[];
+};
+
+/**
+ * struct nvme_ana_group_desc - ANA Group Descriptor
+ * @grpid: ANA group id
+ * @nnsids: Number of namespaces in @nsids
+ * @chgcnt: Change counter
+ * @state: ANA state
+ * @rsvd17: Reserved
+ * @nsids: List of namespaces
+ */
+struct nvme_ana_group_desc {
+ __le32 grpid;
+ __le32 nnsids;
+ __le64 chgcnt;
+ __u8 state;
+ __u8 rsvd17[15];
+ __le32 nsids[];
+};
+
+/**
+ * enum nvme_ana_state - ANA Group Descriptor - Asymmetric Namespace Access State
+ * @NVME_ANA_STATE_OPTIMIZED: ANA Optimized state
+ * @NVME_ANA_STATE_NONOPTIMIZED: ANA Non-Optimized state
+ * @NVME_ANA_STATE_INACCESSIBLE: ANA Inaccessible state
+ * @NVME_ANA_STATE_PERSISTENT_LOSS: ANA Persistent Loss state
+ * @NVME_ANA_STATE_CHANGE: ANA Change state
+ */
+enum nvme_ana_state {
+ NVME_ANA_STATE_OPTIMIZED = 0x1,
+ NVME_ANA_STATE_NONOPTIMIZED = 0x2,
+ NVME_ANA_STATE_INACCESSIBLE = 0x3,
+ NVME_ANA_STATE_PERSISTENT_LOSS = 0x4,
+ NVME_ANA_STATE_CHANGE = 0xf,
+};
+
+/**
+ * struct nvme_ana_log - Asymmetric Namespace Access Log
+ * @chgcnt: Change Count
+ * @ngrps: Number of ANA Group Descriptors
+ * @rsvd10: Reserved
+ * @descs: ANA Group Descriptor
+ */
+struct nvme_ana_log {
+ __le64 chgcnt;
+ __le16 ngrps;
+ __u8 rsvd10[6];
+ struct nvme_ana_group_desc descs[];
+};
+
+/**
+ * struct nvme_persistent_event_log - Persistent Event Log
+ * @lid: Log Identifier
+ * @rsvd1: Reserved
+ * @tnev: Total Number of Events
+ * @tll: Total Log Length
+ * @rv: Log Revision
+ * @rsvd17: Reserved
+ * @lhl: Log Header Length
+ * @ts: Timestamp
+ * @poh: Power on Hours
+ * @pcc: Power Cycle Count
+ * @vid: PCI Vendor ID
+ * @ssvid: PCI Subsystem Vendor ID
+ * @sn: Serial Number
+ * @mn: Model Number
+ * @subnqn: NVM Subsystem NVMe Qualified Name
+ * @gen_number: Generation Number
+ * @rci: Reporting Context Information
+ * @rsvd378: Reserved
+ * @seb: Supported Events Bitmap
+ */
+struct nvme_persistent_event_log {
+ __u8 lid;
+ __u8 rsvd1[3];
+ __le32 tnev;
+ __le64 tll;
+ __u8 rv;
+ __u8 rsvd17;
+ __le16 lhl;
+ __le64 ts;
+ __u8 poh[16];
+ __le64 pcc;
+ __le16 vid;
+ __le16 ssvid;
+ char sn[20];
+ char mn[40];
+ char subnqn[NVME_NQN_LENGTH];
+ __le16 gen_number;
+ __le32 rci;
+ __u8 rsvd378[102];
+ __u8 seb[32];
+} __attribute__((packed));
+
+/**
+ * struct nvme_persistent_event_entry - Persistent Event
+ * @etype: Event Type
+ * @etype_rev: Event Type Revision
+ * @ehl: Event Header Length
+ * @ehai: Event Header Additional Info
+ * @cntlid: Controller Identifier
+ * @ets: Event Timestamp
+ * @pelpid: Port Identifier
+ * @rsvd16: Reserved
+ * @vsil: Vendor Specific Information Length
+ * @el: Event Length
+ */
+struct nvme_persistent_event_entry {
+ __u8 etype;
+ __u8 etype_rev;
+ __u8 ehl;
+ __u8 ehai;
+ __le16 cntlid;
+ __le64 ets;
+ __le16 pelpid;
+ __u8 rsvd16[4];
+ __le16 vsil;
+ __le16 el;
+} __attribute__((packed));
+
+/**
+ * enum nvme_persistent_event_types - Persistent event log events
+ * @NVME_PEL_SMART_HEALTH_EVENT: SMART / Health Log Snapshot Event
+ * @NVME_PEL_FW_COMMIT_EVENT: Firmware Commit Event
+ * @NVME_PEL_TIMESTAMP_EVENT: Timestamp Change Event
+ * @NVME_PEL_POWER_ON_RESET_EVENT: Power-on or Reset Event
+ * @NVME_PEL_NSS_HW_ERROR_EVENT: NVM Subsystem Hardware Error Event
+ * @NVME_PEL_CHANGE_NS_EVENT: Change Namespace Event
+ * @NVME_PEL_FORMAT_START_EVENT: Format NVM Start Event
+ * @NVME_PEL_FORMAT_COMPLETION_EVENT: Format NVM Completion Event
+ * @NVME_PEL_SANITIZE_START_EVENT: Sanitize Start Event
+ * @NVME_PEL_SANITIZE_COMPLETION_EVENT: Sanitize Completion Event
+ * @NVME_PEL_SET_FEATURE_EVENT: Set Feature Event
+ * @NVME_PEL_TELEMETRY_CRT: Telemetry Log Create Event
+ * @NVME_PEL_THERMAL_EXCURSION_EVENT: Thermal Excursion Event
+ */
+enum nvme_persistent_event_types {
+ NVME_PEL_SMART_HEALTH_EVENT = 0x01,
+ NVME_PEL_FW_COMMIT_EVENT = 0x02,
+ NVME_PEL_TIMESTAMP_EVENT = 0x03,
+ NVME_PEL_POWER_ON_RESET_EVENT = 0x04,
+ NVME_PEL_NSS_HW_ERROR_EVENT = 0x05,
+ NVME_PEL_CHANGE_NS_EVENT = 0x06,
+ NVME_PEL_FORMAT_START_EVENT = 0x07,
+ NVME_PEL_FORMAT_COMPLETION_EVENT = 0x08,
+ NVME_PEL_SANITIZE_START_EVENT = 0x09,
+ NVME_PEL_SANITIZE_COMPLETION_EVENT = 0x0a,
+ NVME_PEL_SET_FEATURE_EVENT = 0x0b,
+ NVME_PEL_TELEMETRY_CRT = 0x0c,
+ NVME_PEL_THERMAL_EXCURSION_EVENT = 0x0d,
+};
+
+/**
+ * struct nvme_fw_commit_event - Firmware Commit Event Data
+ * @old_fw_rev: Old Firmware Revision
+ * @new_fw_rev: New Firmware Revision
+ * @fw_commit_action: Firmware Commit Action
+ * @fw_slot: Firmware Slot
+ * @sct_fw: Status Code Type for Firmware Commit Command
+ * @sc_fw: Status Returned for Firmware Commit Command
+ * @vndr_assign_fw_commit_rc: Vendor Assigned Firmware Commit Result Code
+ */
+struct nvme_fw_commit_event {
+ __le64 old_fw_rev;
+ __le64 new_fw_rev;
+ __u8 fw_commit_action;
+ __u8 fw_slot;
+ __u8 sct_fw;
+ __u8 sc_fw;
+ __le16 vndr_assign_fw_commit_rc;
+} __attribute__((packed));
+
+/**
+ * struct nvme_timestamp - Timestamp - Data Structure for Get Features
+ * @timestamp: Timestamp value based on origin and synch field
+ * @attr: Attribute
+ * @rsvd: Reserved
+ */
+struct nvme_timestamp {
+ __u8 timestamp[6];
+ __u8 attr;
+ __u8 rsvd;
+};
+
+/**
+ * struct nvme_time_stamp_change_event - Timestamp Change Event
+ * @previous_timestamp: Previous Timestamp
+ * @ml_secs_since_reset: Milliseconds Since Reset
+ */
+struct nvme_time_stamp_change_event {
+ __le64 previous_timestamp;
+ __le64 ml_secs_since_reset;
+};
+
+/**
+ * struct nvme_power_on_reset_info_list - Controller Reset Information
+ * @cid: Controller ID
+ * @fw_act: Firmware Activation
+ * @op_in_prog: Operation in Progress
+ * @rsvd4: Reserved
+ * @ctrl_power_cycle: Controller Power Cycle
+ * @power_on_ml_seconds: Power on milliseconds
+ * @ctrl_time_stamp: Controller Timestamp
+ */
+struct nvme_power_on_reset_info_list {
+ __le16 cid;
+ __u8 fw_act;
+ __u8 op_in_prog;
+ __u8 rsvd4[12];
+ __le32 ctrl_power_cycle;
+ __le64 power_on_ml_seconds;
+ __le64 ctrl_time_stamp;
+} __attribute__((packed));
+
+/**
+ * struct nvme_nss_hw_err_event - NVM Subsystem Hardware Error Event
+ * @nss_hw_err_event_code: NVM Subsystem Hardware Error Event Code
+ * @rsvd2: Reserved
+ * @add_hw_err_info: Additional Hardware Error Information
+ */
+struct nvme_nss_hw_err_event {
+ __le16 nss_hw_err_event_code;
+ __u8 rsvd2[2];
+ __u8 *add_hw_err_info;
+};
+
+/**
+ * struct nvme_change_ns_event - Change Namespace Event Data
+ * @nsmgt_cdw10: Namespace Management CDW10
+ * @rsvd4: Reserved
+ * @nsze: Namespace Size
+ * @rsvd16: Reserved
+ * @nscap: Namespace Capacity
+ * @flbas: Formatted LBA Size
+ * @dps: End-to-end Data Protection Type Settings
+ * @nmic: Namespace Multi-path I/O and Namespace Sharing Capabilities
+ * @rsvd35: Reserved
+ * @ana_grp_id: ANA Group Identifier
+ * @nvmset_id: NVM Set Identifier
+ * @rsvd42: Reserved
+ * @nsid: Namespace ID
+ */
+struct nvme_change_ns_event {
+ __le32 nsmgt_cdw10;
+ __u8 rsvd4[4];
+ __le64 nsze;
+ __u8 rsvd16[8];
+ __le64 nscap;
+ __u8 flbas;
+ __u8 dps;
+ __u8 nmic;
+ __u8 rsvd35;
+ __le32 ana_grp_id;
+ __le16 nvmset_id;
+ __le16 rsvd42;
+ __le32 nsid;
+};
+
+/**
+ * struct nvme_format_nvm_start_event - Format NVM Start Event Data
+ * @nsid: Namespace Identifier
+ * @fna: Format NVM Attributes
+ * @rsvd5: Reserved
+ * @format_nvm_cdw10: Format NVM CDW10
+ */
+struct nvme_format_nvm_start_event {
+ __le32 nsid;
+ __u8 fna;
+ __u8 rsvd5[3];
+ __le32 format_nvm_cdw10;
+};
+
+/**
+ * struct nvme_format_nvm_compln_event - Format NVM Completion Event Data
+ * @nsid: Namespace Identifier
+ * @smallest_fpi: Smallest Format Progress Indicator
+ * @format_nvm_status: Format NVM Status
+ * @compln_info: Completion Information
+ * @status_field: Status Field
+ */
+struct nvme_format_nvm_compln_event {
+ __le32 nsid;
+ __u8 smallest_fpi;
+ __u8 format_nvm_status;
+ __le16 compln_info;
+ __le32 status_field;
+};
+
+/**
+ * struct nvme_sanitize_start_event - Sanitize Start Event Data
+ * @sani_cap: SANICAP
+ * @sani_cdw10: Sanitize CDW10
+ * @sani_cdw11: Sanitize CDW11
+ */
+struct nvme_sanitize_start_event {
+ __le32 sani_cap;
+ __le32 sani_cdw10;
+ __le32 sani_cdw11;
+};
+
+/**
+ * struct nvme_sanitize_compln_event - Sanitize Completion Event Data
+ * @sani_prog: Sanitize Progress
+ * @sani_status: Sanitize Status
+ * @cmpln_info: Completion Information
+ * @rsvd6: Reserved
+ */
+struct nvme_sanitize_compln_event {
+ __le16 sani_prog;
+ __le16 sani_status;
+ __le16 cmpln_info;
+ __u8 rsvd6[2];
+};
+
+/**
+ * struct nvme_set_feature_event - Set Feature Event Data
+ * @layout: Set Feature Event Layout
+ * @cdw_mem: Command Dwords Memory buffer
+ */
+struct nvme_set_feature_event {
+ __le32 layout;
+ __le32 cdw_mem[0];
+};
+
+/**
+ * struct nvme_thermal_exc_event - Thermal Excursion Event Data
+ * @over_temp: Over Temperature
+ * @threshold: temperature threshold
+ */
+struct nvme_thermal_exc_event {
+ __u8 over_temp;
+ __u8 threshold;
+};
+
+/**
+ * struct nvme_lba_rd - LBA Range Descriptor
+ * @rslba: Range Starting LBA
+ * @rnlb: Range Number of Logical Blocks
+ * @rsvd12: Reserved
+ */
+struct nvme_lba_rd {
+ __le64 rslba;
+ __le32 rnlb;
+ __u8 rsvd12[4];
+};
+
+/**
+ * struct nvme_lbas_ns_element - LBA Status Log Namespace Element
+ * @neid: Namespace Element Identifier
+ * @nlrd: Number of LBA Range Descriptors
+ * @ratype: Recommended Action Type. see @enum nvme_lba_status_atype
+ * @rsvd8: Reserved
+ * @lba_rd: LBA Range Descriptor
+ */
+struct nvme_lbas_ns_element {
+ __le32 neid;
+ __le32 nlrd;
+ __u8 ratype;
+ __u8 rsvd8[7];
+ struct nvme_lba_rd lba_rd[];
+};
+
+/**
+ * enum nvme_lba_status_atype - Potentially Unrecoverable LBAs
+ * @NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED: Potentially Unrecoverable LBAs
+ * @NVME_LBA_STATUS_ATYPE_SCAN_TRACKED: Potentially Unrecoverable LBAs
+ * associated with physical storage
+ */
+enum nvme_lba_status_atype {
+ NVME_LBA_STATUS_ATYPE_SCAN_UNTRACKED = 0x10,
+ NVME_LBA_STATUS_ATYPE_SCAN_TRACKED = 0x11,
+};
+
+/**
+ * struct nvme_lba_status_log - LBA Status Information Log
+ * @lslplen: LBA Status Log Page Length
+ * @nlslne: Number of LBA Status Log Namespace Elements
+ * @estulb: Estimate of Unrecoverable Logical Blocks
+ * @rsvd12: Reserved
+ * @lsgc: LBA Status Generation Counter
+ * @elements: LBA Status Log Namespace Element List
+ */
+struct nvme_lba_status_log {
+ __le32 lslplen;
+ __le32 nlslne;
+ __le32 estulb;
+ __u8 rsvd12[2];
+ __le16 lsgc;
+ struct nvme_lbas_ns_element elements[];
+};
+
+/**
+ * struct nvme_eg_event_aggregate_log - Endurance Group Event Aggregate
+ * @nr_entries: Number of Entries
+ * @egids: Endurance Group Identifier
+ */
+struct nvme_eg_event_aggregate_log {
+ __le64 nr_entries;
+ __le16 egids[];
+};
+
+/**
+ * enum nvme_fid_supported_effects - FID Supported and Effects Data Structure definitions
+ * @NVME_FID_SUPPORTED_EFFECTS_FSUPP: FID Supported
+ * @NVME_FID_SUPPORTED_EFFECTS_UDCC: User Data Content Change
+ * @NVME_FID_SUPPORTED_EFFECTS_NCC: Namespace Capability Change
+ * @NVME_FID_SUPPORTED_EFFECTS_NIC: Namespace Inventory Change
+ * @NVME_FID_SUPPORTED_EFFECTS_CCC: Controller Capability Change
+ * @NVME_FID_SUPPORTED_EFFECTS_UUID_SEL: UUID Selection Supported
+ * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_SHIFT: FID Scope Shift
+ * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_MASK: FID Scope Mask
+ * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_NS: Namespace Scope
+ * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_CTRL: Controller Scope
+ * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_NVM_SET: NVM Set Scope
+ * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_ENDGRP: Endurance Group Scope
+ * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_DOMAIN: Domain Scope
+ * @NVME_FID_SUPPORTED_EFFECTS_SCOPE_NSS: NVM Subsystem Scope
+ */
+enum nvme_fid_supported_effects {
+ NVME_FID_SUPPORTED_EFFECTS_FSUPP = 1 << 0,
+ NVME_FID_SUPPORTED_EFFECTS_UDCC = 1 << 1,
+ NVME_FID_SUPPORTED_EFFECTS_NCC = 1 << 2,
+ NVME_FID_SUPPORTED_EFFECTS_NIC = 1 << 3,
+ NVME_FID_SUPPORTED_EFFECTS_CCC = 1 << 4,
+ NVME_FID_SUPPORTED_EFFECTS_UUID_SEL = 1 << 19,
+ NVME_FID_SUPPORTED_EFFECTS_SCOPE_SHIFT = 20,
+ NVME_FID_SUPPORTED_EFFECTS_SCOPE_MASK = 0xfff,
+ NVME_FID_SUPPORTED_EFFECTS_SCOPE_NS = 1 << 0,
+ NVME_FID_SUPPORTED_EFFECTS_SCOPE_CTRL = 1 << 1,
+ NVME_FID_SUPPORTED_EFFECTS_SCOPE_NVM_SET= 1 << 2,
+ NVME_FID_SUPPORTED_EFFECTS_SCOPE_ENDGRP = 1 << 3,
+ NVME_FID_SUPPORTED_EFFECTS_SCOPE_DOMAIN = 1 << 4,
+ NVME_FID_SUPPORTED_EFFECTS_SCOPE_NSS = 1 << 5,
+};
+
+/**
+ * struct nvme_fid_supported_effects_log - Feature Identifiers Supported and Effects
+ * @fid_support: Feature Identifier Supported
+ *
+ */
+struct nvme_fid_supported_effects_log {
+ __le32 fid_support[NVME_LOG_FID_SUPPORTED_EFFECTS_MAX];
+};
+
+/**
+ * enum nvme_mi_cmd_supported_effects - MI Command Supported and Effects Data Structure
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_CSUPP: Command Supported
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_UDCC: User Data Content Change
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_NCC: Namespace Capability Change
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_NIC: Namespace Inventory Change
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_CCC: Controller Capability Change
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_SHIFT: 20 bit shift
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_MASK: 12 bit mask - 0xfff
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NS: Namespace Scope
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_CTRL: Controller Scope
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NVM_SET: NVM Set Scope
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_ENDGRP: Endurance Group Scope
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_DOMAIN: Domain Scope
+ * @NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NSS: NVM Subsystem Scope
+ */
+enum nvme_mi_cmd_supported_effects {
+ NVME_MI_CMD_SUPPORTED_EFFECTS_CSUPP = 1 << 0,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_UDCC = 1 << 1,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_NCC = 1 << 2,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_NIC = 1 << 3,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_CCC = 1 << 4,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_SHIFT = 20,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_MASK = 0xfff,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NS = 1 << 0,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_CTRL = 1 << 1,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NVM_SET = 1 << 2,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_ENDGRP = 1 << 3,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_DOMAIN = 1 << 4,
+ NVME_MI_CMD_SUPPORTED_EFFECTS_SCOPE_NSS = 1 << 5,
+};
+
+/**
+ * struct nvme_mi_cmd_supported_effects_log - NVMe-MI Commands Supported and Effects Log
+ * @mi_cmd_support: NVMe-MI Commands Supported
+ * @reserved1: Reserved
+ */
+struct nvme_mi_cmd_supported_effects_log {
+ __le32 mi_cmd_support[NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_MAX];
+ __le32 reserved1[NVME_LOG_MI_CMD_SUPPORTED_EFFECTS_RESERVED];
+};
+
+/**
+ * struct nvme_boot_partition - Boot Partition Log
+ * @lid: Boot Partition Identifier
+ * @rsvd1: Reserved
+ * @bpinfo: Boot Partition Information
+ * @rsvd8: Reserved
+ * @boot_partition_data: Contains the contents of the
+ * specified Boot Partition
+ */
+struct nvme_boot_partition {
+ __u8 lid;
+ __u8 rsvd1[3];
+ __le32 bpinfo;
+ __u8 rsvd8[8];
+ __u8 boot_partition_data[];
+};
+
+/**
+ * struct nvme_eom_lane_desc - EOM Lane Descriptor
+ * @rsvd0: Reserved
+ * @mstatus: Measurement Status
+ * @lane: Lane number
+ * @eye: Eye number
+ * @top: Absolute number of rows from center to top edge of eye
+ * @bottom: Absolute number of rows from center to bottom edge of eye
+ * @left: Absolute number of rows from center to left edge of eye
+ * @right: Absolute number of rows from center to right edge of eye
+ * @nrows: Number of Rows
+ * @ncols: Number of Columns
+ * @edlen: Eye Data Length
+ * @rsvd18: Reserved
+ * @eye_desc: Printable Eye, Eye Data, and any Padding
+ */
+struct nvme_eom_lane_desc {
+ __u8 rsvd0;
+ __u8 mstatus;
+ __u8 lane;
+ __u8 eye;
+ __le16 top;
+ __le16 bottom;
+ __le16 left;
+ __le16 right;
+ __le16 nrows;
+ __le16 ncols;
+ __le16 edlen;
+ __u8 rsvd18[14];
+ __u8 eye_desc[];
+};
+
+/**
+ * struct nvme_phy_rx_eom_log - Physical Interface Receiver Eye Opening Measurement Log
+ * @lid: Log Identifier
+ * @eomip: EOM In Progress
+ * @hsize: Header Size
+ * @rsize: Result Size
+ * @eomdgn: EOM Data Generation Number
+ * @lr: Log Revision
+ * @odp: Optional Data Present
+ * @lanes: Number of lanes configured for this port
+ * @epl: Eyes Per Lane
+ * @lspfc: Log Specific Parameter Field Copy
+ * @li: Link Information
+ * @rsvd15: Reserved
+ * @lsic: Log Specific Identifier Copy
+ * @dsize: Descriptor Size
+ * @nd: Number of Descriptors
+ * @maxtb: Maximum Top Bottom
+ * @maxlr: Maximum Left Right
+ * @etgood: Estimated Time for Good Quality
+ * @etbetter: Estimated Time for Better Quality
+ * @etbest: Estimated Time for Best Quality
+ * @rsvd36: Reserved
+ * @descs: EOM Lane Descriptors
+ */
+struct nvme_phy_rx_eom_log {
+ __u8 lid;
+ __u8 eomip;
+ __le16 hsize;
+ __le32 rsize;
+ __u8 eomdgn;
+ __u8 lr;
+ __u8 odp;
+ __u8 lanes;
+ __u8 epl;
+ __u8 lspfc;
+ __u8 li;
+ __u8 rsvd15[3];
+ __le16 lsic;
+ __le32 dsize;
+ __le16 nd;
+ __le16 maxtb;
+ __le16 maxlr;
+ __le16 etgood;
+ __le16 etbetter;
+ __le16 etbest;
+ __u8 rsvd36[28];
+ struct nvme_eom_lane_desc descs[];
+};
+
+/**
+ * enum nvme_eom_optional_data - EOM Optional Data Present Fields
+ * @NVME_EOM_EYE_DATA_PRESENT: Eye Data Present
+ * @NVME_EOM_PRINTABLE_EYE_PRESENT: Printable Eye Present
+ */
+enum nvme_eom_optional_data {
+ NVME_EOM_EYE_DATA_PRESENT = 1,
+ NVME_EOM_PRINTABLE_EYE_PRESENT = 1 << 1,
+};
+
+/**
+ * enum nvme_phy_rx_eom_progress - EOM In Progress Values
+ * @NVME_PHY_RX_EOM_NOT_STARTED: EOM Not Started
+ * @NVME_PHY_RX_EOM_IN_PROGRESS: EOM In Progress
+ * @NVME_PHY_RX_EOM_COMPLETED: EOM Completed
+ */
+enum nvme_phy_rx_eom_progress {
+ NVME_PHY_RX_EOM_NOT_STARTED = 0,
+ NVME_PHY_RX_EOM_IN_PROGRESS = 1,
+ NVME_PHY_RX_EOM_COMPLETED = 2,
+};
+
+/**
+ * struct nvme_media_unit_stat_desc - Media Unit Status Descriptor
+ * @muid: Media Unit Identifier
+ * @domainid: Domain Identifier
+ * @endgid: Endurance Group Identifier
+ * @nvmsetid: NVM Set Identifier
+ * @cap_adj_fctr: Capacity Adjustment Factor
+ * @avl_spare: Available Spare
+ * @percent_used: Percentage Used
+ * @mucs: Number of Channels attached to media units
+ * @cio: Channel Identifiers Offset
+ */
+struct nvme_media_unit_stat_desc {
+ __le16 muid;
+ __le16 domainid;
+ __le16 endgid;
+ __le16 nvmsetid;
+ __le16 cap_adj_fctr;
+ __u8 avl_spare;
+ __u8 percent_used;
+ __u8 mucs;
+ __u8 cio;
+};
+
+/**
+ * struct nvme_media_unit_stat_log - Media Unit Status
+ * @nmu: Number unit status descriptor
+ * @cchans: Number of Channels
+ * @sel_config: Selected Configuration
+ * @rsvd6: Reserved
+ * @mus_desc: Media unit statistic descriptors
+ */
+struct nvme_media_unit_stat_log {
+ __le16 nmu;
+ __le16 cchans;
+ __le16 sel_config;
+ __u8 rsvd6[10];
+ struct nvme_media_unit_stat_desc mus_desc[];
+};
+
+/**
+ * struct nvme_media_unit_config_desc - Media Unit Configuration Descriptor
+ * @muid: Media Unit Identifier
+ * @rsvd2: Reserved
+ * @mudl: Media Unit Descriptor Length
+ */
+struct nvme_media_unit_config_desc {
+ __le16 muid;
+ __u8 rsvd2[4];
+ __le16 mudl;
+};
+
+/**
+ * struct nvme_channel_config_desc - Channel Configuration Descriptor
+ * @chanid: Channel Identifier
+ * @chmus: Number Channel Media Units
+ * @mu_config_desc: Channel Unit config descriptors.
+ * See @struct nvme_media_unit_config_desc
+ */
+struct nvme_channel_config_desc {
+ __le16 chanid;
+ __le16 chmus;
+ struct nvme_media_unit_config_desc mu_config_desc[];
+};
+
+/**
+ * struct nvme_end_grp_chan_desc - Endurance Group Channel Configuration Descriptor
+ * @egchans: Number of Channels
+ * @chan_config_desc: Channel config descriptors.
+ * See @struct nvme_channel_config_desc
+ */
+struct nvme_end_grp_chan_desc {
+ __le16 egchans;
+ struct nvme_channel_config_desc chan_config_desc[];
+};
+
+/**
+ * struct nvme_end_grp_config_desc - Endurance Group Configuration Descriptor
+ * @endgid: Endurance Group Identifier
+ * @cap_adj_factor: Capacity Adjustment Factor
+ * @rsvd4: Reserved
+ * @tegcap: Total Endurance Group Capacity
+ * @segcap: Spare Endurance Group Capacity
+ * @end_est: Endurance Estimate
+ * @egsets: Number of NVM Sets
+ * @rsvd64: Reserved
+ * @nvmsetid: NVM Set Identifier
+ */
+struct nvme_end_grp_config_desc {
+ __le16 endgid;
+ __le16 cap_adj_factor;
+ __u8 rsvd4[12];
+ __u8 tegcap[16];
+ __u8 segcap[16];
+ __u8 end_est[16];
+ __u8 rsvd64[16];
+ __le16 egsets;
+ __le16 nvmsetid[];
+};
+
+/**
+ * struct nvme_capacity_config_desc - Capacity Configuration structure definitions
+ * @cap_config_id: Capacity Configuration Identifier
+ * @domainid: Domain Identifier
+ * @egcn: Number Endurance Group Configuration
+ * Descriptors
+ * @rsvd6: Reserved
+ * @egcd: Endurance Group Config descriptors.
+ * See @struct nvme_end_grp_config_desc
+ */
+struct nvme_capacity_config_desc {
+ __le16 cap_config_id;
+ __le16 domainid;
+ __le16 egcn;
+ __u8 rsvd6[26];
+ struct nvme_end_grp_config_desc egcd[];
+};
+
+/**
+ * struct nvme_supported_cap_config_list_log - Supported Capacity Configuration list log page
+ * @sccn: Number of capacity configuration
+ * @rsvd1: Reserved
+ * @cap_config_desc: Capacity configuration descriptor.
+ * See @struct nvme_capacity_config_desc
+ */
+struct nvme_supported_cap_config_list_log {
+ __u8 sccn;
+ __u8 rsvd1[15];
+ struct nvme_capacity_config_desc cap_config_desc[];
+};
+
+/**
+ * struct nvme_resv_notification_log - Reservation Notification Log
+ * @lpc: Log Page Count
+ * @rnlpt: See &enum nvme_resv_notify_rnlpt.
+ * @nalp: Number of Available Log Pages
+ * @rsvd9: Reserved
+ * @nsid: Namespace ID
+ * @rsvd16: Reserved
+ */
+struct nvme_resv_notification_log {
+ __le64 lpc;
+ __u8 rnlpt;
+ __u8 nalp;
+ __u8 rsvd9[2];
+ __le32 nsid;
+ __u8 rsvd16[48];
+};
+
+/**
+ * enum nvme_resv_notify_rnlpt - Reservation Notification Log - Reservation Notification Log Page Type
+ * @NVME_RESV_NOTIFY_RNLPT_EMPTY: Empty Log Page
+ * @NVME_RESV_NOTIFY_RNLPT_REGISTRATION_PREEMPTED: Registration Preempted
+ * @NVME_RESV_NOTIFY_RNLPT_RESERVATION_RELEASED: Reservation Released
+ * @NVME_RESV_NOTIFY_RNLPT_RESERVATION_PREEMPTED: Reservation Preempted
+ */
+enum nvme_resv_notify_rnlpt {
+ NVME_RESV_NOTIFY_RNLPT_EMPTY = 0,
+ NVME_RESV_NOTIFY_RNLPT_REGISTRATION_PREEMPTED = 1,
+ NVME_RESV_NOTIFY_RNLPT_RESERVATION_RELEASED = 2,
+ NVME_RESV_NOTIFY_RNLPT_RESERVATION_PREEMPTED = 3,
+};
+
+/**
+ * struct nvme_sanitize_log_page - Sanitize Status (Log Identifier 81h)
+ * @sprog: Sanitize Progress (SPROG): indicates the fraction complete of the
+ * sanitize operation. The value is a numerator of the fraction
+ * complete that has 65,536 (10000h) as its denominator. This value
+ * shall be set to FFFFh if the @sstat field is not set to
+ * %NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS.
+ * @sstat: Sanitize Status (SSTAT): indicates the status associated with
+ * the most recent sanitize operation. See &enum nvme_sanitize_sstat.
+ * @scdw10: Sanitize Command Dword 10 Information (SCDW10): contains the value
+ * of the Command Dword 10 field of the Sanitize command that started
+ * the sanitize operation.
+ * @eto: Estimated Time For Overwrite: indicates the number of seconds required
+ * to complete an Overwrite sanitize operation with 16 passes in
+ * the background when the No-Deallocate Modifies Media After Sanitize
+ * field is not set to 10b. A value of 0h indicates that the sanitize
+ * operation is expected to be completed in the background when the
+ * Sanitize command that started that operation is completed. A value
+ * of FFFFFFFFh indicates that no time period is reported.
+ * @etbe: Estimated Time For Block Erase: indicates the number of seconds
+ * required to complete a Block Erase sanitize operation in the
+ * background when the No-Deallocate Modifies Media After Sanitize
+ * field is not set to 10b. A value of 0h indicates that the sanitize
+ * operation is expected to be completed in the background when the
+ * Sanitize command that started that operation is completed.
+ * A value of FFFFFFFFh indicates that no time period is reported.
+ * @etce: Estimated Time For Crypto Erase: indicates the number of seconds
+ * required to complete a Crypto Erase sanitize operation in the
+ * background when the No-Deallocate Modifies Media After Sanitize
+ * field is not set to 10b. A value of 0h indicates that the sanitize
+ * operation is expected to be completed in the background when the
+ * Sanitize command that started that operation is completed.
+ * A value of FFFFFFFFh indicates that no time period is reported.
+ * @etond: Estimated Time For Overwrite With No-Deallocate Media Modification:
+ * indicates the number of seconds required to complete an Overwrite
+ * sanitize operation and the associated additional media modification
+ * after the Overwrite sanitize operation in the background when
+ * the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+ * command that requested the Overwrite sanitize operation; and
+ * the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+ * A value of 0h indicates that the sanitize operation is expected
+ * to be completed in the background when the Sanitize command that
+ * started that operation is completed. A value of FFFFFFFFh indicates
+ * that no time period is reported.
+ * @etbend: Estimated Time For Block Erase With No-Deallocate Media Modification:
+ * indicates the number of seconds required to complete a Block Erase
+ * sanitize operation and the associated additional media modification
+ * after the Block Erase sanitize operation in the background when
+ * the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+ * command that requested the Overwrite sanitize operation; and
+ * the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+ * A value of 0h indicates that the sanitize operation is expected
+ * to be completed in the background when the Sanitize command that
+ * started that operation is completed. A value of FFFFFFFFh indicates
+ * that no time period is reported.
+ * @etcend: Estimated Time For Crypto Erase With No-Deallocate Media Modification:
+ * indicates the number of seconds required to complete a Crypto Erase
+ * sanitize operation and the associated additional media modification
+ * after the Crypto Erase sanitize operation in the background when
+ * the No-Deallocate After Sanitize bit was set to 1 in the Sanitize
+ * command that requested the Overwrite sanitize operation; and
+ * the No-Deallocate Modifies Media After Sanitize field is set to 10b.
+ * A value of 0h indicates that the sanitize operation is expected
+ * to be completed in the background when the Sanitize command that
+ * started that operation is completed. A value of FFFFFFFFh indicates
+ * that no time period is reported.
+ * @rsvd32: Reserved
+ */
+struct nvme_sanitize_log_page {
+ __le16 sprog;
+ __le16 sstat;
+ __le32 scdw10;
+ __le32 eto;
+ __le32 etbe;
+ __le32 etce;
+ __le32 etond;
+ __le32 etbend;
+ __le32 etcend;
+ __u8 rsvd32[480];
+};
+
+/**
+ * enum nvme_sanitize_sstat - Sanitize Status (SSTAT)
+ * @NVME_SANITIZE_SSTAT_STATUS_SHIFT: Shift amount to get the status value of
+ * the most recent sanitize operation from
+ * the &struct nvme_sanitize_log_page.sstat
+ * field.
+ * @NVME_SANITIZE_SSTAT_STATUS_MASK: Mask to get the status value of the most
+ * recent sanitize operation.
+ * @NVME_SANITIZE_SSTAT_STATUS_NEVER_SANITIZED: The NVM subsystem has never been
+ * sanitized.
+ * @NVME_SANITIZE_SSTAT_STATUS_COMPLETE_SUCCESS: The most recent sanitize operation
+ * completed successfully including any
+ * additional media modification.
+ * @NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS: A sanitize operation is currently in progress.
+ * @NVME_SANITIZE_SSTAT_STATUS_COMPLETED_FAILED: The most recent sanitize operation
+ * failed.
+ * @NVME_SANITIZE_SSTAT_STATUS_ND_COMPLETE_SUCCESS: The most recent sanitize operation
+ * for which No-Deallocate After Sanitize was
+ * requested has completed successfully with
+ * deallocation of all user data.
+ * @NVME_SANITIZE_SSTAT_COMPLETED_PASSES_SHIFT: Shift amount to get the number
+ * of completed passes if the most recent
+ * sanitize operation was an Overwrite. This
+ * value shall be cleared to 0h if the most
+ * recent sanitize operation was not
+ * an Overwrite.
+ * @NVME_SANITIZE_SSTAT_COMPLETED_PASSES_MASK: Mask to get the number of completed
+ * passes.
+ * @NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT: Shift amount to get the Global
+ * Data Erased value from the
+ * &struct nvme_sanitize_log_page.sstat field.
+ * @NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK: Mask to get the Global Data Erased
+ * value.
+ * @NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED: Global Data Erased: if set, then no
+ * namespace user data in the NVM subsystem
+ * has been written to and no Persistent
+ * Memory Region in the NVM subsystem has
+ * been enabled since being manufactured and
+ * the NVM subsystem has never been sanitized;
+ * or since the most recent successful sanitize
+ * operation.
+ */
+enum nvme_sanitize_sstat {
+ NVME_SANITIZE_SSTAT_STATUS_SHIFT = 0,
+ NVME_SANITIZE_SSTAT_STATUS_MASK = 0x7,
+ NVME_SANITIZE_SSTAT_STATUS_NEVER_SANITIZED = 0,
+ NVME_SANITIZE_SSTAT_STATUS_COMPLETE_SUCCESS = 1,
+ NVME_SANITIZE_SSTAT_STATUS_IN_PROGESS = 2,
+ NVME_SANITIZE_SSTAT_STATUS_COMPLETED_FAILED = 3,
+ NVME_SANITIZE_SSTAT_STATUS_ND_COMPLETE_SUCCESS = 4,
+ NVME_SANITIZE_SSTAT_COMPLETED_PASSES_SHIFT = 3,
+ NVME_SANITIZE_SSTAT_COMPLETED_PASSES_MASK = 0x1f,
+ NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT = 8,
+ NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK = 0x1,
+ NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED = 1 << NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT,
+};
+
+/**
+ * struct nvme_zns_changed_zone_log - ZNS Changed Zone List log
+ * @nrzid: Number of Zone Identifiers
+ * @rsvd2: Reserved
+ * @zid: Zone Identifier
+ */
+struct nvme_zns_changed_zone_log {
+ __le16 nrzid;
+ __u8 rsvd2[6];
+ __le64 zid[NVME_ZNS_CHANGED_ZONES_MAX];
+};
+
+/**
+ * enum nvme_zns_zt - Zone Descriptor Data Structure - Zone Type
+ * @NVME_ZONE_TYPE_SEQWRITE_REQ: Sequential Write Required
+ */
+enum nvme_zns_zt {
+ NVME_ZONE_TYPE_SEQWRITE_REQ = 0x2,
+};
+
+/**
+ * enum nvme_zns_za - Zone Descriptor Data Structure
+ * @NVME_ZNS_ZA_ZFC: Zone Finished by Controller
+ * @NVME_ZNS_ZA_FZR: Finish Zone Recommended
+ * @NVME_ZNS_ZA_RZR: Reset Zone Recommended
+ * @NVME_ZNS_ZA_ZRWAV:
+ * @NVME_ZNS_ZA_ZDEV: Zone Descriptor Extension Valid
+ */
+enum nvme_zns_za {
+ NVME_ZNS_ZA_ZFC = 1 << 0,
+ NVME_ZNS_ZA_FZR = 1 << 1,
+ NVME_ZNS_ZA_RZR = 1 << 2,
+ NVME_ZNS_ZA_ZRWAV = 1 << 3,
+ NVME_ZNS_ZA_ZDEV = 1 << 7,
+};
+
+/**
+ * enum nvme_zns_zs - Zone Descriptor Data Structure - Zone State
+ * @NVME_ZNS_ZS_EMPTY: Empty state
+ * @NVME_ZNS_ZS_IMPL_OPEN: Implicitly open state
+ * @NVME_ZNS_ZS_EXPL_OPEN: Explicitly open state
+ * @NVME_ZNS_ZS_CLOSED: Closed state
+ * @NVME_ZNS_ZS_READ_ONLY: Read only state
+ * @NVME_ZNS_ZS_FULL: Full state
+ * @NVME_ZNS_ZS_OFFLINE: Offline state
+ */
+enum nvme_zns_zs {
+ NVME_ZNS_ZS_EMPTY = 0x1,
+ NVME_ZNS_ZS_IMPL_OPEN = 0x2,
+ NVME_ZNS_ZS_EXPL_OPEN = 0x3,
+ NVME_ZNS_ZS_CLOSED = 0x4,
+ NVME_ZNS_ZS_READ_ONLY = 0xd,
+ NVME_ZNS_ZS_FULL = 0xe,
+ NVME_ZNS_ZS_OFFLINE = 0xf,
+};
+
+/**
+ * struct nvme_zns_desc - Zone Descriptor Data Structure
+ * @zt: Zone Type
+ * @zs: Zone State
+ * @za: Zone Attributes
+ * @zai: Zone Attributes Information
+ * @rsvd4: Reserved
+ * @zcap: Zone Capacity
+ * @zslba: Zone Start Logical Block Address
+ * @wp: Write Pointer
+ * @rsvd32: Reserved
+ */
+struct nvme_zns_desc {
+ __u8 zt;
+ __u8 zs;
+ __u8 za;
+ __u8 zai;
+ __u8 rsvd4[4];
+ __le64 zcap;
+ __le64 zslba;
+ __le64 wp;
+ __u8 rsvd32[32];
+};
+
+/**
+ * struct nvme_zone_report - Report Zones Data Structure
+ * @nr_zones: Number of descriptors in @entries
+ * @rsvd8: Reserved
+ * @entries: Zoned namespace descriptors
+ */
+struct nvme_zone_report {
+ __le64 nr_zones;
+ __u8 rsvd8[56];
+ struct nvme_zns_desc entries[];
+};
+
+/**
+ * enum nvme_fdp_ruh_type - Reclaim Unit Handle Type
+ * @NVME_FDP_RUHT_INITIALLY_ISOLATED: Initially Isolated
+ * @NVME_FDP_RUHT_PERSISTENTLY_ISOLATED: Persistently Isolated
+ */
+enum nvme_fdp_ruh_type {
+ NVME_FDP_RUHT_INITIALLY_ISOLATED = 1,
+ NVME_FDP_RUHT_PERSISTENTLY_ISOLATED = 2,
+};
+
+/**
+ * struct nvme_fdp_ruh_desc - Reclaim Unit Handle Descriptor
+ * @ruht: Reclaim Unit Handle Type
+ * @rsvd1: Reserved
+ */
+struct nvme_fdp_ruh_desc {
+ __u8 ruht;
+ __u8 rsvd1[3];
+};
+
+/**
+ * enum nvme_fdp_config_fdpa - FDP Attributes
+ * @NVME_FDP_CONFIG_FDPA_RGIF_SHIFT: Reclaim Group Identifier Format Shift
+ * @NVME_FDP_CONFIG_FDPA_RGIF_MASK: Reclaim Group Identifier Format Mask
+ * @NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT: FDP Volatile Write Cache Shift
+ * @NVME_FDP_CONFIG_FDPA_FDPVWC_MASK: FDP Volatile Write Cache Mask
+ * @NVME_FDP_CONFIG_FDPA_VALID_SHIFT: FDP Configuration Valid Shift
+ * @NVME_FDP_CONFIG_FDPA_VALID_MASK: FDP Configuration Valid Mask
+ */
+enum nvme_fdp_config_fdpa {
+ NVME_FDP_CONFIG_FDPA_RGIF_SHIFT = 0,
+ NVME_FDP_CONFIG_FDPA_RGIF_MASK = 0xf,
+ NVME_FDP_CONFIG_FDPA_FDPVWC_SHIFT = 4,
+ NVME_FDP_CONFIG_FDPA_FDPVWC_MASK = 0x1,
+ NVME_FDP_CONFIG_FDPA_VALID_SHIFT = 7,
+ NVME_FDP_CONFIG_FDPA_VALID_MASK = 0x1,
+};
+
+/**
+ * struct nvme_fdp_config_desc - FDP Configuration Descriptor
+ * @size: Descriptor size
+ * @fdpa: FDP Attributes (&enum nvme_fdp_config_fdpa)
+ * @vss: Vendor Specific Size
+ * @nrg: Number of Reclaim Groups
+ * @nruh: Number of Reclaim Unit Handles
+ * @maxpids: Max Placement Identifiers
+ * @nnss: Number of Namespaces Supported
+ * @runs: Reclaim Unit Nominal Size
+ * @erutl: Estimated Reclaim Unit Time Limit
+ * @rsvd28: Reserved
+ * @ruhs: Reclaim Unit Handle descriptors (&struct nvme_fdp_ruh_desc)
+ */
+struct nvme_fdp_config_desc {
+ __le16 size;
+ __u8 fdpa;
+ __u8 vss;
+ __le32 nrg;
+ __le16 nruh;
+ __le16 maxpids;
+ __le32 nnss;
+ __le64 runs;
+ __le32 erutl;
+ __u8 rsvd28[36];
+ struct nvme_fdp_ruh_desc ruhs[];
+};
+
+/**
+ * struct nvme_fdp_config_log - FDP Configurations Log Page
+ * @n: Number of FDP Configurations
+ * @version: Log page version
+ * @rsvd3: Reserved
+ * @size: Log page size in bytes
+ * @rsvd8: Reserved
+ * @configs: FDP Configuration descriptors (&struct nvme_fdp_config_desc)
+ */
+struct nvme_fdp_config_log {
+ __le16 n;
+ __u8 version;
+ __u8 rsvd3;
+ __le32 size;
+ __u8 rsvd8[8];
+ struct nvme_fdp_config_desc configs[];
+};
+
+/**
+ * enum nvme_fdp_ruha - Reclaim Unit Handle Attributes
+ * @NVME_FDP_RUHA_HOST_SHIFT: Host Specified Reclaim Unit Handle Shift
+ * @NVME_FDP_RUHA_HOST_MASK: Host Specified Reclaim Unit Handle Mask
+ * @NVME_FDP_RUHA_CTRL_SHIFT: Controller Specified Reclaim Unit Handle Shift
+ * @NVME_FDP_RUHA_CTRL_MASK: Controller Specified Reclaim Unit Handle Mask
+ */
+enum nvme_fdp_ruha {
+ NVME_FDP_RUHA_HOST_SHIFT = 0,
+ NVME_FDP_RUHA_HOST_MASK = 0x1,
+ NVME_FDP_RUHA_CTRL_SHIFT = 1,
+ NVME_FDP_RUHA_CTRL_MASK = 0x1,
+};
+
+/**
+ * struct nvme_fdp_ruhu_desc - Reclaim Unit Handle Usage Descriptor
+ * @ruha: Reclaim Unit Handle Attributes (&enum nvme_fdp_ruha)
+ * @rsvd1: Reserved
+ */
+struct nvme_fdp_ruhu_desc {
+ __u8 ruha;
+ __u8 rsvd1[7];
+};
+
+/**
+ * struct nvme_fdp_ruhu_log - Reclaim Unit Handle Usage Log Page
+ * @nruh: Number of Reclaim Unit Handles
+ * @rsvd2: Reserved
+ * @ruhus: Reclaim Unit Handle Usage descriptors
+ */
+struct nvme_fdp_ruhu_log {
+ __le16 nruh;
+ __u8 rsvd2[6];
+ struct nvme_fdp_ruhu_desc ruhus[];
+};
+
+/**
+ * struct nvme_fdp_stats_log - FDP Statistics Log Page
+ * @hbmw: Host Bytes with Metadata Written
+ * @mbmw: Media Bytes with Metadata Written
+ * @mbe: Media Bytes Erased
+ * @rsvd48: Reserved
+ */
+struct nvme_fdp_stats_log {
+ __u8 hbmw[16];
+ __u8 mbmw[16];
+ __u8 mbe[16];
+ __u8 rsvd48[16];
+};
+
+/**
+ * enum nvme_fdp_event_type - FDP Event Types
+ * @NVME_FDP_EVENT_RUNFW: Reclaim Unit Not Fully Written
+ * @NVME_FDP_EVENT_RUTLE: Reclaim Unit Time Limit Exceeded
+ * @NVME_FDP_EVENT_RESET: Controller Level Reset Modified Reclaim Unit Handles
+ * @NVME_FDP_EVENT_PID: Invalid Placement Identifier
+ * @NVME_FDP_EVENT_REALLOC: Media Reallocated
+ * @NVME_FDP_EVENT_MODIFY: Implicitly Modified Reclaim Unit Handle
+ */
+enum nvme_fdp_event_type {
+ /* Host Events */
+ NVME_FDP_EVENT_RUNFW = 0x0,
+ NVME_FDP_EVENT_RUTLE = 0x1,
+ NVME_FDP_EVENT_RESET = 0x2,
+ NVME_FDP_EVENT_PID = 0x3,
+
+ /* Controller Events */
+ NVME_FDP_EVENT_REALLOC = 0x80,
+ NVME_FDP_EVENT_MODIFY = 0x81,
+};
+
+/**
+ * enum nvme_fdp_event_realloc_flags - Media Reallocated Event Type Specific Flags
+ * @NVME_FDP_EVENT_REALLOC_F_LBAV: LBA Valid
+ */
+enum nvme_fdp_event_realloc_flags {
+ NVME_FDP_EVENT_REALLOC_F_LBAV = 1 << 0,
+};
+
+/**
+ * struct nvme_fdp_event_realloc - Media Reallocated Event Type Specific Information
+ * @flags: Event Type Specific flags (&enum nvme_fdp_event_realloc_flags)
+ * @rsvd1: Reserved
+ * @nlbam: Number of LBAs Moved
+ * @lba: Logical Block Address
+ * @rsvd12: Reserved
+ */
+struct nvme_fdp_event_realloc {
+ __u8 flags;
+ __u8 rsvd1;
+ __le16 nlbam;
+ __le64 lba;
+ __u8 rsvd12[4];
+};
+
+/**
+ * enum nvme_fdp_event_flags - FDP Event Flags
+ * @NVME_FDP_EVENT_F_PIV: Placement Identifier Valid
+ * @NVME_FDP_EVENT_F_NSIDV: Namespace Identifier Valid
+ * @NVME_FDP_EVENT_F_LV: Location Valid
+ */
+enum nvme_fdp_event_flags {
+ NVME_FDP_EVENT_F_PIV = 1 << 0,
+ NVME_FDP_EVENT_F_NSIDV = 1 << 1,
+ NVME_FDP_EVENT_F_LV = 1 << 2,
+};
+
+/**
+ * struct nvme_fdp_event - FDP Event
+ * @type: Event Type (&enum nvme_fdp_event_type)
+ * @flags: Event Flags (&enum nvme_fdp_event_flags)
+ * @pid: Placement Identifier
+ * @ts: Timestamp
+ * @nsid: Namespace Identifier
+ * @type_specific: Event Type Specific Information
+ * @rgid: Reclaim Group Identifier
+ * @ruhid: Reclaim Unit Handle Identifier
+ * @rsvd35: Reserved
+ * @vs: Vendor Specific
+ */
+struct nvme_fdp_event {
+ __u8 type;
+ __u8 flags;
+ __le16 pid;
+ struct nvme_timestamp ts;
+ __le32 nsid;
+ __u8 type_specific[16];
+ __le16 rgid;
+ __u8 ruhid;
+ __u8 rsvd35[5];
+ __u8 vs[24];
+};
+
+/**
+ * struct nvme_fdp_events_log - FDP Events Log Page
+ * @n: Number of FDP Events
+ * @rsvd4: Reserved
+ * @events: FDP Events (&struct nvme_fdp_event)
+ */
+struct nvme_fdp_events_log {
+ __le32 n;
+ __u8 rsvd4[60];
+ struct nvme_fdp_event events[63];
+};
+
+/**
+ * struct nvme_feat_fdp_events_cdw11 - FDP Events Feature Command Dword 11
+ * @phndl: Placement Handle
+ * @noet: Number of FDP Event Types
+ * @rsvd24: Reserved
+ */
+struct nvme_feat_fdp_events_cdw11 {
+ __le16 phndl;
+ __u8 noet;
+ __u8 rsvd24;
+};
+
+/**
+ * enum nvme_fdp_supported_event_attributes - Supported FDP Event Attributes
+ * @NVME_FDP_SUPP_EVENT_ENABLED_SHIFT: FDP Event Enable Shift
+ * @NVME_FDP_SUPP_EVENT_ENABLED_MASK: FDP Event Enable Mask
+ */
+enum nvme_fdp_supported_event_attributes {
+ NVME_FDP_SUPP_EVENT_ENABLED_SHIFT = 0,
+ NVME_FDP_SUPP_EVENT_ENABLED_MASK = 0x1,
+};
+
+/**
+ * struct nvme_fdp_supported_event_desc - Supported FDP Event Descriptor
+ * @evt: FDP Event Type
+ * @evta: FDP Event Type Attributes (&enum nvme_fdp_supported_event_attributes)
+ */
+struct nvme_fdp_supported_event_desc {
+ __u8 evt;
+ __u8 evta;
+};
+
+/**
+ * struct nvme_fdp_ruh_status_desc - Reclaim Unit Handle Status Descriptor
+ * @pid: Placement Identifier
+ * @ruhid: Reclaim Unit Handle Identifier
+ * @earutr: Estimated Active Reclaim Unit Time Remaining
+ * @ruamw: Reclaim Unit Available Media Writes
+ * @rsvd16: Reserved
+ */
+struct nvme_fdp_ruh_status_desc {
+ __le16 pid;
+ __le16 ruhid;
+ __le32 earutr;
+ __le64 ruamw;
+ __u8 rsvd16[16];
+};
+
+/**
+ * struct nvme_fdp_ruh_status - Reclaim Unit Handle Status
+ * @rsvd0: Reserved
+ * @nruhsd: Number of Reclaim Unit Handle Status Descriptors
+ * @ruhss: Reclaim Unit Handle Status descriptors
+ */
+struct nvme_fdp_ruh_status {
+ __u8 rsvd0[14];
+ __le16 nruhsd;
+ struct nvme_fdp_ruh_status_desc ruhss[];
+};
+
+/**
+ * struct nvme_lba_status_desc - LBA Status Descriptor Entry
+ * @dslba: Descriptor Starting LBA
+ * @nlb: Number of Logical Blocks
+ * @rsvd12: Reserved
+ * @status: Additional status about this LBA range
+ * @rsvd14: Reserved
+ */
+struct nvme_lba_status_desc {
+ __le64 dslba;
+ __le32 nlb;
+ __u8 rsvd12;
+ __u8 status;
+ __u8 rsvd14[2];
+};
+
+/**
+ * struct nvme_lba_status - LBA Status Descriptor List
+ * @nlsd: Number of LBA Status Descriptors
+ * @cmpc: Completion Condition
+ * @rsvd5: Reserved
+ * @descs: LBA status descriptor Entry
+ */
+struct nvme_lba_status {
+ __le32 nlsd;
+ __u8 cmpc;
+ __u8 rsvd5[3];
+ struct nvme_lba_status_desc descs[];
+};
+
+/**
+ * struct nvme_feat_auto_pst - Autonomous Power State Transition
+ * @apst_entry: See &enum nvme_apst_entry
+ */
+struct nvme_feat_auto_pst {
+ __le64 apst_entry[32];
+};
+
+/**
+ * enum nvme_apst_entry - Autonomous Power State Transition
+ * @NVME_APST_ENTRY_ITPS_SHIFT: Idle Transition Power State Shift
+ * @NVME_APST_ENTRY_ITPT_SHIFT: Idle Time Prior to Transition Shift
+ * @NVME_APST_ENTRY_ITPS_MASK: Idle Transition Power State Mask
+ * @NVME_APST_ENTRY_ITPT_MASK: Idle Time Prior to Transition Mask
+ */
+enum nvme_apst_entry {
+ NVME_APST_ENTRY_ITPS_SHIFT = 3,
+ NVME_APST_ENTRY_ITPT_SHIFT = 8,
+ NVME_APST_ENTRY_ITPS_MASK = 0x1f,
+ NVME_APST_ENTRY_ITPT_MASK = 0xffffff,
+};
+
+/**
+ * struct nvme_metadata_element_desc - Metadata Element Descriptor
+ * @type: Element Type (ET)
+ * @rev: Element Revision (ER)
+ * @len: Element Length (ELEN)
+ * @val: Element Value (EVAL), UTF-8 string
+ */
+struct nvme_metadata_element_desc {
+ __u8 type;
+ __u8 rev;
+ __le16 len;
+ __u8 val[0];
+};
+
+/**
+ * struct nvme_host_metadata - Host Metadata Data Structure
+ * @ndesc: Number of metadata element descriptors
+ * @rsvd1: Reserved
+ * @descs: Metadata element descriptors
+ * @descs_buf: Metadata element descriptor buffer
+ */
+struct nvme_host_metadata {
+ __u8 ndesc;
+ __u8 rsvd1;
+ union {
+ struct nvme_metadata_element_desc descs[0];
+ __u8 descs_buf[4094];
+ };
+};
+
+/**
+ * enum nvme_ctrl_metadata_type - Controller Metadata Element Types
+ * @NVME_CTRL_METADATA_OS_CTRL_NAME: Name of the controller in
+ * the operating system.
+ * @NVME_CTRL_METADATA_OS_DRIVER_NAME: Name of the driver in the
+ * operating system.
+ * @NVME_CTRL_METADATA_OS_DRIVER_VER: Version of the driver in
+ * the operating system.
+ * @NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME: Name of the controller in
+ * the pre-boot environment.
+ * @NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME: Name of the driver in the
+ * pre-boot environment.
+ * @NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER: Version of the driver in the
+ * pre-boot environment.
+ * @NVME_CTRL_METADATA_SYS_PROC_MODEL: Model of the processor.
+ * @NVME_CTRL_METADATA_CHIPSET_DRV_NAME: Chipset driver name.
+ * @NVME_CTRL_METADATA_CHIPSET_DRV_VERSION: Chipset driver version.
+ * @NVME_CTRL_METADATA_OS_NAME_AND_BUILD: Operating system name and build.
+ * @NVME_CTRL_METADATA_SYS_PROD_NAME: System product name.
+ * @NVME_CTRL_METADATA_FIRMWARE_VERSION: Host firmware (e.g UEFI) version.
+ * @NVME_CTRL_METADATA_OS_DRIVER_FILENAME: Operating system driver filename.
+ * @NVME_CTRL_METADATA_DISPLAY_DRV_NAME: Display driver name.
+ * @NVME_CTRL_METADATA_DISPLAY_DRV_VERSION: Display driver version.
+ * @NVME_CTRL_METADATA_HOST_DET_FAIL_REC: Failure record.
+ */
+enum nvme_ctrl_metadata_type {
+ NVME_CTRL_METADATA_OS_CTRL_NAME = 0x01,
+ NVME_CTRL_METADATA_OS_DRIVER_NAME = 0x02,
+ NVME_CTRL_METADATA_OS_DRIVER_VER = 0x03,
+ NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME = 0x04,
+ NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME = 0x05,
+ NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER = 0x06,
+ NVME_CTRL_METADATA_SYS_PROC_MODEL = 0x07,
+ NVME_CTRL_METADATA_CHIPSET_DRV_NAME = 0x08,
+ NVME_CTRL_METADATA_CHIPSET_DRV_VERSION = 0x09,
+ NVME_CTRL_METADATA_OS_NAME_AND_BUILD = 0x0a,
+ NVME_CTRL_METADATA_SYS_PROD_NAME = 0x0b,
+ NVME_CTRL_METADATA_FIRMWARE_VERSION = 0x0c,
+ NVME_CTRL_METADATA_OS_DRIVER_FILENAME = 0x0d,
+ NVME_CTRL_METADATA_DISPLAY_DRV_NAME = 0x0e,
+ NVME_CTRL_METADATA_DISPLAY_DRV_VERSION = 0x0f,
+ NVME_CTRL_METADATA_HOST_DET_FAIL_REC = 0x10,
+};
+
+/**
+ * enum nvme_ns_metadata_type - Namespace Metadata Element Types
+ * @NVME_NS_METADATA_OS_NS_NAME: Name of the namespace in the
+ * operating system
+ * @NVME_NS_METADATA_PRE_BOOT_NS_NAME: Name of the namespace in the pre-boot
+ * environment.
+ * @NVME_NS_METADATA_OS_NS_QUAL_1: First qualifier of the Operating System
+ * Namespace Name.
+ * @NVME_NS_METADATA_OS_NS_QUAL_2: Second qualifier of the Operating System
+ * Namespace Name.
+ */
+enum nvme_ns_metadata_type {
+ NVME_NS_METADATA_OS_NS_NAME = 0x01,
+ NVME_NS_METADATA_PRE_BOOT_NS_NAME = 0x02,
+ NVME_NS_METADATA_OS_NS_QUAL_1 = 0x03,
+ NVME_NS_METADATA_OS_NS_QUAL_2 = 0x04,
+};
+
+/**
+ * struct nvme_lba_range_type_entry - LBA Range Type - Data Structure Entry
+ * @type: Specifies the Type of the LBA range
+ * @attributes: Specifies attributes of the LBA range
+ * @rsvd2: Reserved
+ * @slba: Starting LBA
+ * @nlb: Number of Logical Blocks
+ * @guid: Unique Identifier
+ * @rsvd48: Reserved
+ */
+struct nvme_lba_range_type_entry {
+ __u8 type;
+ __u8 attributes;
+ __u8 rsvd2[14];
+ __le64 slba;
+ __le64 nlb;
+ __u8 guid[16];
+ __u8 rsvd48[16];
+};
+
+/**
+ * enum nvme_lbart - LBA Range Type - Data Structure Entry
+ * @NVME_LBART_TYPE_GP: General Purpose
+ * @NVME_LBART_TYPE_FS: Filesystem
+ * @NVME_LBART_TYPE_RAID: RAID
+ * @NVME_LBART_TYPE_CACHE: Cache
+ * @NVME_LBART_TYPE_SWAP: Page / swap file
+ * @NVME_LBART_ATTRIB_TEMP: Temp
+ * @NVME_LBART_ATTRIB_HIDE: Hidden
+ */
+enum nvme_lbart {
+ NVME_LBART_TYPE_GP = 0,
+ NVME_LBART_TYPE_FS = 1,
+ NVME_LBART_TYPE_RAID = 2,
+ NVME_LBART_TYPE_CACHE = 3,
+ NVME_LBART_TYPE_SWAP = 4,
+ NVME_LBART_ATTRIB_TEMP = 1 << 0,
+ NVME_LBART_ATTRIB_HIDE = 1 << 1,
+};
+
+/**
+ * struct nvme_lba_range_type - LBA Range Type
+ * @entry: LBA range type entry. See @struct nvme_lba_range_type_entry
+ */
+struct nvme_lba_range_type {
+ struct nvme_lba_range_type_entry entry[NVME_FEAT_LBA_RANGE_MAX];
+};
+
+/**
+ * struct nvme_plm_config - Predictable Latency Mode - Deterministic Threshold Configuration Data Structure
+ * @ee: Enable Event
+ * @rsvd2: Reserved
+ * @dtwinrt: DTWIN Reads Threshold
+ * @dtwinwt: DTWIN Writes Threshold
+ * @dtwintt: DTWIN Time Threshold
+ * @rsvd56: Reserved
+ */
+struct nvme_plm_config {
+ __le16 ee;
+ __u8 rsvd2[30];
+ __le64 dtwinrt;
+ __le64 dtwinwt;
+ __le64 dtwintt;
+ __u8 rsvd56[456];
+};
+
+/**
+ * struct nvme_feat_host_behavior - Host Behavior Support - Data Structure
+ * @acre: Advanced Command Retry Enable
+ * @etdas: Extended Telemetry Data Area 4 Supported
+ * @lbafee: LBA Format Extension Enable
+ * @rsvd3: Reserved
+ * @cdfe: Copy Descriptor Formats Enable
+ * @rsvd6: Reserved
+ */
+struct nvme_feat_host_behavior {
+ __u8 acre;
+ __u8 etdas;
+ __u8 lbafee;
+ __u8 rsvd3;
+ __u16 cdfe;
+ __u8 rsvd6[506];
+};
+
+/**
+ * enum nvme_host_behavior_support - Enable Advanced Command
+ * @NVME_ENABLE_ACRE: Enable Advanced Command Retry Enable
+ */
+enum nvme_host_behavior_support {
+ NVME_ENABLE_ACRE = 1 << 0,
+};
+
+/**
+ * struct nvme_dsm_range - Dataset Management - Range Definition
+ * @cattr: Context Attributes
+ * @nlb: Length in logical blocks
+ * @slba: Starting LBA
+ */
+struct nvme_dsm_range {
+ __le32 cattr;
+ __le32 nlb;
+ __le64 slba;
+};
+
+/**
+ * struct nvme_copy_range - Copy - Source Range Entries Descriptor Format
+ * @rsvd0: Reserved
+ * @slba: Starting LBA
+ * @nlb: Number of Logical Blocks
+ * @rsvd18: Reserved
+ * @eilbrt: Expected Initial Logical Block Reference Tag /
+ * Expected Logical Block Storage Tag
+ * @elbatm: Expected Logical Block Application Tag Mask
+ * @elbat: Expected Logical Block Application Tag
+ */
+struct nvme_copy_range {
+ __u8 rsvd0[8];
+ __le64 slba;
+ __le16 nlb;
+ __u8 rsvd18[6];
+ __le32 eilbrt;
+ __le16 elbat;
+ __le16 elbatm;
+};
+
+/**
+ * struct nvme_copy_range_f1 - Copy - Source Range Entries Descriptor Format 1h
+ * @rsvd0: Reserved
+ * @slba: Starting LBA
+ * @nlb: Number of Logical Blocks
+ * @rsvd18: Reserved
+ * @elbt: Expected Initial Logical Block Reference Tag /
+ * Expected Logical Block Storage Tag
+ * @elbatm: Expected Logical Block Application Tag Mask
+ * @elbat: Expected Logical Block Application Tag
+ */
+struct nvme_copy_range_f1 {
+ __u8 rsvd0[8];
+ __le64 slba;
+ __le16 nlb;
+ __u8 rsvd18[8];
+ __u8 elbt[10];
+ __le16 elbat;
+ __le16 elbatm;
+};
+
+/**
+ * enum nvme_copy_range_sopt - NVMe Copy Range Source Options
+ * @NVME_COPY_SOPT_FCO: NVMe Copy Source Option Fast Copy Only
+ */
+enum nvme_copy_range_sopt {
+ NVME_COPY_SOPT_FCO = 1 << 15,
+};
+
+/**
+ * struct nvme_copy_range_f2 - Copy - Source Range Entries Descriptor Format 2h
+ * @snsid: Source Namespace Identifier
+ * @rsvd4: Reserved
+ * @slba: Starting LBA
+ * @nlb: Number of Logical Blocks
+ * @rsvd18: Reserved
+ * @sopt: Source Options
+ * @eilbrt: Expected Initial Logical Block Reference Tag /
+ * Expected Logical Block Storage Tag
+ * @elbatm: Expected Logical Block Application Tag Mask
+ * @elbat: Expected Logical Block Application Tag
+ */
+struct nvme_copy_range_f2 {
+ __le32 snsid;
+ __u8 rsvd4[4];
+ __le64 slba;
+ __le16 nlb;
+ __u8 rsvd18[4];
+ __le16 sopt;
+ __le32 eilbrt;
+ __le16 elbat;
+ __le16 elbatm;
+};
+
+/**
+ * struct nvme_copy_range_f3 - Copy - Source Range Entries Descriptor Format 3h
+ * @snsid: Source Namespace Identifier
+ * @rsvd4: Reserved
+ * @slba: Starting LBA
+ * @nlb: Number of Logical Blocks
+ * @rsvd18: Reserved
+ * @sopt: Source Options
+ * @rsvd24: Reserved
+ * @elbt: Expected Initial Logical Block Reference Tag /
+ * Expected Logical Block Storage Tag
+ * @elbatm: Expected Logical Block Application Tag Mask
+ * @elbat: Expected Logical Block Application Tag
+ */
+struct nvme_copy_range_f3 {
+ __le32 snsid;
+ __u8 rsvd4[4];
+ __le64 slba;
+ __le16 nlb;
+ __u8 rsvd18[4];
+ __le16 sopt;
+ __u8 rsvd24[2];
+ __u8 elbt[10];
+ __le16 elbat;
+ __le16 elbatm;
+};
+
+/**
+ * struct nvme_registered_ctrl - Registered Controller Data Structure
+ * @cntlid: Controller ID
+ * @rcsts: Reservation Status
+ * @rsvd3: Reserved
+ * @hostid: Host Identifier
+ * @rkey: Reservation Key
+ */
+struct nvme_registered_ctrl {
+ __le16 cntlid;
+ __u8 rcsts;
+ __u8 rsvd3[5];
+ __le64 hostid;
+ __le64 rkey;
+};
+
+/**
+ * struct nvme_registered_ctrl_ext - Registered Controller Extended Data Structure
+ * @cntlid: Controller ID
+ * @rcsts: Reservation Status
+ * @rsvd3: Reserved
+ * @rkey: Reservation Key
+ * @hostid: Host Identifier
+ * @rsvd32: Reserved
+ */
+struct nvme_registered_ctrl_ext {
+ __le16 cntlid;
+ __u8 rcsts;
+ __u8 rsvd3[5];
+ __le64 rkey;
+ __u8 hostid[16];
+ __u8 rsvd32[32];
+};
+
+/**
+ * struct nvme_resv_status - Reservation Status Data Structure
+ * @gen: Generation
+ * @rtype: Reservation Type
+ * @regctl: Number of Registered Controllers
+ * @rsvd7: Reserved
+ * @ptpls: Persist Through Power Loss State
+ * @rsvd10: Reserved
+ * @rsvd24: Reserved
+ * @regctl_eds: Registered Controller Extended Data Structure
+ * @regctl_ds: Registered Controller Data Structure
+ */
+struct nvme_resv_status {
+ __le32 gen;
+ __u8 rtype;
+ __u8 regctl[2];
+ __u8 rsvd7[2];
+ __u8 ptpls;
+ __u8 rsvd10[14];
+ union {
+ struct {
+ __u8 rsvd24[40];
+ struct nvme_registered_ctrl_ext regctl_eds[0];
+ };
+ struct nvme_registered_ctrl regctl_ds[0];
+ };
+};
+
+/**
+ * struct nvme_streams_directive_params - Streams Directive - Return Parameters Data Structure
+ * @msl: Max Streams Limit
+ * @nssa: NVM Subsystem Streams Available
+ * @nsso: NVM Subsystem Streams Open
+ * @nssc: NVM Subsystem Stream Capability
+ * @rsvd: Reserved
+ * @sws: Stream Write Size
+ * @sgs: Stream Granularity Size
+ * @nsa: Namespace Streams Allocated
+ * @nso: Namespace Streams Open
+ * @rsvd2: Reserved
+ */
+struct nvme_streams_directive_params {
+ __le16 msl;
+ __le16 nssa;
+ __le16 nsso;
+ __u8 nssc;
+ __u8 rsvd[9];
+ __le32 sws;
+ __le16 sgs;
+ __le16 nsa;
+ __le16 nso;
+ __u8 rsvd2[6];
+};
+
+/**
+ * struct nvme_streams_directive_status - Streams Directive - Get Status Data Structure
+ * @osc: Open Stream Count
+ * @sid: Stream Identifier
+ */
+struct nvme_streams_directive_status {
+ __le16 osc;
+ __le16 sid[];
+};
+
+/**
+ * struct nvme_id_directives - Identify Directive - Return Parameters Data Structure
+ * @supported: Identify directive is supported
+ * @enabled: Identify directive is Enabled
+ * @rsvd64: Reserved
+ */
+struct nvme_id_directives {
+ __u8 supported[32];
+ __u8 enabled[32];
+ __u8 rsvd64[4032];
+};
+
+/**
+ * enum nvme_directive_types - Directives Supported or Enabled
+ * @NVME_ID_DIR_ID_BIT: Identify directive is supported
+ * @NVME_ID_DIR_SD_BIT: Streams directive is supported
+ * @NVME_ID_DIR_DP_BIT: Direct Placement directive is supported
+ */
+enum nvme_directive_types {
+ NVME_ID_DIR_ID_BIT = 0,
+ NVME_ID_DIR_SD_BIT = 1,
+ NVME_ID_DIR_DP_BIT = 2,
+};
+
+/**
+ * struct nvme_host_mem_buf_attrs - Host Memory Buffer - Attributes Data Structure
+ * @hsize: Host Memory Buffer Size
+ * @hmdlal: Host Memory Descriptor List Lower Address
+ * @hmdlau: Host Memory Descriptor List Upper Address
+ * @hmdlec: Host Memory Descriptor List Entry Count
+ * @rsvd16: Reserved
+ */
+struct nvme_host_mem_buf_attrs {
+ __le32 hsize;
+ __le32 hmdlal;
+ __le32 hmdlau;
+ __le32 hmdlec;
+ __u8 rsvd16[4080];
+
+};
+
+/**
+ * enum nvme_ae_type - Asynchronous Event Type
+ * @NVME_AER_ERROR: Error event
+ * @NVME_AER_SMART: SMART / Health Status event
+ * @NVME_AER_NOTICE: Notice event
+ * @NVME_AER_CSS: NVM Command Set Specific events
+ * @NVME_AER_VS: Vendor Specific event
+ */
+enum nvme_ae_type {
+ NVME_AER_ERROR = 0,
+ NVME_AER_SMART = 1,
+ NVME_AER_NOTICE = 2,
+ NVME_AER_CSS = 6,
+ NVME_AER_VS = 7,
+};
+
+/**
+ * enum nvme_ae_info_error - Asynchronous Event Information - Error Status
+ * @NVME_AER_ERROR_INVALID_DB_REG: Write to Invalid Doorbell Register
+ * @NVME_AER_ERROR_INVALID_DB_VAL: Invalid Doorbell Write Value
+ * @NVME_AER_ERROR_DIAG_FAILURE: Diagnostic Failure
+ * @NVME_AER_ERROR_PERSISTENT_INTERNAL_ERROR: Persistent Internal Error
+ * @NVME_AER_ERROR_TRANSIENT_INTERNAL_ERROR: Transient Internal Error
+ * @NVME_AER_ERROR_FW_IMAGE_LOAD_ERROR: Firmware Image Load Error
+ */
+enum nvme_ae_info_error {
+ NVME_AER_ERROR_INVALID_DB_REG = 0x00,
+ NVME_AER_ERROR_INVALID_DB_VAL = 0x01,
+ NVME_AER_ERROR_DIAG_FAILURE = 0x02,
+ NVME_AER_ERROR_PERSISTENT_INTERNAL_ERROR = 0x03,
+ NVME_AER_ERROR_TRANSIENT_INTERNAL_ERROR = 0x04,
+ NVME_AER_ERROR_FW_IMAGE_LOAD_ERROR = 0x05,
+};
+
+/**
+ * enum nvme_ae_info_smart - Asynchronous Event Information - SMART / Health Status
+ * @NVME_AER_SMART_SUBSYSTEM_RELIABILITY: NVM subsystem Reliability
+ * @NVME_AER_SMART_TEMPERATURE_THRESHOLD: Temperature Threshold
+ * @NVME_AER_SMART_SPARE_THRESHOLD: Spare Below Threshold
+ */
+enum nvme_ae_info_smart {
+ NVME_AER_SMART_SUBSYSTEM_RELIABILITY = 0x00,
+ NVME_AER_SMART_TEMPERATURE_THRESHOLD = 0x01,
+ NVME_AER_SMART_SPARE_THRESHOLD = 0x02,
+};
+
+/**
+ * enum nvme_ae_info_css_nvm - Asynchronous Event Information - I/O Command Specific Status
+ * @NVME_AER_CSS_NVM_RESERVATION: Reservation Log Page Available
+ * @NVME_AER_CSS_NVM_SANITIZE_COMPLETED: Sanitize Operation Completed
+ * @NVME_AER_CSS_NVM_UNEXPECTED_SANITIZE_DEALLOC: Sanitize Operation Completed
+ * With Unexpected Deallocation
+ */
+enum nvme_ae_info_css_nvm {
+ NVME_AER_CSS_NVM_RESERVATION = 0x00,
+ NVME_AER_CSS_NVM_SANITIZE_COMPLETED = 0x01,
+ NVME_AER_CSS_NVM_UNEXPECTED_SANITIZE_DEALLOC = 0x02,
+};
+
+/**
+ * enum nvme_ae_info_notice - Asynchronous Event Information - Notice
+ * @NVME_AER_NOTICE_NS_CHANGED: Namespace Attribute Changed
+ * @NVME_AER_NOTICE_FW_ACT_STARTING: Firmware Activation Starting
+ * @NVME_AER_NOTICE_TELEMETRY: Telemetry Log Changed
+ * @NVME_AER_NOTICE_ANA: Asymmetric Namespace Access Change
+ * @NVME_AER_NOTICE_PL_EVENT: Predictable Latency Event Aggregate Log Change
+ * @NVME_AER_NOTICE_LBA_STATUS_ALERT: LBA Status Information Alert
+ * @NVME_AER_NOTICE_EG_EVENT: Endurance Group Event Aggregate Log Page Change
+ * @NVME_AER_NOTICE_DISC_CHANGED: Discovery Log Page Change
+ */
+enum nvme_ae_info_notice {
+ NVME_AER_NOTICE_NS_CHANGED = 0x00,
+ NVME_AER_NOTICE_FW_ACT_STARTING = 0x01,
+ NVME_AER_NOTICE_TELEMETRY = 0x02,
+ NVME_AER_NOTICE_ANA = 0x03,
+ NVME_AER_NOTICE_PL_EVENT = 0x04,
+ NVME_AER_NOTICE_LBA_STATUS_ALERT = 0x05,
+ NVME_AER_NOTICE_EG_EVENT = 0x06,
+ NVME_AER_NOTICE_DISC_CHANGED = 0xf0,
+};
+
+/**
+ * enum nvme_subsys_type - Type of the NVM subsystem.
+ * @NVME_NQN_DISC: Discovery type target subsystem. Describes a referral to another
+ * Discovery Service composed of Discovery controllers that provide
+ * additional discovery records. Multiple Referral entries may
+ * be reported for each Discovery Service (if that Discovery Service
+ * has multiple NVM subsystem ports or supports multiple protocols).
+ * @NVME_NQN_NVME: NVME type target subsystem. Describes an NVM subsystem whose
+ * controllers may have attached namespaces (an NVM subsystem
+ * that is not composed of Discovery controllers). Multiple NVM
+ * Subsystem entries may be reported for each NVM subsystem if
+ * that NVM subsystem has multiple NVM subsystem ports.
+ * @NVME_NQN_CURR: Current Discovery type target subsystem. Describes this Discovery
+ * subsystem (the Discovery Service that contains the controller
+ * processing the Get Log Page command). Multiple Current Discovery
+ * Subsystem entries may be reported for this Discovery subsystem
+ * if the current Discovery subsystem has multiple NVM subsystem
+ * ports.
+ */
+enum nvme_subsys_type {
+ NVME_NQN_DISC = 1,
+ NVME_NQN_NVME = 2,
+ NVME_NQN_CURR = 3,
+};
+
+#define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery"
+#define NVME_RDMA_IP_PORT 4420
+#define NVME_DISC_IP_PORT 8009
+
+/* However the max length of a qualified name is another size */
+#define NVMF_NQN_SIZE 223
+#define NVMF_TRSVCID_SIZE 32
+
+/**
+ * enum nvmf_disc_eflags - Discovery Log Page entry flags.
+ * @NVMF_DISC_EFLAGS_NONE: Indicates that none of the DUPRETINFO or EPCSD
+ * features are supported.
+ * @NVMF_DISC_EFLAGS_DUPRETINFO: Duplicate Returned Information (DUPRETINFO):
+ * Indicates that using the content of this entry
+ * to access this Discovery Service returns the same
+ * information that is returned by using the content
+ * of other entries in this log page that also have
+ * this flag set.
+ * @NVMF_DISC_EFLAGS_EPCSD: Explicit Persistent Connection Support for Discovery (EPCSD):
+ * Indicates that Explicit Persistent Connections are
+ * supported for the Discovery controller.
+ * @NVMF_DISC_EFLAGS_NCC: No CDC Connectivity (NCC): If set to
+ * '1', then no DDC that describes this entry
+ * is currently connected to the CDC. If
+ * cleared to '0', then at least one DDC that
+ * describes this entry is currently
+ * connected to the CDC. If the Discovery
+ * controller returning this log page is not
+ * a CDC, then this bit shall be cleared to
+ * '0' and should be ignored by the host.
+ */
+enum nvmf_disc_eflags {
+ NVMF_DISC_EFLAGS_NONE = 0,
+ NVMF_DISC_EFLAGS_DUPRETINFO = 1 << 0,
+ NVMF_DISC_EFLAGS_EPCSD = 1 << 1,
+ NVMF_DISC_EFLAGS_NCC = 1 << 2,
+};
+
+/* Backwards compatibility. Will be removed with next major release */
+#define NVMF_DISC_EFLAGS_BOTH (NVMF_DISC_EFLAGS_DUPRETINFO | NVMF_DISC_EFLAGS_EPCSD)
+
+/**
+ * union nvmf_tsas - Transport Specific Address Subtype
+ * @common: Common transport specific attributes
+ * @rdma: RDMA transport specific attribute settings
+ * @qptype: RDMA QP Service Type (RDMA_QPTYPE): Specifies the type of RDMA
+ * Queue Pair. See &enum nvmf_rdma_qptype.
+ * @prtype: RDMA Provider Type (RDMA_PRTYPE): Specifies the type of RDMA
+ * provider. See &enum nvmf_rdma_prtype.
+ * @cms: RDMA Connection Management Service (RDMA_CMS): Specifies the type
+ * of RDMA IP Connection Management Service. See &enum nvmf_rdma_cms.
+ * @pkey: RDMA_PKEY: Specifies the Partition Key when AF_IB (InfiniBand)
+ * address family type is used.
+ * @tcp: TCP transport specific attribute settings
+ * @sectype: Security Type (SECTYPE): Specifies the type of security used by the
+ * NVMe/TCP port. If SECTYPE is a value of 0h (No Security), then the
+ * host shall set up a normal TCP connection. See &enum nvmf_tcp_sectype.
+ */
+union nvmf_tsas {
+ char common[NVMF_TSAS_SIZE];
+ struct rdma {
+ __u8 qptype;
+ __u8 prtype;
+ __u8 cms;
+ __u8 rsvd3[5];
+ __le16 pkey;
+ __u8 rsvd10[246];
+ } rdma;
+ struct tcp {
+ __u8 sectype;
+ } tcp;
+};
+
+/**
+ * struct nvmf_disc_log_entry - Discovery Log Page entry
+ * @trtype: Transport Type (TRTYPE): Specifies the NVMe Transport type.
+ * See &enum nvmf_trtype.
+ * @adrfam: Address Family (ADRFAM): Specifies the address family.
+ * See &enum nvmf_addr_family.
+ * @subtype: Subsystem Type (SUBTYPE): Specifies the type of the NVM subsystem
+ * that is indicated in this entry. See &enum nvme_subsys_type.
+ * @treq: Transport Requirements (TREQ): Indicates requirements for the NVMe
+ * Transport. See &enum nvmf_treq.
+ * @portid: Port ID (PORTID): Specifies a particular NVM subsystem port.
+ * Different NVMe Transports or address families may utilize the same
+ * Port ID value (e.g. a Port ID may support both iWARP and RoCE).
+ * @cntlid: Controller ID (CNTLID): Specifies the controller ID. If the NVM
+ * subsystem uses a dynamic controller model, then this field shall
+ * be set to FFFFh. If the NVM subsystem uses a static controller model,
+ * then this field may be set to a specific controller ID (values 0h
+ * to FFEFh are valid). If the NVM subsystem uses a static controller
+ * model and the value indicated is FFFEh, then the host should remember
+ * the Controller ID returned as part of the Fabrics Connect command
+ * in order to re-establish an association in the future with the same
+ * controller.
+ * @asqsz: Admin Max SQ Size (ASQSZ): Specifies the maximum size of an Admin
+ * Submission Queue. This applies to all controllers in the NVM
+ * subsystem. The value shall be a minimum of 32 entries.
+ * @eflags: Entry Flags (EFLAGS): Indicates additional information related to
+ * the current entry. See &enum nvmf_disc_eflags.
+ * @rsvd12: Reserved
+ * @trsvcid: Transport Service Identifier (TRSVCID): Specifies the NVMe Transport
+ * service identifier as an ASCII string. The NVMe Transport service
+ * identifier is specified by the associated NVMe Transport binding
+ * specification.
+ * @rsvd64: Reserved
+ * @subnqn: NVM Subsystem Qualified Name (SUBNQN): NVMe Qualified Name (NQN)
+ * that uniquely identifies the NVM subsystem. For a subsystem, if that
+ * Discovery subsystem has a unique NQN (i.e., the NVM Subsystem NVMe
+ * Qualified Name (SUBNQN) field in that Discovery subsystem's Identify
+ * Controller data structure contains a unique NQN value), then the
+ * value returned shall be that unique NQN. If the Discovery subsystem
+ * does not have a unique NQN, then the value returned shall be the
+ * well-known Discovery Service NQN (nqn.2014-08.org.nvmexpress.discovery).
+ * @traddr: Transport Address (TRADDR): Specifies the address of the NVM subsystem
+ * that may be used for a Connect command as an ASCII string. The
+ * Address Family field describes the reference for parsing this field.
+ * @tsas: Transport specific attribute settings
+ */
+struct nvmf_disc_log_entry {
+ __u8 trtype;
+ __u8 adrfam;
+ __u8 subtype;
+ __u8 treq;
+ __le16 portid;
+ __le16 cntlid;
+ __le16 asqsz;
+ __le16 eflags;
+ __u8 rsvd12[20];
+ char trsvcid[NVMF_TRSVCID_SIZE];
+ __u8 rsvd64[192];
+ char subnqn[NVME_NQN_LENGTH];
+ char traddr[NVMF_TRADDR_SIZE];
+ union nvmf_tsas tsas;
+};
+
+/**
+ * enum nvmf_trtype - Transport Type codes for Discovery Log Page entry TRTYPE field
+ * @NVMF_TRTYPE_UNSPECIFIED: Not indicated
+ * @NVMF_TRTYPE_RDMA: RDMA
+ * @NVMF_TRTYPE_FC: Fibre Channel
+ * @NVMF_TRTYPE_TCP: TCP
+ * @NVMF_TRTYPE_LOOP: Intra-host Transport (i.e., loopback), reserved
+ * for host usage.
+ * @NVMF_TRTYPE_MAX: Maximum value for &enum nvmf_trtype
+ */
+enum nvmf_trtype {
+ NVMF_TRTYPE_UNSPECIFIED = 0,
+ NVMF_TRTYPE_RDMA = 1,
+ NVMF_TRTYPE_FC = 2,
+ NVMF_TRTYPE_TCP = 3,
+ NVMF_TRTYPE_LOOP = 254,
+ NVMF_TRTYPE_MAX,
+};
+
+/**
+ * enum nvmf_addr_family - Address Family codes for Discovery Log Page entry ADRFAM field
+ * @NVMF_ADDR_FAMILY_PCI: PCIe
+ * @NVMF_ADDR_FAMILY_IP4: AF_INET: IPv4 address family.
+ * @NVMF_ADDR_FAMILY_IP6: AF_INET6: IPv6 address family.
+ * @NVMF_ADDR_FAMILY_IB: AF_IB: InfiniBand address family.
+ * @NVMF_ADDR_FAMILY_FC: Fibre Channel address family.
+ * @NVMF_ADDR_FAMILY_LOOP: Intra-host Transport (i.e., loopback), reserved
+ * for host usage.
+ */
+enum nvmf_addr_family {
+ NVMF_ADDR_FAMILY_PCI = 0,
+ NVMF_ADDR_FAMILY_IP4 = 1,
+ NVMF_ADDR_FAMILY_IP6 = 2,
+ NVMF_ADDR_FAMILY_IB = 3,
+ NVMF_ADDR_FAMILY_FC = 4,
+ NVMF_ADDR_FAMILY_LOOP = 254,
+};
+
+/**
+ * enum nvmf_treq - Transport Requirements codes for Discovery Log Page entry TREQ field
+ * @NVMF_TREQ_NOT_SPECIFIED: Not specified
+ * @NVMF_TREQ_REQUIRED: Required
+ * @NVMF_TREQ_NOT_REQUIRED: Not Required
+ * @NVMF_TREQ_DISABLE_SQFLOW: SQ flow control disable supported
+ */
+enum nvmf_treq {
+ NVMF_TREQ_NOT_SPECIFIED = 0,
+ NVMF_TREQ_REQUIRED = 1,
+ NVMF_TREQ_NOT_REQUIRED = 2,
+ NVMF_TREQ_DISABLE_SQFLOW = 4,
+};
+
+/**
+ * enum nvmf_rdma_qptype - RDMA QP Service Type codes for Discovery Log Page
+ * entry TSAS RDMA_QPTYPE field
+ * @NVMF_RDMA_QPTYPE_CONNECTED: Reliable Connected
+ * @NVMF_RDMA_QPTYPE_DATAGRAM: Reliable Datagram
+ */
+enum nvmf_rdma_qptype {
+ NVMF_RDMA_QPTYPE_CONNECTED = 1,
+ NVMF_RDMA_QPTYPE_DATAGRAM = 2,
+};
+
+/**
+ * enum nvmf_rdma_prtype - RDMA Provider Type codes for Discovery Log Page
+ * entry TSAS RDMA_PRTYPE field
+ * @NVMF_RDMA_PRTYPE_NOT_SPECIFIED: No Provider Specified
+ * @NVMF_RDMA_PRTYPE_IB: InfiniBand
+ * @NVMF_RDMA_PRTYPE_ROCE: InfiniBand RoCE
+ * @NVMF_RDMA_PRTYPE_ROCEV2: InfiniBand RoCEV2
+ * @NVMF_RDMA_PRTYPE_IWARP: iWARP
+ */
+enum nvmf_rdma_prtype {
+ NVMF_RDMA_PRTYPE_NOT_SPECIFIED = 1,
+ NVMF_RDMA_PRTYPE_IB = 2,
+ NVMF_RDMA_PRTYPE_ROCE = 3,
+ NVMF_RDMA_PRTYPE_ROCEV2 = 4,
+ NVMF_RDMA_PRTYPE_IWARP = 5,
+};
+
+/**
+ * enum nvmf_rdma_cms - RDMA Connection Management Service Type codes for
+ * Discovery Log Page entry TSAS RDMA_CMS field
+ * @NVMF_RDMA_CMS_RDMA_CM: Sockets based endpoint addressing
+ *
+ */
+enum nvmf_rdma_cms {
+ NVMF_RDMA_CMS_RDMA_CM = 1,
+};
+
+/**
+ * enum nvmf_tcp_sectype - Transport Specific Address Subtype Definition for
+ * NVMe/TCP Transport
+ * @NVMF_TCP_SECTYPE_NONE: No Security
+ * @NVMF_TCP_SECTYPE_TLS: Transport Layer Security version 1.2
+ * @NVMF_TCP_SECTYPE_TLS13: Transport Layer Security version 1.3 or a subsequent
+ * version. The TLS protocol negotiates the version and
+ * cipher suite for each TCP connection.
+ */
+enum nvmf_tcp_sectype {
+ NVMF_TCP_SECTYPE_NONE = 0,
+ NVMF_TCP_SECTYPE_TLS = 1,
+ NVMF_TCP_SECTYPE_TLS13 = 2,
+};
+
+/**
+ * enum nvmf_log_discovery_lid_support - Discovery log specific support
+ * @NVMF_LOG_DISC_LID_NONE: None
+ * @NVMF_LOG_DISC_LID_EXTDLPES: Extended Discovery Log Page Entries Supported
+ * @NVMF_LOG_DISC_LID_PLEOS: Port Local Entries Only Supported
+ * @NVMF_LOG_DISC_LID_ALLSUBES: All NVM Subsystem Entries Supported
+ */
+enum nvmf_log_discovery_lid_support {
+ NVMF_LOG_DISC_LID_NONE = 0,
+ NVMF_LOG_DISC_LID_EXTDLPES = (1 << 0),
+ NVMF_LOG_DISC_LID_PLEOS = (1 << 1),
+ NVMF_LOG_DISC_LID_ALLSUBES = (1 << 2),
+};
+
+/**
+ * enum nvmf_log_discovery_lsp - Discovery log specific field
+ * @NVMF_LOG_DISC_LSP_NONE: None
+ * @NVMF_LOG_DISC_LSP_EXTDLPE: Extended Discovery Log Page Entries
+ * @NVMF_LOG_DISC_LSP_PLEO: Port Local Entries Only
+ * @NVMF_LOG_DISC_LSP_ALLSUBE: All NVM Subsystem Entries
+ */
+enum nvmf_log_discovery_lsp {
+ NVMF_LOG_DISC_LSP_NONE = 0,
+ NVMF_LOG_DISC_LSP_EXTDLPE = (1 << 0),
+ NVMF_LOG_DISC_LSP_PLEO = (1 << 1),
+ NVMF_LOG_DISC_LSP_ALLSUBE = (1 << 2),
+};
+
+/**
+ * struct nvmf_discovery_log - Discovery Log Page (Log Identifier 70h)
+ * @genctr: Generation Counter (GENCTR): Indicates the version of the discovery
+ * information, starting at a value of 0h. For each change in the
+ * Discovery Log Page, this counter is incremented by one. If the value
+ * of this field is FFFFFFFF_FFFFFFFFh, then the field shall be cleared
+ * to 0h when incremented (i.e., rolls over to 0h).
+ * @numrec: Number of Records (NUMREC): Indicates the number of records
+ * contained in the log.
+ * @recfmt: Record Format (RECFMT): Specifies the format of the Discovery Log
+ * Page. If a new format is defined, this value is incremented by one.
+ * The format of the record specified in this definition shall be 0h.
+ * @rsvd14: Reserved
+ * @entries: Discovery Log Page Entries - see &struct nvmf_disc_log_entry.
+ */
+struct nvmf_discovery_log {
+ __le64 genctr;
+ __le64 numrec;
+ __le16 recfmt;
+ __u8 rsvd14[1006];
+ struct nvmf_disc_log_entry entries[];
+};
+
+/*
+ * Discovery Information Management (DIM) command. This is sent by a
+ * host to a Discovery Controller (DC) to perform explicit registration.
+ */
+#define NVMF_ENAME_LEN 256
+#define NVMF_EVER_LEN 64
+
+/**
+ * enum nvmf_dim_tas - Discovery Information Management Task
+ * @NVMF_DIM_TAS_REGISTER: Register
+ * @NVMF_DIM_TAS_DEREGISTER: Deregister
+ * @NVMF_DIM_TAS_UPDATE: Update
+ */
+enum nvmf_dim_tas {
+ NVMF_DIM_TAS_REGISTER = 0x00,
+ NVMF_DIM_TAS_DEREGISTER = 0x01,
+ NVMF_DIM_TAS_UPDATE = 0x02,
+};
+
+/**
+ * enum nvmf_dim_entfmt - Discovery Information Management Entry Format
+ * @NVMF_DIM_ENTFMT_BASIC: Basic discovery information entry
+ * @NVMF_DIM_ENTFMT_EXTENDED: Extended discovery information entry
+ */
+enum nvmf_dim_entfmt {
+ NVMF_DIM_ENTFMT_BASIC = 0x01,
+ NVMF_DIM_ENTFMT_EXTENDED = 0x02,
+};
+
+/**
+ * enum nvmf_dim_etype -Discovery Information Management Entity Type
+ * @NVMF_DIM_ETYPE_HOST: Host
+ * @NVMF_DIM_ETYPE_DDC: Direct Discovery controller
+ * @NVMF_DIM_ETYPE_CDC: Centralized Discovery controller
+ */
+enum nvmf_dim_etype {
+ NVMF_DIM_ETYPE_HOST = 0x01,
+ NVMF_DIM_ETYPE_DDC = 0x02,
+ NVMF_DIM_ETYPE_CDC = 0x03,
+};
+
+/**
+ * enum nvmf_exattype - Extended Attribute Type
+ * @NVMF_EXATTYPE_HOSTID: Host Identifier
+ * @NVMF_EXATTYPE_SYMNAME: Symblic Name
+ */
+enum nvmf_exattype {
+ NVMF_EXATTYPE_HOSTID = 0x01,
+ NVMF_EXATTYPE_SYMNAME = 0x02,
+};
+
+/**
+ * struct nvmf_ext_attr - Extended Attribute (EXAT)
+ * @exattype: Extended Attribute Type (EXATTYPE) - see @enum nvmf_exattype
+ * @exatlen: Extended Attribute Length (EXATLEN)
+ * @exatval: Extended Attribute Value (EXATVAL) - size allocated for array
+ * must be a multiple of 4 bytes
+ */
+struct nvmf_ext_attr {
+ __le16 exattype;
+ __le16 exatlen;
+ __u8 exatval[];
+};
+
+/**
+ * struct nvmf_ext_die - Extended Discovery Information Entry (DIE)
+ * @trtype: Transport Type (&enum nvmf_trtype)
+ * @adrfam: Address Family (&enum nvmf_addr_family)
+ * @subtype: Subsystem Type (&enum nvme_subsys_type)
+ * @treq: Transport Requirements (&enum nvmf_treq)
+ * @portid: Port ID
+ * @cntlid: Controller ID
+ * @asqsz: Admin Max SQ Size
+ * @rsvd10: Reserved
+ * @trsvcid: Transport Service Identifier
+ * @resv64: Reserved
+ * @nqn: NVM Qualified Name
+ * @traddr: Transport Address
+ * @tsas: Transport Specific Address Subtype (&union nvmf_tsas)
+ * @tel: Total Entry Length
+ * @numexat: Number of Extended Attributes
+ * @resv1030: Reserved
+ * @exat: Extended Attributes 0 (&struct nvmf_ext_attr)
+ */
+struct nvmf_ext_die {
+ __u8 trtype;
+ __u8 adrfam;
+ __u8 subtype;
+ __u8 treq;
+ __le16 portid;
+ __le16 cntlid;
+ __le16 asqsz;
+ __u8 rsvd10[22];
+ char trsvcid[NVMF_TRSVCID_SIZE];
+ __u8 resv64[192];
+ char nqn[NVME_NQN_LENGTH];
+ char traddr[NVMF_TRADDR_SIZE];
+ union nvmf_tsas tsas;
+ __le32 tel;
+ __le16 numexat;
+ __u8 resv1030[2];
+ struct nvmf_ext_attr exat[];
+};
+
+/**
+ * union nvmf_die - Discovery Information Entry (DIE)
+ * @basic: Basic format (&struct nvmf_disc_log_entry)
+ * @extended: Extended format (&struct nvmf_ext_die)
+ *
+ * Depending on the ENTFMT specified in the DIM, DIEs can be entered
+ * with the Basic or Extended formats. For Basic format, each entry
+ * has a fixed length. Therefore, the "basic" field defined below can
+ * be accessed as a C array. For the Extended format, however, each
+ * entry is of variable length (TEL). Therefore, the "extended" field
+ * defined below cannot be accessed as a C array. Instead, the
+ * "extended" field is akin to a linked-list, where one can "walk"
+ * through the list. To move to the next entry, one simply adds the
+ * current entry's length (TEL) to the "walk" pointer. The number of
+ * entries in the list is specified by NUMENT. Although extended
+ * entries are of a variable lengths (TEL), TEL is always a multiple of
+ * 4 bytes.
+ */
+union nvmf_die {
+ struct nvmf_disc_log_entry basic[0];
+ struct nvmf_ext_die extended;
+};
+
+/**
+ * struct nvmf_dim_data - Discovery Information Management (DIM) - Data
+ * @tdl: Total Data Length
+ * @rsvd4: Reserved
+ * @nument: Number of entries
+ * @entfmt: Entry Format (&enum nvmf_dim_entfmt)
+ * @etype: Entity Type (&enum nvmf_dim_etype)
+ * @portlcl: Port Local
+ * @rsvd21: Reserved
+ * @ektype: Entry Key Type
+ * @eid: Entity Identifier (e.g. Host NQN)
+ * @ename: Entity Name (e.g. hostname)
+ * @ever: Entity Version (e.g. OS Name/Version)
+ * @rsvd600: Reserved
+ * @die: Discovery Information Entry (see @nument above)
+ */
+struct nvmf_dim_data {
+ __le32 tdl;
+ __u8 rsvd4[4];
+ __le64 nument;
+ __le16 entfmt;
+ __le16 etype;
+ __u8 portlcl;
+ __u8 rsvd21;
+ __le16 ektype;
+ char eid[NVME_NQN_LENGTH];
+ char ename[NVMF_ENAME_LEN];
+ char ever[NVMF_EVER_LEN];
+ __u8 rsvd600[424];
+ union nvmf_die die[];
+};
+
+/**
+ * struct nvmf_connect_data - Data payload for the 'connect' command
+ * @hostid: Host ID of the connecting host
+ * @cntlid: Requested controller ID
+ * @rsvd4: Reserved
+ * @subsysnqn: Subsystem NQN to connect to
+ * @hostnqn: Host NQN of the connecting host
+ * @rsvd5: Reserved
+ */
+struct nvmf_connect_data {
+ __u8 hostid[16];
+ __le16 cntlid;
+ char rsvd4[238];
+ char subsysnqn[NVME_NQN_LENGTH];
+ char hostnqn[NVME_NQN_LENGTH];
+ char rsvd5[256];
+};
+
+/**
+ * struct nvme_mi_read_nvm_ss_info - NVM Subsystem Information Data Structure
+ * @nump: Number of Ports
+ * @mjr: NVMe-MI Major Version Number
+ * @mnr: NVMe-MI Minor Version Number
+ * @rsvd3: Reserved
+ */
+struct nvme_mi_read_nvm_ss_info {
+ __u8 nump;
+ __u8 mjr;
+ __u8 mnr;
+ __u8 rsvd3[29];
+};
+
+/**
+ * struct nvme_mi_port_pcie - PCIe Port Specific Data
+ * @mps: PCIe Maximum Payload Size
+ * @sls: PCIe Supported Link Speeds Vector
+ * @cls: PCIe Current Link Speed
+ * @mlw: PCIe Maximum Link Width
+ * @nlw: PCIe Negotiated Link Width
+ * @pn: PCIe Port Number
+ * @rsvd14: Reserved
+ */
+struct nvme_mi_port_pcie {
+ __u8 mps;
+ __u8 sls;
+ __u8 cls;
+ __u8 mlw;
+ __u8 nlw;
+ __u8 pn;
+ __u8 rsvd14[18];
+};
+
+/**
+ * struct nvme_mi_port_smb - SMBus Port Specific Data
+ * @vpd_addr: Current VPD SMBus/I2C Address
+ * @mvpd_freq: Maximum VPD Access SMBus/I2C Frequency
+ * @mme_addr: Current Management Endpoint SMBus/I2C Address
+ * @mme_freq: Maximum Management Endpoint SMBus/I2C Frequency
+ * @nvmebm: NVMe Basic Management
+ * @rsvd13: Reserved
+ */
+struct nvme_mi_port_smb {
+ __u8 vpd_addr;
+ __u8 mvpd_freq;
+ __u8 mme_addr;
+ __u8 mme_freq;
+ __u8 nvmebm;
+ __u8 rsvd13[19];
+};
+
+/**
+ * struct nvme_mi_read_port_info - Port Information Data Structure
+ * @portt: Port Type
+ * @rsvd1: Reserved
+ * @mmctptus: Maximum MCTP Transmission Unit Size
+ * @meb: Management Endpoint Buffer Size
+ * @pcie: PCIe Port Specific Data
+ * @smb: SMBus Port Specific Data
+ */
+struct nvme_mi_read_port_info {
+ __u8 portt;
+ __u8 rsvd1;
+ __le16 mmctptus;
+ __le32 meb;
+ union {
+ struct nvme_mi_port_pcie pcie;
+ struct nvme_mi_port_smb smb;
+ };
+};
+
+/**
+ * struct nvme_mi_read_ctrl_info - Controller Information Data Structure
+ * @portid: Port Identifier
+ * @rsvd1: Reserved
+ * @prii: PCIe Routing ID Information
+ * @pri: PCIe Routing ID
+ * @vid: PCI Vendor ID
+ * @did: PCI Device ID
+ * @ssvid: PCI Subsystem Vendor ID
+ * @ssid: PCI Subsystem Device ID
+ * @rsvd16: Reserved
+ */
+struct nvme_mi_read_ctrl_info {
+ __u8 portid;
+ __u8 rsvd1[4];
+ __u8 prii;
+ __le16 pri;
+ __le16 vid;
+ __le16 did;
+ __le16 ssvid;
+ __le16 ssid;
+ __u8 rsvd16[16];
+};
+
+/**
+ * struct nvme_mi_osc - Optionally Supported Command Data Structure
+ * @type: Command Type
+ * @opc: Opcode
+ */
+struct nvme_mi_osc {
+ __u8 type;
+ __u8 opc;
+};
+
+/**
+ * struct nvme_mi_read_sc_list - Management Endpoint Buffer Supported Command List Data Structure
+ * @numcmd: Number of Commands
+ * @cmds: MEB supported Command Data Structure.
+ * See @struct nvme_mi_osc
+ */
+struct nvme_mi_read_sc_list {
+ __le16 numcmd;
+ struct nvme_mi_osc cmds[];
+};
+
+/**
+ * struct nvme_mi_nvm_ss_health_status - Subsystem Management Data Structure
+ * @nss: NVM Subsystem Status
+ * @sw: Smart Warnings
+ * @ctemp: Composite Temperature
+ * @pdlu: Percentage Drive Life Used
+ * @ccs: Composite Controller Status
+ * @rsvd8: Reserved
+ */
+struct nvme_mi_nvm_ss_health_status {
+ __u8 nss;
+ __u8 sw;
+ __u8 ctemp;
+ __u8 pdlu;
+ __le16 ccs;
+ __u8 rsvd8[2];
+};
+
+/**
+ * enum nvme_mi_ccs - Get State Control Primitive Success Response Fields - Control Primitive Specific Response
+ * @NVME_MI_CCS_RDY: Ready
+ * @NVME_MI_CCS_CFS: Controller Fatal Status
+ * @NVME_MI_CCS_SHST: Shutdown Status
+ * @NVME_MI_CCS_NSSRO: NVM Subsystem Reset Occurred
+ * @NVME_MI_CCS_CECO: Controller Enable Change Occurred
+ * @NVME_MI_CCS_NAC: Namespace Attribute Changed
+ * @NVME_MI_CCS_FA: Firmware Activated
+ * @NVME_MI_CCS_CSTS: Controller Status Change
+ * @NVME_MI_CCS_CTEMP: Composite Temperature Change
+ * @NVME_MI_CCS_PDLU: Percentage Used
+ * @NVME_MI_CCS_SPARE: Available Spare
+ * @NVME_MI_CCS_CCWARN: Critical Warning
+ */
+enum nvme_mi_ccs {
+ NVME_MI_CCS_RDY = 1 << 0,
+ NVME_MI_CCS_CFS = 1 << 1,
+ NVME_MI_CCS_SHST = 1 << 2,
+ NVME_MI_CCS_NSSRO = 1 << 4,
+ NVME_MI_CCS_CECO = 1 << 5,
+ NVME_MI_CCS_NAC = 1 << 6,
+ NVME_MI_CCS_FA = 1 << 7,
+ NVME_MI_CCS_CSTS = 1 << 8,
+ NVME_MI_CCS_CTEMP = 1 << 9,
+ NVME_MI_CCS_PDLU = 1 << 10,
+ NVME_MI_CCS_SPARE = 1 << 11,
+ NVME_MI_CCS_CCWARN = 1 << 12,
+};
+
+/* backwards compat for old "CCS" definitions */
+#define nvme_mi_css nvme_mi_ccs
+#define NVME_MI_CSS_CFS NVME_MI_CCS_CFS
+#define NVME_MI_CSS_SHST NVME_MI_CCS_SHST
+#define NVME_MI_CSS_NSSRO NVME_MI_CCS_NSSRO
+#define NVME_MI_CSS_CECO NVME_MI_CCS_CECO
+#define NVME_MI_CSS_NAC NVME_MI_CCS_NAC
+#define NVME_MI_CSS_FA NVME_MI_CCS_FA
+#define NVME_MI_CSS_CSTS NVME_MI_CCS_CSTS
+#define NVME_MI_CSS_CTEMP NVME_MI_CCS_CTEMP
+#define NVME_MI_CSS_PDLU NVME_MI_CCS_PDLU
+#define NVME_MI_CSS_SPARE NVME_MI_CCS_SPARE
+#define NVME_MI_CSS_CCWARN NVME_MI_CCS_CCWARN
+
+/**
+ * struct nvme_mi_ctrl_health_status - Controller Health Data Structure (CHDS)
+ * @ctlid: Controller Identifier
+ * @csts: Controller Status
+ * @ctemp: Composite Temperature
+ * @pdlu: Percentage Used
+ * @spare: Available Spare
+ * @cwarn: Critical Warning
+ * @rsvd9: Reserved
+ */
+struct nvme_mi_ctrl_health_status {
+ __le16 ctlid;
+ __le16 csts;
+ __le16 ctemp;
+ __u8 pdlu;
+ __u8 spare;
+ __u8 cwarn;
+ __u8 rsvd9[7];
+};
+
+/**
+ * enum nvme_mi_csts - Controller Health Data Structure (CHDS) - Controller Status (CSTS)
+ * @NVME_MI_CSTS_RDY: Ready
+ * @NVME_MI_CSTS_CFS: Controller Fatal Status
+ * @NVME_MI_CSTS_SHST: Shutdown Status
+ * @NVME_MI_CSTS_NSSRO: NVM Subsystem Reset Occurred
+ * @NVME_MI_CSTS_CECO: Controller Enable Change Occurred
+ * @NVME_MI_CSTS_NAC: Namespace Attribute Changed
+ * @NVME_MI_CSTS_FA: Firmware Activated
+ */
+enum nvme_mi_csts {
+ NVME_MI_CSTS_RDY = 1 << 0,
+ NVME_MI_CSTS_CFS = 1 << 1,
+ NVME_MI_CSTS_SHST = 1 << 2,
+ NVME_MI_CSTS_NSSRO = 1 << 4,
+ NVME_MI_CSTS_CECO = 1 << 5,
+ NVME_MI_CSTS_NAC = 1 << 6,
+ NVME_MI_CSTS_FA = 1 << 7,
+};
+
+/**
+ * enum nvme_mi_cwarn - Controller Health Data Structure (CHDS) - Critical Warning (CWARN)
+ * @NVME_MI_CWARN_ST: Spare Threshold
+ * @NVME_MI_CWARN_TAUT: Temperature Above or Under Threshold
+ * @NVME_MI_CWARN_RD: Reliability Degraded
+ * @NVME_MI_CWARN_RO: Read Only
+ * @NVME_MI_CWARN_VMBF: Volatile Memory Backup Failed
+ */
+enum nvme_mi_cwarn {
+ NVME_MI_CWARN_ST = 1 << 0,
+ NVME_MI_CWARN_TAUT = 1 << 1,
+ NVME_MI_CWARN_RD = 1 << 2,
+ NVME_MI_CWARN_RO = 1 << 3,
+ NVME_MI_CWARN_VMBF = 1 << 4,
+};
+
+/**
+ * struct nvme_mi_vpd_mra - NVMe MultiRecord Area
+ * @nmravn: NVMe MultiRecord Area Version Number
+ * @ff: Form Factor
+ * @rsvd7: Reserved
+ * @i18vpwr: Initial 1.8 V Power Supply Requirements
+ * @m18vpwr: Maximum 1.8 V Power Supply Requirements
+ * @i33vpwr: Initial 3.3 V Power Supply Requirements
+ * @m33vpwr: Maximum 3.3 V Power Supply Requirements
+ * @rsvd17: Reserved
+ * @m33vapsr: Maximum 3.3 Vi aux Power Supply Requirements
+ * @i5vapsr: Initial 5 V Power Supply Requirements
+ * @m5vapsr: Maximum 5 V Power Supply Requirements
+ * @i12vapsr: Initial 12 V Power Supply Requirements
+ * @m12vapsr: Maximum 12 V Power Supply Requirements
+ * @mtl: Maximum Thermal Load
+ * @tnvmcap: Total NVM Capacity
+ * @rsvd37: Reserved
+ */
+struct nvme_mi_vpd_mra {
+ __u8 nmravn;
+ __u8 ff;
+ __u8 rsvd7[6];
+ __u8 i18vpwr;
+ __u8 m18vpwr;
+ __u8 i33vpwr;
+ __u8 m33vpwr;
+ __u8 rsvd17;
+ __u8 m33vapsr;
+ __u8 i5vapsr;
+ __u8 m5vapsr;
+ __u8 i12vapsr;
+ __u8 m12vapsr;
+ __u8 mtl;
+ __u8 tnvmcap[16];
+ __u8 rsvd37[27];
+};
+
+/**
+ * struct nvme_mi_vpd_ppmra - NVMe PCIe Port MultiRecord Area
+ * @nppmravn: NVMe PCIe Port MultiRecord Area Version Number
+ * @pn: PCIe Port Number
+ * @ppi: Port Information
+ * @ls: PCIe Link Speed
+ * @mlw: PCIe Maximum Link Width
+ * @mctp: MCTP Support
+ * @refccap: Ref Clk Capability
+ * @pi: Port Identifier
+ * @rsvd13: Reserved
+ */
+struct nvme_mi_vpd_ppmra {
+ __u8 nppmravn;
+ __u8 pn;
+ __u8 ppi;
+ __u8 ls;
+ __u8 mlw;
+ __u8 mctp;
+ __u8 refccap;
+ __u8 pi;
+ __u8 rsvd13[3];
+};
+
+/**
+ * struct nvme_mi_vpd_telem - Vital Product Data Element Descriptor
+ * @type: Type of the Element Descriptor
+ * @rev: Revision of the Element Descriptor
+ * @len: Number of bytes in the Element Descriptor
+ * @data: Type-specific information associated with
+ * the Element Descriptor
+ */
+struct nvme_mi_vpd_telem {
+ __u8 type;
+ __u8 rev;
+ __u8 len;
+ __u8 data[0];
+};
+
+/**
+ * enum nvme_mi_elem - Element Descriptor Types
+ * @NVME_MI_ELEM_EED: Extended Element Descriptor
+ * @NVME_MI_ELEM_USCE: Upstream Connector Element Descriptor
+ * @NVME_MI_ELEM_ECED: Expansion Connector Element Descriptor
+ * @NVME_MI_ELEM_LED: Label Element Descriptor
+ * @NVME_MI_ELEM_SMBMED: SMBus/I2C Mux Element Descriptor
+ * @NVME_MI_ELEM_PCIESED: PCIe Switch Element Descriptor
+ * @NVME_MI_ELEM_NVMED: NVM Subsystem Element Descriptor
+ */
+enum nvme_mi_elem {
+ NVME_MI_ELEM_EED = 1,
+ NVME_MI_ELEM_USCE = 2,
+ NVME_MI_ELEM_ECED = 3,
+ NVME_MI_ELEM_LED = 4,
+ NVME_MI_ELEM_SMBMED = 5,
+ NVME_MI_ELEM_PCIESED = 6,
+ NVME_MI_ELEM_NVMED = 7,
+};
+
+/**
+ * struct nvme_mi_vpd_tra - Vital Product Data Topology MultiRecord
+ * @vn: Version Number
+ * @rsvd6: Reserved
+ * @ec: Element Count
+ * @elems: Element Descriptor
+ */
+struct nvme_mi_vpd_tra {
+ __u8 vn;
+ __u8 rsvd6;
+ __u8 ec;
+ struct nvme_mi_vpd_telem elems[0];
+};
+
+/**
+ * struct nvme_mi_vpd_mr_common - NVMe MultiRecord Area
+ * @type: NVMe Record Type ID
+ * @rf: Record Format
+ * @rlen: Record Length
+ * @rchksum: Record Checksum
+ * @hchksum: Header Checksum
+ * @nmra: NVMe MultiRecord Area
+ * @ppmra: NVMe PCIe Port MultiRecord Area
+ * @tmra: Topology MultiRecord Area
+ */
+struct nvme_mi_vpd_mr_common {
+ __u8 type;
+ __u8 rf;
+ __u8 rlen;
+ __u8 rchksum;
+ __u8 hchksum;
+
+ union {
+ struct nvme_mi_vpd_mra nmra;
+ struct nvme_mi_vpd_ppmra ppmra;
+ struct nvme_mi_vpd_tra tmra;
+ };
+};
+
+/**
+ * struct nvme_mi_vpd_hdr - Vital Product Data Common Header
+ * @ipmiver: IPMI Format Version Number
+ * @iuaoff: Internal Use Area Starting Offset
+ * @ciaoff: Chassis Info Area Starting Offset
+ * @biaoff: Board Info Area Starting Offset
+ * @piaoff: Product Info Area Starting Offset
+ * @mrioff: MultiRecord Info Area Starting Offset
+ * @rsvd6: Reserved
+ * @chchk: Common Header Checksum
+ * @vpd: Vital Product Data
+ */
+struct nvme_mi_vpd_hdr {
+ __u8 ipmiver;
+ __u8 iuaoff;
+ __u8 ciaoff;
+ __u8 biaoff;
+ __u8 piaoff;
+ __u8 mrioff;
+ __u8 rsvd6;
+ __u8 chchk;
+ __u8 vpd[];
+};
+
+/**
+ * enum nvme_status_field - Defines all parts of the nvme status field: status
+ * code, status code type, and additional flags.
+ * @NVME_SCT_GENERIC: Generic errors applicable to multiple opcodes
+ * @NVME_SCT_CMD_SPECIFIC: Errors associated to a specific opcode
+ * @NVME_SCT_MEDIA: Errors associated with media and data integrity
+ * @NVME_SCT_PATH: Errors associated with the paths connection
+ * @NVME_SCT_VS: Vendor specific errors
+ * @NVME_SCT_MASK: Mask to get the value of the Status Code Type
+ * @NVME_SCT_SHIFT: Shift value to get the value of the Status
+ * Code Type
+ * @NVME_SC_MASK: Mask to get the value of the status code.
+ * @NVME_SC_SHIFT: Shift value to get the value of the status
+ * code.
+ * @NVME_SC_SUCCESS: Successful Completion: The command
+ * completed without error.
+ * @NVME_SC_INVALID_OPCODE: Invalid Command Opcode: A reserved coded
+ * value or an unsupported value in the
+ * command opcode field.
+ * @NVME_SC_INVALID_FIELD: Invalid Field in Command: A reserved
+ * coded value or an unsupported value in a
+ * defined field.
+ * @NVME_SC_CMDID_CONFLICT: Command ID Conflict: The command
+ * identifier is already in use.
+ * @NVME_SC_DATA_XFER_ERROR: Data Transfer Error: Transferring the
+ * data or metadata associated with a
+ * command experienced an error.
+ * @NVME_SC_POWER_LOSS: Commands Aborted due to Power Loss
+ * Notification: Indicates that the command
+ * was aborted due to a power loss
+ * notification.
+ * @NVME_SC_INTERNAL: Internal Error: The command was not
+ * completed successfully due to an internal error.
+ * @NVME_SC_ABORT_REQ: Command Abort Requested: The command was
+ * aborted due to an Abort command being
+ * received that specified the Submission
+ * Queue Identifier and Command Identifier
+ * of this command.
+ * @NVME_SC_ABORT_QUEUE: Command Aborted due to SQ Deletion: The
+ * command was aborted due to a Delete I/O
+ * Submission Queue request received for the
+ * Submission Queue to which the command was
+ * submitted.
+ * @NVME_SC_FUSED_FAIL: Command Aborted due to Failed Fused Command:
+ * The command was aborted due to the other
+ * command in a fused operation failing.
+ * @NVME_SC_FUSED_MISSING: Aborted due to Missing Fused Command: The
+ * fused command was aborted due to the
+ * adjacent submission queue entry not
+ * containing a fused command that is the
+ * other command.
+ * @NVME_SC_INVALID_NS: Invalid Namespace or Format: The
+ * namespace or the format of that namespace
+ * is invalid.
+ * @NVME_SC_CMD_SEQ_ERROR: Command Sequence Error: The command was
+ * aborted due to a protocol violation in a
+ * multi-command sequence.
+ * @NVME_SC_SGL_INVALID_LAST: Invalid SGL Segment Descriptor: The
+ * command includes an invalid SGL Last
+ * Segment or SGL Segment descriptor.
+ * @NVME_SC_SGL_INVALID_COUNT: Invalid Number of SGL Descriptors: There
+ * is an SGL Last Segment descriptor or an
+ * SGL Segment descriptor in a location
+ * other than the last descriptor of a
+ * segment based on the length indicated.
+ * @NVME_SC_SGL_INVALID_DATA: Data SGL Length Invalid: This may occur
+ * if the length of a Data SGL is too short.
+ * This may occur if the length of a Data
+ * SGL is too long and the controller does
+ * not support SGL transfers longer than the
+ * amount of data to be transferred as
+ * indicated in the SGL Support field of the
+ * Identify Controller data structure.
+ * @NVME_SC_SGL_INVALID_METADATA: Metadata SGL Length Invalid: This may
+ * occur if the length of a Metadata SGL is
+ * too short. This may occur if the length
+ * of a Metadata SGL is too long and the
+ * controller does not support SGL transfers
+ * longer than the amount of data to be
+ * transferred as indicated in the SGL
+ * Support field of the Identify Controller
+ * data structure.
+ * @NVME_SC_SGL_INVALID_TYPE: SGL Descriptor Type Invalid: The type of
+ * an SGL Descriptor is a type that is not
+ * supported by the controller.
+ * @NVME_SC_CMB_INVALID_USE: Invalid Use of Controller Memory Buffer:
+ * The attempted use of the Controller
+ * Memory Buffer is not supported by the
+ * controller.
+ * @NVME_SC_PRP_INVALID_OFFSET: PRP Offset Invalid: The Offset field for
+ * a PRP entry is invalid.
+ * @NVME_SC_AWU_EXCEEDED: Atomic Write Unit Exceeded: The length
+ * specified exceeds the atomic write unit size.
+ * @NVME_SC_OP_DENIED: Operation Denied: The command was denied
+ * due to lack of access rights. Refer to
+ * the appropriate security specification.
+ * @NVME_SC_SGL_INVALID_OFFSET: SGL Offset Invalid: The offset specified
+ * in a descriptor is invalid. This may
+ * occur when using capsules for data
+ * transfers in NVMe over Fabrics
+ * implementations and an invalid offset in
+ * the capsule is specified.
+ * @NVME_SC_HOSTID_FORMAT: Host Identifier Inconsistent Format: The
+ * NVM subsystem detected the simultaneous
+ * use of 64- bit and 128-bit Host
+ * Identifier values on different
+ * controllers.
+ * @NVME_SC_KAT_EXPIRED: Keep Alive Timer Expired: The Keep Alive
+ * Timer expired.
+ * @NVME_SC_KAT_INVALID: Keep Alive Timeout Invalid: The Keep
+ * Alive Timeout value specified is invalid.
+ * @NVME_SC_CMD_ABORTED_PREMEPT: Command Aborted due to Preempt and Abort:
+ * The command was aborted due to a
+ * Reservation Acquire command.
+ * @NVME_SC_SANITIZE_FAILED: Sanitize Failed: The most recent sanitize
+ * operation failed and no recovery action
+ * has been successfully completed.
+ * @NVME_SC_SANITIZE_IN_PROGRESS: Sanitize In Progress: The requested
+ * function (e.g., command) is prohibited
+ * while a sanitize operation is in
+ * progress.
+ * @NVME_SC_SGL_INVALID_GRANULARITY: SGL Data Block Granularity Invalid: The
+ * Address alignment or Length granularity
+ * for an SGL Data Block descriptor is
+ * invalid.
+ * @NVME_SC_CMD_IN_CMBQ_NOT_SUPP: Command Not Supported for Queue in CMB:
+ * The implementation does not support
+ * submission of the command to a Submission
+ * Queue in the Controller Memory Buffer or
+ * command completion to a Completion Queue
+ * in the Controller Memory Buffer.
+ * @NVME_SC_NS_WRITE_PROTECTED: Namespace is Write Protected: The command
+ * is prohibited while the namespace is
+ * write protected as a result of a change
+ * in the namespace write protection state
+ * as defined by the Namespace Write
+ * Protection State Machine.
+ * @NVME_SC_CMD_INTERRUPTED: Command Interrupted: Command processing
+ * was interrupted and the controller is
+ * unable to successfully complete the
+ * command. The host should retry the
+ * command.
+ * @NVME_SC_TRAN_TPORT_ERROR: Transient Transport Error: A transient
+ * transport error was detected. If the
+ * command is retried on the same
+ * controller, the command is likely to
+ * succeed. A command that fails with a
+ * transient transport error four or more
+ * times should be treated as a persistent
+ * transport error that is not likely to
+ * succeed if retried on the same
+ * controller.
+ * @NVME_SC_PROHIBITED_BY_CMD_AND_FEAT: Command Prohibited by Command and Feature
+ * Lockdown: The command was aborted due to
+ * command execution being prohibited by
+ * the Command and Feature Lockdown.
+ * @NVME_SC_ADMIN_CMD_MEDIA_NOT_READY: Admin Command Media Not Ready: The Admin
+ * command requires access to media and
+ * the media is not ready.
+ * @NVME_SC_FDP_DISABLED: Command is not allowed when
+ * Flexible Data Placement is disabled.
+ * @NVME_SC_INVALID_PLACEMENT_HANDLE_LIST: The Placement Handle List is invalid
+ * due to invalid Reclaim Unit Handle Identifier or
+ * valid Reclaim Unit Handle Identifier but restricted or
+ * the Placement Handle List number of entries exceeded the
+ * maximum number allowed.
+ * @NVME_SC_LBA_RANGE: LBA Out of Range: The command references
+ * an LBA that exceeds the size of the namespace.
+ * @NVME_SC_CAP_EXCEEDED: Capacity Exceeded: Execution of the
+ * command has caused the capacity of the
+ * namespace to be exceeded.
+ * @NVME_SC_NS_NOT_READY: Namespace Not Ready: The namespace is not
+ * ready to be accessed as a result of a
+ * condition other than a condition that is
+ * reported as an Asymmetric Namespace
+ * Access condition.
+ * @NVME_SC_RESERVATION_CONFLICT: Reservation Conflict: The command was
+ * aborted due to a conflict with a
+ * reservation held on the accessed
+ * namespace.
+ * @NVME_SC_FORMAT_IN_PROGRESS: Format In Progress: A Format NVM command
+ * is in progress on the namespace.
+ * @NVME_SC_CQ_INVALID: Completion Queue Invalid: The Completion
+ * Queue identifier specified in the command
+ * does not exist.
+ * @NVME_SC_QID_INVALID: Invalid Queue Identifier: The creation of
+ * the I/O Completion Queue failed due to an
+ * invalid queue identifier specified as
+ * part of the command. An invalid queue
+ * identifier is one that is currently in
+ * use or one that is outside the range
+ * supported by the controller.
+ * @NVME_SC_QUEUE_SIZE: Invalid Queue Size: The host attempted to
+ * create an I/O Completion Queue with an
+ * invalid number of entries.
+ * @NVME_SC_ABORT_LIMIT: Abort Command Limit Exceeded: The number
+ * of concurrently outstanding Abort commands
+ * has exceeded the limit indicated in the
+ * Identify Controller data structure.
+ * @NVME_SC_ABORT_MISSING: Abort Command is missing: The abort
+ * command is missing.
+ * @NVME_SC_ASYNC_LIMIT: Asynchronous Event Request Limit
+ * Exceeded: The number of concurrently
+ * outstanding Asynchronous Event Request
+ * commands has been exceeded.
+ * @NVME_SC_FIRMWARE_SLOT: Invalid Firmware Slot: The firmware slot
+ * indicated is invalid or read only. This
+ * error is indicated if the firmware slot
+ * exceeds the number supported.
+ * @NVME_SC_FIRMWARE_IMAGE: Invalid Firmware Image: The firmware
+ * image specified for activation is invalid
+ * and not loaded by the controller.
+ * @NVME_SC_INVALID_VECTOR: Invalid Interrupt Vector: The creation of
+ * the I/O Completion Queue failed due to an
+ * invalid interrupt vector specified as
+ * part of the command.
+ * @NVME_SC_INVALID_LOG_PAGE: Invalid Log Page: The log page indicated
+ * is invalid. This error condition is also
+ * returned if a reserved log page is
+ * requested.
+ * @NVME_SC_INVALID_FORMAT: Invalid Format: The LBA Format specified
+ * is not supported.
+ * @NVME_SC_FW_NEEDS_CONV_RESET: Firmware Activation Requires Conventional Reset:
+ * The firmware commit was successful,
+ * however, activation of the firmware image
+ * requires a conventional reset.
+ * @NVME_SC_INVALID_QUEUE: Invalid Queue Deletion: Invalid I/O
+ * Completion Queue specified to delete.
+ * @NVME_SC_FEATURE_NOT_SAVEABLE: Feature Identifier Not Saveable: The
+ * Feature Identifier specified does not
+ * support a saveable value.
+ * @NVME_SC_FEATURE_NOT_CHANGEABLE: Feature Not Changeable: The Feature
+ * Identifier is not able to be changed.
+ * @NVME_SC_FEATURE_NOT_PER_NS: Feature Not Namespace Specific: The
+ * Feature Identifier specified is not
+ * namespace specific. The Feature
+ * Identifier settings apply across all
+ * namespaces.
+ * @NVME_SC_FW_NEEDS_SUBSYS_RESET: Firmware Activation Requires NVM
+ * Subsystem Reset: The firmware commit was
+ * successful, however, activation of the
+ * firmware image requires an NVM Subsystem.
+ * @NVME_SC_FW_NEEDS_RESET: Firmware Activation Requires Controller
+ * Level Reset: The firmware commit was
+ * successful; however, the image specified
+ * does not support being activated without
+ * a reset.
+ * @NVME_SC_FW_NEEDS_MAX_TIME: Firmware Activation Requires Maximum Time
+ * Violation: The image specified if
+ * activated immediately would exceed the
+ * Maximum Time for Firmware Activation
+ * (MTFA) value reported in Identify
+ * Controller.
+ * @NVME_SC_FW_ACTIVATE_PROHIBITED: Firmware Activation Prohibited: The image
+ * specified is being prohibited from
+ * activation by the controller for vendor
+ * specific reasons.
+ * @NVME_SC_OVERLAPPING_RANGE: Overlapping Range: The downloaded
+ * firmware image has overlapping ranges.
+ * @NVME_SC_NS_INSUFFICIENT_CAP: Namespace Insufficient Capacity: Creating
+ * the namespace requires more free space
+ * than is currently available.
+ * @NVME_SC_NS_ID_UNAVAILABLE: Namespace Identifier Unavailable: The
+ * number of namespaces supported has been
+ * exceeded.
+ * @NVME_SC_NS_ALREADY_ATTACHED: Namespace Already Attached: The
+ * controller is already attached to the
+ * namespace specified.
+ * @NVME_SC_NS_IS_PRIVATE: Namespace Is Private: The namespace is
+ * private and is already attached to one
+ * controller.
+ * @NVME_SC_NS_NOT_ATTACHED: Namespace Not Attached: The request to
+ * detach the controller could not be
+ * completed because the controller is not
+ * attached to the namespace.
+ * @NVME_SC_THIN_PROV_NOT_SUPP: Thin Provisioning Not Supported: Thin
+ * provisioning is not supported by the
+ * controller.
+ * @NVME_SC_CTRL_LIST_INVALID: Controller List Invalid: The controller
+ * list provided contains invalid controller
+ * ids.
+ * @NVME_SC_SELF_TEST_IN_PROGRESS: Device Self-test In Progress: The controller
+ * or NVM subsystem already has a device
+ * self-test operation in process.
+ * @NVME_SC_BP_WRITE_PROHIBITED: Boot Partition Write Prohibited: The
+ * command is trying to modify a locked Boot
+ * Partition.
+ * @NVME_SC_INVALID_CTRL_ID: Invalid Controller Identifier:
+ * @NVME_SC_INVALID_SEC_CTRL_STATE: Invalid Secondary Controller State
+ * @NVME_SC_INVALID_CTRL_RESOURCES: Invalid Number of Controller Resources
+ * @NVME_SC_INVALID_RESOURCE_ID: Invalid Resource Identifier
+ * @NVME_SC_PMR_SAN_PROHIBITED: Sanitize Prohibited While Persistent
+ * Memory Region is Enabled
+ * @NVME_SC_ANA_GROUP_ID_INVALID: ANA Group Identifier Invalid: The specified
+ * ANA Group Identifier (ANAGRPID) is not
+ * supported in the submitted command.
+ * @NVME_SC_ANA_ATTACH_FAILED: ANA Attach Failed: The controller is not
+ * attached to the namespace as a result
+ * of an ANA condition.
+ * @NVME_SC_INSUFFICIENT_CAP: Insufficient Capacity: Requested operation
+ * requires more free space than is currently
+ * available.
+ * @NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED: Namespace Attachment Limit Exceeded:
+ * Attaching the ns to a controller causes
+ * max number of ns attachments allowed
+ * to be exceeded.
+ * @NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED: Prohibition of Command Execution
+ * Not Supported
+ * @NVME_SC_IOCS_NOT_SUPPORTED: I/O Command Set Not Supported
+ * @NVME_SC_IOCS_NOT_ENABLED: I/O Command Set Not Enabled
+ * @NVME_SC_IOCS_COMBINATION_REJECTED: I/O Command Set Combination Rejected
+ * @NVME_SC_INVALID_IOCS: Invalid I/O Command Set
+ * @NVME_SC_ID_UNAVAILABLE: Identifier Unavailable
+ * @NVME_SC_INVALID_DISCOVERY_INFO: The discovery information provided in
+ * one or more extended discovery
+ * information entries is not applicable
+ * for the type of entity selected in
+ * the Entity Type (ETYPE) field of the
+ * Discovery Information Management
+ * command data portion’s header.
+ * @NVME_SC_ZONING_DATA_STRUCT_LOCKED:The requested Zoning data structure
+ * is locked on the CDC.
+ * @NVME_SC_ZONING_DATA_STRUCT_NOTFND:The requested Zoning data structure
+ * does not exist on the CDC.
+ * @NVME_SC_INSUFFICIENT_DISC_RES: The number of discover information
+ * entries provided in the data portion
+ * of the Discovery Information
+ * Management command for a registration
+ * task (i.e., TAS field cleared to 0h)
+ * exceeds the available capacity for
+ * new discovery information entries on
+ * the CDC or DDC. This may be a
+ * transient condition.
+ * @NVME_SC_REQSTD_FUNCTION_DISABLED: Fabric Zoning is not enabled on the
+ * CDC
+ * @NVME_SC_ZONEGRP_ORIGINATOR_INVLD: The NQN contained in the ZoneGroup
+ * Originator field does not match the
+ * Host NQN used by the DDC to connect
+ * to the CDC.
+ * @NVME_SC_BAD_ATTRIBUTES: Conflicting Dataset Management Attributes
+ * @NVME_SC_INVALID_PI: Invalid Protection Information
+ * @NVME_SC_READ_ONLY: Attempted Write to Read Only Range
+ * @NVME_SC_CMD_SIZE_LIMIT_EXCEEDED: Command Size Limit Exceeded
+ * @NVME_SC_INCOMPATIBLE_NS: Incompatible Namespace or Format: At
+ * least one source namespace and the
+ * destination namespace have incompatible
+ * formats.
+ * @NVME_SC_FAST_COPY_NOT_POSSIBLE: Fast Copy Not Possible: The Fast Copy
+ * Only (FCO) bit was set to ‘1’ in a Source
+ * Range entry and the controller was not
+ * able to use fast copy operations to copy
+ * the specified data.
+ * @NVME_SC_OVERLAPPING_IO_RANGE: Overlapping I/O Range: A source logical
+ * block range overlaps the destination
+ * logical block range.
+ * @NVME_SC_INSUFFICIENT_RESOURCES: Insufficient Resources: A resource
+ * shortage prevented the controller from
+ * performing the requested copy.
+ * @NVME_SC_CONNECT_FORMAT: Incompatible Format: The NVM subsystem
+ * does not support the record format
+ * specified by the host.
+ * @NVME_SC_CONNECT_CTRL_BUSY: Controller Busy: The controller is
+ * already associated with a host.
+ * @NVME_SC_CONNECT_INVALID_PARAM: Connect Invalid Parameters: One or more
+ * of the command parameters.
+ * @NVME_SC_CONNECT_RESTART_DISC: Connect Restart Discovery: The NVM
+ * subsystem requested is not available.
+ * @NVME_SC_CONNECT_INVALID_HOST: Connect Invalid Host: The host is either
+ * not allowed to establish an association
+ * to any controller in the NVM subsystem or
+ * the host is not allowed to establish an
+ * association to the specified controller
+ * @NVME_SC_DISCONNECT_INVALID_QTYPE: Invalid Queue Type: The command was sent
+ * on the wrong queue type.
+ * @NVME_SC_DISCOVERY_RESTART: Discover Restart: The snapshot of the
+ * records is now invalid or out of date.
+ * @NVME_SC_AUTH_REQUIRED: Authentication Required: NVMe in-band
+ * authentication is required and the queue
+ * has not yet been authenticated.
+ * @NVME_SC_WRITE_FAULT: Write Fault: The write data could not be
+ * committed to the media.
+ * @NVME_SC_READ_ERROR: Unrecovered Read Error: The read data
+ * could not be recovered from the media.
+ * @NVME_SC_GUARD_CHECK: End-to-end Guard Check Error: The command
+ * was aborted due to an end-to-end guard
+ * check failure.
+ * @NVME_SC_APPTAG_CHECK: End-to-end Application Tag Check Error:
+ * The command was aborted due to an
+ * end-to-end application tag check failure.
+ * @NVME_SC_REFTAG_CHECK: End-to-end Reference Tag Check Error: The
+ * command was aborted due to an end-to-end
+ * reference tag check failure.
+ * @NVME_SC_COMPARE_FAILED: Compare Failure: The command failed due
+ * to a miscompare during a Compare command.
+ * @NVME_SC_ACCESS_DENIED: Access Denied: Access to the namespace
+ * and/or LBA range is denied due to lack of
+ * access rights.
+ * @NVME_SC_UNWRITTEN_BLOCK: Deallocated or Unwritten Logical Block:
+ * The command failed due to an attempt to
+ * read from or verify an LBA range
+ * containing a deallocated or unwritten
+ * logical block.
+ * @NVME_SC_STORAGE_TAG_CHECK: End-to-End Storage Tag Check Error: The
+ * command was aborted due to an end-to-end
+ * storage tag check failure.
+ * @NVME_SC_ANA_INTERNAL_PATH_ERROR: Internal Path Error: The command was not
+ * completed as the result of a controller
+ * internal error that is specific to the
+ * controller processing the command.
+ * @NVME_SC_ANA_PERSISTENT_LOSS: Asymmetric Access Persistent Loss: The
+ * requested function (e.g., command) is not
+ * able to be performed as a result of the
+ * relationship between the controller and
+ * the namespace being in the ANA Persistent
+ * Loss state.
+ * @NVME_SC_ANA_INACCESSIBLE: Asymmetric Access Inaccessible: The
+ * requested function (e.g., command) is not
+ * able to be performed as a result of the
+ * relationship between the controller and
+ * the namespace being in the ANA
+ * Inaccessible state.
+ * @NVME_SC_ANA_TRANSITION: Asymmetric Access Transition: The
+ * requested function (e.g., command) is not
+ * able to be performed as a result of the
+ * relationship between the controller and
+ * the namespace transitioning between
+ * Asymmetric Namespace Access states.
+ * @NVME_SC_CTRL_PATH_ERROR: Controller Pathing Error: A pathing error
+ * was detected by the controller.
+ * @NVME_SC_HOST_PATH_ERROR: Host Pathing Error: A pathing error was
+ * detected by the host.
+ * @NVME_SC_CMD_ABORTED_BY_HOST: Command Aborted By Host: The command was
+ * aborted as a result of host action.
+ * @NVME_SC_CRD: Mask to get value of Command Retry Delay
+ * index
+ * @NVME_SC_MORE: More bit. If set, more status information
+ * for this command as part of the Error
+ * Information log that may be retrieved with
+ * the Get Log Page command.
+ * @NVME_SC_DNR: Do Not Retry bit. If set, if the same
+ * command is re-submitted to any controller
+ * in the NVM subsystem, then that
+ * re-submitted command is expected to fail.
+ * @NVME_SC_ZNS_INVALID_OP_REQUEST: Invalid Zone Operation Request:
+ * The operation requested is invalid. This may be due to
+ * various conditions, including: attempting to allocate a
+ * ZRWA when a zone is not in the ZSE:Empty state; or
+ * invalid Flush Explicit ZRWA Range Send Zone Action
+ * operation.
+ * @NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE: ZRWA Resources Unavailable:
+ * No ZRWAs are available.
+ * @NVME_SC_ZNS_BOUNDARY_ERROR: Zone Boundary Error: The command specifies
+ * logical blocks in more than one zone.
+ * @NVME_SC_ZNS_FULL: Zone Is Full: The accessed zone is in the
+ * ZSF:Full state.
+ * @NVME_SC_ZNS_READ_ONLY: Zone Is Read Only: The accessed zone is
+ * in the ZSRO:Read Only state.
+ * @NVME_SC_ZNS_OFFLINE: Zone Is Offline: The accessed zone is
+ * in the ZSO:Offline state.
+ * @NVME_SC_ZNS_INVALID_WRITE: Zone Invalid Write: The write to a zone
+ * was not at the write pointer.
+ * @NVME_SC_ZNS_TOO_MANY_ACTIVE: Too Many Active Zones: The controller
+ * does not allow additional active zones.
+ * @NVME_SC_ZNS_TOO_MANY_OPENS: Too Many Open Zones: The controller does
+ * not allow additional open zones.
+ * @NVME_SC_ZNS_INVAL_TRANSITION: Invalid Zone State Transition: The request
+ * is not a valid zone state transition.
+ */
+enum nvme_status_field {
+ /*
+ * Status Code Type indicators
+ */
+ NVME_SCT_GENERIC = 0x0,
+ NVME_SCT_CMD_SPECIFIC = 0x1,
+ NVME_SCT_MEDIA = 0x2,
+ NVME_SCT_PATH = 0x3,
+ NVME_SCT_VS = 0x7,
+ NVME_SCT_MASK = 0x7,
+ NVME_SCT_SHIFT = 0x8,
+
+ /*
+ * Status Code inidicators
+ */
+ NVME_SC_MASK = 0xff,
+ NVME_SC_SHIFT = 0x0,
+
+ /*
+ * Generic Command Status Codes:
+ */
+ NVME_SC_SUCCESS = 0x0,
+ NVME_SC_INVALID_OPCODE = 0x1,
+ NVME_SC_INVALID_FIELD = 0x2,
+ NVME_SC_CMDID_CONFLICT = 0x3,
+ NVME_SC_DATA_XFER_ERROR = 0x4,
+ NVME_SC_POWER_LOSS = 0x5,
+ NVME_SC_INTERNAL = 0x6,
+ NVME_SC_ABORT_REQ = 0x7,
+ NVME_SC_ABORT_QUEUE = 0x8,
+ NVME_SC_FUSED_FAIL = 0x9,
+ NVME_SC_FUSED_MISSING = 0xa,
+ NVME_SC_INVALID_NS = 0xb,
+ NVME_SC_CMD_SEQ_ERROR = 0xc,
+ NVME_SC_SGL_INVALID_LAST = 0xd,
+ NVME_SC_SGL_INVALID_COUNT = 0xe,
+ NVME_SC_SGL_INVALID_DATA = 0xf,
+ NVME_SC_SGL_INVALID_METADATA = 0x10,
+ NVME_SC_SGL_INVALID_TYPE = 0x11,
+ NVME_SC_CMB_INVALID_USE = 0x12,
+ NVME_SC_PRP_INVALID_OFFSET = 0x13,
+ NVME_SC_AWU_EXCEEDED = 0x14,
+ NVME_SC_OP_DENIED = 0x15,
+ NVME_SC_SGL_INVALID_OFFSET = 0x16,
+ NVME_SC_HOSTID_FORMAT = 0x18,
+ NVME_SC_KAT_EXPIRED = 0x19,
+ NVME_SC_KAT_INVALID = 0x1a,
+ NVME_SC_CMD_ABORTED_PREMEPT = 0x1b,
+ NVME_SC_SANITIZE_FAILED = 0x1c,
+ NVME_SC_SANITIZE_IN_PROGRESS = 0x1d,
+ NVME_SC_SGL_INVALID_GRANULARITY = 0x1e,
+ NVME_SC_CMD_IN_CMBQ_NOT_SUPP = 0x1f,
+ NVME_SC_NS_WRITE_PROTECTED = 0x20,
+ NVME_SC_CMD_INTERRUPTED = 0x21,
+ NVME_SC_TRAN_TPORT_ERROR = 0x22,
+ NVME_SC_PROHIBITED_BY_CMD_AND_FEAT = 0x23,
+ NVME_SC_ADMIN_CMD_MEDIA_NOT_READY = 0x24,
+ NVME_SC_FDP_DISABLED = 0x29,
+ NVME_SC_INVALID_PLACEMENT_HANDLE_LIST = 0x2A,
+ NVME_SC_LBA_RANGE = 0x80,
+ NVME_SC_CAP_EXCEEDED = 0x81,
+ NVME_SC_NS_NOT_READY = 0x82,
+ NVME_SC_RESERVATION_CONFLICT = 0x83,
+ NVME_SC_FORMAT_IN_PROGRESS = 0x84,
+
+ /*
+ * Command Specific Status Codes:
+ */
+ NVME_SC_CQ_INVALID = 0x00,
+ NVME_SC_QID_INVALID = 0x01,
+ NVME_SC_QUEUE_SIZE = 0x02,
+ NVME_SC_ABORT_LIMIT = 0x03,
+ NVME_SC_ABORT_MISSING = 0x04,
+ NVME_SC_ASYNC_LIMIT = 0x05,
+ NVME_SC_FIRMWARE_SLOT = 0x06,
+ NVME_SC_FIRMWARE_IMAGE = 0x07,
+ NVME_SC_INVALID_VECTOR = 0x08,
+ NVME_SC_INVALID_LOG_PAGE = 0x09,
+ NVME_SC_INVALID_FORMAT = 0x0a,
+ NVME_SC_FW_NEEDS_CONV_RESET = 0x0b,
+ NVME_SC_INVALID_QUEUE = 0x0c,
+ NVME_SC_FEATURE_NOT_SAVEABLE = 0x0d,
+ NVME_SC_FEATURE_NOT_CHANGEABLE = 0x0e,
+ NVME_SC_FEATURE_NOT_PER_NS = 0x0f,
+ NVME_SC_FW_NEEDS_SUBSYS_RESET = 0x10,
+ NVME_SC_FW_NEEDS_RESET = 0x11,
+ NVME_SC_FW_NEEDS_MAX_TIME = 0x12,
+ NVME_SC_FW_ACTIVATE_PROHIBITED = 0x13,
+ NVME_SC_OVERLAPPING_RANGE = 0x14,
+ NVME_SC_NS_INSUFFICIENT_CAP = 0x15,
+ NVME_SC_NS_ID_UNAVAILABLE = 0x16,
+ NVME_SC_NS_ALREADY_ATTACHED = 0x18,
+ NVME_SC_NS_IS_PRIVATE = 0x19,
+ NVME_SC_NS_NOT_ATTACHED = 0x1a,
+ NVME_SC_THIN_PROV_NOT_SUPP = 0x1b,
+ NVME_SC_CTRL_LIST_INVALID = 0x1c,
+ NVME_SC_SELF_TEST_IN_PROGRESS = 0x1d,
+ NVME_SC_BP_WRITE_PROHIBITED = 0x1e,
+ NVME_SC_INVALID_CTRL_ID = 0x1f,
+ NVME_SC_INVALID_SEC_CTRL_STATE = 0x20,
+ NVME_SC_INVALID_CTRL_RESOURCES = 0x21,
+ NVME_SC_INVALID_RESOURCE_ID = 0x22,
+ NVME_SC_PMR_SAN_PROHIBITED = 0x23,
+ NVME_SC_ANA_GROUP_ID_INVALID = 0x24,
+ NVME_SC_ANA_ATTACH_FAILED = 0x25,
+ NVME_SC_INSUFFICIENT_CAP = 0x26,
+ NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED = 0x27,
+ NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED = 0x28,
+
+ /*
+ * Command Set Specific - Namespace Types commands:
+ */
+ NVME_SC_IOCS_NOT_SUPPORTED = 0x29,
+ NVME_SC_IOCS_NOT_ENABLED = 0x2a,
+ NVME_SC_IOCS_COMBINATION_REJECTED = 0x2b,
+ NVME_SC_INVALID_IOCS = 0x2c,
+ NVME_SC_ID_UNAVAILABLE = 0x2d,
+
+ /*
+ * Discovery Information Management
+ */
+ NVME_SC_INVALID_DISCOVERY_INFO = 0x2f,
+ NVME_SC_ZONING_DATA_STRUCT_LOCKED = 0x30,
+ NVME_SC_ZONING_DATA_STRUCT_NOTFND = 0x31,
+ NVME_SC_INSUFFICIENT_DISC_RES = 0x32,
+ NVME_SC_REQSTD_FUNCTION_DISABLED = 0x33,
+ NVME_SC_ZONEGRP_ORIGINATOR_INVLD = 0x34,
+
+ /*
+ * I/O Command Set Specific - NVM commands:
+ */
+ NVME_SC_BAD_ATTRIBUTES = 0x80,
+ NVME_SC_INVALID_PI = 0x81,
+ NVME_SC_READ_ONLY = 0x82,
+ NVME_SC_CMD_SIZE_LIMIT_EXCEEDED = 0x83,
+ NVME_SC_INCOMPATIBLE_NS = 0x85,
+ NVME_SC_FAST_COPY_NOT_POSSIBLE = 0x86,
+ NVME_SC_OVERLAPPING_IO_RANGE = 0x87,
+ NVME_SC_INSUFFICIENT_RESOURCES = 0x89,
+
+ /*
+ * I/O Command Set Specific - Fabrics commands:
+ */
+ NVME_SC_CONNECT_FORMAT = 0x80,
+ NVME_SC_CONNECT_CTRL_BUSY = 0x81,
+ NVME_SC_CONNECT_INVALID_PARAM = 0x82,
+ NVME_SC_CONNECT_RESTART_DISC = 0x83,
+ NVME_SC_CONNECT_INVALID_HOST = 0x84,
+ NVME_SC_DISCONNECT_INVALID_QTYPE= 0x85,
+ NVME_SC_DISCOVERY_RESTART = 0x90,
+ NVME_SC_AUTH_REQUIRED = 0x91,
+
+ /*
+ * I/O Command Set Specific - ZNS commands:
+ */
+ NVME_SC_ZNS_INVALID_OP_REQUEST = 0xb6,
+ NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE = 0xb7,
+ NVME_SC_ZNS_BOUNDARY_ERROR = 0xb8,
+ NVME_SC_ZNS_FULL = 0xb9,
+ NVME_SC_ZNS_READ_ONLY = 0xba,
+ NVME_SC_ZNS_OFFLINE = 0xbb,
+ NVME_SC_ZNS_INVALID_WRITE = 0xbc,
+ NVME_SC_ZNS_TOO_MANY_ACTIVE = 0xbd,
+ NVME_SC_ZNS_TOO_MANY_OPENS = 0xbe,
+ NVME_SC_ZNS_INVAL_TRANSITION = 0xbf,
+
+ /*
+ * Media and Data Integrity Errors:
+ */
+ NVME_SC_WRITE_FAULT = 0x80,
+ NVME_SC_READ_ERROR = 0x81,
+ NVME_SC_GUARD_CHECK = 0x82,
+ NVME_SC_APPTAG_CHECK = 0x83,
+ NVME_SC_REFTAG_CHECK = 0x84,
+ NVME_SC_COMPARE_FAILED = 0x85,
+ NVME_SC_ACCESS_DENIED = 0x86,
+ NVME_SC_UNWRITTEN_BLOCK = 0x87,
+ NVME_SC_STORAGE_TAG_CHECK = 0x88,
+
+ /*
+ * Path-related Errors:
+ */
+ NVME_SC_ANA_INTERNAL_PATH_ERROR = 0x00,
+ NVME_SC_ANA_PERSISTENT_LOSS = 0x01,
+ NVME_SC_ANA_INACCESSIBLE = 0x02,
+ NVME_SC_ANA_TRANSITION = 0x03,
+ NVME_SC_CTRL_PATH_ERROR = 0x60,
+ NVME_SC_HOST_PATH_ERROR = 0x70,
+ NVME_SC_CMD_ABORTED_BY_HOST = 0x71,
+
+ /*
+ * Additional status field flags
+ */
+ NVME_SC_CRD = 0x1800,
+ NVME_SC_MORE = 0x2000,
+ NVME_SC_DNR = 0x4000,
+};
+
+/**
+ * nvme_status_code_type() - Returns the NVMe Status Code Type
+ * @status_field: The NVMe Completion Queue Entry's Status Field
+ * See &enum nvme_status_field
+ *
+ * Returns: status code type
+ */
+static inline __u16 nvme_status_code_type(__u16 status_field)
+{
+ return NVME_GET(status_field, SCT);
+}
+
+/**
+ * nvme_status_code() - Returns the NVMe Status Code
+ * @status_field: The NVMe Completion Queue Entry's Status Field
+ * See &enum nvme_status_field
+ *
+ * Returns: status code
+ */
+static inline __u16 nvme_status_code(__u16 status_field)
+{
+ return NVME_GET(status_field, SC);
+}
+
+/**
+ * enum nvme_status_type - type encoding for NVMe return values, when
+ * represented as an int.
+ *
+ * The nvme_* api returns an int, with negative values indicating an internal
+ * or syscall error, zero signifying success, positive values representing
+ * the NVMe status.
+ *
+ * That latter case (the NVMe status) may represent status values from
+ * different parts of the transport/controller/etc, and are at most 16 bits of
+ * data. So, we use the most-significant 3 bits of the signed int to indicate
+ * which type of status this is.
+ *
+ * @NVME_STATUS_TYPE_SHIFT: shift value for status bits
+ * @NVME_STATUS_TYPE_MASK: mask value for status bits
+ *
+ * @NVME_STATUS_TYPE_NVME: NVMe command status value, typically from CDW3
+ * @NVME_STATUS_TYPE_MI: NVMe-MI header status
+ */
+enum nvme_status_type {
+ NVME_STATUS_TYPE_SHIFT = 27,
+ NVME_STATUS_TYPE_MASK = 0x7,
+
+ NVME_STATUS_TYPE_NVME = 0,
+ NVME_STATUS_TYPE_MI = 1,
+};
+
+/**
+ * nvme_status_get_type() - extract the type from a nvme_* return value
+ * @status: the (non-negative) return value from the NVMe API
+ *
+ * Returns: the type component of the status.
+ */
+static inline __u32 nvme_status_get_type(int status)
+{
+ return NVME_GET(status, STATUS_TYPE);
+}
+
+/**
+ * nvme_status_get_value() - extract the status value from a nvme_* return
+ * value
+ * @status: the (non-negative) return value from the NVMe API
+ *
+ * Returns: the value component of the status; the set of values will depend
+ * on the status type.
+ */
+static inline __u32 nvme_status_get_value(int status)
+{
+ return status & ~NVME_SET(NVME_STATUS_TYPE_MASK, STATUS_TYPE);
+}
+
+/**
+ * nvme_status_equals() - helper to check a status against a type and value
+ * @status: the (non-negative) return value from the NVMe API
+ * @type: the status type
+ * @value: the status value
+ *
+ * Returns: true if @status is of the specified type and value
+ */
+static inline __u32 nvme_status_equals(int status, enum nvme_status_type type,
+ unsigned int value)
+{
+ if (status < 0)
+ return false;
+
+ return nvme_status_get_type(status) == type &&
+ nvme_status_get_value(status) == value;
+}
+
+/**
+ * enum nvme_admin_opcode - Known NVMe admin opcodes
+ * @nvme_admin_delete_sq: Delete I/O Submission Queue
+ * @nvme_admin_create_sq: Create I/O Submission Queue
+ * @nvme_admin_get_log_page: Get Log Page
+ * @nvme_admin_delete_cq: Delete I/O Completion Queue
+ * @nvme_admin_create_cq: Create I/O Completion Queue
+ * @nvme_admin_identify: Identify
+ * @nvme_admin_abort_cmd: Abort
+ * @nvme_admin_set_features: Set Features
+ * @nvme_admin_get_features: Get Features
+ * @nvme_admin_async_event: Asynchronous Event Request
+ * @nvme_admin_ns_mgmt: Namespace Management
+ * @nvme_admin_fw_activate: Firmware Commit
+ * @nvme_admin_fw_commit: Firmware Commit
+ * @nvme_admin_fw_download: Firmware Image Download
+ * @nvme_admin_dev_self_test: Device Self-test
+ * @nvme_admin_ns_attach: Namespace Attachment
+ * @nvme_admin_keep_alive: Keep Alive
+ * @nvme_admin_directive_send: Directive Send
+ * @nvme_admin_directive_recv: Directive Receive
+ * @nvme_admin_virtual_mgmt: Virtualization Management
+ * @nvme_admin_nvme_mi_send: NVMe-MI Send
+ * @nvme_admin_nvme_mi_recv: NVMe-MI Receive
+ * @nvme_admin_capacity_mgmt: Capacity Management
+ * @nvme_admin_discovery_info_mgmt: Discovery Information Management (DIM)
+ * @nvme_admin_fabric_zoning_recv: Fabric Zoning Receive
+ * @nvme_admin_lockdown: Lockdown
+ * @nvme_admin_fabric_zoning_lookup: Fabric Zoning Lookup
+ * @nvme_admin_fabric_zoning_send: Fabric Zoning Send
+ * @nvme_admin_dbbuf: Doorbell Buffer Config
+ * @nvme_admin_fabrics: Fabrics Commands
+ * @nvme_admin_format_nvm: Format NVM
+ * @nvme_admin_security_send: Security Send
+ * @nvme_admin_security_recv: Security Receive
+ * @nvme_admin_sanitize_nvm: Sanitize
+ * @nvme_admin_get_lba_status: Get LBA Status
+ */
+enum nvme_admin_opcode {
+ nvme_admin_delete_sq = 0x00,
+ nvme_admin_create_sq = 0x01,
+ nvme_admin_get_log_page = 0x02,
+ nvme_admin_delete_cq = 0x04,
+ nvme_admin_create_cq = 0x05,
+ nvme_admin_identify = 0x06,
+ nvme_admin_abort_cmd = 0x08,
+ nvme_admin_set_features = 0x09,
+ nvme_admin_get_features = 0x0a,
+ nvme_admin_async_event = 0x0c,
+ nvme_admin_ns_mgmt = 0x0d,
+ nvme_admin_fw_commit = 0x10,
+ nvme_admin_fw_activate = nvme_admin_fw_commit,
+ nvme_admin_fw_download = 0x11,
+ nvme_admin_dev_self_test = 0x14,
+ nvme_admin_ns_attach = 0x15,
+ nvme_admin_keep_alive = 0x18,
+ nvme_admin_directive_send = 0x19,
+ nvme_admin_directive_recv = 0x1a,
+ nvme_admin_virtual_mgmt = 0x1c,
+ nvme_admin_nvme_mi_send = 0x1d,
+ nvme_admin_nvme_mi_recv = 0x1e,
+ nvme_admin_capacity_mgmt = 0x20,
+ nvme_admin_discovery_info_mgmt = 0x21,
+ nvme_admin_fabric_zoning_recv = 0x22,
+ nvme_admin_lockdown = 0x24,
+ nvme_admin_fabric_zoning_lookup = 0x25,
+ nvme_admin_fabric_zoning_send = 0x29,
+ nvme_admin_dbbuf = 0x7c,
+ nvme_admin_fabrics = 0x7f,
+ nvme_admin_format_nvm = 0x80,
+ nvme_admin_security_send = 0x81,
+ nvme_admin_security_recv = 0x82,
+ nvme_admin_sanitize_nvm = 0x84,
+ nvme_admin_get_lba_status = 0x86,
+};
+
+/**
+ * enum nvme_identify_cns - Identify - CNS Values
+ * @NVME_IDENTIFY_CNS_NS: Identify Namespace data structure
+ * @NVME_IDENTIFY_CNS_CTRL: Identify Controller data structure
+ * @NVME_IDENTIFY_CNS_NS_ACTIVE_LIST: Active Namespace ID list
+ * @NVME_IDENTIFY_CNS_NS_DESC_LIST: Namespace Identification Descriptor list
+ * @NVME_IDENTIFY_CNS_NVMSET_LIST: NVM Set List
+ * @NVME_IDENTIFY_CNS_CSI_NS: I/O Command Set specific Identify
+ * Namespace data structure
+ * @NVME_IDENTIFY_CNS_CSI_CTRL: I/O Command Set specific Identify
+ * Controller data structure
+ * @NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST: Active Namespace ID list associated
+ * with the specified I/O Command Set
+ * @NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS: I/O Command Set Independent Identify
+ * @NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT: Namespace user data format
+ * @NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT: I/O Command Set specific user data
+ * format
+ * Namespace data structure
+ * @NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST: Allocated Namespace ID list
+ * @NVME_IDENTIFY_CNS_ALLOCATED_NS: Identify Namespace data structure for
+ * the specified allocated NSID
+ * @NVME_IDENTIFY_CNS_NS_CTRL_LIST: Controller List of controllers attached
+ * to the specified NSID
+ * @NVME_IDENTIFY_CNS_CTRL_LIST: Controller List of controllers that exist
+ * in the NVM subsystem
+ * @NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP: Primary Controller Capabilities data
+ * structure for the specified primary controller
+ * @NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST: Secondary Controller list of controllers
+ * associated with the primary controller
+ * processing the command
+ * @NVME_IDENTIFY_CNS_NS_GRANULARITY: A Namespace Granularity List
+ * @NVME_IDENTIFY_CNS_UUID_LIST: A UUID List
+ * @NVME_IDENTIFY_CNS_DOMAIN_LIST: Domain List
+ * @NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID: Endurance Group List
+ * @NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST: I/O Command Set specific Allocated Namespace
+ * ID list
+ * @NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE: I/O Command Set specific ID Namespace
+ * Data Structure for Allocated Namespace ID
+ * @NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE: Base Specification 2.0a section 5.17.2.21
+ */
+enum nvme_identify_cns {
+ NVME_IDENTIFY_CNS_NS = 0x00,
+ NVME_IDENTIFY_CNS_CTRL = 0x01,
+ NVME_IDENTIFY_CNS_NS_ACTIVE_LIST = 0x02,
+ NVME_IDENTIFY_CNS_NS_DESC_LIST = 0x03,
+ NVME_IDENTIFY_CNS_NVMSET_LIST = 0x04,
+ NVME_IDENTIFY_CNS_CSI_NS = 0x05,
+ NVME_IDENTIFY_CNS_CSI_CTRL = 0x06,
+ NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST = 0x07,
+ NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS = 0x08,
+ NVME_IDENTIFY_CNS_NS_USER_DATA_FORMAT = 0x09,
+ NVME_IDENTIFY_CNS_CSI_NS_USER_DATA_FORMAT = 0x0A,
+ NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST = 0x10,
+ NVME_IDENTIFY_CNS_ALLOCATED_NS = 0x11,
+ NVME_IDENTIFY_CNS_NS_CTRL_LIST = 0x12,
+ NVME_IDENTIFY_CNS_CTRL_LIST = 0x13,
+ NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP = 0x14,
+ NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST = 0x15,
+ NVME_IDENTIFY_CNS_NS_GRANULARITY = 0x16,
+ NVME_IDENTIFY_CNS_UUID_LIST = 0x17,
+ NVME_IDENTIFY_CNS_DOMAIN_LIST = 0x18,
+ NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID = 0x19,
+ NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST = 0x1A,
+ NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE = 0x1B,
+ NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE = 0x1C,
+};
+
+/**
+ * enum nvme_cmd_get_log_lid - Get Log Page -Log Page Identifiers
+ * @NVME_LOG_LID_SUPPORTED_LOG_PAGES: Supported Log Pages
+ * @NVME_LOG_LID_ERROR: Error Information
+ * @NVME_LOG_LID_SMART: SMART / Health Information
+ * @NVME_LOG_LID_FW_SLOT: Firmware Slot Information
+ * @NVME_LOG_LID_CHANGED_NS: Changed Namespace List
+ * @NVME_LOG_LID_CMD_EFFECTS: Commands Supported and Effects
+ * @NVME_LOG_LID_DEVICE_SELF_TEST: Device Self-test
+ * @NVME_LOG_LID_TELEMETRY_HOST: Telemetry Host-Initiated
+ * @NVME_LOG_LID_TELEMETRY_CTRL: Telemetry Controller-Initiated
+ * @NVME_LOG_LID_ENDURANCE_GROUP: Endurance Group Information
+ * @NVME_LOG_LID_PREDICTABLE_LAT_NVMSET: Predictable Latency Per NVM Set
+ * @NVME_LOG_LID_PREDICTABLE_LAT_AGG: Predictable Latency Event Aggregate
+ * @NVME_LOG_LID_ANA: Asymmetric Namespace Access
+ * @NVME_LOG_LID_PERSISTENT_EVENT: Persistent Event Log
+ * @NVME_LOG_LID_LBA_STATUS: LBA Status Information
+ * @NVME_LOG_LID_ENDURANCE_GRP_EVT: Endurance Group Event Aggregate
+ * @NVME_LOG_LID_MEDIA_UNIT_STATUS: Media Unit Status
+ * @NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST: Supported Capacity Configuration Lis
+ * @NVME_LOG_LID_FID_SUPPORTED_EFFECTS: Feature Identifiers Supported and Effects
+ * @NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS: NVMe-MI Commands Supported and Effects
+ * @NVME_LOG_LID_BOOT_PARTITION: Boot Partition
+ * @NVME_LOG_LID_PHY_RX_EOM: Physical Interface Receiver Eye Opening Measurement
+ * @NVME_LOG_LID_FDP_CONFIGS: FDP Configurations
+ * @NVME_LOG_LID_FDP_RUH_USAGE: Reclaim Unit Handle Usage
+ * @NVME_LOG_LID_FDP_STATS: FDP Statistics
+ * @NVME_LOG_LID_FDP_EVENTS: FDP Events
+ * @NVME_LOG_LID_DISCOVER: Discovery
+ * @NVME_LOG_LID_RESERVATION: Reservation Notification
+ * @NVME_LOG_LID_SANITIZE: Sanitize Status
+ * @NVME_LOG_LID_ZNS_CHANGED_ZONES: Changed Zone List
+ */
+enum nvme_cmd_get_log_lid {
+ NVME_LOG_LID_SUPPORTED_LOG_PAGES = 0x00,
+ NVME_LOG_LID_ERROR = 0x01,
+ NVME_LOG_LID_SMART = 0x02,
+ NVME_LOG_LID_FW_SLOT = 0x03,
+ NVME_LOG_LID_CHANGED_NS = 0x04,
+ NVME_LOG_LID_CMD_EFFECTS = 0x05,
+ NVME_LOG_LID_DEVICE_SELF_TEST = 0x06,
+ NVME_LOG_LID_TELEMETRY_HOST = 0x07,
+ NVME_LOG_LID_TELEMETRY_CTRL = 0x08,
+ NVME_LOG_LID_ENDURANCE_GROUP = 0x09,
+ NVME_LOG_LID_PREDICTABLE_LAT_NVMSET = 0x0a,
+ NVME_LOG_LID_PREDICTABLE_LAT_AGG = 0x0b,
+ NVME_LOG_LID_ANA = 0x0c,
+ NVME_LOG_LID_PERSISTENT_EVENT = 0x0d,
+ NVME_LOG_LID_LBA_STATUS = 0x0e,
+ NVME_LOG_LID_ENDURANCE_GRP_EVT = 0x0f,
+ NVME_LOG_LID_MEDIA_UNIT_STATUS = 0x10,
+ NVME_LOG_LID_SUPPORTED_CAP_CONFIG_LIST = 0x11,
+ NVME_LOG_LID_FID_SUPPORTED_EFFECTS = 0x12,
+ NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS = 0x13,
+ NVME_LOG_LID_BOOT_PARTITION = 0x15,
+ NVME_LOG_LID_PHY_RX_EOM = 0x19,
+ NVME_LOG_LID_FDP_CONFIGS = 0x20,
+ NVME_LOG_LID_FDP_RUH_USAGE = 0x21,
+ NVME_LOG_LID_FDP_STATS = 0x22,
+ NVME_LOG_LID_FDP_EVENTS = 0x23,
+ NVME_LOG_LID_DISCOVER = 0x70,
+ NVME_LOG_LID_RESERVATION = 0x80,
+ NVME_LOG_LID_SANITIZE = 0x81,
+ NVME_LOG_LID_ZNS_CHANGED_ZONES = 0xbf,
+};
+
+/**
+ * enum nvme_features_id - Features - Feature Identifiers
+ * @NVME_FEAT_FID_ARBITRATION: Arbitration
+ * @NVME_FEAT_FID_POWER_MGMT: Power Management
+ * @NVME_FEAT_FID_LBA_RANGE: LBA Range Type
+ * @NVME_FEAT_FID_TEMP_THRESH: Temperature Threshold
+ * @NVME_FEAT_FID_ERR_RECOVERY: Error Recovery
+ * @NVME_FEAT_FID_VOLATILE_WC: Volatile Write Cache
+ * @NVME_FEAT_FID_NUM_QUEUES: Number of Queues
+ * @NVME_FEAT_FID_IRQ_COALESCE: Interrupt Coalescing
+ * @NVME_FEAT_FID_IRQ_CONFIG: Interrupt Vector Configuration
+ * @NVME_FEAT_FID_WRITE_ATOMIC: Write Atomicity Normal
+ * @NVME_FEAT_FID_ASYNC_EVENT: Asynchronous Event Configuration
+ * @NVME_FEAT_FID_AUTO_PST: Autonomous Power State Transition
+ * @NVME_FEAT_FID_HOST_MEM_BUF: Host Memory Buffer
+ * @NVME_FEAT_FID_TIMESTAMP: Timestamp
+ * @NVME_FEAT_FID_KATO: Keep Alive Timer
+ * @NVME_FEAT_FID_HCTM: Host Controlled Thermal Management
+ * @NVME_FEAT_FID_NOPSC: Non-Operational Power State Config
+ * @NVME_FEAT_FID_RRL: Read Recovery Level Config
+ * @NVME_FEAT_FID_PLM_CONFIG: Predictable Latency Mode Config
+ * @NVME_FEAT_FID_PLM_WINDOW: Predictable Latency Mode Window
+ * @NVME_FEAT_FID_LBA_STS_INTERVAL: LBA Status Information Report Interval
+ * @NVME_FEAT_FID_HOST_BEHAVIOR: Host Behavior Support
+ * @NVME_FEAT_FID_SANITIZE: Endurance Group Event Configuration
+ * @NVME_FEAT_FID_ENDURANCE_EVT_CFG: Endurance Group Event Configuration
+ * @NVME_FEAT_FID_IOCS_PROFILE: I/O Command Set Profile
+ * @NVME_FEAT_FID_SPINUP_CONTROL: Spinup Control
+ * @NVME_FEAT_FID_FDP: Flexible Data Placement
+ * @NVME_FEAT_FID_FDP_EVENTS: FDP Events
+ * @NVME_FEAT_FID_ENH_CTRL_METADATA: Enhanced Controller Metadata
+ * @NVME_FEAT_FID_CTRL_METADATA: Controller Metadata
+ * @NVME_FEAT_FID_NS_METADATA: Namespace Metadata
+ * @NVME_FEAT_FID_SW_PROGRESS: Software Progress Marker
+ * @NVME_FEAT_FID_HOST_ID: Host Identifier
+ * @NVME_FEAT_FID_RESV_MASK: Reservation Notification Mask
+ * @NVME_FEAT_FID_RESV_PERSIST: Reservation Persistence
+ * @NVME_FEAT_FID_WRITE_PROTECT: Namespace Write Protection Config
+ */
+enum nvme_features_id {
+ NVME_FEAT_FID_ARBITRATION = 0x01,
+ NVME_FEAT_FID_POWER_MGMT = 0x02,
+ NVME_FEAT_FID_LBA_RANGE = 0x03,
+ NVME_FEAT_FID_TEMP_THRESH = 0x04,
+ NVME_FEAT_FID_ERR_RECOVERY = 0x05,
+ NVME_FEAT_FID_VOLATILE_WC = 0x06,
+ NVME_FEAT_FID_NUM_QUEUES = 0x07,
+ NVME_FEAT_FID_IRQ_COALESCE = 0x08,
+ NVME_FEAT_FID_IRQ_CONFIG = 0x09,
+ NVME_FEAT_FID_WRITE_ATOMIC = 0x0a,
+ NVME_FEAT_FID_ASYNC_EVENT = 0x0b,
+ NVME_FEAT_FID_AUTO_PST = 0x0c,
+ NVME_FEAT_FID_HOST_MEM_BUF = 0x0d,
+ NVME_FEAT_FID_TIMESTAMP = 0x0e,
+ NVME_FEAT_FID_KATO = 0x0f,
+ NVME_FEAT_FID_HCTM = 0x10,
+ NVME_FEAT_FID_NOPSC = 0x11,
+ NVME_FEAT_FID_RRL = 0x12,
+ NVME_FEAT_FID_PLM_CONFIG = 0x13,
+ NVME_FEAT_FID_PLM_WINDOW = 0x14,
+ NVME_FEAT_FID_LBA_STS_INTERVAL = 0x15,
+ NVME_FEAT_FID_HOST_BEHAVIOR = 0x16,
+ NVME_FEAT_FID_SANITIZE = 0x17,
+ NVME_FEAT_FID_ENDURANCE_EVT_CFG = 0x18,
+ NVME_FEAT_FID_IOCS_PROFILE = 0x19, /* XXX: Placeholder until assigned */
+ NVME_FEAT_FID_SPINUP_CONTROL = 0x1a,
+ NVME_FEAT_FID_FDP = 0x1d,
+ NVME_FEAT_FID_FDP_EVENTS = 0x1e,
+ NVME_FEAT_FID_ENH_CTRL_METADATA = 0x7d,
+ NVME_FEAT_FID_CTRL_METADATA = 0x7e,
+ NVME_FEAT_FID_NS_METADATA = 0x7f,
+ NVME_FEAT_FID_SW_PROGRESS = 0x80,
+ NVME_FEAT_FID_HOST_ID = 0x81,
+ NVME_FEAT_FID_RESV_MASK = 0x82,
+ NVME_FEAT_FID_RESV_PERSIST = 0x83,
+ NVME_FEAT_FID_WRITE_PROTECT = 0x84,
+};
+
+/**
+ * enum nvme_feat - Features Access Shifts/Masks values
+ * @NVME_FEAT_ARBITRATION_BURST_SHIFT:
+ * @NVME_FEAT_ARBITRATION_BURST_MASK:
+ * @NVME_FEAT_ARBITRATION_LPW_SHIFT:
+ * @NVME_FEAT_ARBITRATION_LPW_MASK:
+ * @NVME_FEAT_ARBITRATION_MPW_SHIFT:
+ * @NVME_FEAT_ARBITRATION_MPW_MASK:
+ * @NVME_FEAT_ARBITRATION_HPW_SHIFT:
+ * @NVME_FEAT_ARBITRATION_HPW_MASK:
+ * @NVME_FEAT_PWRMGMT_PS_SHIFT:
+ * @NVME_FEAT_PWRMGMT_PS_MASK:
+ * @NVME_FEAT_PWRMGMT_WH_SHIFT:
+ * @NVME_FEAT_PWRMGMT_WH_MASK:
+ * @NVME_FEAT_LBAR_NR_SHIFT:
+ * @NVME_FEAT_LBAR_NR_MASK:
+ * @NVME_FEAT_TT_TMPTH_SHIFT:
+ * @NVME_FEAT_TT_TMPTH_MASK:
+ * @NVME_FEAT_TT_TMPSEL_SHIFT:
+ * @NVME_FEAT_TT_TMPSEL_MASK:
+ * @NVME_FEAT_TT_THSEL_SHIFT:
+ * @NVME_FEAT_TT_THSEL_MASK:
+ * @NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT:
+ * @NVME_FEAT_ERROR_RECOVERY_TLER_MASK:
+ * @NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT:
+ * @NVME_FEAT_ERROR_RECOVERY_DULBE_MASK:
+ * @NVME_FEAT_VWC_WCE_SHIFT:
+ * @NVME_FEAT_VWC_WCE_MASK:
+ * @NVME_FEAT_NRQS_NSQR_SHIFT:
+ * @NVME_FEAT_NRQS_NSQR_MASK:
+ * @NVME_FEAT_NRQS_NCQR_SHIFT:
+ * @NVME_FEAT_NRQS_NCQR_MASK:
+ * @NVME_FEAT_IRQC_THR_SHIFT:
+ * @NVME_FEAT_IRQC_THR_MASK:
+ * @NVME_FEAT_IRQC_TIME_SHIFT:
+ * @NVME_FEAT_IRQC_TIME_MASK:
+ * @NVME_FEAT_ICFG_IV_SHIFT:
+ * @NVME_FEAT_ICFG_IV_MASK:
+ * @NVME_FEAT_ICFG_CD_SHIFT:
+ * @NVME_FEAT_ICFG_CD_MASK:
+ * @NVME_FEAT_WA_DN_SHIFT:
+ * @NVME_FEAT_WA_DN_MASK:
+ * @NVME_FEAT_AE_SMART_SHIFT:
+ * @NVME_FEAT_AE_SMART_MASK:
+ * @NVME_FEAT_AE_NAN_SHIFT:
+ * @NVME_FEAT_AE_NAN_MASK:
+ * @NVME_FEAT_AE_FW_SHIFT:
+ * @NVME_FEAT_AE_FW_MASK:
+ * @NVME_FEAT_AE_TELEM_SHIFT:
+ * @NVME_FEAT_AE_TELEM_MASK:
+ * @NVME_FEAT_AE_ANA_SHIFT:
+ * @NVME_FEAT_AE_ANA_MASK:
+ * @NVME_FEAT_AE_PLA_SHIFT:
+ * @NVME_FEAT_AE_PLA_MASK:
+ * @NVME_FEAT_AE_LBAS_SHIFT:
+ * @NVME_FEAT_AE_LBAS_MASK:
+ * @NVME_FEAT_AE_EGA_SHIFT:
+ * @NVME_FEAT_AE_EGA_MASK:
+ * @NVME_FEAT_APST_APSTE_SHIFT:
+ * @NVME_FEAT_APST_APSTE_MASK:
+ * @NVME_FEAT_HMEM_EHM_SHIFT:
+ * @NVME_FEAT_HMEM_EHM_MASK:
+ * @NVME_FEAT_HCTM_TMT2_SHIFT:
+ * @NVME_FEAT_HCTM_TMT2_MASK:
+ * @NVME_FEAT_HCTM_TMT1_SHIFT:
+ * @NVME_FEAT_HCTM_TMT1_MASK:
+ * @NVME_FEAT_NOPS_NOPPME_SHIFT:
+ * @NVME_FEAT_NOPS_NOPPME_MASK:
+ * @NVME_FEAT_RRL_RRL_SHIFT:
+ * @NVME_FEAT_RRL_RRL_MASK:
+ * @NVME_FEAT_PLM_PLME_SHIFT:
+ * @NVME_FEAT_PLM_PLME_MASK:
+ * @NVME_FEAT_PLMW_WS_SHIFT:
+ * @NVME_FEAT_PLMW_WS_MASK:
+ * @NVME_FEAT_LBAS_LSIRI_SHIFT:
+ * @NVME_FEAT_LBAS_LSIRI_MASK:
+ * @NVME_FEAT_LBAS_LSIPI_SHIFT:
+ * @NVME_FEAT_LBAS_LSIPI_MASK:
+ * @NVME_FEAT_SC_NODRM_SHIFT:
+ * @NVME_FEAT_SC_NODRM_MASK:
+ * @NVME_FEAT_EG_ENDGID_SHIFT:
+ * @NVME_FEAT_EG_ENDGID_MASK:
+ * @NVME_FEAT_EG_EGCW_SHIFT:
+ * @NVME_FEAT_EG_EGCW_MASK:
+ * @NVME_FEAT_SPM_PBSLC_SHIFT:
+ * @NVME_FEAT_SPM_PBSLC_MASK:
+ * @NVME_FEAT_HOSTID_EXHID_SHIFT:
+ * @NVME_FEAT_HOSTID_EXHID_MASK:
+ * @NVME_FEAT_RM_REGPRE_SHIFT:
+ * @NVME_FEAT_RM_REGPRE_MASK:
+ * @NVME_FEAT_RM_RESREL_SHIFT:
+ * @NVME_FEAT_RM_RESREL_MASK:
+ * @NVME_FEAT_RM_RESPRE_SHIFT:
+ * @NVME_FEAT_RM_RESPRE_MASK:
+ * @NVME_FEAT_RP_PTPL_SHIFT:
+ * @NVME_FEAT_RP_PTPL_MASK:
+ * @NVME_FEAT_WP_WPS_SHIFT:
+ * @NVME_FEAT_WP_WPS_MASK:
+ * @NVME_FEAT_IOCSP_IOCSCI_SHIFT:
+ * @NVME_FEAT_IOCSP_IOCSCI_MASK:
+ * @NVME_FEAT_FDP_ENABLED_SHIFT:
+ * @NVME_FEAT_FDP_ENABLED_MASK:
+ * @NVME_FEAT_FDP_INDEX_SHIFT:
+ * @NVME_FEAT_FDP_INDEX_MASK:
+ * @NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT:
+ * @NVME_FEAT_FDP_EVENTS_ENABLE_MASK:
+ */
+enum nvme_feat {
+ NVME_FEAT_ARBITRATION_BURST_SHIFT = 0,
+ NVME_FEAT_ARBITRATION_BURST_MASK = 0x7,
+ NVME_FEAT_ARBITRATION_LPW_SHIFT = 8,
+ NVME_FEAT_ARBITRATION_LPW_MASK = 0xff,
+ NVME_FEAT_ARBITRATION_MPW_SHIFT = 16,
+ NVME_FEAT_ARBITRATION_MPW_MASK = 0xff,
+ NVME_FEAT_ARBITRATION_HPW_SHIFT = 24,
+ NVME_FEAT_ARBITRATION_HPW_MASK = 0xff,
+ NVME_FEAT_PWRMGMT_PS_SHIFT = 0,
+ NVME_FEAT_PWRMGMT_PS_MASK = 0x1f,
+ NVME_FEAT_PWRMGMT_WH_SHIFT = 5,
+ NVME_FEAT_PWRMGMT_WH_MASK = 0x7,
+ NVME_FEAT_LBAR_NR_SHIFT = 0,
+ NVME_FEAT_LBAR_NR_MASK = 0x3f,
+ NVME_FEAT_TT_TMPTH_SHIFT = 0,
+ NVME_FEAT_TT_TMPTH_MASK = 0xffff,
+ NVME_FEAT_TT_TMPSEL_SHIFT = 16,
+ NVME_FEAT_TT_TMPSEL_MASK = 0xf,
+ NVME_FEAT_TT_THSEL_SHIFT = 20,
+ NVME_FEAT_TT_THSEL_MASK = 0x3,
+ NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT = 0,
+ NVME_FEAT_ERROR_RECOVERY_TLER_MASK = 0xffff,
+ NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT = 16,
+ NVME_FEAT_ERROR_RECOVERY_DULBE_MASK = 0x1,
+ NVME_FEAT_VWC_WCE_SHIFT = 0,
+ NVME_FEAT_VWC_WCE_MASK = 0x1,
+ NVME_FEAT_NRQS_NSQR_SHIFT = 0,
+ NVME_FEAT_NRQS_NSQR_MASK = 0xffff,
+ NVME_FEAT_NRQS_NCQR_SHIFT = 16,
+ NVME_FEAT_NRQS_NCQR_MASK = 0xffff,
+ NVME_FEAT_IRQC_THR_SHIFT = 0,
+ NVME_FEAT_IRQC_THR_MASK = 0xff,
+ NVME_FEAT_IRQC_TIME_SHIFT = 8,
+ NVME_FEAT_IRQC_TIME_MASK = 0xff,
+ NVME_FEAT_ICFG_IV_SHIFT = 0,
+ NVME_FEAT_ICFG_IV_MASK = 0xffff,
+ NVME_FEAT_ICFG_CD_SHIFT = 16,
+ NVME_FEAT_ICFG_CD_MASK = 0x1,
+ NVME_FEAT_WA_DN_SHIFT = 0,
+ NVME_FEAT_WA_DN_MASK = 0x1,
+ NVME_FEAT_AE_SMART_SHIFT = 0,
+ NVME_FEAT_AE_SMART_MASK = 0xff,
+ NVME_FEAT_AE_NAN_SHIFT = 8,
+ NVME_FEAT_AE_NAN_MASK = 0x1,
+ NVME_FEAT_AE_FW_SHIFT = 9,
+ NVME_FEAT_AE_FW_MASK = 0x1,
+ NVME_FEAT_AE_TELEM_SHIFT = 10,
+ NVME_FEAT_AE_TELEM_MASK = 0x1,
+ NVME_FEAT_AE_ANA_SHIFT = 11,
+ NVME_FEAT_AE_ANA_MASK = 0x1,
+ NVME_FEAT_AE_PLA_SHIFT = 12,
+ NVME_FEAT_AE_PLA_MASK = 0x1,
+ NVME_FEAT_AE_LBAS_SHIFT = 13,
+ NVME_FEAT_AE_LBAS_MASK = 0x1,
+ NVME_FEAT_AE_EGA_SHIFT = 14,
+ NVME_FEAT_AE_EGA_MASK = 0x1,
+ NVME_FEAT_APST_APSTE_SHIFT = 0,
+ NVME_FEAT_APST_APSTE_MASK = 0x1,
+ NVME_FEAT_HMEM_EHM_SHIFT = 0,
+ NVME_FEAT_HMEM_EHM_MASK = 0x1,
+ NVME_FEAT_HCTM_TMT2_SHIFT = 0,
+ NVME_FEAT_HCTM_TMT2_MASK = 0xffff,
+ NVME_FEAT_HCTM_TMT1_SHIFT = 16,
+ NVME_FEAT_HCTM_TMT1_MASK = 0xffff,
+ NVME_FEAT_NOPS_NOPPME_SHIFT = 0,
+ NVME_FEAT_NOPS_NOPPME_MASK = 0x1,
+ NVME_FEAT_RRL_RRL_SHIFT = 0,
+ NVME_FEAT_RRL_RRL_MASK = 0xff,
+ NVME_FEAT_PLM_PLME_SHIFT = 0,
+ NVME_FEAT_PLM_PLME_MASK = 0x1,
+ NVME_FEAT_PLMW_WS_SHIFT = 0,
+ NVME_FEAT_PLMW_WS_MASK = 0x7,
+ NVME_FEAT_LBAS_LSIRI_SHIFT = 0,
+ NVME_FEAT_LBAS_LSIRI_MASK = 0xffff,
+ NVME_FEAT_LBAS_LSIPI_SHIFT = 16,
+ NVME_FEAT_LBAS_LSIPI_MASK = 0xffff,
+ NVME_FEAT_SC_NODRM_SHIFT = 0,
+ NVME_FEAT_SC_NODRM_MASK = 0x1,
+ NVME_FEAT_EG_ENDGID_SHIFT = 0,
+ NVME_FEAT_EG_ENDGID_MASK = 0xffff,
+ NVME_FEAT_EG_EGCW_SHIFT = 16,
+ NVME_FEAT_EG_EGCW_MASK = 0xff,
+ NVME_FEAT_SPM_PBSLC_SHIFT = 0,
+ NVME_FEAT_SPM_PBSLC_MASK = 0xff,
+ NVME_FEAT_HOSTID_EXHID_SHIFT = 0,
+ NVME_FEAT_HOSTID_EXHID_MASK = 0x1,
+ NVME_FEAT_RM_REGPRE_SHIFT = 1,
+ NVME_FEAT_RM_REGPRE_MASK = 0x1,
+ NVME_FEAT_RM_RESREL_SHIFT = 2,
+ NVME_FEAT_RM_RESREL_MASK = 0x1,
+ NVME_FEAT_RM_RESPRE_SHIFT = 0x3,
+ NVME_FEAT_RM_RESPRE_MASK = 0x1,
+ NVME_FEAT_RP_PTPL_SHIFT = 0,
+ NVME_FEAT_RP_PTPL_MASK = 0x1,
+ NVME_FEAT_WP_WPS_SHIFT = 0,
+ NVME_FEAT_WP_WPS_MASK = 0x7,
+ NVME_FEAT_IOCSP_IOCSCI_SHIFT = 0,
+ NVME_FEAT_IOCSP_IOCSCI_MASK = 0x1ff,
+ NVME_FEAT_FDP_ENABLED_SHIFT = 0,
+ NVME_FEAT_FDP_ENABLED_MASK = 0x1,
+ NVME_FEAT_FDP_INDEX_SHIFT = 8,
+ NVME_FEAT_FDP_INDEX_MASK = 0xf,
+ NVME_FEAT_FDP_EVENTS_ENABLE_SHIFT = 0,
+ NVME_FEAT_FDP_EVENTS_ENABLE_MASK = 0x1,
+};
+
+/**
+ * enum nvme_get_features_sel - Get Features - Select
+ * @NVME_GET_FEATURES_SEL_CURRENT: Current value
+ * @NVME_GET_FEATURES_SEL_DEFAULT: Default value
+ * @NVME_GET_FEATURES_SEL_SAVED: Saved value
+ * @NVME_GET_FEATURES_SEL_SUPPORTED: Supported capabilities
+ */
+enum nvme_get_features_sel {
+ NVME_GET_FEATURES_SEL_CURRENT = 0,
+ NVME_GET_FEATURES_SEL_DEFAULT = 1,
+ NVME_GET_FEATURES_SEL_SAVED = 2,
+ NVME_GET_FEATURES_SEL_SUPPORTED = 3,
+};
+
+/**
+ * enum nvme_cmd_format_mset - Format NVM - Metadata Settings
+ * @NVME_FORMAT_MSET_SEPARATE: indicates that the metadata is transferred
+ * as part of a separate buffer.
+ * @NVME_FORMAT_MSET_EXTENDED: indicates that the metadata is transferred
+ * as part of an extended data LBA.
+ */
+enum nvme_cmd_format_mset {
+ NVME_FORMAT_MSET_SEPARATE = 0,
+ NVME_FORMAT_MSET_EXTENDED = 1,
+};
+
+/**
+ * enum nvme_cmd_format_pi - Format NVM - Protection Information
+ * @NVME_FORMAT_PI_DISABLE: Protection information is not enabled.
+ * @NVME_FORMAT_PI_TYPE1: Protection information is enabled, Type 1.
+ * @NVME_FORMAT_PI_TYPE2: Protection information is enabled, Type 2.
+ * @NVME_FORMAT_PI_TYPE3: Protection information is enabled, Type 3.
+ */
+enum nvme_cmd_format_pi {
+ NVME_FORMAT_PI_DISABLE = 0,
+ NVME_FORMAT_PI_TYPE1 = 1,
+ NVME_FORMAT_PI_TYPE2 = 2,
+ NVME_FORMAT_PI_TYPE3 = 3,
+};
+
+/**
+ * enum nvme_cmd_format_pil - Format NVM - Protection Information Location
+ * @NVME_FORMAT_PIL_LAST: Protection information is transferred as the last
+ * bytes of metadata.
+ * @NVME_FORMAT_PIL_FIRST: Protection information is transferred as the first
+ * bytes of metadata.
+ */
+enum nvme_cmd_format_pil {
+ NVME_FORMAT_PIL_LAST = 0,
+ NVME_FORMAT_PIL_FIRST = 1,
+};
+
+/**
+ * enum nvme_cmd_format_ses - Format NVM - Secure Erase Settings
+ * @NVME_FORMAT_SES_NONE: No secure erase operation requested.
+ * @NVME_FORMAT_SES_USER_DATA_ERASE: User Data Erase: All user data shall be erased,
+ * contents of the user data after the erase is
+ * indeterminate (e.g. the user data may be zero
+ * filled, one filled, etc.). If a User Data Erase
+ * is requested and all affected user data is
+ * encrypted, then the controller is allowed
+ * to use a cryptographic erase to perform
+ * the requested User Data Erase.
+ * @NVME_FORMAT_SES_CRYPTO_ERASE: Cryptographic Erase: All user data shall
+ * be erased cryptographically. This is
+ * accomplished by deleting the encryption key.
+ */
+enum nvme_cmd_format_ses {
+ NVME_FORMAT_SES_NONE = 0,
+ NVME_FORMAT_SES_USER_DATA_ERASE = 1,
+ NVME_FORMAT_SES_CRYPTO_ERASE = 2,
+};
+
+/**
+ * enum nvme_ns_mgmt_sel - Namespace Management - Select
+ * @NVME_NS_MGMT_SEL_CREATE: Namespace Create selection
+ * @NVME_NS_MGMT_SEL_DELETE: Namespace Delete selection
+ */
+enum nvme_ns_mgmt_sel {
+ NVME_NS_MGMT_SEL_CREATE = 0,
+ NVME_NS_MGMT_SEL_DELETE = 1,
+};
+
+/**
+ * enum nvme_ns_attach_sel - Namespace Attachment - Select
+ * @NVME_NS_ATTACH_SEL_CTRL_ATTACH: Namespace attach selection
+ * @NVME_NS_ATTACH_SEL_CTRL_DEATTACH: Namespace detach selection
+ */
+enum nvme_ns_attach_sel {
+ NVME_NS_ATTACH_SEL_CTRL_ATTACH = 0,
+ NVME_NS_ATTACH_SEL_CTRL_DEATTACH = 1,
+};
+
+/**
+ * enum nvme_fw_commit_ca - Firmware Commit - Commit Action
+ * @NVME_FW_COMMIT_CA_REPLACE: Downloaded image replaces the existing
+ * image, if any, in the specified Firmware
+ * Slot. The newly placed image is not
+ * activated.
+ * @NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE: Downloaded image replaces the existing
+ * image, if any, in the specified Firmware
+ * Slot. The newly placed image is activated
+ * at the next Controller Level Reset.
+ * @NVME_FW_COMMIT_CA_SET_ACTIVE: The existing image in the specified
+ * Firmware Slot is activated at the
+ * next Controller Level Reset.
+ * @NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE: Downloaded image replaces the existing
+ * image, if any, in the specified Firmware
+ * Slot and is then activated immediately.
+ * If there is not a newly downloaded image,
+ * then the existing image in the specified
+ * firmware slot is activated immediately.
+ * @NVME_FW_COMMIT_CA_REPLACE_BOOT_PARTITION: Downloaded image replaces the Boot
+ * Partition specified by the Boot
+ * Partition ID field.
+ * @NVME_FW_COMMIT_CA_ACTIVATE_BOOT_PARTITION: Mark the Boot Partition specified in
+ * the BPID field as active and update
+ * BPINFO.ABPID.
+ */
+enum nvme_fw_commit_ca {
+ NVME_FW_COMMIT_CA_REPLACE = 0,
+ NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE = 1,
+ NVME_FW_COMMIT_CA_SET_ACTIVE = 2,
+ NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE = 3,
+ NVME_FW_COMMIT_CA_REPLACE_BOOT_PARTITION = 6,
+ NVME_FW_COMMIT_CA_ACTIVATE_BOOT_PARTITION = 7,
+};
+
+/**
+ * enum nvme_directive_dtype - Directive Types
+ * @NVME_DIRECTIVE_DTYPE_IDENTIFY: Identify directive type
+ * @NVME_DIRECTIVE_DTYPE_STREAMS: Streams directive type
+ */
+enum nvme_directive_dtype {
+ NVME_DIRECTIVE_DTYPE_IDENTIFY = 0,
+ NVME_DIRECTIVE_DTYPE_STREAMS = 1,
+};
+
+/**
+ * enum nvme_directive_receive_doper - Directive Receive Directive Operation
+ * @NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM:
+ * @NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM:
+ * @NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS:
+ * @NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE:
+ */
+enum nvme_directive_receive_doper {
+ NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM = 0x01,
+ NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM = 0x01,
+ NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS = 0x02,
+ NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE = 0x03,
+};
+
+/**
+ * enum nvme_directive_send_doper - Directive Send Directive Operation
+ * @NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR:
+ * @NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER:
+ * @NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE:
+ */
+enum nvme_directive_send_doper {
+ NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR = 0x01,
+ NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER = 0x01,
+ NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE = 0x02,
+};
+
+/**
+ * enum nvme_directive_send_identify_endir - Enable Directive
+ * @NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_DISABLE:
+ * @NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_ENABLE:
+ */
+enum nvme_directive_send_identify_endir {
+ NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_DISABLE = 0,
+ NVME_DIRECTIVE_SEND_IDENTIFY_ENDIR_ENABLE = 1,
+};
+
+/**
+ * enum nvme_sanitize_sanact - Sanitize Action
+ * @NVME_SANITIZE_SANACT_EXIT_FAILURE: Exit Failure Mode.
+ * @NVME_SANITIZE_SANACT_START_BLOCK_ERASE: Start a Block Erase sanitize operation.
+ * @NVME_SANITIZE_SANACT_START_OVERWRITE: Start an Overwrite sanitize operation.
+ * @NVME_SANITIZE_SANACT_START_CRYPTO_ERASE: Start a Crypto Erase sanitize operation.
+ */
+enum nvme_sanitize_sanact {
+ NVME_SANITIZE_SANACT_EXIT_FAILURE = 1,
+ NVME_SANITIZE_SANACT_START_BLOCK_ERASE = 2,
+ NVME_SANITIZE_SANACT_START_OVERWRITE = 3,
+ NVME_SANITIZE_SANACT_START_CRYPTO_ERASE = 4,
+};
+
+/**
+ * enum nvme_dst_stc - Action taken by the Device Self-test command
+ * @NVME_DST_STC_SHORT: Start a short device self-test operation
+ * @NVME_DST_STC_LONG: Start an extended device self-test operation
+ * @NVME_DST_STC_VS: Start a vendor specific device self-test operation
+ * @NVME_DST_STC_ABORT: Abort device self-test operation
+ */
+enum nvme_dst_stc {
+ NVME_DST_STC_SHORT = 0x1,
+ NVME_DST_STC_LONG = 0x2,
+ NVME_DST_STC_VS = 0xe,
+ NVME_DST_STC_ABORT = 0xf,
+};
+
+/**
+ * enum nvme_virt_mgmt_act - Virtualization Management - Action
+ * @NVME_VIRT_MGMT_ACT_PRIM_CTRL_FLEX_ALLOC: Primary Controller Flexible
+ * Allocation
+ * @NVME_VIRT_MGMT_ACT_OFFLINE_SEC_CTRL: Secondary Controller Offline
+ * @NVME_VIRT_MGMT_ACT_ASSIGN_SEC_CTRL: Secondary Controller Assign
+ * @NVME_VIRT_MGMT_ACT_ONLINE_SEC_CTRL: Secondary Controller Online
+ */
+enum nvme_virt_mgmt_act {
+ NVME_VIRT_MGMT_ACT_PRIM_CTRL_FLEX_ALLOC = 1,
+ NVME_VIRT_MGMT_ACT_OFFLINE_SEC_CTRL = 7,
+ NVME_VIRT_MGMT_ACT_ASSIGN_SEC_CTRL = 8,
+ NVME_VIRT_MGMT_ACT_ONLINE_SEC_CTRL = 9,
+};
+
+/**
+ * enum nvme_virt_mgmt_rt - Virtualization Management - Resource Type
+ * @NVME_VIRT_MGMT_RT_VQ_RESOURCE: VQ Resources
+ * @NVME_VIRT_MGMT_RT_VI_RESOURCE: VI Resources
+ */
+enum nvme_virt_mgmt_rt {
+ NVME_VIRT_MGMT_RT_VQ_RESOURCE = 0,
+ NVME_VIRT_MGMT_RT_VI_RESOURCE = 1,
+};
+
+/**
+ * enum nvme_ns_write_protect_cfg - Write Protection - Write Protection State
+ * @NVME_NS_WP_CFG_NONE: No Write Protect
+ * @NVME_NS_WP_CFG_PROTECT: Write Protect
+ * @NVME_NS_WP_CFG_PROTECT_POWER_CYCLE: Write Protect Until Power Cycle
+ * @NVME_NS_WP_CFG_PROTECT_PERMANENT: Permanent Write Protect
+ */
+enum nvme_ns_write_protect_cfg {
+ NVME_NS_WP_CFG_NONE = 0,
+ NVME_NS_WP_CFG_PROTECT = 1,
+ NVME_NS_WP_CFG_PROTECT_POWER_CYCLE = 2,
+ NVME_NS_WP_CFG_PROTECT_PERMANENT = 3,
+};
+
+/**
+ * enum nvme_log_ana_lsp - Asymmetric Namespace Access - Return Groups Only
+ * @NVME_LOG_ANA_LSP_RGO_NAMESPACES:
+ * @NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY:
+ */
+enum nvme_log_ana_lsp {
+ NVME_LOG_ANA_LSP_RGO_NAMESPACES = 0,
+ NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY = 1,
+};
+
+/**
+ * enum nvme_log_phy_rx_eom_action - Physical Interface Receiver Eye Opening Measurement Action
+ * @NVME_LOG_PHY_RX_EOM_READ: Read Log Data
+ * @NVME_LOG_PHY_RX_EOM_START_READ: Start Measurement and Read Log Data
+ * @NVME_LOG_PHY_RX_EOM_ABORT_CLEAR: Abort Measurement and Clear Log Data
+ */
+enum nvme_log_phy_rx_eom_action {
+ NVME_LOG_PHY_RX_EOM_READ = 0,
+ NVME_LOG_PHY_RX_EOM_START_READ = 1,
+ NVME_LOG_PHY_RX_EOM_ABORT_CLEAR = 2,
+};
+
+/**
+ * enum nvme_log_phy_rx_eom_quality - Physical Interface Receiver Eye Opening Measurement Quality
+ * @NVME_LOG_PHY_RX_EOM_GOOD: <= Better Quality
+ * @NVME_LOG_PHY_RX_EOM_BETTER: <= Best Quality, >= Good Quality
+ * @NVME_LOG_PHY_RX_EOM_BEST: >= Better Quality
+ */
+enum nvme_log_phy_rx_eom_quality {
+ NVME_LOG_PHY_RX_EOM_GOOD = 0,
+ NVME_LOG_PHY_RX_EOM_BETTER = 1,
+ NVME_LOG_PHY_RX_EOM_BEST = 2,
+};
+
+/**
+ * enum nvme_pevent_log_action - Persistent Event Log - Action
+ * @NVME_PEVENT_LOG_READ: Read Log Data
+ * @NVME_PEVENT_LOG_EST_CTX_AND_READ: Establish Context and Read Log Data
+ * @NVME_PEVENT_LOG_RELEASE_CTX: Release Context
+ */
+enum nvme_pevent_log_action {
+ NVME_PEVENT_LOG_READ = 0x0,
+ NVME_PEVENT_LOG_EST_CTX_AND_READ = 0x1,
+ NVME_PEVENT_LOG_RELEASE_CTX = 0x2,
+};
+
+/**
+ * enum nvme_feat_tmpthresh_thsel - Temperature Threshold - Threshold Type Select
+ * @NVME_FEATURE_TEMPTHRESH_THSEL_OVER: Over temperature threshold select
+ * @NVME_FEATURE_TEMPTHRESH_THSEL_UNDER: Under temperature threshold select
+ */
+enum nvme_feat_tmpthresh_thsel {
+ NVME_FEATURE_TEMPTHRESH_THSEL_OVER = 0,
+ NVME_FEATURE_TEMPTHRESH_THSEL_UNDER = 1,
+};
+
+/**
+ * enum nvme_features_async_event_config_flags - Asynchronous Event Configuration configuration flags
+ * @NVME_FEATURE_AENCFG_SMART_CRIT_SPARE:
+ * @NVME_FEATURE_AENCFG_SMART_CRIT_TEMPERATURE:
+ * @NVME_FEATURE_AENCFG_SMART_CRIT_DEGRADED:
+ * @NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY:
+ * @NVME_FEATURE_AENCFG_SMART_CRIT_VOLATILE_BACKUP:
+ * @NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY_PMR:
+ * @NVME_FEATURE_AENCFG_NOTICE_NAMESPACE_ATTRIBUTES:
+ * @NVME_FEATURE_AENCFG_NOTICE_FIRMWARE_ACTIVATION:
+ * @NVME_FEATURE_AENCFG_NOTICE_TELEMETRY_LOG:
+ * @NVME_FEATURE_AENCFG_NOTICE_ANA_CHANGE:
+ * @NVME_FEATURE_AENCFG_NOTICE_PL_EVENT:
+ * @NVME_FEATURE_AENCFG_NOTICE_LBA_STATUS:
+ * @NVME_FEATURE_AENCFG_NOTICE_EG_EVENT:
+ * @NVME_FEATURE_AENCFG_NOTICE_DISCOVERY_CHANGE:
+ */
+enum nvme_features_async_event_config_flags {
+ NVME_FEATURE_AENCFG_SMART_CRIT_SPARE = 1 << 0,
+ NVME_FEATURE_AENCFG_SMART_CRIT_TEMPERATURE = 1 << 1,
+ NVME_FEATURE_AENCFG_SMART_CRIT_DEGRADED = 1 << 2,
+ NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY = 1 << 3,
+ NVME_FEATURE_AENCFG_SMART_CRIT_VOLATILE_BACKUP = 1 << 4,
+ NVME_FEATURE_AENCFG_SMART_CRIT_READ_ONLY_PMR = 1 << 5,
+ NVME_FEATURE_AENCFG_NOTICE_NAMESPACE_ATTRIBUTES = 1 << 8,
+ NVME_FEATURE_AENCFG_NOTICE_FIRMWARE_ACTIVATION = 1 << 9,
+ NVME_FEATURE_AENCFG_NOTICE_TELEMETRY_LOG = 1 << 10,
+ NVME_FEATURE_AENCFG_NOTICE_ANA_CHANGE = 1 << 11,
+ NVME_FEATURE_AENCFG_NOTICE_PL_EVENT = 1 << 12,
+ NVME_FEATURE_AENCFG_NOTICE_LBA_STATUS = 1 << 13,
+ NVME_FEATURE_AENCFG_NOTICE_EG_EVENT = 1 << 14,
+ NVME_FEATURE_AENCFG_NOTICE_DISCOVERY_CHANGE = 1 << 31,
+};
+
+/**
+ * enum nvme_feat_plm_window_select - Predictable Latency Per NVM Set Log
+ * @NVME_FEATURE_PLM_DTWIN: Deterministic Window select
+ * @NVME_FEATURE_PLM_NDWIN: Non-Deterministic Window select
+ */
+enum nvme_feat_plm_window_select {
+ NVME_FEATURE_PLM_DTWIN = 1,
+ NVME_FEATURE_PLM_NDWIN = 2,
+};
+
+/**
+ * enum nvme_feat_resv_notify_flags - Reservation Notification Configuration
+ * @NVME_FEAT_RESV_NOTIFY_REGPRE: Mask Registration Preempted Notification
+ * @NVME_FEAT_RESV_NOTIFY_RESREL: Mask Reservation Released Notification
+ * @NVME_FEAT_RESV_NOTIFY_RESPRE: Mask Reservation Preempted Notification
+ */
+enum nvme_feat_resv_notify_flags {
+ NVME_FEAT_RESV_NOTIFY_REGPRE = 1 << 1,
+ NVME_FEAT_RESV_NOTIFY_RESREL = 1 << 2,
+ NVME_FEAT_RESV_NOTIFY_RESPRE = 1 << 3,
+};
+
+/**
+ * enum nvme_feat_nswpcfg_state - Write Protection - Write Protection State
+ * @NVME_FEAT_NS_NO_WRITE_PROTECT: No Write Protect
+ * @NVME_FEAT_NS_WRITE_PROTECT: Write Protect
+ * @NVME_FEAT_NS_WRITE_PROTECT_PWR_CYCLE: Write Protect Until Power Cycle
+ * @NVME_FEAT_NS_WRITE_PROTECT_PERMANENT: Permanent Write Protect
+ */
+enum nvme_feat_nswpcfg_state {
+ NVME_FEAT_NS_NO_WRITE_PROTECT = 0,
+ NVME_FEAT_NS_WRITE_PROTECT = 1,
+ NVME_FEAT_NS_WRITE_PROTECT_PWR_CYCLE = 2,
+ NVME_FEAT_NS_WRITE_PROTECT_PERMANENT = 3,
+};
+
+/**
+ * enum nvme_fctype - Fabrics Command Types
+ * @nvme_fabrics_type_property_set: Property set
+ * @nvme_fabrics_type_connect: Connect
+ * @nvme_fabrics_type_property_get: Property Get
+ * @nvme_fabrics_type_auth_send: Authentication Send
+ * @nvme_fabrics_type_auth_receive: Authentication Receive
+ * @nvme_fabrics_type_disconnect: Disconnect
+ */
+enum nvme_fctype {
+ nvme_fabrics_type_property_set = 0x00,
+ nvme_fabrics_type_connect = 0x01,
+ nvme_fabrics_type_property_get = 0x04,
+ nvme_fabrics_type_auth_send = 0x05,
+ nvme_fabrics_type_auth_receive = 0x06,
+ nvme_fabrics_type_disconnect = 0x08,
+};
+
+/**
+ * enum nvme_data_tfr - Data transfer direction of the command
+ * @NVME_DATA_TFR_NO_DATA_TFR: No data transfer
+ * @NVME_DATA_TFR_HOST_TO_CTRL: Host to controller
+ * @NVME_DATA_TFR_CTRL_TO_HOST: Controller to host
+ * @NVME_DATA_TFR_BIDIRECTIONAL: Bidirectional
+ */
+enum nvme_data_tfr {
+ NVME_DATA_TFR_NO_DATA_TFR = 0x0,
+ NVME_DATA_TFR_HOST_TO_CTRL = 0x1,
+ NVME_DATA_TFR_CTRL_TO_HOST = 0x2,
+ NVME_DATA_TFR_BIDIRECTIONAL = 0x3,
+};
+
+/**
+ * enum nvme_io_opcode - Opcodes for I/O Commands
+ * @nvme_cmd_flush: Flush
+ * @nvme_cmd_write: Write
+ * @nvme_cmd_read: Read
+ * @nvme_cmd_write_uncor: Write Uncorrectable
+ * @nvme_cmd_compare: Compare
+ * @nvme_cmd_write_zeroes: write Zeros
+ * @nvme_cmd_dsm: Dataset Management
+ * @nvme_cmd_verify: Verify
+ * @nvme_cmd_resv_register: Reservation Register
+ * @nvme_cmd_resv_report: Reservation Report
+ * @nvme_cmd_resv_acquire: Reservation Acquire
+ * @nvme_cmd_io_mgmt_recv: I/O Management Receive
+ * @nvme_cmd_resv_release: Reservation Release
+ * @nvme_cmd_copy: Copy
+ * @nvme_cmd_io_mgmt_send: I/O Management Send
+ * @nvme_zns_cmd_mgmt_send: Zone Management Send
+ * @nvme_zns_cmd_mgmt_recv: Zone Management Receive
+ * @nvme_zns_cmd_append: Zone Append
+ */
+enum nvme_io_opcode {
+ nvme_cmd_flush = 0x00,
+ nvme_cmd_write = 0x01,
+ nvme_cmd_read = 0x02,
+ nvme_cmd_write_uncor = 0x04,
+ nvme_cmd_compare = 0x05,
+ nvme_cmd_write_zeroes = 0x08,
+ nvme_cmd_dsm = 0x09,
+ nvme_cmd_verify = 0x0c,
+ nvme_cmd_resv_register = 0x0d,
+ nvme_cmd_resv_report = 0x0e,
+ nvme_cmd_resv_acquire = 0x11,
+ nvme_cmd_io_mgmt_recv = 0x12,
+ nvme_cmd_resv_release = 0x15,
+ nvme_cmd_copy = 0x19,
+ nvme_cmd_io_mgmt_send = 0x1d,
+ nvme_zns_cmd_mgmt_send = 0x79,
+ nvme_zns_cmd_mgmt_recv = 0x7a,
+ nvme_zns_cmd_append = 0x7d,
+};
+
+/**
+ * enum nvme_io_control_flags - I/O control flags
+ * @NVME_IO_DTYPE_STREAMS: Directive Type Streams
+ * @NVME_IO_STC: Storage Tag Check
+ * @NVME_IO_DEAC: Deallocate
+ * @NVME_IO_ZNS_APPEND_PIREMAP: Protection Information Remap
+ * @NVME_IO_PRINFO_PRCHK_REF: Protection Information Check Reference Tag
+ * @NVME_IO_PRINFO_PRCHK_APP: Protection Information Check Application Tag
+ * @NVME_IO_PRINFO_PRCHK_GUARD: Protection Information Check Guard field
+ * @NVME_IO_PRINFO_PRACT: Protection Information Action
+ * @NVME_IO_FUA: Force Unit Access
+ * @NVME_IO_LR: Limited Retry
+ */
+enum nvme_io_control_flags {
+ NVME_IO_DTYPE_STREAMS = 1 << 4,
+ NVME_IO_STC = 1 << 8,
+ NVME_IO_DEAC = 1 << 9,
+ NVME_IO_ZNS_APPEND_PIREMAP = 1 << 9,
+ NVME_IO_PRINFO_PRCHK_REF = 1 << 10,
+ NVME_IO_PRINFO_PRCHK_APP = 1 << 11,
+ NVME_IO_PRINFO_PRCHK_GUARD = 1 << 12,
+ NVME_IO_PRINFO_PRACT = 1 << 13,
+ NVME_IO_FUA = 1 << 14,
+ NVME_IO_LR = 1 << 15,
+};
+
+/**
+ * enum nvme_io_dsm_flags - Dataset Management flags
+ * @NVME_IO_DSM_FREQ_UNSPEC: No frequency information provided
+ * @NVME_IO_DSM_FREQ_TYPICAL: Typical number of reads and writes
+ * expected for this LBA range
+ * @NVME_IO_DSM_FREQ_RARE: Infrequent writes and infrequent
+ * reads to the LBA range indicated
+ * @NVME_IO_DSM_FREQ_READS: Infrequent writes and frequent
+ * reads to the LBA range indicated
+ * @NVME_IO_DSM_FREQ_WRITES: Frequent writes and infrequent
+ * reads to the LBA range indicated
+ * @NVME_IO_DSM_FREQ_RW: Frequent writes and frequent reads
+ * to the LBA range indicated
+ * @NVME_IO_DSM_FREQ_ONCE:
+ * @NVME_IO_DSM_FREQ_PREFETCH:
+ * @NVME_IO_DSM_FREQ_TEMP:
+ * @NVME_IO_DSM_LATENCY_NONE: No latency information provided
+ * @NVME_IO_DSM_LATENCY_IDLE: Longer latency acceptable
+ * @NVME_IO_DSM_LATENCY_NORM: Typical latency
+ * @NVME_IO_DSM_LATENCY_LOW: Smallest possible latency
+ * @NVME_IO_DSM_SEQ_REQ:
+ * @NVME_IO_DSM_COMPRESSED:
+ */
+enum nvme_io_dsm_flags {
+ NVME_IO_DSM_FREQ_UNSPEC = 0,
+ NVME_IO_DSM_FREQ_TYPICAL = 1,
+ NVME_IO_DSM_FREQ_RARE = 2,
+ NVME_IO_DSM_FREQ_READS = 3,
+ NVME_IO_DSM_FREQ_WRITES = 4,
+ NVME_IO_DSM_FREQ_RW = 5,
+ NVME_IO_DSM_FREQ_ONCE = 6,
+ NVME_IO_DSM_FREQ_PREFETCH = 7,
+ NVME_IO_DSM_FREQ_TEMP = 8,
+ NVME_IO_DSM_LATENCY_NONE = 0 << 4,
+ NVME_IO_DSM_LATENCY_IDLE = 1 << 4,
+ NVME_IO_DSM_LATENCY_NORM = 2 << 4,
+ NVME_IO_DSM_LATENCY_LOW = 3 << 4,
+ NVME_IO_DSM_SEQ_REQ = 1 << 6,
+ NVME_IO_DSM_COMPRESSED = 1 << 7,
+};
+
+/**
+ * enum nvme_dsm_attributes - Dataset Management attributes
+ * @NVME_DSMGMT_IDR: Attribute -Integral Dataset for Read
+ * @NVME_DSMGMT_IDW: Attribute - Integral Dataset for Write
+ * @NVME_DSMGMT_AD: Attribute - Deallocate
+ */
+enum nvme_dsm_attributes {
+ NVME_DSMGMT_IDR = 1 << 0,
+ NVME_DSMGMT_IDW = 1 << 1,
+ NVME_DSMGMT_AD = 1 << 2,
+};
+
+/**
+ * enum nvme_resv_rtype - Reservation Type Encoding
+ * @NVME_RESERVATION_RTYPE_WE: Write Exclusive Reservation
+ * @NVME_RESERVATION_RTYPE_EA: Exclusive Access Reservation
+ * @NVME_RESERVATION_RTYPE_WERO: Write Exclusive - Registrants Only Reservation
+ * @NVME_RESERVATION_RTYPE_EARO: Exclusive Access - Registrants Only Reservation
+ * @NVME_RESERVATION_RTYPE_WEAR: Write Exclusive - All Registrants Reservation
+ * @NVME_RESERVATION_RTYPE_EAAR: Exclusive Access - All Registrants Reservation
+ */
+enum nvme_resv_rtype {
+ NVME_RESERVATION_RTYPE_WE = 1,
+ NVME_RESERVATION_RTYPE_EA = 2,
+ NVME_RESERVATION_RTYPE_WERO = 3,
+ NVME_RESERVATION_RTYPE_EARO = 4,
+ NVME_RESERVATION_RTYPE_WEAR = 5,
+ NVME_RESERVATION_RTYPE_EAAR = 6,
+};
+
+/**
+ * enum nvme_resv_racqa - Reservation Acquire - Reservation Acquire Action
+ * @NVME_RESERVATION_RACQA_ACQUIRE: Acquire
+ * @NVME_RESERVATION_RACQA_PREEMPT: Preempt
+ * @NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT: Preempt and Abort
+ */
+enum nvme_resv_racqa {
+ NVME_RESERVATION_RACQA_ACQUIRE = 0,
+ NVME_RESERVATION_RACQA_PREEMPT = 1,
+ NVME_RESERVATION_RACQA_PREEMPT_AND_ABORT = 2,
+};
+
+/**
+ * enum nvme_resv_rrega - Reservation Register - Reservation Register Action
+ * @NVME_RESERVATION_RREGA_REGISTER_KEY: Register Reservation Key
+ * @NVME_RESERVATION_RREGA_UNREGISTER_KEY: Unregister Reservation Key
+ * @NVME_RESERVATION_RREGA_REPLACE_KEY: Replace Reservation Key
+ */
+enum nvme_resv_rrega {
+ NVME_RESERVATION_RREGA_REGISTER_KEY = 0,
+ NVME_RESERVATION_RREGA_UNREGISTER_KEY = 1,
+ NVME_RESERVATION_RREGA_REPLACE_KEY = 2,
+};
+
+/**
+ * enum nvme_resv_cptpl - Reservation Register - Change Persist Through Power Loss State
+ * @NVME_RESERVATION_CPTPL_NO_CHANGE: No change to PTPL state
+ * @NVME_RESERVATION_CPTPL_CLEAR: Reservations are released and
+ * registrants are cleared on a power on
+ * @NVME_RESERVATION_CPTPL_PERSIST: Reservations and registrants persist
+ * across a power loss
+ */
+enum nvme_resv_cptpl {
+ NVME_RESERVATION_CPTPL_NO_CHANGE = 0,
+ NVME_RESERVATION_CPTPL_CLEAR = 2,
+ NVME_RESERVATION_CPTPL_PERSIST = 3,
+};
+
+/**
+ * enum nvme_resv_rrela - Reservation Release - Reservation Release Action
+ * @NVME_RESERVATION_RRELA_RELEASE: Release
+ * @NVME_RESERVATION_RRELA_CLEAR: Clear
+ */
+enum nvme_resv_rrela {
+ NVME_RESERVATION_RRELA_RELEASE = 0,
+ NVME_RESERVATION_RRELA_CLEAR = 1
+};
+
+/**
+ * enum nvme_zns_send_action - Zone Management Send - Zone Send Action
+ * @NVME_ZNS_ZSA_CLOSE: Close Zone
+ * @NVME_ZNS_ZSA_FINISH: Finish Zone
+ * @NVME_ZNS_ZSA_OPEN: Open Zone
+ * @NVME_ZNS_ZSA_RESET: Reset Zone
+ * @NVME_ZNS_ZSA_OFFLINE: Offline Zone
+ * @NVME_ZNS_ZSA_SET_DESC_EXT: Set Zone Descriptor Extension
+ * @NVME_ZNS_ZSA_ZRWA_FLUSH: Flush
+ */
+enum nvme_zns_send_action {
+ NVME_ZNS_ZSA_CLOSE = 0x1,
+ NVME_ZNS_ZSA_FINISH = 0x2,
+ NVME_ZNS_ZSA_OPEN = 0x3,
+ NVME_ZNS_ZSA_RESET = 0x4,
+ NVME_ZNS_ZSA_OFFLINE = 0x5,
+ NVME_ZNS_ZSA_SET_DESC_EXT = 0x10,
+ NVME_ZNS_ZSA_ZRWA_FLUSH = 0x11,
+};
+
+/**
+ * enum nvme_zns_recv_action - Zone Management Receive - Zone Receive Action Specific Features
+ * @NVME_ZNS_ZRA_REPORT_ZONES: Report Zones
+ * @NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES: Extended Report Zones
+ */
+enum nvme_zns_recv_action {
+ NVME_ZNS_ZRA_REPORT_ZONES = 0x0,
+ NVME_ZNS_ZRA_EXTENDED_REPORT_ZONES = 0x1,
+};
+
+/**
+ * enum nvme_zns_report_options - Zone Management Receive - Zone Receive Action Specific Field
+ * @NVME_ZNS_ZRAS_REPORT_ALL: List all zones
+ * @NVME_ZNS_ZRAS_REPORT_EMPTY: List the zones in the ZSE:Empty state
+ * @NVME_ZNS_ZRAS_REPORT_IMPL_OPENED: List the zones in the ZSIO:Implicitly Opened state
+ * @NVME_ZNS_ZRAS_REPORT_EXPL_OPENED: List the zones in the ZSEO:Explicitly Opened state
+ * @NVME_ZNS_ZRAS_REPORT_CLOSED: List the zones in the ZSC:Closed state
+ * @NVME_ZNS_ZRAS_REPORT_FULL: List the zones in the ZSF:Full state
+ * @NVME_ZNS_ZRAS_REPORT_READ_ONLY: List the zones in the ZSRO:Read Only state
+ * @NVME_ZNS_ZRAS_REPORT_OFFLINE: List the zones in the ZSO:Offline state
+ */
+enum nvme_zns_report_options {
+ NVME_ZNS_ZRAS_REPORT_ALL = 0x0,
+ NVME_ZNS_ZRAS_REPORT_EMPTY = 0x1,
+ NVME_ZNS_ZRAS_REPORT_IMPL_OPENED = 0x2,
+ NVME_ZNS_ZRAS_REPORT_EXPL_OPENED = 0x3,
+ NVME_ZNS_ZRAS_REPORT_CLOSED = 0x4,
+ NVME_ZNS_ZRAS_REPORT_FULL = 0x5,
+ NVME_ZNS_ZRAS_REPORT_READ_ONLY = 0x6,
+ NVME_ZNS_ZRAS_REPORT_OFFLINE = 0x7,
+};
+
+/**
+ * enum nvme_io_mgmt_recv_mo - I/O Management Receive - Management Operation
+ * @NVME_IO_MGMT_RECV_RUH_STATUS: Reclaim Unit Handle Status
+ */
+enum nvme_io_mgmt_recv_mo {
+ NVME_IO_MGMT_RECV_RUH_STATUS = 0x1,
+};
+
+/**
+ * enum nvme_io_mgmt_send_mo - I/O Management Send - Management Operation
+ * @NVME_IO_MGMT_SEND_RUH_UPDATE: Reclaim Unit Handle Update
+ */
+enum nvme_io_mgmt_send_mo {
+ NVME_IO_MGMT_SEND_RUH_UPDATE = 0x1,
+};
+
+#ifndef SWIG
+/**
+ * struct nvme_ns_mgmt_host_sw_specified - Namespace management Host Software
+ * Specified Fields.
+ * @nsze: Namespace Size indicates the total size of the namespace in
+ * logical blocks. The number of logical blocks is based on the
+ * formatted LBA size.
+ * @ncap: Namespace Capacity indicates the maximum number of logical blocks
+ * that may be allocated in the namespace at any point in time. The
+ * number of logical blocks is based on the formatted LBA size.
+ * @rsvd16: Reserved
+ * @flbas: Formatted LBA Size, see &enum nvme_id_ns_flbas.
+ * @rsvd27: Reserved
+ * @dps: End-to-end Data Protection Type Settings, see
+ * &enum nvme_id_ns_dps.
+ * @nmic: Namespace Multi-path I/O and Namespace Sharing Capabilities, see
+ * &enum nvme_id_ns_nmic.
+ * @rsvd31: Reserved
+ * @anagrpid: ANA Group Identifier indicates the ANA Group Identifier of the
+ * ANA group of which the namespace is a member.
+ * @rsvd96: Reserved
+ * @nvmsetid: NVM Set Identifier indicates the NVM Set with which this
+ * namespace is associated.
+ * @endgid: Endurance Group Identifier indicates the Endurance Group with
+ * which this namespace is associated.
+ * @rsvd104: Reserved
+ * @lbstm: Logical Block Storage Tag Mask Identifies the mask for the
+ * Storage Tag field for the protection information
+ * @nphndls: Number of Placement Handles specifies the number of Placement
+ * Handles included in the Placement Handle List
+ * @rsvd394: Reserved
+ * @rsvd499: Reserved for I/O Command Sets that extend this specification.
+ * @zns: rsvd499( Zoned Namespace Command Set specific field )
+ * @znsco: Zoned Namespace Create Options
+ * Bits 7-1: Reserved.
+ * Bits 0: Allocate ZRWA Resources (AZR): If set to ‘1’, then the
+ * namespace is to be created with the number of ZRWA resource specified
+ * in the RNUMZRWA field of this data structure. If cleared to ‘0’, then
+ * no ZRWA resources are allocated to the namespace to be created. If
+ * the ZRWASUP bit is cleared to ‘0’, then this field shall be ignored
+ * by the controller.
+ * @rar: Requested Active Resources specifies the number of active
+ * resources to be allocated to the created namespace.
+ * @ror: Requested Open Resources specifies the number of open resources
+ * to be allocated to the created namespace.
+ * @rnumzrwa: Requested Number of ZRWA Resources specifies the number of ZRWA
+ * resources to be allocated to the created namespace.
+ * see &struct nvme_ns_mgmt_host_sw_specified_zns.
+ * @phndl: Placement Handle Associated RUH : This field specifies the Reclaim
+ * Unit Handle Identifier to be associated with the Placement Handle
+ * value. If the Flexible Data Placement capability is not supported or
+ * not enabled in specified Endurance Group, then the controller shall
+ * ignore this field.
+ * @rsvd768: Reserved
+ */
+struct nvme_ns_mgmt_host_sw_specified {
+ __le64 nsze;
+ __le64 ncap;
+ __u8 rsvd16[10];
+ __u8 flbas;
+ __u8 rsvd27[2];
+ __u8 dps;
+ __u8 nmic;
+ __u8 rsvd31[61];
+ __le32 anagrpid;
+ __u8 rsvd96[4];
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 rsvd104[280];
+ __le64 lbstm;
+ __le16 nphndls;
+ __u8 rsvd394[105];
+ union {
+ __u8 rsvd499[13];
+ struct {
+ __u8 znsco;
+ __le32 rar;
+ __le32 ror;
+ __le32 rnumzrwa;
+ } __attribute__((packed)) zns;
+ };
+ __le16 phndl[128];
+ __u8 rsvd768[3328];
+};
+#endif /* SWIG */
+
+#endif /* _LIBNVME_TYPES_H */
diff --git a/src/nvme/util.c b/src/nvme/util.c
new file mode 100644
index 0000000..f091da7
--- /dev/null
+++ b/src/nvme/util.c
@@ -0,0 +1,1137 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include <ccan/endian/endian.h>
+
+#include "cleanup.h"
+#include "private.h"
+#include "util.h"
+#include "log.h"
+
+/* The bionic libc implementation doesn't define LINE_MAX */
+#ifndef LINE_MAX
+#define LINE_MAX 2048
+#endif
+
+/* Source Code Control System, query version of binary with 'what' */
+const char sccsid[] = "@(#)libnvme " GIT_VERSION;
+
+static inline __u8 nvme_generic_status_to_errno(__u16 status)
+{
+ switch (status) {
+ case NVME_SC_INVALID_OPCODE:
+ case NVME_SC_INVALID_FIELD:
+ case NVME_SC_INVALID_NS:
+ case NVME_SC_SGL_INVALID_LAST:
+ case NVME_SC_SGL_INVALID_COUNT:
+ case NVME_SC_SGL_INVALID_DATA:
+ case NVME_SC_SGL_INVALID_METADATA:
+ case NVME_SC_SGL_INVALID_TYPE:
+ case NVME_SC_SGL_INVALID_OFFSET:
+ case NVME_SC_PRP_INVALID_OFFSET:
+ case NVME_SC_CMB_INVALID_USE:
+ case NVME_SC_KAT_INVALID:
+ return EINVAL;
+ case NVME_SC_CMDID_CONFLICT:
+ return EADDRINUSE;
+ case NVME_SC_DATA_XFER_ERROR:
+ case NVME_SC_INTERNAL:
+ case NVME_SC_SANITIZE_FAILED:
+ return EIO;
+ case NVME_SC_POWER_LOSS:
+ case NVME_SC_ABORT_REQ:
+ case NVME_SC_ABORT_QUEUE:
+ case NVME_SC_FUSED_FAIL:
+ case NVME_SC_FUSED_MISSING:
+ return EWOULDBLOCK;
+ case NVME_SC_CMD_SEQ_ERROR:
+ return EILSEQ;
+ case NVME_SC_SANITIZE_IN_PROGRESS:
+ case NVME_SC_FORMAT_IN_PROGRESS:
+ return EINPROGRESS;
+ case NVME_SC_NS_WRITE_PROTECTED:
+ case NVME_SC_NS_NOT_READY:
+ case NVME_SC_RESERVATION_CONFLICT:
+ case NVME_SC_OP_DENIED:
+ case NVME_SC_ADMIN_CMD_MEDIA_NOT_READY:
+ return EACCES;
+ case NVME_SC_LBA_RANGE:
+ return EREMOTEIO;
+ case NVME_SC_CAP_EXCEEDED:
+ case NVME_SC_AWU_EXCEEDED:
+ return ENOSPC;
+ }
+ return EIO;
+}
+
+static inline __u8 nvme_cmd_specific_status_to_errno(__u16 status)
+{
+ switch (status) {
+ case NVME_SC_CQ_INVALID:
+ case NVME_SC_QID_INVALID:
+ case NVME_SC_QUEUE_SIZE:
+ case NVME_SC_FIRMWARE_SLOT:
+ case NVME_SC_FIRMWARE_IMAGE:
+ case NVME_SC_INVALID_VECTOR:
+ case NVME_SC_INVALID_LOG_PAGE:
+ case NVME_SC_INVALID_FORMAT:
+ case NVME_SC_INVALID_QUEUE:
+ case NVME_SC_NS_INSUFFICIENT_CAP:
+ case NVME_SC_NS_ID_UNAVAILABLE:
+ case NVME_SC_CTRL_LIST_INVALID:
+ case NVME_SC_BAD_ATTRIBUTES:
+ case NVME_SC_INVALID_PI:
+ case NVME_SC_INVALID_CTRL_ID:
+ case NVME_SC_INVALID_SEC_CTRL_STATE:
+ case NVME_SC_INVALID_CTRL_RESOURCES:
+ case NVME_SC_INVALID_RESOURCE_ID:
+ case NVME_SC_ANA_GROUP_ID_INVALID:
+ case NVME_SC_INSUFFICIENT_CAP:
+ case NVME_SC_INVALID_IOCS:
+ case NVME_SC_ID_UNAVAILABLE:
+ return EINVAL;
+ case NVME_SC_ABORT_LIMIT:
+ case NVME_SC_ASYNC_LIMIT:
+ case NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED:
+ return EDQUOT;
+ case NVME_SC_FW_NEEDS_CONV_RESET:
+ case NVME_SC_FW_NEEDS_SUBSYS_RESET:
+ case NVME_SC_FW_NEEDS_MAX_TIME:
+ case NVME_SC_FW_NEEDS_RESET:
+ return ERESTART;
+ case NVME_SC_FEATURE_NOT_SAVEABLE:
+ case NVME_SC_FEATURE_NOT_CHANGEABLE:
+ case NVME_SC_FEATURE_NOT_PER_NS:
+ case NVME_SC_FW_ACTIVATE_PROHIBITED:
+ case NVME_SC_NS_IS_PRIVATE:
+ case NVME_SC_BP_WRITE_PROHIBITED:
+ case NVME_SC_READ_ONLY:
+ case NVME_SC_PMR_SAN_PROHIBITED:
+ return EPERM;
+ case NVME_SC_OVERLAPPING_RANGE:
+ case NVME_SC_NS_NOT_ATTACHED:
+ return ENOSPC;
+ case NVME_SC_NS_ALREADY_ATTACHED:
+ return EALREADY;
+ case NVME_SC_THIN_PROV_NOT_SUPP:
+ case NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED:
+ return EOPNOTSUPP;
+ case NVME_SC_ABORT_MISSING:
+ return EWOULDBLOCK;
+ case NVME_SC_SELF_TEST_IN_PROGRESS:
+ return EINPROGRESS;
+ }
+
+ return EIO;
+}
+
+static inline __u8 nvme_fabrics_status_to_errno(__u16 status)
+{
+ switch (status) {
+ case NVME_SC_CONNECT_FORMAT:
+ case NVME_SC_CONNECT_INVALID_PARAM:
+ case NVME_SC_DISCONNECT_INVALID_QTYPE:
+ return EINVAL;
+ case NVME_SC_CONNECT_CTRL_BUSY:
+ return EBUSY;
+ case NVME_SC_CONNECT_RESTART_DISC:
+ return ERESTART;
+ case NVME_SC_CONNECT_INVALID_HOST:
+ return ECONNREFUSED;
+ case NVME_SC_DISCOVERY_RESTART:
+ return EAGAIN;
+ case NVME_SC_AUTH_REQUIRED:
+ return EPERM;
+ }
+
+ return EIO;
+}
+
+__u8 nvme_status_to_errno(int status, bool fabrics)
+{
+ __u16 sc;
+
+ if (!status)
+ return 0;
+ if (status < 0)
+ return errno;
+
+ sc = nvme_status_code(status);
+ switch (nvme_status_code_type(status)) {
+ case NVME_SCT_GENERIC:
+ return nvme_generic_status_to_errno(sc);
+ case NVME_SCT_CMD_SPECIFIC:
+ if (fabrics)
+ return nvme_fabrics_status_to_errno(sc);
+ return nvme_cmd_specific_status_to_errno(sc);
+ default:
+ return EIO;
+ }
+}
+
+static const char * const generic_status[] = {
+ [NVME_SC_SUCCESS] = "Successful Completion: The command completed without error",
+ [NVME_SC_INVALID_OPCODE] = "Invalid Command Opcode: A reserved coded value or an unsupported value in the command opcode field",
+ [NVME_SC_INVALID_FIELD] = "Invalid Field in Command: A reserved coded value or an unsupported value in a defined field",
+ [NVME_SC_CMDID_CONFLICT] = "Command ID Conflict: The command identifier is already in use",
+ [NVME_SC_DATA_XFER_ERROR] = "Data Transfer Error: Transferring the data or metadata associated with a command experienced an error",
+ [NVME_SC_POWER_LOSS] = "Commands Aborted due to Power Loss Notification: Indicates that the command was aborted due to a power loss notification",
+ [NVME_SC_INTERNAL] = "Internal Error: The command was not completed successfully due to an internal error",
+ [NVME_SC_ABORT_REQ] = "Command Abort Requested: The command was aborted due to an Abort command",
+ [NVME_SC_ABORT_QUEUE] = "Command Aborted due to SQ Deletion: The command was aborted due to a Delete I/O Submission Queue",
+ [NVME_SC_FUSED_FAIL] = "Command Aborted due to Failed Fused Command: The command was aborted due to the other command in a fused operation failing",
+ [NVME_SC_FUSED_MISSING] = "Command Aborted due to Missing Fused Command: The fused command was aborted due to the adjacent submission queue entry not containing a fused command",
+ [NVME_SC_INVALID_NS] = "Invalid Namespace or Format: The namespace or the format of that namespace is invalid",
+ [NVME_SC_CMD_SEQ_ERROR] = "Command Sequence Error: The command was aborted due to a protocol violation in a multi- command sequence",
+ [NVME_SC_SGL_INVALID_LAST] = "Invalid SGL Segment Descriptor: The command includes an invalid SGL Last Segment or SGL Segment descriptor",
+ [NVME_SC_SGL_INVALID_COUNT] = "Invalid Number of SGL Descriptors: There is an SGL Last Segment descriptor or an SGL Segment descriptor in a location other than the last descriptor of a segment based on the length indicated",
+ [NVME_SC_SGL_INVALID_DATA] = "Data SGL Length Invalid: The length of a Data SGL is too short or too long and the controller does not support SGL transfers longer than the amount of data to be transferred",
+ [NVME_SC_SGL_INVALID_METADATA] = "Metadata SGL Length Invalid: The length of a Metadata SGL is too short or too long and the controller does not support SGL transfers longer than the amount of data to be transferred",
+ [NVME_SC_SGL_INVALID_TYPE] = "SGL Descriptor Type Invalid: The type of an SGL Descriptor is a type that is not supported by the controller",
+ [NVME_SC_CMB_INVALID_USE] = "Invalid Use of Controller Memory Buffer: The attempted use of the Controller Memory Buffer is not supported by the controller",
+ [NVME_SC_PRP_INVALID_OFFSET] = "PRP Offset Invalid: The Offset field for a PRP entry is invalid",
+ [NVME_SC_AWU_EXCEEDED] = "Atomic Write Unit Exceeded: The length specified exceeds the atomic write unit size",
+ [NVME_SC_OP_DENIED] = "Operation Denied: The command was denied due to lack of access rights",
+ [NVME_SC_SGL_INVALID_OFFSET] = "SGL Offset Invalid: The offset specified in a descriptor is invalid",
+ [NVME_SC_HOSTID_FORMAT] = "Host Identifier Inconsistent Format: The NVM subsystem detected the simultaneous use of 64- bit and 128-bit Host Identifier values on different controllers",
+ [NVME_SC_KAT_EXPIRED] = "Keep Alive Timer Expired: The Keep Alive Timer expired",
+ [NVME_SC_KAT_INVALID] = "Keep Alive Timeout Invalid: The Keep Alive Timeout value specified is invalid",
+ [NVME_SC_CMD_ABORTED_PREMEPT] = "Command Aborted due to Preempt and Abort: The command was aborted due to a Reservation Acquire command",
+ [NVME_SC_SANITIZE_FAILED] = "Sanitize Failed: The most recent sanitize operation failed and no recovery action has been successfully completed",
+ [NVME_SC_SANITIZE_IN_PROGRESS] = "Sanitize In Progress: The requested function is prohibited while a sanitize operation is in progress",
+ [NVME_SC_SGL_INVALID_GRANULARITY] = "SGL Data Block Granularity Invalid: The Address alignment or Length granularity for an SGL Data Block descriptor is invalid",
+ [NVME_SC_CMD_IN_CMBQ_NOT_SUPP] = "Command Not Supported for Queue in CMB: The controller does not support Submission Queue in the Controller Memory Buffer or Completion Queue in the Controller Memory Buffer",
+ [NVME_SC_NS_WRITE_PROTECTED] = "Namespace is Write Protected: The command is prohibited while the namespace is write protected",
+ [NVME_SC_CMD_INTERRUPTED] = "Command Interrupted: Command processing was interrupted and the controller is unable to successfully complete the command",
+ [NVME_SC_TRAN_TPORT_ERROR] = "Transient Transport Error: A transient transport error was detected",
+ [NVME_SC_PROHIBITED_BY_CMD_AND_FEAT] = "Command Prohibited by Command and Feature Lockdown: The command was aborted due to command execution being prohibited by the Command and Feature Lockdown",
+ [NVME_SC_ADMIN_CMD_MEDIA_NOT_READY] = "Admin Command Media Not Ready: The Admin command requires access to media and the media is not ready",
+ [NVME_SC_LBA_RANGE] = "LBA Out of Range: The command references an LBA that exceeds the size of the namespace",
+ [NVME_SC_CAP_EXCEEDED] = "Capacity Exceeded: Execution of the command has caused the capacity of the namespace to be exceeded",
+ [NVME_SC_NS_NOT_READY] = "Namespace Not Ready: The namespace is not ready to be accessed",
+ [NVME_SC_RESERVATION_CONFLICT] = "Reservation Conflict: The command was aborted due to a conflict with a reservation held on the accessed namespace",
+ [NVME_SC_FORMAT_IN_PROGRESS] = "Format In Progress: A Format NVM command is in progress on the namespace",
+};
+
+static const char * const cmd_spec_status[] = {
+ [NVME_SC_CQ_INVALID] = "Completion Queue Invalid: The Completion Queue identifier specified in the command does not exist",
+ [NVME_SC_QID_INVALID] = "Invalid Queue Identifier: The creation of the I/O Completion Queue failed due to an invalid queue identifier specified as part of the command",
+ [NVME_SC_QUEUE_SIZE] = "Invalid Queue Size: The host attempted to create an I/O Completion Queue with an invalid number of entries",
+ [NVME_SC_ABORT_LIMIT] = "Abort Command Limit Exceeded: The number of concurrently outstanding Abort commands has exceeded the limit indicated in the Identify Controller data structure",
+ [NVME_SC_ABORT_MISSING] = "Abort Command Is Missing: The abort command is missing",
+ [NVME_SC_ASYNC_LIMIT] = "Asynchronous Event Request Limit Exceeded: The number of concurrently outstanding Asynchronous Event Request commands has been exceeded",
+ [NVME_SC_FIRMWARE_SLOT] = "Invalid Firmware Slot: The firmware slot indicated is invalid or read only",
+ [NVME_SC_FIRMWARE_IMAGE] = "Invalid Firmware Image: The firmware image specified for activation is invalid and not loaded by the controller",
+ [NVME_SC_INVALID_VECTOR] = "Invalid Interrupt Vector: The creation of the I/O Completion Queue failed due to an invalid interrupt vector specified as part of the command",
+ [NVME_SC_INVALID_LOG_PAGE] = "Invalid Log Page: The log page indicated is invalid",
+ [NVME_SC_INVALID_FORMAT] = "Invalid Format: The LBA Format specified is not supported",
+ [NVME_SC_FW_NEEDS_CONV_RESET] = "Firmware Activation Requires Conventional Reset: The firmware commit was successful, however, activation of the firmware image requires a conventional reset",
+ [NVME_SC_INVALID_QUEUE] = "Invalid Queue Deletion: Invalid I/O Completion Queue specified to delete",
+ [NVME_SC_FEATURE_NOT_SAVEABLE] = "Feature Identifier Not Saveable: The Feature Identifier specified does not support a saveable value",
+ [NVME_SC_FEATURE_NOT_CHANGEABLE] = "Feature Not Changeable: The Feature Identifier is not able to be changed",
+ [NVME_SC_FEATURE_NOT_PER_NS] = "Feature Not Namespace Specific: The Feature Identifier specified is not namespace specific",
+ [NVME_SC_FW_NEEDS_SUBSYS_RESET] = "Firmware Activation Requires NVM Subsystem Reset: The firmware commit was successful, however, activation of the firmware image requires an NVM Subsystem",
+ [NVME_SC_FW_NEEDS_RESET] = "Firmware Activation Requires Controller Level Reset: The firmware commit was successful; however, the image specified does not support being activated without a reset",
+ [NVME_SC_FW_NEEDS_MAX_TIME] = "Firmware Activation Requires Maximum Time Violation: The image specified if activated immediately would exceed the Maximum Time for Firmware Activation (MTFA) value reported in Identify Controller",
+ [NVME_SC_FW_ACTIVATE_PROHIBITED] = "Firmware Activation Prohibited: The image specified is being prohibited from activation by the controller for vendor specific reasons",
+ [NVME_SC_OVERLAPPING_RANGE] = "Overlapping Range: The downloaded firmware image has overlapping ranges",
+ [NVME_SC_NS_INSUFFICIENT_CAP] = "Namespace Insufficient Capacity: Creating the namespace requires more free space than is currently available",
+ [NVME_SC_NS_ID_UNAVAILABLE] = "Namespace Identifier Unavailable: The number of namespaces supported has been exceeded",
+ [NVME_SC_NS_ALREADY_ATTACHED] = "Namespace Already Attached: The controller is already attached to the namespace specified",
+ [NVME_SC_NS_IS_PRIVATE] = "Namespace Is Private: The namespace is private and is already attached to one controller",
+ [NVME_SC_NS_NOT_ATTACHED] = "Namespace Not Attached: The request to detach the controller could not be completed because the controller is not attached to the namespace",
+ [NVME_SC_THIN_PROV_NOT_SUPP] = "Thin Provisioning Not Supported: Thin provisioning is not supported by the controller",
+ [NVME_SC_CTRL_LIST_INVALID] = "Controller List Invalid: The controller list provided contains invalid controller ids",
+ [NVME_SC_SELF_TEST_IN_PROGRESS] = "Device Self-test In Progress: The controller or NVM subsystem already has a device self-test operation in process",
+ [NVME_SC_BP_WRITE_PROHIBITED] = "Boot Partition Write Prohibited: The command tried to modify a locked Boot Partition",
+ [NVME_SC_INVALID_CTRL_ID] = "Invalid Controller Identifier: An invalid controller id was specified",
+ [NVME_SC_INVALID_SEC_CTRL_STATE] = "Invalid Secondary Controller State: The requested secondary controller action is invalid based on the secondary and primary controllers current states",
+ [NVME_SC_INVALID_CTRL_RESOURCES] = "Invalid Number of Controller Resources: The specified number of Flexible Resources is invalid",
+ [NVME_SC_INVALID_RESOURCE_ID] = "Invalid Resource Identifier: At least one of the specified resource identifiers was invalid",
+ [NVME_SC_PMR_SAN_PROHIBITED] = "Sanitize Prohibited While Persistent Memory Region is Enabled",
+ [NVME_SC_ANA_GROUP_ID_INVALID] = "ANA Group Identifier Invalid: The specified ANA Group Identifier (ANAGRPID) is not supported in the submitted command",
+ [NVME_SC_ANA_ATTACH_FAILED] = "ANA Attach Failed: The controller is not attached to the namespace as a result of an ANA condition",
+ [NVME_SC_INSUFFICIENT_CAP] = "Insufficient Capacity: Requested operation requires more free space than is currently available",
+ [NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED] = "Namespace Attachment Limit Exceeded: Attaching the ns to a controller causes max number of ns attachments allowed to be exceeded",
+ [NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED] = "Prohibition of Command Execution Not Supported",
+ [NVME_SC_IOCS_NOT_SUPPORTED] = "The I/O command set is not supported",
+ [NVME_SC_IOCS_NOT_ENABLED] = "The I/O command set is not enabled",
+ [NVME_SC_IOCS_COMBINATION_REJECTED] = "The I/O command set combination is rejected",
+ [NVME_SC_INVALID_IOCS] = "The I/O command set is invalid",
+ [NVME_SC_ID_UNAVAILABLE] = "Identifier Unavailable: The number of Endurance Groups or NVM Sets supported has been exceeded",
+ [NVME_SC_INVALID_DISCOVERY_INFO] = "Discovery Info Entry not applicable to selected entity",
+ [NVME_SC_ZONING_DATA_STRUCT_LOCKED] = "The requested Zoning data structure is locked on the CDC",
+ [NVME_SC_ZONING_DATA_STRUCT_NOTFND] = "The requested Zoning data structure does not exist on the CDC",
+ [NVME_SC_INSUFFICIENT_DISC_RES] = "Discovery Info entries exceed Discovery Controller's capacity",
+ [NVME_SC_REQSTD_FUNCTION_DISABLED] = "Fabric Zoning is not enabled on the CDC",
+ [NVME_SC_ZONEGRP_ORIGINATOR_INVLD] = "The NQN contained in the ZoneGroup Originator field does not match the Host NQN used by the DDC to connect to the CDC",
+};
+
+static const char * const nvm_status[] = {
+ [NVME_SC_BAD_ATTRIBUTES] = "Conflicting Attributes: The attributes specified in the command are conflicting",
+ [NVME_SC_INVALID_PI] = "Invalid Protection Information: The command's Protection Information Field settings are invalid for the namespace's Protection Information format",
+ [NVME_SC_READ_ONLY] = "Attempted Write to Read Only Range: The LBA range specified contains read-only blocks",
+ [NVME_SC_CMD_SIZE_LIMIT_EXCEEDED] = "Command Size Limit Exceeded",
+ [NVME_SC_INCOMPATIBLE_NS] = "Incompatible Namespace or Format",
+ [NVME_SC_FAST_COPY_NOT_POSSIBLE] = "Fast Copy Not Possible",
+ [NVME_SC_OVERLAPPING_IO_RANGE] = "Overlapping I/O Range",
+ [NVME_SC_INSUFFICIENT_RESOURCES] = "Insufficient Resources",
+ [NVME_SC_ZNS_INVALID_OP_REQUEST] = "Invalid Zone Operation Request: The operation requested is invalid",
+ [NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE] = "ZRWA Resources Unavailable: No ZRWAs are available",
+ [NVME_SC_ZNS_BOUNDARY_ERROR] = "Zoned Boundary Error: Invalid Zone Boundary crossing",
+ [NVME_SC_ZNS_FULL] = "Zone Is Full: The accessed zone is in ZSF:Full state",
+ [NVME_SC_ZNS_READ_ONLY] = "Zone Is Read Only: The accessed zone is in ZSRO:Read Only state",
+ [NVME_SC_ZNS_OFFLINE] = "Zone Is Offline: The access zone is in ZSO:Offline state",
+ [NVME_SC_ZNS_INVALID_WRITE] = "Zone Invalid Write: The write to zone was not at the write pointer offset",
+ [NVME_SC_ZNS_TOO_MANY_ACTIVE] = "Too Many Active Zones: The controller does not allow additional active zones",
+ [NVME_SC_ZNS_TOO_MANY_OPENS] = "Too Many Open Zones: The controller does not allow additional open zones",
+ [NVME_SC_ZNS_INVAL_TRANSITION] = "Invalid Zone State Transition: The request is not a valid zone state transition",
+};
+
+static const char * const nvmf_status[] = {
+ [NVME_SC_CONNECT_FORMAT] = "Incompatible Format: The NVM subsystem does not support the record format specified by the host",
+ [NVME_SC_CONNECT_CTRL_BUSY] = "Controller Busy: The controller is already associated with a host",
+ [NVME_SC_CONNECT_INVALID_PARAM] = "Connect Invalid Parameters: One or more of the command parameters",
+ [NVME_SC_CONNECT_RESTART_DISC] = "Connect Restart Discovery: The NVM subsystem requested is not available",
+ [NVME_SC_CONNECT_INVALID_HOST] = "Connect Invalid Host: The host is not allowed to establish an association to either any controller in the NVM subsystem or the specified controller",
+ [NVME_SC_DISCONNECT_INVALID_QTYPE] = "Invalid Queue Type: The command was sent on the wrong queue type",
+ [NVME_SC_DISCOVERY_RESTART] = "Discover Restart: The snapshot of the records is now invalid or out of date",
+ [NVME_SC_AUTH_REQUIRED] = "Authentication Required: NVMe in-band authentication is required and the queue has not yet been authenticated",
+};
+
+static const char * const media_status[] = {
+ [NVME_SC_WRITE_FAULT] = "Write Fault: The write data could not be committed to the media",
+ [NVME_SC_READ_ERROR] = "Unrecovered Read Error: The read data could not be recovered from the media",
+ [NVME_SC_GUARD_CHECK] = "End-to-end Guard Check Error: The command was aborted due to an end-to-end guard check failure",
+ [NVME_SC_APPTAG_CHECK] = "End-to-end Application Tag Check Error: The command was aborted due to an end-to-end application tag check failure",
+ [NVME_SC_REFTAG_CHECK] = "End-to-end Reference Tag Check Error: The command was aborted due to an end-to-end reference tag check failure",
+ [NVME_SC_COMPARE_FAILED] = "Compare Failure: The command failed due to a miscompare during a Compare command",
+ [NVME_SC_ACCESS_DENIED] = "Access Denied: Access to the namespace and/or LBA range is denied due to lack of access rights",
+ [NVME_SC_UNWRITTEN_BLOCK] = "Deallocated or Unwritten Logical Block: The command failed due to an attempt to read from or verify an LBA range containing a deallocated or unwritten logical block",
+ [NVME_SC_STORAGE_TAG_CHECK] = "End-to-End Storage Tag Check Error: The command was aborted due to an end-to-end storage tag check failure",
+};
+
+static const char * const path_status[] = {
+ [NVME_SC_ANA_INTERNAL_PATH_ERROR] = "Internal Path Error: An internal error specific to the controller processing the command prevented completion",
+ [NVME_SC_ANA_PERSISTENT_LOSS] = "Asymmetric Access Persistent Loss: The controller is in a persistent loss state with the requested namespace",
+ [NVME_SC_ANA_INACCESSIBLE] = "Asymmetric Access Inaccessible: The controller is in an inaccessible state with the requested namespace",
+ [NVME_SC_ANA_TRANSITION] = "Asymmetric Access Transition: The controller is currently transitioning states with the requested namespace",
+ [NVME_SC_CTRL_PATH_ERROR] = "Controller Pathing Error: A pathing error was detected by the controller",
+ [NVME_SC_HOST_PATH_ERROR] = "Host Pathing Error: A pathing error was detected by the host",
+ [NVME_SC_CMD_ABORTED_BY_HOST] = "Command Aborted By Host: The command was aborted as a result of host action",
+};
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#define ARGSTR(s, i) arg_str(s, ARRAY_SIZE(s), i)
+
+static const char *arg_str(const char * const *strings,
+ size_t array_size, size_t idx)
+{
+ if (idx < array_size && strings[idx])
+ return strings[idx];
+ return "unrecognized";
+}
+
+const char *nvme_status_to_string(int status, bool fabrics)
+{
+ const char *s = "Unknown status";
+ __u16 sc, sct;
+
+ if (status < 0)
+ return strerror(errno);
+
+ sc = nvme_status_code(status);
+ sct = nvme_status_code_type(status);
+
+ switch (sct) {
+ case NVME_SCT_GENERIC:
+ s = ARGSTR(generic_status, sc);
+ break;
+ case NVME_SCT_CMD_SPECIFIC:
+ if (sc < ARRAY_SIZE(cmd_spec_status))
+ s = ARGSTR(cmd_spec_status, sc);
+ else if (fabrics)
+ s = ARGSTR(nvmf_status, sc);
+ else
+ s = ARGSTR(nvm_status, sc);
+ break;
+ case NVME_SCT_MEDIA:
+ s = ARGSTR(media_status, sc);
+ break;
+ case NVME_SCT_PATH:
+ s = ARGSTR(path_status, sc);
+ break;
+ case NVME_SCT_VS:
+ s = "Vendor Specific Status";
+ break;
+ default:
+ break;
+ }
+
+ return s;
+}
+
+static inline void nvme_init_copy_range_elbt(__u8 *elbt, __u64 eilbrt)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ elbt[9 - i] = (eilbrt >> (8 * i)) & 0xff;
+ elbt[1] = 0;
+ elbt[0] = 0;
+}
+
+void nvme_init_copy_range(struct nvme_copy_range *copy, __u16 *nlbs,
+ __u64 *slbas, __u32 *eilbrts, __u32 *elbatms,
+ __u32 *elbats, __u16 nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++) {
+ copy[i].nlb = cpu_to_le16(nlbs[i]);
+ copy[i].slba = cpu_to_le64(slbas[i]);
+ copy[i].eilbrt = cpu_to_le32(eilbrts[i]);
+ copy[i].elbatm = cpu_to_le16(elbatms[i]);
+ copy[i].elbat = cpu_to_le16(elbats[i]);
+ }
+}
+
+void nvme_init_copy_range_f1(struct nvme_copy_range_f1 *copy, __u16 *nlbs,
+ __u64 *slbas, __u64 *eilbrts, __u32 *elbatms,
+ __u32 *elbats, __u16 nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++) {
+ copy[i].nlb = cpu_to_le16(nlbs[i]);
+ copy[i].slba = cpu_to_le64(slbas[i]);
+ copy[i].elbatm = cpu_to_le16(elbatms[i]);
+ copy[i].elbat = cpu_to_le16(elbats[i]);
+ nvme_init_copy_range_elbt(copy[i].elbt, eilbrts[i]);
+ }
+}
+
+void nvme_init_copy_range_f2(struct nvme_copy_range_f2 *copy, __u32 *snsids,
+ __u16 *nlbs, __u64 *slbas, __u16 *sopts,
+ __u32 *eilbrts, __u32 *elbatms, __u32 *elbats,
+ __u16 nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++) {
+ copy[i].snsid = cpu_to_le32(snsids[i]);
+ copy[i].nlb = cpu_to_le16(nlbs[i]);
+ copy[i].slba = cpu_to_le64(slbas[i]);
+ copy[i].sopt = cpu_to_le16(sopts[i]);
+ copy[i].eilbrt = cpu_to_le32(eilbrts[i]);
+ copy[i].elbatm = cpu_to_le16(elbatms[i]);
+ copy[i].elbat = cpu_to_le16(elbats[i]);
+ }
+}
+
+void nvme_init_copy_range_f3(struct nvme_copy_range_f3 *copy, __u32 *snsids,
+ __u16 *nlbs, __u64 *slbas, __u16 *sopts,
+ __u64 *eilbrts, __u32 *elbatms, __u32 *elbats,
+ __u16 nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++) {
+ copy[i].snsid = cpu_to_le32(snsids[i]);
+ copy[i].nlb = cpu_to_le16(nlbs[i]);
+ copy[i].slba = cpu_to_le64(slbas[i]);
+ copy[i].sopt = cpu_to_le16(sopts[i]);
+ copy[i].elbatm = cpu_to_le16(elbatms[i]);
+ copy[i].elbat = cpu_to_le16(elbats[i]);
+ nvme_init_copy_range_elbt(copy[i].elbt, eilbrts[i]);
+ }
+}
+
+void nvme_init_dsm_range(struct nvme_dsm_range *dsm, __u32 *ctx_attrs,
+ __u32 *llbas, __u64 *slbas, __u16 nr_ranges)
+{
+ int i;
+
+ for (i = 0; i < nr_ranges; i++) {
+ dsm[i].cattr = cpu_to_le32(ctx_attrs[i]);
+ dsm[i].nlb = cpu_to_le32(llbas[i]);
+ dsm[i].slba = cpu_to_le64(slbas[i]);
+ }
+}
+
+void nvme_init_ctrl_list(struct nvme_ctrl_list *cntlist, __u16 num_ctrls,
+ __u16 *ctrlist)
+{
+ int i;
+
+ cntlist->num = cpu_to_le16(num_ctrls);
+ for (i = 0; i < num_ctrls; i++)
+ cntlist->identifier[i] = cpu_to_le16(ctrlist[i]);
+}
+
+int nvme_get_feature_length(int fid, __u32 cdw11, __u32 *len)
+{
+ switch (fid) {
+ case NVME_FEAT_FID_LBA_RANGE:
+ *len = sizeof(struct nvme_lba_range_type);
+ break;
+ case NVME_FEAT_FID_AUTO_PST:
+ *len = sizeof(struct nvme_feat_auto_pst);
+ break;
+ case NVME_FEAT_FID_PLM_CONFIG:
+ *len = sizeof(struct nvme_plm_config);
+ break;
+ case NVME_FEAT_FID_TIMESTAMP:
+ *len = sizeof(struct nvme_timestamp);
+ break;
+ case NVME_FEAT_FID_HOST_BEHAVIOR:
+ *len = sizeof(struct nvme_feat_host_behavior);
+ break;
+ case NVME_FEAT_FID_HOST_ID:
+ *len = (cdw11 & 0x1) ? 16 : 8;
+ break;
+ case NVME_FEAT_FID_HOST_MEM_BUF:
+ *len = sizeof(struct nvme_host_mem_buf_attrs);
+ break;
+ case NVME_FEAT_FID_ARBITRATION:
+ case NVME_FEAT_FID_POWER_MGMT:
+ case NVME_FEAT_FID_TEMP_THRESH:
+ case NVME_FEAT_FID_ERR_RECOVERY:
+ case NVME_FEAT_FID_VOLATILE_WC:
+ case NVME_FEAT_FID_NUM_QUEUES:
+ case NVME_FEAT_FID_IRQ_COALESCE:
+ case NVME_FEAT_FID_IRQ_CONFIG:
+ case NVME_FEAT_FID_WRITE_ATOMIC:
+ case NVME_FEAT_FID_ASYNC_EVENT:
+ case NVME_FEAT_FID_KATO:
+ case NVME_FEAT_FID_HCTM:
+ case NVME_FEAT_FID_NOPSC:
+ case NVME_FEAT_FID_RRL:
+ case NVME_FEAT_FID_PLM_WINDOW:
+ case NVME_FEAT_FID_LBA_STS_INTERVAL:
+ case NVME_FEAT_FID_SANITIZE:
+ case NVME_FEAT_FID_ENDURANCE_EVT_CFG:
+ case NVME_FEAT_FID_SW_PROGRESS:
+ case NVME_FEAT_FID_RESV_MASK:
+ case NVME_FEAT_FID_RESV_PERSIST:
+ case NVME_FEAT_FID_WRITE_PROTECT:
+ *len = 0;
+ break;
+ case NVME_FEAT_FID_ENH_CTRL_METADATA:
+ case NVME_FEAT_FID_CTRL_METADATA:
+ case NVME_FEAT_FID_NS_METADATA:
+ *len = sizeof(struct nvme_host_metadata);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int nvme_get_feature_length2(int fid, __u32 cdw11, enum nvme_data_tfr dir,
+ __u32 *len)
+{
+ switch (fid) {
+ case NVME_FEAT_FID_HOST_MEM_BUF:
+ if (dir == NVME_DATA_TFR_HOST_TO_CTRL) {
+ *len = 0;
+ break;
+ }
+ fallthrough;
+ default:
+ return nvme_get_feature_length(fid, cdw11, len);
+ }
+ return 0;
+}
+
+int nvme_get_directive_receive_length(enum nvme_directive_dtype dtype,
+ enum nvme_directive_receive_doper doper, __u32 *len)
+{
+ switch (dtype) {
+ case NVME_DIRECTIVE_DTYPE_IDENTIFY:
+ switch (doper) {
+ case NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM:
+ *len = sizeof(struct nvme_id_directives);
+ return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ case NVME_DIRECTIVE_DTYPE_STREAMS:
+ switch (doper) {
+ case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM:
+ *len = sizeof(struct nvme_streams_directive_params);
+ return 0;
+ case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS:
+ *len = (128 * 1024) * sizeof(__le16);
+ return 0;
+ case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE:
+ *len = 0;
+ return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static const char * const libnvme_status[] = {
+ [ENVME_CONNECT_RESOLVE] = "failed to resolve host",
+ [ENVME_CONNECT_ADDRFAM] = "unrecognized address family",
+ [ENVME_CONNECT_TRADDR] = "failed to get transport address",
+ [ENVME_CONNECT_TARG] = "no transport specified",
+ [ENVME_CONNECT_AARG] = "no transport address specified",
+ [ENVME_CONNECT_OPEN] = "failed to open nvme-fabrics device",
+ [ENVME_CONNECT_WRITE] = "failed to write to nvme-fabrics device",
+ [ENVME_CONNECT_READ] = "failed to read from nvme-fabrics device",
+ [ENVME_CONNECT_PARSE] = "failed to parse ctrl info",
+ [ENVME_CONNECT_INVAL_TR] = "invalid transport type",
+ [ENVME_CONNECT_LOOKUP_SUBSYS_NAME] = "failed to lookup subsystem name",
+ [ENVME_CONNECT_LOOKUP_SUBSYS] = "failed to lookup subsystem",
+ [ENVME_CONNECT_ALREADY] = "already connected",
+ [ENVME_CONNECT_INVAL] = "invalid arguments/configuration",
+ [ENVME_CONNECT_ADDRINUSE] = "hostnqn already in use",
+ [ENVME_CONNECT_NODEV] = "invalid interface",
+ [ENVME_CONNECT_OPNOTSUPP] = "not supported",
+ [ENVME_CONNECT_CONNREFUSED] = "connection refused",
+ [ENVME_CONNECT_ADDRNOTAVAIL] = "cannot assign requested address",
+};
+
+const char *nvme_errno_to_string(int status)
+{
+ const char *s = ARGSTR(libnvme_status, status);
+
+ return s;
+}
+
+#ifdef HAVE_NETDB
+char *hostname2traddr(struct nvme_root *r, const char *traddr)
+{
+ struct addrinfo *host_info, hints = {.ai_family = AF_UNSPEC};
+ char addrstr[NVMF_TRADDR_SIZE];
+ const char *p;
+ char *ret_traddr = NULL;
+ int ret;
+
+ ret = getaddrinfo(traddr, NULL, &hints, &host_info);
+ if (ret) {
+ nvme_msg(r, LOG_ERR, "failed to resolve host %s info\n",
+ traddr);
+ return NULL;
+ }
+
+ switch (host_info->ai_family) {
+ case AF_INET:
+ p = inet_ntop(host_info->ai_family,
+ &(((struct sockaddr_in *)host_info->ai_addr)->sin_addr),
+ addrstr, NVMF_TRADDR_SIZE);
+ break;
+ case AF_INET6:
+ p = inet_ntop(host_info->ai_family,
+ &(((struct sockaddr_in6 *)host_info->ai_addr)->sin6_addr),
+ addrstr, NVMF_TRADDR_SIZE);
+ break;
+ default:
+ nvme_msg(r, LOG_ERR, "unrecognized address family (%d) %s\n",
+ host_info->ai_family, traddr);
+ goto free_addrinfo;
+ }
+
+ if (!p) {
+ nvme_msg(r, LOG_ERR, "failed to get traddr for %s\n",
+ traddr);
+ goto free_addrinfo;
+ }
+ ret_traddr = strdup(addrstr);
+
+free_addrinfo:
+ freeaddrinfo(host_info);
+ return ret_traddr;
+}
+#else /* HAVE_NETDB */
+char *hostname2traddr(struct nvme_root *r, const char *traddr)
+{
+ nvme_msg(NULL, LOG_ERR, "No support for hostname IP address resolution; " \
+ "recompile with libnss support.\n");
+
+ errno = -ENOTSUP;
+ return NULL;
+}
+#endif /* HAVE_NETDB */
+
+char *startswith(const char *s, const char *prefix)
+{
+ size_t l;
+
+ l = strlen(prefix);
+ if (!strncmp(s, prefix, l))
+ return (char *)s + l;
+
+ return NULL;
+}
+
+char *kv_strip(char *kv)
+{
+ char *s;
+
+ kv[strcspn(kv, "\n\r")] = '\0';
+
+ /* Remove leading newline and spaces */
+ kv += strspn(kv, " \t\n\r");
+
+ /* Skip comments and empty lines */
+ if (*kv == '#' || *kv == '\0') {
+ *kv = '\0';
+ return kv;
+ }
+
+ /* Remove trailing newline chars */
+ kv[strcspn(kv, "\n\r")] = '\0';
+
+ /* Delete trailing comments (including spaces/tabs that precede the #)*/
+ s = &kv[strcspn(kv, "#")];
+ *s-- = '\0';
+ while ((s >= kv) && ((*s == ' ') || (*s == '\t'))) {
+ *s-- = '\0';
+ }
+
+ return kv;
+}
+
+char *kv_keymatch(const char *kv, const char *key)
+{
+ char *value;
+
+ value = startswith(kv, key);
+ if (value) {
+ /* Make sure key is a whole word. I.e. it should be
+ * followed by spaces, tabs, or a equal sign. Skip
+ * leading spaces, tabs, and equal sign (=) */
+ switch (*value) {
+ case ' ':
+ case '\t':
+ case '=':
+ value += strspn(value, " \t=");
+ return value;
+ default: ;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * read_file - read contents of file into @buffer.
+ * @fname: File name
+ * @buffer: Where to save file's contents
+ * @bufsz: Size of @buffer. On success, @bufsz gets decremented by the
+ * number of characters that were writtent to @buffer.
+ *
+ * Return: The number of characters read. If the file cannot be opened or
+ * nothing is read from the file, then this function returns 0.
+ */
+static size_t read_file(const char * fname, char *buffer, size_t *bufsz)
+{
+ char *p;
+ _cleanup_file_ FILE *file = NULL;
+ size_t len;
+
+ file = fopen(fname, "re");
+ if (!file)
+ return 0;
+
+ p = fgets(buffer, *bufsz, file);
+
+ if (!p)
+ return 0;
+
+ /* Strip unwanted trailing chars */
+ len = strcspn(buffer, " \t\n\r");
+ *bufsz -= len;
+
+ return len;
+}
+
+static size_t copy_value(char *buf, size_t buflen, const char *value)
+{
+ size_t val_len;
+
+ memset(buf, 0, buflen);
+
+ /* Remove leading " */
+ if (value[0] == '"')
+ value++;
+
+ /* Remove trailing " */
+ val_len = strcspn(value, "\"");
+
+ memcpy(buf, value, MIN(val_len, buflen-1));
+
+ return val_len;
+}
+
+size_t get_entity_name(char *buffer, size_t bufsz)
+{
+ size_t len = !gethostname(buffer, bufsz) ? strlen(buffer) : 0;
+
+ /* Fill the rest of buffer with zeros */
+ memset(&buffer[len], '\0', bufsz-len);
+
+ return len;
+}
+
+size_t get_entity_version(char *buffer, size_t bufsz)
+{
+ _cleanup_file_ FILE *file = NULL;
+ size_t num_bytes = 0;
+
+ /* /proc/sys/kernel/ostype typically contains the string "Linux" */
+ num_bytes += read_file("/proc/sys/kernel/ostype",
+ &buffer[num_bytes], &bufsz);
+
+ /* /proc/sys/kernel/osrelease contains the Linux
+ * version (e.g. 5.8.0-63-generic)
+ */
+ buffer[num_bytes++] = ' '; /* Append a space */
+ num_bytes += read_file("/proc/sys/kernel/osrelease",
+ &buffer[num_bytes], &bufsz);
+
+ /* /etc/os-release contains Key-Value pairs. We only care about the key
+ * PRETTY_NAME, which contains the Distro's version. For example:
+ * "SUSE Linux Enterprise Server 15 SP4", "Ubuntu 20.04.3 LTS", or
+ * "Fedora Linux 35 (Server Edition)"
+ */
+ file = fopen("/etc/os-release", "re");
+ if (file) {
+ char name[64] = {0};
+ size_t name_len = 0;
+ char ver_id[64] = {0};
+ size_t ver_id_len = 0;
+ char line[LINE_MAX];
+ char *p;
+ char *s;
+
+ /* Read key-value pairs one line at a time */
+ while ((!name_len || !ver_id_len) &&
+ (p = fgets(line, sizeof(line), file)) != NULL) {
+ /* Clean up string by removing leading/trailing blanks
+ * and new line characters. Also eliminate trailing
+ * comments, if any.
+ */
+ p = kv_strip(p);
+
+ /* Empty string? */
+ if (*p == '\0')
+ continue;
+
+ s = kv_keymatch(p, "NAME");
+ if (s)
+ name_len = copy_value(name, sizeof(name), s);
+
+ s = kv_keymatch(p, "VERSION_ID");
+ if (s)
+ ver_id_len = copy_value(ver_id, sizeof(ver_id), s);
+ }
+
+ if (name_len) {
+ /* Append a space */
+ buffer[num_bytes++] = ' ';
+ name_len = MIN(name_len, bufsz);
+ memcpy(&buffer[num_bytes], name, name_len);
+ bufsz -= name_len;
+ num_bytes += name_len;
+ }
+
+ if (ver_id_len) {
+ /* Append a space */
+ buffer[num_bytes++] = ' ';
+ ver_id_len = MIN(ver_id_len, bufsz);
+ memcpy(&buffer[num_bytes], ver_id, ver_id_len);
+ bufsz -= ver_id_len;
+ num_bytes += ver_id_len;
+ }
+ }
+
+ /* Fill the rest of buffer with zeros */
+ memset(&buffer[num_bytes], '\0', bufsz);
+
+ return num_bytes;
+}
+
+struct nvmf_ext_attr *nvmf_exat_ptr_next(struct nvmf_ext_attr *p)
+{
+ return (struct nvmf_ext_attr *)
+ ((uintptr_t)p + (ptrdiff_t)nvmf_exat_size(le16_to_cpu(p->exatlen)));
+}
+
+const char *nvme_get_version(enum nvme_version type)
+{
+ switch(type) {
+ case NVME_VERSION_PROJECT:
+ return PROJECT_VERSION;
+ case NVME_VERSION_GIT:
+ return GIT_VERSION;
+ default:
+ return "n/a";
+ }
+}
+
+int nvme_uuid_to_string(unsigned char uuid[NVME_UUID_LEN], char *str)
+{
+ int n;
+ n = snprintf(str, NVME_UUID_LEN_STRING,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
+ uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
+ uuid[12], uuid[13], uuid[14], uuid[15]);
+ return n != NVME_UUID_LEN_STRING - 1 ? -EINVAL : 0;
+}
+
+int nvme_uuid_from_string(const char *str, unsigned char uuid[NVME_UUID_LEN])
+{
+ int n;
+
+ n = sscanf(str,
+ "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-"
+ "%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
+ &uuid[0], &uuid[1], &uuid[2], &uuid[3], &uuid[4], &uuid[5],
+ &uuid[6], &uuid[7], &uuid[8], &uuid[9], &uuid[10], &uuid[11],
+ &uuid[12], &uuid[13], &uuid[14], &uuid[15]);
+ return n != NVME_UUID_LEN ? -EINVAL : 0;
+
+}
+
+int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN])
+{
+ _cleanup_fd_ int f = -1;
+ ssize_t n;
+
+ f = open("/dev/urandom", O_RDONLY);
+ if (f < 0)
+ return -errno;
+ n = read(f, uuid, NVME_UUID_LEN);
+ if (n < 0)
+ return -errno;
+ else if (n != NVME_UUID_LEN)
+ return -EIO;
+
+ /*
+ * See https://www.rfc-editor.org/rfc/rfc4122#section-4.4
+ * Algorithms for Creating a UUID from Truly Random
+ * or Pseudo-Random Numbers
+ */
+ uuid[6] = (uuid[6] & 0x0f) | 0x40;
+ uuid[8] = (uuid[8] & 0x3f) | 0x80;
+
+ return 0;
+}
+
+int nvme_uuid_find(struct nvme_id_uuid_list *uuid_list, const unsigned char uuid[NVME_UUID_LEN])
+{
+ const unsigned char uuid_end[NVME_UUID_LEN] = {0};
+
+ if ((!uuid_list) || (!uuid)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (int i = 0; i < NVME_ID_UUID_LIST_MAX; i++) {
+ if (memcmp(uuid, &uuid_list->entry[i].uuid, NVME_UUID_LEN) == 0)
+ return i + 1;
+ if (memcmp(uuid_end, &uuid_list->entry[i].uuid, NVME_UUID_LEN) == 0)
+ break;
+ }
+ errno = ENOENT;
+ return -1;
+}
+
+#ifdef HAVE_NETDB
+static bool _nvme_ipaddrs_eq(struct sockaddr *addr1, struct sockaddr *addr2)
+{
+ struct sockaddr_in *sockaddr_v4;
+ struct sockaddr_in6 *sockaddr_v6;
+
+ if (addr1->sa_family == AF_INET && addr2->sa_family == AF_INET) {
+ struct sockaddr_in *sockaddr1 = (struct sockaddr_in *)addr1;
+ struct sockaddr_in *sockaddr2 = (struct sockaddr_in *)addr2;
+ return sockaddr1->sin_addr.s_addr == sockaddr2->sin_addr.s_addr;
+ }
+
+ if (addr1->sa_family == AF_INET6 && addr2->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sockaddr1 = (struct sockaddr_in6 *)addr1;
+ struct sockaddr_in6 *sockaddr2 = (struct sockaddr_in6 *)addr2;
+ return !memcmp(&sockaddr1->sin6_addr, &sockaddr2->sin6_addr, sizeof(struct in6_addr));
+ }
+
+ switch (addr1->sa_family) {
+ case AF_INET:
+ sockaddr_v6 = (struct sockaddr_in6 *)addr2;
+ if (IN6_IS_ADDR_V4MAPPED(&sockaddr_v6->sin6_addr)) {
+ sockaddr_v4 = (struct sockaddr_in *)addr1;
+ return sockaddr_v4->sin_addr.s_addr == sockaddr_v6->sin6_addr.s6_addr32[3];
+ }
+ break;
+
+ case AF_INET6:
+ sockaddr_v6 = (struct sockaddr_in6 *)addr1;
+ if (IN6_IS_ADDR_V4MAPPED(&sockaddr_v6->sin6_addr)) {
+ sockaddr_v4 = (struct sockaddr_in *)addr2;
+ return sockaddr_v4->sin_addr.s_addr == sockaddr_v6->sin6_addr.s6_addr32[3];
+ }
+ break;
+
+ default: ;
+ }
+
+ return false;
+}
+
+bool nvme_ipaddrs_eq(const char *addr1, const char *addr2)
+{
+ bool result = false;
+ struct addrinfo *info1 = NULL, hint1 = { .ai_flags=AI_NUMERICHOST, .ai_family=AF_UNSPEC };
+ struct addrinfo *info2 = NULL, hint2 = { .ai_flags=AI_NUMERICHOST, .ai_family=AF_UNSPEC };
+
+ if (addr1 == addr2)
+ return true;
+
+ if (!addr1 || !addr2)
+ return false;
+
+ if (getaddrinfo(addr1, 0, &hint1, &info1) || !info1)
+ goto ipaddrs_eq_fail;
+
+ if (getaddrinfo(addr2, 0, &hint2, &info2) || !info2)
+ goto ipaddrs_eq_fail;
+
+ result = _nvme_ipaddrs_eq(info1->ai_addr, info2->ai_addr);
+
+ipaddrs_eq_fail:
+ if (info1)
+ freeaddrinfo(info1);
+ if (info2)
+ freeaddrinfo(info2);
+ return result;
+}
+#else /* HAVE_NETDB */
+bool nvme_ipaddrs_eq(const char *addr1, const char *addr2)
+{
+ nvme_msg(NULL, LOG_ERR, "no support for hostname ip address resolution; " \
+ "recompile with libnss support.\n");
+
+ return false;
+}
+#endif /* HAVE_NETDB */
+
+#ifdef HAVE_NETDB
+const char *nvme_iface_matching_addr(const struct ifaddrs *iface_list, const char *addr)
+{
+ const struct ifaddrs *iface_it;
+ struct addrinfo *info = NULL, hint = { .ai_flags = AI_NUMERICHOST, .ai_family = AF_UNSPEC };
+ const char *iface_name = NULL;
+
+ if (!iface_list || !addr || getaddrinfo(addr, 0, &hint, &info) || !info)
+ return NULL;
+
+ /* Walk through the linked list */
+ for (iface_it = iface_list; iface_it != NULL; iface_it = iface_it->ifa_next) {
+ struct sockaddr *ifaddr = iface_it->ifa_addr;
+
+ if (ifaddr && (ifaddr->sa_family == AF_INET || ifaddr->sa_family == AF_INET6) &&
+ _nvme_ipaddrs_eq(info->ai_addr, ifaddr)) {
+ iface_name = iface_it->ifa_name;
+ break;
+ }
+ }
+
+ freeaddrinfo(info);
+
+ return iface_name;
+}
+
+bool nvme_iface_primary_addr_matches(const struct ifaddrs *iface_list, const char *iface, const char *addr)
+{
+ const struct ifaddrs *iface_it;
+ struct addrinfo *info = NULL, hint = { .ai_flags = AI_NUMERICHOST, .ai_family = AF_UNSPEC };
+ bool match_found = false;
+
+ if (!iface_list || !addr || getaddrinfo(addr, 0, &hint, &info) || !info)
+ return false;
+
+ /* Walk through the linked list */
+ for (iface_it = iface_list; iface_it != NULL; iface_it = iface_it->ifa_next) {
+ if (strcmp(iface, iface_it->ifa_name))
+ continue; /* Not the interface we're looking for*/
+
+ /* The interface list is ordered in a way that the primary
+ * address is listed first. As soon as the parsed address
+ * matches the family of the address we're looking for, we
+ * have found the primary address for that family.
+ */
+ if (iface_it->ifa_addr && (iface_it->ifa_addr->sa_family == info->ai_addr->sa_family)) {
+ match_found = _nvme_ipaddrs_eq(info->ai_addr, iface_it->ifa_addr);
+ break;
+ }
+ }
+
+ freeaddrinfo(info);
+
+ return match_found;
+}
+
+#else /* HAVE_NETDB */
+
+const char *nvme_iface_matching_addr(const struct ifaddrs *iface_list, const char *addr)
+{
+ nvme_msg(NULL, LOG_ERR, "no support for interface lookup; "
+ "recompile with libnss support.\n");
+
+ return NULL;
+}
+
+bool nvme_iface_primary_addr_matches(const struct ifaddrs *iface_list, const char *iface, const char *addr)
+{
+ nvme_msg(NULL, LOG_ERR, "no support for interface lookup; "
+ "recompile with libnss support.\n");
+
+ return false;
+}
+
+#endif /* HAVE_NETDB */
+
+void *__nvme_alloc(size_t len)
+{
+ size_t _len = round_up(len, 0x1000);
+ void *p;
+
+ if (posix_memalign((void *)&p, getpagesize(), _len))
+ return NULL;
+
+ memset(p, 0, _len);
+ return p;
+}
diff --git a/src/nvme/util.h b/src/nvme/util.h
new file mode 100644
index 0000000..517c696
--- /dev/null
+++ b/src/nvme/util.h
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
+ */
+#ifndef _LIBNVME_UTIL_H
+#define _LIBNVME_UTIL_H
+
+#include <ifaddrs.h>
+
+#include "types.h"
+
+/**
+ * DOC: util.h
+ *
+ * libnvme utility functions
+ */
+
+/**
+ * enum nvme_connect_err - nvme connect error codes
+ * @ENVME_CONNECT_RESOLVE: failed to resolve host
+ * @ENVME_CONNECT_ADDRFAM: unrecognized address family
+ * @ENVME_CONNECT_TRADDR: failed to get traddr
+ * @ENVME_CONNECT_TARG: need a transport (-t) argument
+ * @ENVME_CONNECT_AARG: need a address (-a) argument
+ * @ENVME_CONNECT_OPEN: failed to open nvme-fabrics device
+ * @ENVME_CONNECT_WRITE: failed to write to nvme-fabrics device
+ * @ENVME_CONNECT_READ: failed to read from nvme-fabrics device
+ * @ENVME_CONNECT_PARSE: failed to parse ctrl info
+ * @ENVME_CONNECT_INVAL_TR: invalid transport type
+ * @ENVME_CONNECT_LOOKUP_SUBSYS_NAME: failed to lookup subsystem name
+ * @ENVME_CONNECT_LOOKUP_SUBSYS: failed to lookup subsystem
+ * @ENVME_CONNECT_ALREADY: the connect attempt failed, already connected
+ * @ENVME_CONNECT_INVAL: invalid arguments/configuration
+ * @ENVME_CONNECT_ADDRINUSE: hostnqn already in use
+ * @ENVME_CONNECT_NODEV: invalid interface
+ * @ENVME_CONNECT_OPNOTSUPP: not supported
+ * @ENVME_CONNECT_CONNREFUSED: connection refused
+ * @ENVME_CONNECT_ADDRNOTAVAIL: cannot assign requested address
+ * @ENVME_CONNECT_IGNORED: connect attempt is ignored due to configuration
+ */
+enum nvme_connect_err {
+ ENVME_CONNECT_RESOLVE = 1000,
+ ENVME_CONNECT_ADDRFAM,
+ ENVME_CONNECT_TRADDR,
+ ENVME_CONNECT_TARG,
+ ENVME_CONNECT_AARG,
+ ENVME_CONNECT_OPEN,
+ ENVME_CONNECT_WRITE,
+ ENVME_CONNECT_READ,
+ ENVME_CONNECT_PARSE,
+ ENVME_CONNECT_INVAL_TR,
+ ENVME_CONNECT_LOOKUP_SUBSYS_NAME,
+ ENVME_CONNECT_LOOKUP_SUBSYS,
+ ENVME_CONNECT_ALREADY,
+ ENVME_CONNECT_INVAL,
+ ENVME_CONNECT_ADDRINUSE,
+ ENVME_CONNECT_NODEV,
+ ENVME_CONNECT_OPNOTSUPP,
+ ENVME_CONNECT_CONNREFUSED,
+ ENVME_CONNECT_ADDRNOTAVAIL,
+ ENVME_CONNECT_IGNORED,
+};
+
+/**
+ * nvme_status_to_errno() - Converts nvme return status to errno
+ * @status: Return status from an nvme passthrough command
+ * @fabrics: Set to true if &status is to a fabrics target.
+ *
+ * Return: An errno representing the nvme status if it is an nvme status field,
+ * or unchanged status is < 0 since errno is already set.
+ */
+__u8 nvme_status_to_errno(int status, bool fabrics);
+
+/**
+ * nvme_status_to_string() - Returns string describing nvme return status.
+ * @status: Return status from an nvme passthrough command
+ * @fabrics: Set to true if &status is to a fabrics target.
+ *
+ * Return: String representation of the nvme status if it is an nvme status field,
+ * or a standard errno string if status is < 0.
+ */
+const char *nvme_status_to_string(int status, bool fabrics);
+
+/**
+ * nvme_errno_to_string() - Returns string describing nvme connect failures
+ * @err: Returned error code from nvme_add_ctrl()
+ *
+ * Return: String representation of the nvme connect error codes
+ */
+const char *nvme_errno_to_string(int err);
+
+/**
+ * nvme_init_ctrl_list() - Initialize an nvme_ctrl_list structure from an array.
+ * @cntlist: The controller list structure to initialize
+ * @num_ctrls: The number of controllers in the array, &ctrlist.
+ * @ctrlist: An array of controller identifiers in CPU native endian.
+ *
+ * This is intended to be used with any command that takes a controller list
+ * argument. See nvme_ns_attach_ctrls() and nvme_ns_detach().
+ */
+void nvme_init_ctrl_list(struct nvme_ctrl_list *cntlist, __u16 num_ctrls,
+ __u16 *ctrlist);
+
+/**
+ * nvme_init_dsm_range() - Constructs a data set range structure
+ * @dsm: DSM range array
+ * @ctx_attrs: Array of context attributes
+ * @llbas: Array of length in logical blocks
+ * @slbas: Array of starting logical blocks
+ * @nr_ranges: The size of the dsm arrays
+ *
+ * Each array must be the same size of size 'nr_ranges'. This is intended to be
+ * used with constructing a payload for nvme_dsm().
+ *
+ * Return: The nvme command status if a response was received or -errno
+ * otherwise.
+ */
+void nvme_init_dsm_range(struct nvme_dsm_range *dsm, __u32 *ctx_attrs,
+ __u32 *llbas, __u64 *slbas, __u16 nr_ranges);
+
+/**
+ * nvme_init_copy_range() - Constructs a copy range structure
+ * @copy: Copy range array
+ * @nlbs: Number of logical blocks
+ * @slbas: Starting LBA
+ * @eilbrts: Expected initial logical block reference tag
+ * @elbatms: Expected logical block application tag mask
+ * @elbats: Expected logical block application tag
+ * @nr: Number of descriptors to construct
+ */
+void nvme_init_copy_range(struct nvme_copy_range *copy, __u16 *nlbs,
+ __u64 *slbas, __u32 *eilbrts, __u32 *elbatms,
+ __u32 *elbats, __u16 nr);
+
+/**
+ * nvme_init_copy_range_f1() - Constructs a copy range f1 structure
+ * @copy: Copy range array
+ * @nlbs: Number of logical blocks
+ * @slbas: Starting LBA
+ * @eilbrts: Expected initial logical block reference tag
+ * @elbatms: Expected logical block application tag mask
+ * @elbats: Expected logical block application tag
+ * @nr: Number of descriptors to construct
+ */
+void nvme_init_copy_range_f1(struct nvme_copy_range_f1 *copy, __u16 *nlbs,
+ __u64 *slbas, __u64 *eilbrts, __u32 *elbatms,
+ __u32 *elbats, __u16 nr);
+
+/**
+ * nvme_init_copy_range_f2() - Constructs a copy range f2 structure
+ * @copy: Copy range array
+ * @snsids: Source namespace identifier
+ * @nlbs: Number of logical blocks
+ * @slbas: Starting LBA
+ * @sopts: Source options
+ * @eilbrts: Expected initial logical block reference tag
+ * @elbatms: Expected logical block application tag mask
+ * @elbats: Expected logical block application tag
+ * @nr: Number of descriptors to construct
+ */
+void nvme_init_copy_range_f2(struct nvme_copy_range_f2 *copy, __u32 *snsids,
+ __u16 *nlbs, __u64 *slbas, __u16 *sopts,
+ __u32 *eilbrts, __u32 *elbatms, __u32 *elbats,
+ __u16 nr);
+
+/**
+ * nvme_init_copy_range_f3() - Constructs a copy range f3 structure
+ * @copy: Copy range array
+ * @snsids: Source namespace identifier
+ * @nlbs: Number of logical blocks
+ * @slbas: Starting LBA
+ * @sopts: Source options
+ * @eilbrts: Expected initial logical block reference tag
+ * @elbatms: Expected logical block application tag mask
+ * @elbats: Expected logical block application tag
+ * @nr: Number of descriptors to construct
+ */
+void nvme_init_copy_range_f3(struct nvme_copy_range_f3 *copy, __u32 *snsids,
+ __u16 *nlbs, __u64 *slbas, __u16 *sopts,
+ __u64 *eilbrts, __u32 *elbatms, __u32 *elbats,
+ __u16 nr);
+
+/**
+ * nvme_get_feature_length() - Retreive the command payload length for a
+ * specific feature identifier
+ * @fid: Feature identifier, see &enum nvme_features_id.
+ * @cdw11: The cdw11 value may affect the transfer (only known fid is
+ * %NVME_FEAT_FID_HOST_ID)
+ * @len: On success, set to this features payload length in bytes.
+ *
+ * Return: 0 on success, -1 with errno set to EINVAL if the function did not
+ * recognize &fid.
+ */
+int nvme_get_feature_length(int fid, __u32 cdw11, __u32 *len);
+
+/**
+ * nvme_get_feature_length2() - Retreive the command payload length for a
+ * specific feature identifier
+ * @fid: Feature identifier, see &enum nvme_features_id.
+ * @cdw11: The cdw11 value may affect the transfer (only known fid is
+ * %NVME_FEAT_FID_HOST_ID)
+ * @dir: Data transfer direction: false - host to controller, true -
+ * controller to host may affect the transfer (only known fid is
+ * %NVME_FEAT_FID_HOST_MEM_BUF).
+ * @len: On success, set to this features payload length in bytes.
+ *
+ * Return: 0 on success, -1 with errno set to EINVAL if the function did not
+ * recognize &fid.
+ */
+int nvme_get_feature_length2(int fid, __u32 cdw11, enum nvme_data_tfr dir,
+ __u32 *len);
+
+/**
+ * nvme_get_directive_receive_length() - Get directive receive length
+ * @dtype: Directive type, see &enum nvme_directive_dtype
+ * @doper: Directive receive operation, see &enum nvme_directive_receive_doper
+ * @len: On success, set to this directives payload length in bytes.
+ *
+ * Return: 0 on success, -1 with errno set to EINVAL if the function did not
+ * recognize &dtype or &doper.
+ */
+int nvme_get_directive_receive_length(enum nvme_directive_dtype dtype,
+ enum nvme_directive_receive_doper doper, __u32 *len);
+
+#define NVME_FEAT_ARB_BURST(v) NVME_GET(v, FEAT_ARBITRATION_BURST)
+#define NVME_FEAT_ARB_LPW(v) NVME_GET(v, FEAT_ARBITRATION_LPW)
+#define NVME_FEAT_ARB_MPW(v) NVME_GET(v, FEAT_ARBITRATION_MPW)
+#define NVME_FEAT_ARB_HPW(v) NVME_GET(v, FEAT_ARBITRATION_HPW)
+
+static inline void nvme_feature_decode_arbitration(__u32 value, __u8 *ab,
+ __u8 *lpw, __u8 *mpw,
+ __u8 *hpw)
+{
+ *ab = NVME_FEAT_ARB_BURST(value);
+ *lpw = NVME_FEAT_ARB_LPW(value);
+ *mpw = NVME_FEAT_ARB_MPW(value);
+ *hpw = NVME_FEAT_ARB_HPW(value);
+};
+
+#define NVME_FEAT_PM_PS(v) NVME_GET(v, FEAT_PWRMGMT_PS)
+#define NVME_FEAT_PM_WH(v) NVME_GET(v, FEAT_PWRMGMT_WH)
+
+static inline void nvme_feature_decode_power_mgmt(__u32 value, __u8 *ps,
+ __u8 *wh)
+{
+ *ps = NVME_FEAT_PM_PS(value);
+ *wh = NVME_FEAT_PM_WH(value);
+}
+
+#define NVME_FEAT_LBAR_NR(v) NVME_GET(v, FEAT_LBAR_NR)
+
+static inline void nvme_feature_decode_lba_range(__u32 value, __u8 *num)
+{
+ *num = NVME_FEAT_LBAR_NR(value);
+}
+
+#define NVME_FEAT_TT_TMPTH(v) NVME_GET(v, FEAT_TT_TMPTH)
+#define NVME_FEAT_TT_TMPSEL(v) NVME_GET(v, FEAT_TT_TMPSEL)
+#define NVME_FEAT_TT_THSEL(v) NVME_GET(v, FEAT_TT_THSEL)
+
+static inline void nvme_feature_decode_temp_threshold(__u32 value, __u16 *tmpth,
+ __u8 *tmpsel, __u8 *thsel)
+{
+ *tmpth = NVME_FEAT_TT_TMPTH(value);
+ *tmpsel = NVME_FEAT_TT_TMPSEL(value);
+ *thsel = NVME_FEAT_TT_THSEL(value);
+}
+
+#define NVME_FEAT_ER_TLER(v) NVME_GET(v, FEAT_ERROR_RECOVERY_TLER)
+#define NVME_FEAT_ER_DULBE(v) NVME_GET(v, FEAT_ERROR_RECOVERY_DULBE)
+
+static inline void nvme_feature_decode_error_recovery(__u32 value, __u16 *tler,
+ bool *dulbe)
+{
+ *tler = NVME_FEAT_ER_TLER(value);
+ *dulbe = NVME_FEAT_ER_DULBE(value);
+}
+
+#define NVME_FEAT_VWC_WCE(v) NVME_GET(v, FEAT_VWC_WCE)
+
+static inline void nvme_feature_decode_volatile_write_cache(__u32 value,
+ bool *wce)
+{
+ *wce = NVME_FEAT_VWC_WCE(value);
+}
+
+#define NVME_FEAT_NRQS_NSQR(v) NVME_GET(v, FEAT_NRQS_NSQR)
+#define NVME_FEAT_NRQS_NCQR(v) NVME_GET(v, FEAT_NRQS_NCQR)
+
+static inline void nvme_feature_decode_number_of_queues(__u32 value,
+ __u16 *nsqr,
+ __u16 *ncqr)
+{
+ *nsqr = NVME_FEAT_NRQS_NSQR(value);
+ *ncqr = NVME_FEAT_NRQS_NCQR(value);
+}
+
+#define NVME_FEAT_IRQC_THR(v) NVME_GET(v, FEAT_IRQC_THR)
+#define NVME_FEAT_IRQC_TIME(v) NVME_GET(v, FEAT_IRQC_TIME)
+
+static inline void nvme_feature_decode_interrupt_coalescing(__u32 value,
+ __u8 *thr,
+ __u8 *time)
+{
+ *thr = NVME_FEAT_IRQC_THR(value);
+ *time = NVME_FEAT_IRQC_TIME(value);
+}
+
+#define NVME_FEAT_ICFG_IV(v) NVME_GET(v, FEAT_ICFG_IV)
+#define NVME_FEAT_ICFG_CD(v) NVME_GET(v, FEAT_ICFG_CD)
+
+static inline void nvme_feature_decode_interrupt_config(__u32 value, __u16 *iv,
+ bool *cd)
+{
+ *iv = NVME_FEAT_ICFG_IV(value);
+ *cd = NVME_FEAT_ICFG_CD(value);
+}
+
+#define NVME_FEAT_WA_DN(v) NVME_GET(v, FEAT_WA_DN)
+
+static inline void nvme_feature_decode_write_atomicity(__u32 value, bool *dn)
+{
+ *dn = NVME_FEAT_WA_DN(value);
+}
+
+#define NVME_FEAT_AE_SMART(v) NVME_GET(v, FEAT_AE_SMART)
+#define NVME_FEAT_AE_NAN(v) NVME_GET(v, FEAT_AE_NAN)
+#define NVME_FEAT_AE_FW(v) NVME_GET(v, FEAT_AE_FW)
+#define NVME_FEAT_AE_TELEM(v) NVME_GET(v, FEAT_AE_TELEM)
+#define NVME_FEAT_AE_ANA(v) NVME_GET(v, FEAT_AE_ANA)
+#define NVME_FEAT_AE_PLA(v) NVME_GET(v, FEAT_AE_PLA)
+#define NVME_FEAT_AE_LBAS(v) NVME_GET(v, FEAT_AE_LBAS)
+#define NVME_FEAT_AE_EGA(v) NVME_GET(v, FEAT_AE_EGA)
+
+static inline void nvme_feature_decode_async_event_config(__u32 value,
+ __u8 *smart, bool *nan, bool *fw, bool *telem,
+ bool *ana, bool *pla, bool *lbas, bool *ega)
+{
+ *smart = NVME_FEAT_AE_SMART(value);
+ *nan = NVME_FEAT_AE_NAN(value);
+ *fw = NVME_FEAT_AE_FW(value);
+ *telem = NVME_FEAT_AE_TELEM(value);
+ *ana = NVME_FEAT_AE_ANA(value);
+ *pla = NVME_FEAT_AE_PLA(value);
+ *lbas = NVME_FEAT_AE_LBAS(value);
+ *ega = NVME_FEAT_AE_EGA(value);
+}
+
+#define NVME_FEAT_APST_APSTE(v) NVME_GET(v, FEAT_APST_APSTE)
+
+static inline void nvme_feature_decode_auto_power_state(__u32 value,
+ bool *apste)
+{
+ *apste = NVME_FEAT_APST_APSTE(value);
+}
+
+#define NVME_FEAT_HMEM_EHM(v) NVME_GET(v, FEAT_HMEM_EHM)
+
+static inline void nvme_feature_decode_host_memory_buffer(__u32 value, bool *ehm)
+{
+ *ehm = NVME_FEAT_HMEM_EHM(value);
+}
+
+#define NVME_FEAT_HCTM_TMT2(v) NVME_GET(v, FEAT_HCTM_TMT2)
+#define NVME_FEAT_HCTM_TMT1(v) NVME_GET(v, FEAT_HCTM_TMT1)
+
+static inline void nvme_feature_decode_host_thermal_mgmt(__u32 value,
+ __u16 *tmt2,
+ __u16 *tmt1)
+{
+ *tmt2 = NVME_FEAT_HCTM_TMT2(value);
+ *tmt1 = NVME_FEAT_HCTM_TMT1(value);
+}
+
+#define NVME_FEAT_NOPS_NOPPME(v) NVME_GET(v, FEAT_NOPS_NOPPME)
+
+static inline void nvme_feature_decode_non_op_power_config(__u32 value,
+ bool *noppme)
+{
+ *noppme = NVME_FEAT_NOPS_NOPPME(value);
+}
+
+#define NVME_FEAT_RRL_RRL(v) NVME_GET(v, FEAT_RRL_RRL)
+
+static inline void nvme_feature_decode_read_recovery_level_config(__u32 value,
+ __u8 *rrl)
+{
+ *rrl = NVME_FEAT_RRL_RRL(value);
+}
+
+#define NVME_FEAT_PLM_PLME(v) NVME_GET(v, FEAT_PLM_PLME)
+
+static inline void nvme_feature_decode_predictable_latency_mode_config(__u32 value,
+ bool *plme)
+{
+ *plme = NVME_FEAT_PLM_PLME(value);
+}
+
+#define NVME_FEAT_PLMW_WS(v) NVME_GET(v, FEAT_PLMW_WS)
+
+static inline void nvme_feature_decode_predictable_latency_mode_window(__u32 value,
+ __u8 *ws)
+{
+ *ws = NVME_FEAT_PLMW_WS(value);
+}
+
+#define NVME_FEAT_LBAS_LSIRI(v) NVME_GET(v, FEAT_LBAS_LSIRI)
+#define NVME_FEAT_LBAS_LSIPI(v) NVME_GET(v, FEAT_LBAS_LSIPI)
+
+static inline void nvme_feature_decode_lba_status_attributes(__u32 value,
+ __u16 *lsiri,
+ __u16 *lsipi)
+{
+ *lsiri = NVME_FEAT_LBAS_LSIRI(value);
+ *lsipi = NVME_FEAT_LBAS_LSIPI(value);
+}
+
+#define NVME_FEAT_SC_NODRM(v) NVME_GET(v, FEAT_SC_NODRM)
+
+static inline void nvme_feature_decode_sanitize_config(__u32 value, bool *nodrm)
+{
+ *nodrm = NVME_FEAT_SC_NODRM(value);
+}
+
+#define NVME_FEAT_EG_ENDGID(v) NVME_GET(v, FEAT_EG_ENDGID)
+#define NVME_FEAT_EG_EGCW(v) NVME_GET(v, FEAT_EG_EGCW)
+
+static inline void nvme_feature_decode_endurance_group_event_config(__u32 value,
+ __u16 *endgid, __u8 *endgcw)
+{
+ *endgid = NVME_FEAT_EG_ENDGID(value);
+ *endgcw = NVME_FEAT_EG_EGCW(value);
+}
+
+#define NVME_FEAT_SPM_PBSLC(v) NVME_GET(v, FEAT_SPM_PBSLC)
+
+static inline void nvme_feature_decode_software_progress_marker(__u32 value,
+ __u8 *pbslc)
+{
+ *pbslc = NVME_FEAT_SPM_PBSLC(value);
+}
+
+#define NVME_FEAT_HOSTID_EXHID(v) NVME_GET(v, FEAT_HOSTID_EXHID)
+
+static inline void nvme_feature_decode_host_identifier(__u32 value, bool *exhid)
+{
+ *exhid = NVME_FEAT_HOSTID_EXHID(value);
+}
+
+#define NVME_FEAT_RM_REGPRE(v) NVME_GET(v, FEAT_RM_REGPRE)
+#define NVME_FEAT_RM_RESREL(v) NVME_GET(v, FEAT_RM_RESREL)
+#define NVME_FEAT_RM_RESPRE(v) NVME_GET(v, FEAT_RM_RESPRE)
+
+static inline void nvme_feature_decode_reservation_notification(__u32 value,
+ bool *regpre,
+ bool *resrel,
+ bool *respre)
+{
+ *regpre = NVME_FEAT_RM_REGPRE(value);
+ *resrel = NVME_FEAT_RM_RESREL(value);
+ *respre = NVME_FEAT_RM_RESPRE(value);
+}
+
+#define NVME_FEAT_RP_PTPL(v) NVME_GET(v, FEAT_RP_PTPL)
+
+static inline void nvme_feature_decode_reservation_persistance(__u32 value,
+ bool *ptpl)
+{
+ *ptpl = NVME_FEAT_RP_PTPL(value);
+}
+
+#define NVME_FEAT_WP_WPS(v) NVME_GET(v, FEAT_WP_WPS)
+
+static inline void nvme_feature_decode_namespace_write_protect(__u32 value,
+ __u8 *wps)
+{
+ *wps = NVME_FEAT_WP_WPS(value);
+}
+
+static inline void nvme_id_ns_flbas_to_lbaf_inuse(__u8 flbas, __u8 *lbaf_inuse)
+{
+ *lbaf_inuse = ((NVME_FLBAS_HIGHER(flbas) >> 1) |
+ NVME_FLBAS_LOWER(flbas));
+}
+
+struct nvme_root;
+
+char *hostname2traddr(struct nvme_root *r, const char *traddr);
+
+/**
+ * get_entity_name - Get Entity Name (ENAME).
+ * @buffer: The buffer where the ENAME will be saved as an ASCII string.
+ * @bufsz: The size of @buffer.
+ *
+ * Per TP8010, ENAME is defined as the name associated with the host (i.e.
+ * hostname).
+ *
+ * Return: Number of characters copied to @buffer.
+ */
+size_t get_entity_name(char *buffer, size_t bufsz);
+
+/**
+ * get_entity_version - Get Entity Version (EVER).
+ * @buffer: The buffer where the EVER will be saved as an ASCII string.
+ * @bufsz: The size of @buffer.
+ *
+ * EVER is defined as the operating system name and version as an ASCII
+ * string. This function reads different files from the file system and
+ * builds a string as follows: [os type] [os release] [distro release]
+ *
+ * E.g. "Linux 5.17.0-rc1 SLES 15.4"
+ *
+ * Return: Number of characters copied to @buffer.
+ */
+size_t get_entity_version(char *buffer, size_t bufsz);
+
+/**
+ * kv_strip - Strip blanks from key value string
+ * @kv: The key-value string to strip
+ *
+ * Strip leading/trailing blanks as well as trailing comments from the
+ * Key=Value string pointed to by @kv.
+ *
+ * Return: A pointer to the stripped string. Note that the original string,
+ * @kv, gets modified.
+ */
+char *kv_strip(char *kv);
+
+/**
+ * kv_keymatch - Look for key in key value string
+ * @kv: The key=value string to search for the presence of @key
+ * @key: The key to look for
+ *
+ * Look for @key in the Key=Value pair pointed to by @k and return a
+ * pointer to the Value if @key is found.
+ *
+ * Check if @kv starts with @key. If it does then make sure that we
+ * have a whole-word match on the @key, and if we do, return a pointer
+ * to the first character of value (i.e. skip leading spaces, tabs,
+ * and equal sign)
+ *
+ * Return: A pointer to the first character of "value" if a match is found.
+ * NULL otherwise.
+ */
+char *kv_keymatch(const char *kv, const char *key);
+
+/**
+ * startswith - Checks that a string starts with a given prefix.
+ * @s: The string to check
+ * @prefix: A string that @s could be starting with
+ *
+ * Return: If @s starts with @prefix, then return a pointer within @s at
+ * the first character after the matched @prefix. NULL otherwise.
+ */
+char *startswith(const char *s, const char *prefix);
+
+#define __round_mask(val, mult) ((__typeof__(val))((mult)-1))
+
+/**
+ * round_up - Round a value @val to the next multiple specified by @mult.
+ * @val: Value to round
+ * @mult: Multiple to round to.
+ *
+ * usage: int x = round_up(13, sizeof(__u32)); // 13 -> 16
+ */
+#define round_up(val, mult) ((((val)-1) | __round_mask((val), (mult)))+1)
+
+/**
+ * nvmf_exat_len() - Return length rounded up by 4
+ * @val_len: Value length
+ *
+ * Return the size in bytes, rounded to a multiple of 4 (e.g., size of
+ * __u32), of the buffer needed to hold the exat value of size
+ * @val_len.
+ *
+ * Return: Length rounded up by 4
+ */
+static inline __u16 nvmf_exat_len(size_t val_len)
+{
+ return (__u16)round_up(val_len, sizeof(__u32));
+}
+
+/**
+ * nvmf_exat_size - Return min aligned size to hold value
+ * @val_len: This is the length of the data to be copied to the "exatval"
+ * field of a "struct nvmf_ext_attr".
+ *
+ * Return the size of the "struct nvmf_ext_attr" needed to hold
+ * a value of size @val_len.
+ *
+ * Return: The size in bytes, rounded to a multiple of 4 (i.e. size of
+ * __u32), of the "struct nvmf_ext_attr" required to hold a string of
+ * length @val_len.
+ */
+static inline __u16 nvmf_exat_size(size_t val_len)
+{
+ return (__u16)(sizeof(struct nvmf_ext_attr) + nvmf_exat_len(val_len));
+}
+
+/**
+ * nvmf_exat_ptr_next - Increment @p to the next element in the array.
+ * @p: Pointer to an element of an array of "struct nvmf_ext_attr".
+ *
+ * Extended attributes are saved to an array of "struct nvmf_ext_attr"
+ * where each element of the array is of variable size. In order to
+ * move to the next element in the array one must increment the
+ * pointer to the current element (@p) by the size of the current
+ * element.
+ *
+ * Return: Pointer to the next element in the array.
+ */
+struct nvmf_ext_attr *nvmf_exat_ptr_next(struct nvmf_ext_attr *p);
+
+/**
+ * enum nvme_version - Selector for version to be returned by @nvme_get_version
+ *
+ * @NVME_VERSION_PROJECT: Project release version
+ * @NVME_VERSION_GIT: Git reference
+ */
+enum nvme_version {
+ NVME_VERSION_PROJECT = 0,
+ NVME_VERSION_GIT = 1,
+};
+
+/**
+ * nvme_get_version - Return version libnvme string
+ * @type: Selects which version type (see @struct nvme_version)
+ *
+ * Return: Returns version string for known types or else "n/a"
+ */
+const char *nvme_get_version(enum nvme_version type);
+
+#define NVME_UUID_LEN_STRING 37 /* 1b4e28ba-2fa1-11d2-883f-0016d3cca427 + \0 */
+#define NVME_UUID_LEN 16
+
+/**
+ * nvme_uuid_to_string - Return string represenation of encoded UUID
+ * @uuid: Binary encoded input UUID
+ * @str: Output string represenation of UUID
+ *
+ * Return: Returns error code if type conversion fails.
+ */
+int nvme_uuid_to_string(unsigned char uuid[NVME_UUID_LEN], char *str);
+
+/**
+ * nvme_uuid_from_string - Return encoded UUID represenation of string UUID
+ * @uuid: Binary encoded input UUID
+ * @str: Output string represenation of UUID
+ *
+ * Return: Returns error code if type conversion fails.
+ */
+int nvme_uuid_from_string(const char *str, unsigned char uuid[NVME_UUID_LEN]);
+
+/**
+ * nvme_uuid_random - Generate random UUID
+ * @uuid: Generated random UUID
+ *
+ * Generate random number according
+ * https://www.rfc-editor.org/rfc/rfc4122#section-4.4
+ *
+ * Return: Returns error code if generating of random number fails.
+ */
+int nvme_uuid_random(unsigned char uuid[NVME_UUID_LEN]);
+
+/**
+ * nvme_uuid_find - Find UUID position on UUID list
+ * @uuid_list: UUID list returned by identify UUID
+ * @uuid: Binary encoded input UUID
+ *
+ * Return: The array position where given UUID is present, or -1 on failure with errno set.
+ */
+int nvme_uuid_find(struct nvme_id_uuid_list *uuid_list, const unsigned char uuid[NVME_UUID_LEN]);
+
+/**
+ * nvme_ipaddrs_eq - Check if 2 IP addresses are equal.
+ * @addr1: IP address (can be IPv4 or IPv6)
+ * @addr2: IP address (can be IPv4 or IPv6)
+ *
+ * Return: true if addr1 == addr2. false otherwise.
+ */
+bool nvme_ipaddrs_eq(const char *addr1, const char *addr2);
+
+/**
+ * nvme_iface_matching_addr - Get interface matching @addr
+ * @iface_list: Interface list returned by getifaddrs()
+ * @addr: Address to match
+ *
+ * Parse the interface list pointed to by @iface_list looking
+ * for the interface that has @addr as one of its assigned
+ * addresses.
+ *
+ * Return: The name of the interface that owns @addr or NULL.
+ */
+const char *nvme_iface_matching_addr(const struct ifaddrs *iface_list, const char *addr);
+
+/**
+ * nvme_iface_primary_addr_matches - Check that interface's primary address matches
+ * @iface_list: Interface list returned by getifaddrs()
+ * @iface: Interface to match
+ * @addr: Address to match
+ *
+ * Parse the interface list pointed to by @iface_list and looking for
+ * interface @iface. The get its primary address and check if it matches
+ * @addr.
+ *
+ * Return: true if a match is found, false otherwise.
+ */
+bool nvme_iface_primary_addr_matches(const struct ifaddrs *iface_list, const char *iface, const char *addr);
+
+#endif /* _LIBNVME_UTIL_H */
diff --git a/subprojects/dbus.wrap b/subprojects/dbus.wrap
new file mode 100644
index 0000000..349d0c5
--- /dev/null
+++ b/subprojects/dbus.wrap
@@ -0,0 +1,4 @@
+[wrap-git]
+url = https://gitlab.freedesktop.org/dbus/dbus.git
+revision = cd1a9bb09a4e9330f8669fb6d385b1e095f97fc2
+depth = 1
diff --git a/subprojects/json-c.wrap b/subprojects/json-c.wrap
new file mode 100644
index 0000000..569f78e
--- /dev/null
+++ b/subprojects/json-c.wrap
@@ -0,0 +1,13 @@
+[wrap-file]
+directory = json-c-0.17
+source_url = https://s3.amazonaws.com/json-c_releases/releases/json-c-0.17.tar.gz
+source_filename = json-c-0.17.tar.gz
+source_hash = 7550914d58fb63b2c3546f3ccfbe11f1c094147bd31a69dcd23714d7956159e6
+patch_filename = json-c_0.17-2_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/json-c_0.17-2/get_patch
+patch_hash = c1a9a7e2ea6bed89a59e13a5684be25899a5a510963154c4450c5a492b8f3984
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/json-c_0.17-2/json-c-0.17.tar.gz
+wrapdb_version = 0.17-2
+
+[provide]
+json-c = json_c_dep
diff --git a/subprojects/openssl.wrap b/subprojects/openssl.wrap
new file mode 100644
index 0000000..b69462f
--- /dev/null
+++ b/subprojects/openssl.wrap
@@ -0,0 +1,15 @@
+[wrap-file]
+directory = openssl-3.0.8
+source_url = https://www.openssl.org/source/openssl-3.0.8.tar.gz
+source_filename = openssl-3.0.8.tar.gz
+source_hash = 6c13d2bf38fdf31eac3ce2a347073673f5d63263398f1f69d0df4a41253e4b3e
+patch_filename = openssl_3.0.8-2_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/openssl_3.0.8-2/get_patch
+patch_hash = e84b5fe469e681e3318184157a0c7c43d4cbacd078bb88f506e31569f8f75072
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/openssl_3.0.8-2/openssl-3.0.8.tar.gz
+wrapdb_version = 3.0.8-2
+
+[provide]
+libcrypto = libcrypto_dep
+libssl = libssl_dep
+openssl = openssl_dep
diff --git a/test/cpp.cc b/test/cpp.cc
new file mode 100644
index 0000000..3d0a7d2
--- /dev/null
+++ b/test/cpp.cc
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+#include <iostream>
+#include <libnvme.h>
+
+int main()
+{
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ nvme_path_t p;
+ nvme_ns_t n;
+
+ r = nvme_scan(NULL);
+ if (!r)
+ return -1;
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ std::cout << nvme_subsystem_get_name(s)
+ << " - NQN=" << nvme_subsystem_get_nqn(s)
+ << "\n";
+ nvme_subsystem_for_each_ctrl(s, c) {
+ std::cout << " `- " << nvme_ctrl_get_name(c)
+ << " " << nvme_ctrl_get_transport(c)
+ << " " << nvme_ctrl_get_address(c)
+ << " " << nvme_ctrl_get_state(c)
+ << "\n";
+ nvme_ctrl_for_each_ns(c, n) {
+ std::cout << " `- "
+ << nvme_ns_get_name(n)
+ << "lba size:"
+ << nvme_ns_get_lba_size(n)
+ << " lba max:"
+ << nvme_ns_get_lba_count(n)
+ << "\n";
+ }
+ nvme_ctrl_for_each_path(c, p) {
+ std::cout << " `- "
+ << nvme_path_get_name(p)
+ << " "
+ << nvme_path_get_ana_state(p)
+ << "\n";
+ }
+ }
+ nvme_subsystem_for_each_ns(s, n) {
+ std::cout << " `- " << nvme_ns_get_name(n)
+ << "lba size:"
+ << nvme_ns_get_lba_size(n)
+ << " lba max:"
+ << nvme_ns_get_lba_count(n) << "\n";
+ }
+ }
+ }
+ std::cout << "\n";
+ nvme_free_tree(r);
+
+ return 0;
+}
diff --git a/test/ioctl/discovery.c b/test/ioctl/discovery.c
new file mode 100644
index 0000000..f5f6f51
--- /dev/null
+++ b/test/ioctl/discovery.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include <libnvme.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ccan/array_size/array_size.h>
+#include <ccan/endian/endian.h>
+
+#include "../../src/nvme/private.h"
+#include "mock.h"
+#include "util.h"
+
+#define TEST_FD 0xFD
+#define HEADER_LEN 20
+
+static void arbitrary_ascii_string(size_t max_len, char *str, char *log_str)
+{
+ size_t len;
+ size_t i;
+
+ len = arbitrary_range(max_len + 1);
+ for (i = 0; i < len; i++) {
+ /*
+ * ASCII strings shall contain only code values 20h through 7Eh.
+ * Exclude 20h (space) because it ends the string.
+ */
+ str[i] = log_str[i] = arbitrary_range(0x7E - 0x20) + 0x20 + 1;
+ }
+ for (i = len; i < max_len; i++) {
+ str[i] = '\0';
+ log_str[i] = ' ';
+ }
+}
+
+static void arbitrary_entry(struct nvmf_disc_log_entry *entry,
+ struct nvmf_disc_log_entry *log_entry)
+{
+ arbitrary(entry, sizeof(*entry));
+ memcpy(log_entry, entry, sizeof(*entry));
+ arbitrary_ascii_string(
+ sizeof(entry->trsvcid), entry->trsvcid, log_entry->trsvcid);
+ arbitrary_ascii_string(
+ sizeof(entry->traddr), entry->traddr, log_entry->traddr);
+}
+
+static void arbitrary_entries(size_t len,
+ struct nvmf_disc_log_entry *entries,
+ struct nvmf_disc_log_entry *log_entries)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ arbitrary_entry(&entries[i], &log_entries[i]);
+}
+
+static void test_no_entries(nvme_ctrl_t c)
+{
+ struct nvmf_discovery_log header = {};
+ /* No entries to fetch after fetching the header */
+ struct mock_cmd mock_admin_cmds[] = {
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header,
+ },
+ };
+ struct nvmf_discovery_log *log = NULL;
+
+ set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
+ check(nvmf_get_discovery_log(c, &log, 1) == 0, "discovery failed: %m");
+ end_mock_cmds();
+ cmp(log, &header, sizeof(header), "incorrect header");
+ free(log);
+}
+
+static void test_four_entries(nvme_ctrl_t c)
+{
+ size_t num_entries = 4;
+ struct nvmf_disc_log_entry entries[num_entries];
+ struct nvmf_disc_log_entry log_entries[num_entries];
+ struct nvmf_discovery_log header = {.numrec = cpu_to_le64(num_entries)};
+ /*
+ * All 4 entries should be fetched at once
+ * followed by the header again (to ensure genctr hasn't changed)
+ */
+ struct mock_cmd mock_admin_cmds[] = {
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = sizeof(entries),
+ .cdw10 = (sizeof(entries) / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .cdw12 = sizeof(header), /* LPOL */
+ .out_data = log_entries,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header,
+ },
+ };
+ struct nvmf_discovery_log *log = NULL;
+
+ arbitrary_entries(num_entries, entries, log_entries);
+ set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
+ check(nvmf_get_discovery_log(c, &log, 1) == 0, "discovery failed: %m");
+ end_mock_cmds();
+ cmp(log, &header, sizeof(header), "incorrect header");
+ cmp(log->entries, entries, sizeof(entries), "incorrect entries");
+ free(log);
+}
+
+static void test_five_entries(nvme_ctrl_t c)
+{
+ size_t num_entries = 5;
+ struct nvmf_disc_log_entry entries[num_entries];
+ struct nvmf_disc_log_entry log_entries[num_entries];
+ size_t first_entries = 4;
+ size_t first_data_len = first_entries * sizeof(*entries);
+ size_t second_entries = num_entries - first_entries;
+ size_t second_data_len = second_entries * sizeof(*entries);
+ struct nvmf_discovery_log header = {.numrec = cpu_to_le64(num_entries)};
+ /*
+ * The first 4 entries (4 KB) are fetched together,
+ * followed by last entry separately.
+ * Finally, the header is fetched again to check genctr.
+ */
+ struct mock_cmd mock_admin_cmds[] = {
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = first_data_len,
+ .cdw10 = (first_data_len / 4 - 1) << 16 /* NUMDL */
+ | 1 << 15 /* RAE */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .cdw12 = sizeof(header), /* LPOL */
+ .out_data = log_entries,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = second_data_len,
+ .cdw10 = (second_data_len / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .cdw12 = sizeof(header) + first_data_len, /* LPOL */
+ .out_data = log_entries + first_entries,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header,
+ },
+ };
+ struct nvmf_discovery_log *log = NULL;
+
+ arbitrary_entries(num_entries, entries, log_entries);
+ set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
+ check(nvmf_get_discovery_log(c, &log, 1) == 0, "discovery failed: %m");
+ end_mock_cmds();
+ cmp(log, &header, sizeof(header), "incorrect header");
+ cmp(log->entries, entries, sizeof(entries), "incorrect entries");
+ free(log);
+}
+
+static void test_genctr_change(nvme_ctrl_t c)
+{
+ struct nvmf_disc_log_entry entries1[1];
+ struct nvmf_discovery_log header1 = {
+ .numrec = cpu_to_le64(ARRAY_SIZE(entries1)),
+ };
+ size_t num_entries2 = 2;
+ struct nvmf_disc_log_entry entries2[num_entries2];
+ struct nvmf_disc_log_entry log_entries2[num_entries2];
+ struct nvmf_discovery_log header2 = {
+ .genctr = cpu_to_le64(1),
+ .numrec = cpu_to_le64(num_entries2),
+ };
+ /*
+ * genctr changes after the entries are fetched the first time,
+ * so the log page entries are refetched
+ */
+ struct mock_cmd mock_admin_cmds[] = {
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header1,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = sizeof(entries1),
+ .cdw10 = (sizeof(entries1) / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* NUMDL */
+ .cdw12 = sizeof(header1), /* LPOL */
+ .out_data = entries1,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header2,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = sizeof(entries2),
+ .cdw10 = (sizeof(entries2) / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .cdw12 = sizeof(header2), /* LPOL */
+ .out_data = log_entries2,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header2,
+ },
+ };
+ struct nvmf_discovery_log *log = NULL;
+
+ arbitrary(entries1, sizeof(entries1));
+ arbitrary_entries(num_entries2, entries2, log_entries2);
+ set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
+ check(nvmf_get_discovery_log(c, &log, 2) == 0, "discovery failed: %m");
+ end_mock_cmds();
+ cmp(log, &header2, sizeof(header2), "incorrect header");
+ cmp(log->entries, entries2, sizeof(entries2), "incorrect entries");
+ free(log);
+}
+
+static void test_max_retries(nvme_ctrl_t c)
+{
+ struct nvmf_disc_log_entry entry;
+ struct nvmf_discovery_log header1 = {.numrec = cpu_to_le64(1)};
+ struct nvmf_discovery_log header2 = {
+ .genctr = cpu_to_le64(1),
+ .numrec = cpu_to_le64(1),
+ };
+ struct nvmf_discovery_log header3 = {
+ .genctr = cpu_to_le64(2),
+ .numrec = cpu_to_le64(1),
+ };
+ /* genctr changes in both attempts, hitting the max retries (2) */
+ struct mock_cmd mock_admin_cmds[] = {
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header1,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = sizeof(entry),
+ .cdw10 = (sizeof(entry) / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .cdw12 = sizeof(header1), /* LPOL */
+ .out_data = &entry,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header2,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = sizeof(entry),
+ .cdw10 = (sizeof(entry) / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .cdw12 = sizeof(header2), /* LPOL */
+ .out_data = &entry,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header3,
+ },
+ };
+ struct nvmf_discovery_log *log = NULL;
+
+ arbitrary(&entry, sizeof(entry));
+ set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
+ check(nvmf_get_discovery_log(c, &log, 2) == -1, "discovery succeeded");
+ end_mock_cmds();
+ check(errno == EAGAIN, "discovery failed: %m");
+ check(!log, "unexpected log page returned");
+}
+
+static void test_header_error(nvme_ctrl_t c)
+{
+ /* Stop after an error in fetching the header the first time */
+ struct mock_cmd mock_admin_cmds[] = {
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .err = NVME_SC_INVALID_OPCODE,
+ },
+ };
+ struct nvmf_discovery_log *log = NULL;
+
+ set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
+ check(nvmf_get_discovery_log(c, &log, 1) == -1, "discovery succeeded");
+ end_mock_cmds();
+ check(!log, "unexpected log page returned");
+}
+
+static void test_entries_error(nvme_ctrl_t c)
+{
+ struct nvmf_discovery_log header = {.numrec = cpu_to_le64(1)};
+ size_t entry_size = sizeof(struct nvmf_disc_log_entry);
+ /* Stop after an error in fetching the entries */
+ struct mock_cmd mock_admin_cmds[] = {
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = entry_size,
+ .cdw10 = (entry_size / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .cdw12 = sizeof(header), /* LPOL */
+ .err = -EIO,
+ },
+ };
+ struct nvmf_discovery_log *log = NULL;
+
+ set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
+ check(nvmf_get_discovery_log(c, &log, 1) == -1, "discovery succeeded");
+ end_mock_cmds();
+ check(errno == EIO, "discovery failed: %m");
+ check(!log, "unexpected log page returned");
+}
+
+static void test_genctr_error(nvme_ctrl_t c)
+{
+ struct nvmf_disc_log_entry entry;
+ struct nvmf_discovery_log header = {.numrec = cpu_to_le64(1)};
+ /* Stop after an error in refetching the header */
+ struct mock_cmd mock_admin_cmds[] = {
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .out_data = &header,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = sizeof(entry),
+ .cdw10 = (sizeof(entry) / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .cdw12 = sizeof(header), /* LPOL */
+ .out_data = &entry,
+ },
+ {
+ .opcode = nvme_admin_get_log_page,
+ .data_len = HEADER_LEN,
+ .cdw10 = (HEADER_LEN / 4 - 1) << 16 /* NUMDL */
+ | NVME_LOG_LID_DISCOVER, /* LID */
+ .err = NVME_SC_INTERNAL,
+ },
+ };
+ struct nvmf_discovery_log *log = NULL;
+
+ arbitrary(&entry, sizeof(entry));
+ set_mock_admin_cmds(mock_admin_cmds, ARRAY_SIZE(mock_admin_cmds));
+ check(nvmf_get_discovery_log(c, &log, 1) == -1, "discovery succeeded");
+ end_mock_cmds();
+ check(!log, "unexpected log page returned");
+}
+
+static void run_test(const char *test_name, void (*test_fn)(nvme_ctrl_t))
+{
+ struct nvme_ctrl c = {.fd = TEST_FD};
+
+ printf("Running test %s...", test_name);
+ fflush(stdout);
+ check(asprintf(&c.name, "%s_ctrl", test_name) >= 0, "asprintf() failed");
+ test_fn(&c);
+ free(c.name);
+ puts(" OK");
+}
+
+#define RUN_TEST(name) run_test(#name, test_ ## name)
+
+int main(void)
+{
+ set_mock_fd(TEST_FD);
+ RUN_TEST(no_entries);
+ RUN_TEST(four_entries);
+ RUN_TEST(five_entries);
+ RUN_TEST(genctr_change);
+ RUN_TEST(max_retries);
+ RUN_TEST(header_error);
+ RUN_TEST(entries_error);
+ RUN_TEST(genctr_error);
+}
diff --git a/test/ioctl/features.c b/test/ioctl/features.c
new file mode 100644
index 0000000..7386497
--- /dev/null
+++ b/test/ioctl/features.c
@@ -0,0 +1,1604 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include <libnvme.h>
+
+#include <errno.h>
+#include <inttypes.h>
+
+#include "mock.h"
+#include "util.h"
+
+#define TEST_FD 0xFD
+#define TEST_TIMEOUT 1234
+#define TEST_NSID 0x89ABCDEF
+#define TEST_CDW11 0x11111111
+#define TEST_CDW12 0x12121212
+#define TEST_CDW13 0x13131313
+#define TEST_CDW15 0x15151515
+#define TEST_UUIDX 0b1001110
+#define TEST_FID 0xFE
+#define TEST_RESULT 0x12345678
+#define TEST_SEL NVME_GET_FEATURES_SEL_SAVED
+#define TEST_SC NVME_SC_INVALID_FIELD
+
+static void test_set_features(void)
+{
+ uint32_t result = 0;
+ uint8_t data[256];
+ struct nvme_set_features_args args = {
+ .result = &result,
+ .data = data,
+ .args_size = sizeof(args),
+ .fd = TEST_FD,
+ .timeout = TEST_TIMEOUT,
+ .nsid = TEST_NSID,
+ .cdw11 = TEST_CDW11,
+ .cdw12 = TEST_CDW12,
+ .cdw13 = TEST_CDW13,
+ .cdw15 = TEST_CDW15,
+ .data_len = sizeof(data),
+ .save = true,
+ .uuidx = TEST_UUIDX,
+ .fid = TEST_FID,
+ };
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .in_data = data,
+ .data_len = sizeof(data),
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | TEST_FID,
+ .cdw11 = TEST_CDW11,
+ .cdw12 = TEST_CDW12,
+ .cdw13 = TEST_CDW13,
+ .cdw14 = TEST_UUIDX,
+ .cdw15 = TEST_CDW15,
+ .timeout_ms = TEST_TIMEOUT,
+ .result = TEST_RESULT,
+ };
+ int err;
+
+ arbitrary(data, sizeof(data));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features(&args);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_features(void)
+{
+ uint32_t result = 0;
+ uint8_t data[256], get_data[sizeof(data)] = {};
+ struct nvme_get_features_args args = {
+ .result = &result,
+ .data = get_data,
+ .args_size = sizeof(args),
+ .fd = TEST_FD,
+ .timeout = TEST_TIMEOUT,
+ .nsid = TEST_NSID,
+ .sel = TEST_SEL,
+ .cdw11 = TEST_CDW11,
+ .data_len = sizeof(data),
+ .fid = TEST_FID,
+ .uuidx = TEST_UUIDX,
+ };
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(data),
+ .cdw10 = TEST_SEL << 8 | TEST_FID,
+ .cdw11 = TEST_CDW11,
+ .cdw14 = TEST_UUIDX,
+ .timeout_ms = TEST_TIMEOUT,
+ .out_data = data,
+ .result = TEST_RESULT,
+ };
+ int err;
+
+ arbitrary(data, sizeof(data));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features(&args);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+ cmp(get_data, data, sizeof(data), "incorrect data");
+}
+
+static void test_set_features_data(void)
+{
+ uint8_t data[128];
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .in_data = data,
+ .data_len = sizeof(data),
+ .cdw10 = TEST_FID,
+ .cdw11 = TEST_CDW11,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(data, sizeof(data));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_data(
+ TEST_FD, TEST_FID, TEST_NSID, TEST_CDW11, false,
+ sizeof(data), data, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_features_data(void)
+{
+ uint8_t data[128], get_data[sizeof(data)] = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(data),
+ .cdw10 = NVME_GET_FEATURES_SEL_CURRENT << 8 | TEST_FID,
+ .out_data = data,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(data, sizeof(data));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_data(
+ TEST_FD, TEST_FID, TEST_NSID, sizeof(data), get_data, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+ cmp(get_data, data, sizeof(data), "incorrect data");
+}
+
+static void test_set_features_simple(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | TEST_FID,
+ .cdw11 = TEST_CDW11,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_simple(
+ TEST_FD, TEST_FID, TEST_NSID, TEST_CDW11, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_features_simple(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = TEST_NSID,
+ .cdw10 = NVME_GET_FEATURES_SEL_CURRENT << 8 | TEST_FID,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_simple(TEST_FD, TEST_FID, TEST_NSID, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_arbitration(void)
+{
+ uint8_t HPW = 0xAA, MPW = 0xBB, LPW = 0xCC, AB = 0b111;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_ARBITRATION,
+ .cdw11 = (uint32_t)HPW << 24 | MPW << 16 | LPW << 8 | AB,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_arbitration(
+ TEST_FD, AB, LPW, MPW, HPW, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_arbitration(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_ARBITRATION,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_arbitration(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_power_mgmt(void)
+{
+ uint8_t PS = 0b10101, WH = 0b101;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_POWER_MGMT,
+ .cdw11 = WH << 5 | PS,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_power_mgmt(TEST_FD, PS, WH, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_power_mgmt(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_POWER_MGMT,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_power_mgmt(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_lba_range(void)
+{
+ uint8_t NUM = 64;
+ struct nvme_lba_range_type range_types;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .in_data = &range_types,
+ .data_len = sizeof(range_types),
+ .cdw10 = NVME_FEAT_FID_LBA_RANGE,
+ .cdw11 = NUM - 1,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(&range_types, sizeof(range_types));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_lba_range(
+ TEST_FD, TEST_NSID, NUM, false, &range_types, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_lba_range(void)
+{
+ struct nvme_lba_range_type range_types, get_range_types = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(range_types),
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_LBA_RANGE,
+ .out_data = &range_types,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(&range_types, sizeof(range_types));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_lba_range2(
+ TEST_FD, TEST_SEL, TEST_NSID, &get_range_types, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+ cmp(&get_range_types, &range_types, sizeof(range_types),
+ "incorrect LBA range types");
+}
+
+static void test_set_temp_thresh(void)
+{
+ uint16_t TMPTH = 0xFEDC;
+ uint8_t TMPSEL = 0x8;
+ enum nvme_feat_tmpthresh_thsel THSEL =
+ NVME_FEATURE_TEMPTHRESH_THSEL_UNDER;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_TEMP_THRESH,
+ .cdw11 = THSEL << 20 | TMPSEL << 16 | TMPTH,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_temp_thresh(
+ TEST_FD, TMPTH, TMPSEL, THSEL, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_temp_thresh(void)
+{
+ /*
+ * nvme_get_features_temp_thresh() doesn't support
+ * specifying TMPSEL and THSEL
+ */
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_TEMP_THRESH,
+ .cdw11 = NVME_FEATURE_TEMPTHRESH_THSEL_OVER << 20,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_temp_thresh(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_err_recovery(void)
+{
+ uint16_t TLER = 0xCDEF;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .cdw10 = NVME_FEAT_FID_ERR_RECOVERY,
+ .cdw11 = 1 << 16 /* DULBE */
+ | TLER,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_err_recovery(
+ TEST_FD, TEST_NSID, TLER, true, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_err_recovery(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = TEST_NSID,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_ERR_RECOVERY,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_err_recovery2(
+ TEST_FD, TEST_SEL, TEST_NSID, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_volatile_wc(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_VOLATILE_WC,
+ .cdw11 = 1 << 0, /* WCE */
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_volatile_wc(TEST_FD, true, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_volatile_wc(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8
+ | NVME_FEAT_FID_VOLATILE_WC,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_volatile_wc(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_num_queues(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_NUM_QUEUES,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_num_queues(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_irq_coalesce(void)
+{
+ uint8_t THR = 0xAB, TIME = 0xCD;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_IRQ_COALESCE,
+ .cdw11 = TIME << 8 | THR,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_irq_coalesce(
+ TEST_FD, THR, TIME, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_irq_coalesce(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_IRQ_COALESCE,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_irq_coalesce(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_irq_config(void)
+{
+ uint16_t IV = 0x1234;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_IRQ_CONFIG,
+ .cdw11 = 1 << 16 /* CD */
+ | IV,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_irq_config(TEST_FD, IV, true, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_irq_config(void)
+{
+ uint16_t IV = 0x5678;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_IRQ_CONFIG,
+ .cdw11 = IV,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_irq_config(TEST_FD, TEST_SEL, IV, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_write_atomic(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_WRITE_ATOMIC,
+ .cdw11 = 1 << 0, /* DN */
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_write_atomic(TEST_FD, true, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_write_atomic(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_WRITE_ATOMIC,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_write_atomic(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_async_event(void)
+{
+ uint32_t EVENTS = 0x87654321;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_ASYNC_EVENT,
+ .cdw11 = EVENTS,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_async_event(TEST_FD, EVENTS, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_async_event(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_ASYNC_EVENT,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_async_event(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_auto_pst(void)
+{
+ struct nvme_feat_auto_pst apst;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .in_data = &apst,
+ .data_len = sizeof(apst),
+ .cdw10 = NVME_FEAT_FID_AUTO_PST,
+ .cdw11 = 1 << 0, /* APSTE */
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(&apst, sizeof(apst));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_auto_pst(TEST_FD, true, false, &apst, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_auto_pst(void)
+{
+ struct nvme_feat_auto_pst apst, get_apst = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .data_len = sizeof(apst),
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_AUTO_PST,
+ .out_data = &apst,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(&apst, sizeof(apst));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_auto_pst(TEST_FD, TEST_SEL, &get_apst, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+ cmp(&get_apst, &apst, sizeof(apst), "incorrect apst");
+}
+
+static void test_get_host_mem_buf(void)
+{
+ struct nvme_host_mem_buf_attrs attrs, get_attrs = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .data_len = sizeof(attrs),
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HOST_MEM_BUF,
+ .out_data = &attrs,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(&attrs, sizeof(attrs));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_host_mem_buf2(
+ TEST_FD, TEST_SEL, &get_attrs, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+ cmp(&get_attrs, &attrs, sizeof(attrs), "incorrect attrs");
+}
+
+static void test_set_timestamp(void)
+{
+ struct nvme_timestamp ts = {.timestamp = {1, 2, 3, 4, 5, 6}};
+ uint64_t timestamp = ts.timestamp[0]
+ | (uint64_t) ts.timestamp[1] << 8
+ | (uint64_t) ts.timestamp[2] << 16
+ | (uint64_t) ts.timestamp[3] << 24
+ | (uint64_t) ts.timestamp[4] << 32
+ | (uint64_t) ts.timestamp[5] << 40;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .in_data = &ts,
+ .data_len = sizeof(ts),
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_TIMESTAMP,
+ };
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_timestamp(TEST_FD, true, timestamp);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+}
+
+static void test_get_timestamp(void)
+{
+ struct nvme_timestamp ts, get_ts = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .data_len = sizeof(ts),
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_TIMESTAMP,
+ .out_data = &ts,
+ };
+ int err;
+
+ arbitrary(&ts, sizeof(ts));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_timestamp(TEST_FD, TEST_SEL, &get_ts);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ cmp(&get_ts, &ts, sizeof(ts), "incorrect timestamp");
+}
+
+static void test_get_kato(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_KATO,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_kato(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_hctm(void)
+{
+ uint16_t TMT2 = 0x4321, TMT1 = 0x8765;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_HCTM,
+ .cdw11 = (uint32_t)TMT1 << 16 | TMT2,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_hctm(TEST_FD, TMT2, TMT1, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_hctm(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HCTM,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_hctm(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_nopsc(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_NOPSC,
+ .cdw11 = 1 << 0 /* NOPPME */,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_nopsc(TEST_FD, true, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_nopsc(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_NOPSC,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_nopsc(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_rrl(void)
+{
+ uint8_t RRL = 0xA;
+ uint16_t NVMSETID = 0x1234;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_RRL,
+ .cdw11 = NVMSETID,
+ .cdw12 = RRL,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_rrl(TEST_FD, RRL, NVMSETID, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_rrl(void)
+{
+ /* nvme_get_features_rrl() doesn't support specifying the NVMSETID */
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_RRL,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_rrl(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_plm_config(void)
+{
+ uint16_t NVMSETID = 0xFEDC;
+ struct nvme_plm_config config;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .in_data = &config,
+ .data_len = sizeof(config),
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_PLM_CONFIG,
+ .cdw11 = NVMSETID,
+ .cdw12 = 1 << 0 /* Predictable Latency Enable */,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(&config, sizeof(config));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_plm_config(
+ TEST_FD, true, NVMSETID, true, &config, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_plm_config(void)
+{
+ uint16_t NVMSETID = 0xABCD;
+ struct nvme_plm_config config, get_config = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .data_len = sizeof(config),
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_PLM_CONFIG,
+ .cdw11 = NVMSETID,
+ .out_data = &config,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(&config, sizeof(config));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_plm_config(
+ TEST_FD, TEST_SEL, NVMSETID, &get_config, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+ cmp(&get_config, &config, sizeof(config), "incorrect PLM config");
+}
+
+static void test_set_plm_window(void)
+{
+ enum nvme_feat_plm_window_select SEL = NVME_FEATURE_PLM_NDWIN;
+ uint16_t NVMSETID = 0x4321;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_PLM_WINDOW,
+ .cdw11 = NVMSETID,
+ .cdw12 = SEL,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_plm_window(
+ TEST_FD, SEL, NVMSETID, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_plm_window(void)
+{
+ uint16_t NVMSETID = 0x8765;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_PLM_WINDOW,
+ .cdw11 = NVMSETID,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_plm_window(
+ TEST_FD, TEST_SEL, NVMSETID, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_lba_sts_interval(void)
+{
+ uint16_t LSIRI = 0x1234, LSIPI = 0x5678;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_LBA_STS_INTERVAL,
+ .cdw11 = LSIPI << 16 | LSIRI,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_lba_sts_interval(
+ TEST_FD, LSIRI, LSIPI, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_lba_sts_interval(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_LBA_STS_INTERVAL,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_lba_sts_interval(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_host_behavior(void)
+{
+ /* nvme_set_features_host_behavior() ignores SAVE */
+ struct nvme_feat_host_behavior behavior;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .in_data = &behavior,
+ .data_len = sizeof(behavior),
+ .cdw10 = NVME_FEAT_FID_HOST_BEHAVIOR,
+ };
+ int err;
+
+ arbitrary(&behavior, sizeof(behavior));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_host_behavior(TEST_FD, true, &behavior);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+}
+
+static void test_get_host_behavior(void)
+{
+ struct nvme_feat_host_behavior behavior, get_behavior = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .data_len = sizeof(behavior),
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HOST_BEHAVIOR,
+ .out_data = &behavior,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ arbitrary(&behavior, sizeof(behavior));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_host_behavior(
+ TEST_FD, TEST_SEL, &get_behavior, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+ cmp(&get_behavior, &behavior, sizeof(behavior), "incorrect behavior");
+}
+
+static void test_set_sanitize(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_SANITIZE,
+ .cdw11 = 1 << 0, /* NODRM */
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_sanitize(TEST_FD, true, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_sanitize(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_SANITIZE,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_sanitize(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_endurance_evt_cfg(void)
+{
+ uint16_t ENDGID = 0x9876;
+ uint8_t EGWARN = 0xCD;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_ENDURANCE_EVT_CFG,
+ .cdw11 = EGWARN << 16 | ENDGID,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_endurance_evt_cfg(
+ TEST_FD, ENDGID, EGWARN, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_endurance_event_cfg(void)
+{
+ uint16_t ENDGID = 0x6789;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_ENDURANCE_EVT_CFG,
+ .cdw11 = ENDGID,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_endurance_event_cfg(
+ TEST_FD, TEST_SEL, ENDGID, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_iocs_profile(void)
+{
+ uint16_t IOCSI = 0b101100111;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_IOCS_PROFILE,
+ .cdw11 = IOCSI,
+ };
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_iocs_profile(TEST_FD, IOCSI, false);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+}
+
+static void test_get_iocs_profile(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_IOCS_PROFILE,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_iocs_profile(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_sw_progress(void)
+{
+ uint8_t PBSLC = 0xBA;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_SW_PROGRESS,
+ .cdw11 = PBSLC,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_sw_progress(TEST_FD, PBSLC, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_sw_progress(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_SW_PROGRESS,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_sw_progress(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_host_id(void)
+{
+ uint8_t hostid[8];
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .in_data = hostid,
+ .data_len = sizeof(hostid),
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_HOST_ID,
+ .result = TEST_RESULT,
+ };
+ int err;
+
+ arbitrary(hostid, sizeof(hostid));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_host_id(TEST_FD, false, true, hostid);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+}
+
+static void test_set_host_id_extended(void)
+{
+ uint8_t hostid[16];
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .in_data = hostid,
+ .data_len = sizeof(hostid),
+ .cdw10 = NVME_FEAT_FID_HOST_ID,
+ .cdw11 = 1 << 0, /* EXHID */
+ .result = TEST_RESULT,
+ };
+ int err;
+
+ arbitrary(hostid, sizeof(hostid));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_host_id(TEST_FD, true, false, hostid);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+}
+
+static void test_get_host_id(void)
+{
+ uint8_t hostid[8], get_hostid[sizeof(hostid)] = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .data_len = sizeof(hostid),
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HOST_ID,
+ .out_data = hostid,
+ .result = TEST_RESULT,
+ };
+ int err;
+
+ arbitrary(hostid, sizeof(hostid));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_host_id(
+ TEST_FD, TEST_SEL, false, sizeof(hostid), get_hostid);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ cmp(get_hostid, hostid, sizeof(hostid), "incorrect host identifier");
+}
+
+static void test_get_host_id_extended(void)
+{
+ uint8_t hostid[16], get_hostid[sizeof(hostid)] = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .data_len = sizeof(hostid),
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_HOST_ID,
+ .cdw11 = 1 << 0, /* EXHID */
+ .out_data = hostid,
+ .result = TEST_RESULT,
+ };
+ int err;
+
+ arbitrary(hostid, sizeof(hostid));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_host_id(
+ TEST_FD, TEST_SEL, true, sizeof(hostid), get_hostid);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ cmp(get_hostid, hostid, sizeof(hostid), "incorrect host identifier");
+}
+
+static void test_set_resv_mask(void)
+{
+ uint32_t MASK = 0x23456789;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .cdw10 = (uint32_t)1 << 31 /* SAVE */
+ | NVME_FEAT_FID_RESV_MASK,
+ .cdw11 = MASK,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_resv_mask2(
+ TEST_FD, TEST_NSID, MASK, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_resv_mask(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = TEST_NSID,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_RESV_MASK,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_resv_mask2(
+ TEST_FD, TEST_SEL, TEST_NSID, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_resv_persist(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .cdw10 = NVME_FEAT_FID_RESV_PERSIST,
+ .cdw11 = 1 << 0, /* PTPL */
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_resv_persist2(
+ TEST_FD, TEST_NSID, true, false, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_resv_persist(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = TEST_NSID,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_RESV_PERSIST,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_resv_persist2(
+ TEST_FD, TEST_SEL, TEST_NSID, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_write_protect(void)
+{
+ /* nvme_set_features_write_protect() ignores SAVE */
+ enum nvme_feat_nswpcfg_state STATE =
+ NVME_FEAT_NS_WRITE_PROTECT_PERMANENT;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .cdw10 = NVME_FEAT_FID_WRITE_PROTECT,
+ .cdw11 = STATE,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_write_protect2(
+ TEST_FD, TEST_NSID, STATE, true, &result);
+ end_mock_cmds();
+ check(err == 0, "set features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_write_protect(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .nsid = TEST_NSID,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_WRITE_PROTECT,
+ .result = TEST_RESULT,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_write_protect(
+ TEST_FD, TEST_NSID, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == 0, "get features returned error %d, errno %m", err);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+/*
+ * All set_features functions tail-call nvme_set_features(),
+ * so testing errors in any of them will do
+ */
+
+static void test_set_status_code_error(void)
+{
+ uint32_t EVENTS = 0x12345678;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .cdw10 = NVME_FEAT_FID_ASYNC_EVENT,
+ .cdw11 = EVENTS,
+ .result = TEST_RESULT,
+ .err = TEST_SC,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_async_event(TEST_FD, EVENTS, false, &result);
+ end_mock_cmds();
+ check(err == TEST_SC, "got error %d, expected %d", err, TEST_SC);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_set_kernel_error(void)
+{
+ uint32_t MASK = 0x87654321;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_set_features,
+ .nsid = TEST_NSID,
+ .cdw10 = NVME_FEAT_FID_RESV_MASK,
+ .cdw11 = MASK,
+ .result = TEST_RESULT,
+ .err = -EIO,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_set_features_resv_mask2(
+ TEST_FD, TEST_NSID, MASK, false, &result);
+ end_mock_cmds();
+ check(err == -1, "got error %d, expected -1", err);
+ check(errno == EIO, "unexpected error %m");
+ check(!result, "result unexpectedly set to %" PRIu32, result);
+}
+
+/*
+ * All get_features functions tail-call nvme_get_features(),
+ * so testing errors in any of them will do
+ */
+
+static void test_get_status_code_error(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_KATO,
+ .result = TEST_RESULT,
+ .err = TEST_SC,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_kato(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == TEST_SC, "got error %d, expected %d", err, TEST_SC);
+ check(result == TEST_RESULT,
+ "got result %" PRIu32 ", expected %" PRIu32, result, TEST_RESULT);
+}
+
+static void test_get_kernel_error(void)
+{
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_get_features,
+ .cdw10 = TEST_SEL << 8 | NVME_FEAT_FID_NUM_QUEUES,
+ .result = TEST_RESULT,
+ .err = -EBUSY,
+ };
+ uint32_t result = 0;
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_get_features_num_queues(TEST_FD, TEST_SEL, &result);
+ end_mock_cmds();
+ check(err == -1, "got error %d, expected -1", err);
+ check(errno == EBUSY, "unexpected error %m");
+ check(!result, "result unexpectedly set to %" PRIu32, result);
+}
+
+static void run_test(const char *test_name, void (*test_fn)(void))
+{
+ printf("Running test %s...", test_name);
+ fflush(stdout);
+ test_fn();
+ puts(" OK");
+}
+
+#define RUN_TEST(name) run_test(#name, test_ ## name)
+
+int main(void)
+{
+ set_mock_fd(TEST_FD);
+ RUN_TEST(set_features);
+ RUN_TEST(get_features);
+ RUN_TEST(set_features_data);
+ RUN_TEST(get_features_data);
+ RUN_TEST(set_features_simple);
+ RUN_TEST(get_features_simple);
+ RUN_TEST(set_arbitration);
+ RUN_TEST(get_arbitration);
+ RUN_TEST(set_power_mgmt);
+ RUN_TEST(get_power_mgmt);
+ RUN_TEST(set_lba_range);
+ RUN_TEST(get_lba_range);
+ RUN_TEST(set_temp_thresh);
+ RUN_TEST(get_temp_thresh);
+ RUN_TEST(set_err_recovery);
+ RUN_TEST(get_err_recovery);
+ RUN_TEST(set_volatile_wc);
+ RUN_TEST(get_volatile_wc);
+ RUN_TEST(get_num_queues);
+ RUN_TEST(set_irq_coalesce);
+ RUN_TEST(get_irq_coalesce);
+ RUN_TEST(set_irq_config);
+ RUN_TEST(get_irq_config);
+ RUN_TEST(set_write_atomic);
+ RUN_TEST(get_write_atomic);
+ RUN_TEST(set_async_event);
+ RUN_TEST(get_async_event);
+ RUN_TEST(set_auto_pst);
+ RUN_TEST(get_auto_pst);
+ RUN_TEST(get_host_mem_buf);
+ RUN_TEST(set_timestamp);
+ RUN_TEST(get_timestamp);
+ RUN_TEST(get_kato);
+ RUN_TEST(set_hctm);
+ RUN_TEST(get_hctm);
+ RUN_TEST(set_nopsc);
+ RUN_TEST(get_nopsc);
+ RUN_TEST(set_rrl);
+ RUN_TEST(get_rrl);
+ RUN_TEST(set_plm_config);
+ RUN_TEST(get_plm_config);
+ RUN_TEST(set_plm_window);
+ RUN_TEST(get_plm_window);
+ RUN_TEST(set_lba_sts_interval);
+ RUN_TEST(get_lba_sts_interval);
+ RUN_TEST(set_host_behavior);
+ RUN_TEST(get_host_behavior);
+ RUN_TEST(set_sanitize);
+ RUN_TEST(get_sanitize);
+ RUN_TEST(set_endurance_evt_cfg);
+ RUN_TEST(get_endurance_event_cfg);
+ RUN_TEST(set_iocs_profile);
+ RUN_TEST(get_iocs_profile);
+ RUN_TEST(set_sw_progress);
+ RUN_TEST(get_sw_progress);
+ RUN_TEST(set_host_id);
+ RUN_TEST(set_host_id_extended);
+ RUN_TEST(get_host_id);
+ RUN_TEST(get_host_id_extended);
+ RUN_TEST(set_resv_mask);
+ RUN_TEST(get_resv_mask);
+ RUN_TEST(set_resv_persist);
+ RUN_TEST(get_resv_persist);
+ RUN_TEST(set_write_protect);
+ RUN_TEST(get_write_protect);
+ RUN_TEST(set_status_code_error);
+ RUN_TEST(set_kernel_error);
+ RUN_TEST(get_status_code_error);
+ RUN_TEST(get_kernel_error);
+}
diff --git a/test/ioctl/identify.c b/test/ioctl/identify.c
new file mode 100644
index 0000000..ccde02b
--- /dev/null
+++ b/test/ioctl/identify.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include <libnvme.h>
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "mock.h"
+#include "util.h"
+
+#define TEST_FD 0xFD
+#define TEST_NSID 0x12345678
+#define TEST_NVMSETID 0xABCD
+#define TEST_UUID 123
+#define TEST_CSI NVME_CSI_KV
+#define TEST_CNTID 0x4321
+#define TEST_DOMID 0xFEDC
+#define TEST_ENDGID 0x0123
+#define TEST_SC NVME_SC_INVALID_FIELD
+
+static void test_ns(void)
+{
+ struct nvme_id_ns expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_NS,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_ns(TEST_FD, TEST_NSID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_ctrl(void)
+{
+ struct nvme_id_ctrl expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_CTRL,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_ctrl(TEST_FD, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_active_ns_list(void)
+{
+ struct nvme_ns_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_NS_ACTIVE_LIST,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_active_ns_list(TEST_FD, TEST_NSID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_ns_descs(void)
+{
+ uint8_t expected_id[NVME_IDENTIFY_DATA_SIZE];
+ struct nvme_ns_id_desc *id;
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_NS_DESC_LIST,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(expected_id, sizeof(expected_id));
+ id = calloc(1, NVME_IDENTIFY_DATA_SIZE);
+ check(id, "memory allocation failed");
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_ns_descs(TEST_FD, TEST_NSID, id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(id, expected_id, sizeof(expected_id), "incorrect identify data");
+ free(id);
+}
+
+static void test_nvmset_list(void)
+{
+ struct nvme_id_nvmset_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_NVMSET_LIST,
+ .cdw11 = TEST_NVMSETID,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_nvmset_list(TEST_FD, TEST_NVMSETID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_ns_csi(void)
+{
+ uint8_t expected_id[NVME_IDENTIFY_DATA_SIZE];
+ uint8_t id[NVME_IDENTIFY_DATA_SIZE] = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_CSI_NS,
+ .cdw11 = TEST_CSI << 24,
+ .cdw14 = TEST_UUID,
+ .out_data = expected_id,
+ };
+ int err;
+
+ arbitrary(expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_ns_csi(TEST_FD, TEST_NSID, TEST_UUID, TEST_CSI, id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(id, expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_zns_identify_ns(void)
+{
+ struct nvme_zns_id_ns expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_CSI_NS,
+ .cdw11 = NVME_CSI_ZNS << 24,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_zns_identify_ns(TEST_FD, TEST_NSID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_nvm_identify_ctrl(void)
+{
+ struct nvme_id_ctrl_nvm expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_CSI_CTRL,
+ .cdw11 = NVME_CSI_NVM << 24,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_nvm_identify_ctrl(TEST_FD, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_zns_identify_ctrl(void)
+{
+ struct nvme_zns_id_ctrl expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_CSI_CTRL,
+ .cdw11 = NVME_CSI_ZNS << 24,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_zns_identify_ctrl(TEST_FD, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_active_ns_list_csi(void)
+{
+ struct nvme_ns_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_CSI_NS_ACTIVE_LIST,
+ .cdw11 = TEST_CSI << 24,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_active_ns_list_csi(
+ TEST_FD, TEST_NSID, TEST_CSI, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_independent_identify_ns(void)
+{
+ struct nvme_id_independent_id_ns expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ /* That's a mouthful! */
+ err = nvme_identify_independent_identify_ns(TEST_FD, TEST_NSID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_allocated_ns_list(void)
+{
+ struct nvme_ns_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_allocated_ns_list(TEST_FD, TEST_NSID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_allocated_ns(void)
+{
+ struct nvme_id_ns expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_ALLOCATED_NS,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_allocated_ns(TEST_FD, TEST_NSID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_nsid_ctrl_list(void)
+{
+ struct nvme_ctrl_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = TEST_CNTID << 16
+ | NVME_IDENTIFY_CNS_NS_CTRL_LIST,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_nsid_ctrl_list(TEST_FD, TEST_NSID, TEST_CNTID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_ctrl_list(void)
+{
+ struct nvme_ctrl_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = TEST_CNTID << 16
+ | NVME_IDENTIFY_CNS_CTRL_LIST,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_ctrl_list(TEST_FD, TEST_CNTID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_primary_ctrl(void)
+{
+ struct nvme_primary_ctrl_cap expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = TEST_CNTID << 16
+ | NVME_IDENTIFY_CNS_PRIMARY_CTRL_CAP,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_primary_ctrl(TEST_FD, TEST_CNTID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_secondary_ctrl_list(void)
+{
+ struct nvme_secondary_ctrl_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = TEST_CNTID << 16
+ | NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_secondary_ctrl_list(TEST_FD, TEST_CNTID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_ns_granularity(void)
+{
+ struct nvme_id_ns_granularity_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_NS_GRANULARITY,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_ns_granularity(TEST_FD, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_uuid(void)
+{
+ struct nvme_id_uuid_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_UUID_LIST,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_uuid(TEST_FD, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_domain_list(void)
+{
+ struct nvme_id_domain_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_DOMAIN_LIST,
+ .cdw11 = TEST_DOMID,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_domain_list(TEST_FD, TEST_DOMID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_endurance_group_list(void)
+{
+ struct nvme_id_endurance_group_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_ENDURANCE_GROUP_ID,
+ .cdw11 = TEST_ENDGID,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_endurance_group_list(TEST_FD, TEST_ENDGID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_allocated_ns_list_csi(void)
+{
+ struct nvme_ns_list expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(expected_id),
+ .cdw10 = NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST,
+ .cdw11 = TEST_CSI << 24,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_allocated_ns_list_csi(
+ TEST_FD, TEST_NSID, TEST_CSI, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+static void test_iocs(void)
+{
+ struct nvme_id_iocs expected_id, id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(expected_id),
+ .cdw10 = TEST_CNTID << 16
+ | NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE,
+ .out_data = &expected_id,
+ };
+ int err;
+
+ arbitrary(&expected_id, sizeof(expected_id));
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_iocs(TEST_FD, TEST_CNTID, &id);
+ end_mock_cmds();
+ check(err == 0, "identify returned error %d, errno %m", err);
+ cmp(&id, &expected_id, sizeof(id), "incorrect identify data");
+}
+
+/*
+ * All identify functions tail-call nvme_identify(),
+ * so testing errors in any of them will do
+ */
+
+static void test_status_code_error(void)
+{
+ struct nvme_id_nvmset_list id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .data_len = sizeof(id),
+ .cdw10 = NVME_IDENTIFY_CNS_NVMSET_LIST,
+ .cdw11 = TEST_NVMSETID,
+ .err = TEST_SC,
+ };
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_nvmset_list(TEST_FD, TEST_NVMSETID, &id);
+ end_mock_cmds();
+ check(err == TEST_SC, "got error %d, expected %d", err, TEST_SC);
+}
+
+static void test_kernel_error(void)
+{
+ struct nvme_id_ns id = {};
+ struct mock_cmd mock_admin_cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = TEST_NSID,
+ .data_len = sizeof(id),
+ .cdw10 = NVME_IDENTIFY_CNS_NS,
+ .err = -EIO,
+ };
+ int err;
+
+ set_mock_admin_cmds(&mock_admin_cmd, 1);
+ err = nvme_identify_ns(TEST_FD, TEST_NSID, &id);
+ end_mock_cmds();
+ check(err == -1, "got error %d, expected -1", err);
+ check(errno == EIO, "unexpected error %m");
+}
+
+static void run_test(const char *test_name, void (*test_fn)(void))
+{
+ printf("Running test %s...", test_name);
+ fflush(stdout);
+ test_fn();
+ puts(" OK");
+}
+
+#define RUN_TEST(name) run_test(#name, test_ ## name)
+
+int main(void)
+{
+ set_mock_fd(TEST_FD);
+ RUN_TEST(ns);
+ RUN_TEST(ctrl);
+ RUN_TEST(active_ns_list);
+ RUN_TEST(ns_descs);
+ RUN_TEST(nvmset_list);
+ RUN_TEST(ns_csi);
+ RUN_TEST(zns_identify_ns);
+ RUN_TEST(nvm_identify_ctrl);
+ RUN_TEST(zns_identify_ctrl);
+ RUN_TEST(active_ns_list_csi);
+ RUN_TEST(independent_identify_ns);
+ RUN_TEST(allocated_ns_list);
+ RUN_TEST(allocated_ns);
+ RUN_TEST(nsid_ctrl_list);
+ RUN_TEST(ctrl_list);
+ RUN_TEST(primary_ctrl);
+ RUN_TEST(secondary_ctrl_list);
+ RUN_TEST(ns_granularity);
+ RUN_TEST(uuid);
+ RUN_TEST(domain_list);
+ RUN_TEST(endurance_group_list);
+ RUN_TEST(allocated_ns_list_csi);
+ RUN_TEST(iocs);
+ RUN_TEST(status_code_error);
+ RUN_TEST(kernel_error);
+}
diff --git a/test/ioctl/meson.build b/test/ioctl/meson.build
new file mode 100644
index 0000000..b329d27
--- /dev/null
+++ b/test/ioctl/meson.build
@@ -0,0 +1,42 @@
+mock_ioctl = library(
+ 'mock-ioctl',
+ ['mock.c', 'util.c'],
+)
+
+# Add mock-ioctl to the LD_PRELOAD path so it overrides libc.
+# Append to LD_PRELOAD so existing libraries, e.g. libasan, are kept.
+# If libasan isn't specified in the LD_PRELOAD path, ASAN warns about mock-ioctl
+# being loaded first because its memory allocations might not get intercepted.
+# But it appears this isn't a problem; ASAN errors in mock-ioctl are reported.
+# This is likely because the executable still links with libasan before libc.
+mock_ioctl_env = environment()
+mock_ioctl_env.append('LD_PRELOAD', mock_ioctl.full_path())
+mock_ioctl_env.set('ASAN_OPTIONS', 'verify_asan_link_order=0')
+
+discovery = executable(
+ 'test-discovery',
+ 'discovery.c',
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir],
+ link_with: mock_ioctl,
+)
+
+test('discovery', discovery, env: mock_ioctl_env)
+
+features = executable(
+ 'test-features',
+ 'features.c',
+ dependencies: libnvme_dep,
+ link_with: mock_ioctl,
+)
+
+test('features', features, env: mock_ioctl_env)
+
+identify = executable(
+ 'test-identify',
+ 'identify.c',
+ dependencies: libnvme_dep,
+ link_with: mock_ioctl,
+)
+
+test('identify', identify, env: mock_ioctl_env)
diff --git a/test/ioctl/mock.c b/test/ioctl/mock.c
new file mode 100644
index 0000000..a97a357
--- /dev/null
+++ b/test/ioctl/mock.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "mock.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "../../src/nvme/ioctl.h"
+#include "util.h"
+
+struct mock_cmds {
+ const char *name;
+ const struct mock_cmd *cmds;
+ size_t remaining_cmds;
+};
+
+static int mock_fd = -1;
+static struct mock_cmds mock_admin_cmds = {.name = "admin"};
+static struct mock_cmds mock_io_cmds = {.name = "IO"};
+
+static void set_mock_cmds(
+ struct mock_cmds *mock_cmds, const struct mock_cmd *cmds, size_t len)
+{
+ mock_cmds->cmds = cmds;
+ mock_cmds->remaining_cmds = len;
+}
+
+static void mock_cmds_done(const struct mock_cmds *mock_cmds)
+{
+ check(!mock_cmds->remaining_cmds,
+ "%zu %s commands not executed",
+ mock_cmds->remaining_cmds, mock_cmds->name);
+}
+
+void set_mock_fd(int fd)
+{
+ mock_fd = fd;
+}
+
+void set_mock_admin_cmds(const struct mock_cmd *cmds, size_t len)
+{
+ set_mock_cmds(&mock_admin_cmds, cmds, len);
+}
+
+void set_mock_io_cmds(const struct mock_cmd *cmds, size_t len)
+{
+ set_mock_cmds(&mock_io_cmds, cmds, len);
+}
+
+void end_mock_cmds(void)
+{
+ mock_cmds_done(&mock_admin_cmds);
+ mock_cmds_done(&mock_io_cmds);
+}
+
+#define execute_ioctl(cmd, mock_cmd) ({ \
+ check((cmd)->opcode == (mock_cmd)->opcode, \
+ "got opcode %" PRIu8 ", expected %" PRIu8, \
+ (cmd)->opcode, (mock_cmd)->opcode); \
+ check((cmd)->flags == (mock_cmd)->flags, \
+ "got flags %" PRIu8 ", expected %" PRIu8, \
+ (cmd)->flags, (mock_cmd)->flags); \
+ check((cmd)->nsid == (mock_cmd)->nsid, \
+ "got nsid %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->nsid, (mock_cmd)->nsid); \
+ check((cmd)->cdw2 == (mock_cmd)->cdw2, \
+ "got cdw2 %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->cdw2, (mock_cmd)->cdw2); \
+ check((cmd)->cdw3 == (mock_cmd)->cdw3, \
+ "got cdw3 %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->cdw3, (mock_cmd)->cdw3); \
+ check((cmd)->metadata_len == (mock_cmd)->metadata_len, \
+ "got metadata_len %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->metadata_len, (mock_cmd)->metadata_len); \
+ if ((cmd)->metadata_len) { \
+ cmp((void const *)(uintptr_t)(cmd)->metadata, \
+ (mock_cmd)->metadata, \
+ (cmd)->metadata_len, \
+ "incorrect metadata"); \
+ } \
+ __u32 data_len = (cmd)->data_len; \
+ check(data_len == (mock_cmd)->data_len, \
+ "got data_len %" PRIu32 ", expected %" PRIu32, \
+ data_len, (mock_cmd)->data_len); \
+ void *data = (void *)(uintptr_t)(cmd)->addr; \
+ if ((mock_cmd)->in_data) { \
+ cmp(data, (mock_cmd)->in_data, data_len, "incorrect data"); \
+ } \
+ check((cmd)->cdw10 == (mock_cmd)->cdw10, \
+ "got cdw10 %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->cdw10, (mock_cmd)->cdw10); \
+ check((cmd)->cdw11 == (mock_cmd)->cdw11, \
+ "got cdw11 %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->cdw11, (mock_cmd)->cdw11); \
+ check((cmd)->cdw12 == (mock_cmd)->cdw12, \
+ "got cdw12 %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->cdw12, (mock_cmd)->cdw12); \
+ check((cmd)->cdw13 == (mock_cmd)->cdw13, \
+ "got cdw13 %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->cdw13, (mock_cmd)->cdw13); \
+ check((cmd)->cdw14 == (mock_cmd)->cdw14, \
+ "got cdw14 %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->cdw14, (mock_cmd)->cdw14); \
+ check((cmd)->cdw15 == (mock_cmd)->cdw15, \
+ "got cdw15 %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->cdw15, (mock_cmd)->cdw15); \
+ check((cmd)->timeout_ms == (mock_cmd)->timeout_ms, \
+ "got timeout_ms %" PRIu32 ", expected %" PRIu32, \
+ (cmd)->timeout_ms, (mock_cmd)->timeout_ms); \
+ (cmd)->result = (mock_cmd)->result; \
+ if ((mock_cmd)->out_data) { \
+ memcpy(data, (mock_cmd)->out_data, data_len); \
+ } \
+})
+
+#ifdef HAVE_GLIBC_IOCTL
+int ioctl(int fd, unsigned long request, ...)
+#else
+int ioctl(int fd, int request, ...)
+#endif
+{
+ struct mock_cmds *mock_cmds;
+ bool result64;
+ const struct mock_cmd *mock_cmd;
+ va_list args;
+ void *cmd;
+
+ check(fd == mock_fd, "got fd %d, expected %d", fd, mock_fd);
+ switch (request) {
+ case NVME_IOCTL_ADMIN_CMD:
+ mock_cmds = &mock_admin_cmds;
+ result64 = false;
+ break;
+ case NVME_IOCTL_ADMIN64_CMD:
+ mock_cmds = &mock_admin_cmds;
+ result64 = true;
+ break;
+ case NVME_IOCTL_IO_CMD:
+ mock_cmds = &mock_io_cmds;
+ result64 = false;
+ break;
+ case NVME_IOCTL_IO64_CMD:
+ mock_cmds = &mock_io_cmds;
+ result64 = true;
+ break;
+ default:
+ fail("unexpected %s %lu", __func__, (unsigned long) request);
+ }
+ check(mock_cmds->remaining_cmds,
+ "unexpected %s command", mock_cmds->name);
+ mock_cmd = mock_cmds->cmds++;
+ mock_cmds->remaining_cmds--;
+
+ va_start(args, request);
+ cmd = va_arg(args, void *);
+ va_end(args);
+ if (result64) {
+ execute_ioctl((struct nvme_passthru_cmd64 *)cmd, mock_cmd);
+ } else {
+ check((uint32_t)mock_cmd->result == mock_cmd->result,
+ "expected 64-bit %s for result %" PRIu64,
+ __func__, mock_cmd->result);
+ execute_ioctl((struct nvme_passthru_cmd *)cmd, mock_cmd);
+ }
+ if (mock_cmd->err < 0) {
+ errno = -mock_cmd->err;
+ return -1;
+ }
+
+ return mock_cmd->err;
+}
diff --git a/test/ioctl/mock.h b/test/ioctl/mock.h
new file mode 100644
index 0000000..192eba8
--- /dev/null
+++ b/test/ioctl/mock.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifndef _LIBNVME_TEST_IOCTL_MOCK_H
+#define _LIBNVME_TEST_IOCTL_MOCK_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * struct mock_cmd - a mock NVMe passthru ioctl() invocation
+ * @opcode: the expected `opcode` passed to ioctl()
+ * @flags: the expected `flags` passed to ioctl()
+ * @nsid: the expected `nsid` passed to ioctl()
+ * @cdw2: the expected `cdw2` passed to ioctl()
+ * @cdw3: the expected `cdw3` passed to ioctl()
+ * @metadata: the expected `metadata` of length `metadata_len` passed to ioctl()
+ * @in_data: the expected `addr` of length `data_len` passed to ioctl().
+ * Set this to NULL to skip checking the data,
+ * for example if the command is in the read direction.
+ * @metadata_len: the expected `metadata_len` passed to ioctl()
+ * @data_len: the expected `data_len` passed to ioctl()
+ * @cdw10: the expected `cdw10` passed to ioctl()
+ * @cdw11: the expected `cdw11` passed to ioctl()
+ * @cdw12: the expected `cdw12` passed to ioctl()
+ * @cdw13: the expected `cdw13` passed to ioctl()
+ * @cdw14: the expected `cdw14` passed to ioctl()
+ * @cdw15: the expected `cdw15` passed to ioctl()
+ * @timeout_ms: the expected `timeout_ms` passed to ioctl()
+ * @out_data: if not NULL, `data_len` bytes to copy to the caller's `addr`
+ * @result: copied to the caller's `result`.
+ * If `result` doesn't fit in a u32, the ioctl() must be the 64-bit one.
+ * @err: If negative, ioctl() returns -1 and sets `errno` to `-err`.
+ * Otherwise, ioctl() returns `err`, representing a NVMe status code.
+ */
+struct mock_cmd {
+ uint8_t opcode;
+ uint8_t flags;
+ uint32_t nsid;
+ uint32_t cdw2;
+ uint32_t cdw3;
+ const void *metadata;
+ const void *in_data;
+ 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;
+ uint32_t timeout_ms;
+ const void *out_data;
+ uint64_t result;
+ int err;
+};
+
+/**
+ * set_mock_fd() - sets the expected file descriptor for NVMe passthru ioctls()
+ * @fd: file descriptor expected to be passed to ioctl()
+ */
+void set_mock_fd(int fd);
+
+/**
+ * set_mock_admin_cmds() - mocks NVMe admin passthru ioctl() invocations
+ * @cmds: pointer to start of the mock_cmd slice
+ * @len: length of the mock_cmd slice (number of ioctl() invocations)
+ *
+ * Provides a sequence of mocks for NVMe admin passthru ioctl() invocations.
+ * Each ioctl() consumes the next mock from the sequence.
+ * Its arguments are checked against the mock's expected arguments,
+ * aborting the process if unexpected arguments are passed.
+ * The mock results (return value, NVMe result and data)
+ * are returned from the ioctl().
+ *
+ * Analogous to set_mock_io_cmds(), but for admin commands.
+ * Both admin and IO mocks can be active at the same time.
+ */
+void set_mock_admin_cmds(const struct mock_cmd *cmds, size_t len);
+
+/**
+ * set_mock_io_cmds() - mocks NVMe IO passthru ioctl() invocations
+ * @cmds: pointer to start of the mock_cmd slice
+ * @len: length of the mock_cmd slice (number of ioctl() invocations)
+ *
+ * Provides a sequence of mocks for NVMe IO passthru ioctl() invocations.
+ * Each ioctl() consumes the next mock from the sequence.
+ * Its arguments are checked against the mock's expected arguments,
+ * aborting the process if unexpected arguments are passed.
+ * The mock results (return value, NVMe result and data)
+ * are returned from the ioctl().
+ *
+ * Analogous to set_mock_admin_cmds(), but for IO commands.
+ * Both admin and IO mocks can be active at the same time.
+ */
+void set_mock_io_cmds(const struct mock_cmd *cmds, size_t len);
+
+/**
+ * end_mock_cmds() - finishes mocking NVMe passthru ioctl() invocations
+ *
+ * Checks that all mock ioctl() invocations were performed.
+ */
+void end_mock_cmds(void);
+
+#endif /* #ifndef _LIBNVME_TEST_IOCTL_MOCK_H */
diff --git a/test/ioctl/util.c b/test/ioctl/util.c
new file mode 100644
index 0000000..09c6e7f
--- /dev/null
+++ b/test/ioctl/util.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "util.h"
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void hexdump(const uint8_t *buf, size_t len)
+{
+ size_t i = 0;
+
+ if (!len)
+ return;
+
+ for (;;) {
+ fprintf(stderr, "%02X", buf[i++]);
+ if (i >= len)
+ break;
+
+ fputc(i % 16 > 0 ? ' ' : '\n', stderr);
+ }
+ fputc('\n', stderr);
+}
+
+void fail(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputc('\n', stderr);
+ abort();
+}
+
+void cmp(const void *actual, const void *expected, size_t len, const char *msg)
+{
+ if (memcmp(actual, expected, len) == 0)
+ return;
+
+ fputs(msg, stderr);
+ fputs("\nactual:\n", stderr);
+ hexdump(actual, len);
+ fputs("expected:\n", stderr);
+ hexdump(expected, len);
+ abort();
+}
+
+void arbitrary(void *buf_, size_t len)
+{
+ uint8_t *buf = buf_;
+
+ while (len--)
+ *(buf++) = rand();
+}
+
+size_t arbitrary_range(size_t max)
+{
+ size_t value;
+ arbitrary(&value, sizeof(value));
+ return value % max;
+}
diff --git a/test/ioctl/util.h b/test/ioctl/util.h
new file mode 100644
index 0000000..aa86422
--- /dev/null
+++ b/test/ioctl/util.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifndef _LIBNVME_TEST_IOCTL_UTIL_H
+#define _LIBNVME_TEST_IOCTL_UTIL_H
+
+#include <stddef.h>
+#include <stdnoreturn.h>
+
+noreturn void fail(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
+#define check(condition, fmt...) ((condition) || (fail(fmt), 0))
+
+void cmp(const void *actual, const void *expected, size_t len, const char *msg);
+
+void arbitrary(void *buf, size_t len);
+
+size_t arbitrary_range(size_t max);
+
+#endif /* #ifndef _LIBNVME_TEST_IOCTL_UTIL_H */
diff --git a/test/meson.build b/test/meson.build
new file mode 100644
index 0000000..93e6999
--- /dev/null
+++ b/test/meson.build
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2021 Dell Inc.
+#
+# Authors: Martin Belanger <Martin.Belanger@dell.com>
+
+# These tests all require interaction with a real NVMe device, so we don't
+# define as meson unit-tests, and therefore get run as part of the 'test'
+# target. However, they're available for developer use, when hardware is
+# available.
+main = executable(
+ 'main-test',
+ ['test.c'],
+ dependencies: [libnvme_dep],
+ include_directories: [incdir, internal_incdir]
+)
+
+if cxx_available
+ cpp = executable(
+ 'test-cpp',
+ ['cpp.cc'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+ )
+endif
+
+register = executable(
+ 'test-register',
+ ['register.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+zns = executable(
+ 'test-zns',
+ ['zns.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+mi = executable(
+ 'test-mi',
+ ['mi.c', 'utils.c'],
+ dependencies: libnvme_mi_test_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+test('mi', mi)
+
+mi_mctp = executable(
+ 'test-mi-mctp',
+ ['mi-mctp.c', 'utils.c'],
+ dependencies: libnvme_mi_test_dep,
+ include_directories: [incdir, internal_incdir],
+)
+
+test('mi-mctp', mi_mctp)
+
+uuid = executable(
+ 'test-uuid',
+ ['uuid.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+test('uuid', uuid)
+
+if conf.get('HAVE_NETDB')
+ mock_ifaddrs = library(
+ 'mock-ifaddrs',
+ ['mock-ifaddrs.c', ],
+ )
+
+ # See comment in test/ioctl/meson.build explaining how LD_PRELOAD is used
+ mock_ifaddrs_env = environment()
+ mock_ifaddrs_env.append('LD_PRELOAD', mock_ifaddrs.full_path())
+ mock_ifaddrs_env.set('ASAN_OPTIONS', 'verify_asan_link_order=0')
+
+ tree = executable(
+ 'tree',
+ ['tree.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir],
+ link_with: mock_ifaddrs,
+ )
+
+ test('tree', tree, env: mock_ifaddrs_env)
+
+ test_util = executable(
+ 'test-util',
+ ['test-util.c'],
+ include_directories: [incdir, internal_incdir]
+ )
+ test('util', test_util)
+endif
+
+subdir('ioctl')
+subdir('nbft')
+
+if json_c_dep.found()
+ subdir('sysfs')
+endif
diff --git a/test/mi-mctp.c b/test/mi-mctp.c
new file mode 100644
index 0000000..5711c03
--- /dev/null
+++ b/test/mi-mctp.c
@@ -0,0 +1,780 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2022 Code Construct
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+
+#include <ccan/array_size/array_size.h>
+#include <ccan/endian/endian.h>
+
+#include "libnvme-mi.h"
+#include "nvme/private.h"
+#include "utils.h"
+
+/* 4096 byte max MCTP message, plus space for header data */
+#define MAX_BUFSIZ 8192
+
+struct test_peer;
+
+typedef int (*rx_test_fn)(struct test_peer *peer, void *buf, size_t len);
+typedef int (*poll_test_fn)(struct test_peer *peer,
+ struct pollfd *fds, nfds_t nfds, int timeout);
+
+/* Our fake MCTP "peer".
+ *
+ * The terms TX (transmit) and RX (receive) are from the perspective of
+ * the NVMe device. TX is device-to-libnvme, RX is libnvme-to-device.
+ *
+ * The RX and TX buffers are linear versions of the data sent and received by
+ * libnvme-mi, and *include* the MCTP message type byte (even though it's
+ * omitted in the sendmsg/recvmsg interface), so that the buffer inspection
+ * in the tests can exactly match the NVMe-MI spec packet diagrams.
+ */
+static struct test_peer {
+ /* rx (sendmsg) data sent from libnvme, and return value */
+ unsigned char rx_buf[MAX_BUFSIZ];
+ size_t rx_buf_len;
+ ssize_t rx_rc; /* if zero, return the sendmsg len */
+ int rx_errno;
+
+ /* tx (recvmsg) data to be received by libnvme and return value */
+ unsigned char tx_buf[MAX_BUFSIZ];
+ size_t tx_buf_len;
+ ssize_t tx_rc; /* if zero, return the recvmsg len */
+ int tx_errno;
+
+ /* Optional, called before TX, may set tx_buf according to request.
+ * Return value stored in tx_res, may be used by test */
+ rx_test_fn tx_fn;
+ void *tx_data;
+ int tx_fn_res;
+
+ poll_test_fn poll_fn;
+ void *poll_data;
+
+ /* store sd from socket() setup */
+ int sd;
+} test_peer;
+
+/* ensure tests start from a standard state */
+void reset_test_peer(void)
+{
+ int tmp = test_peer.sd;
+ memset(&test_peer, 0, sizeof(test_peer));
+ test_peer.tx_buf[0] = NVME_MI_MSGTYPE_NVME;
+ test_peer.rx_buf[0] = NVME_MI_MSGTYPE_NVME;
+ test_peer.sd = tmp;
+}
+
+/* calculate MIC of peer-to-libnvme data, expand buf by 4 bytes and insert
+ * the new MIC */
+static void test_set_tx_mic(struct test_peer *peer)
+{
+ extern __u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len);
+ __u32 crc = 0xffffffff;
+ __le32 crc_le;
+
+ assert(peer->tx_buf_len + sizeof(crc_le) <= MAX_BUFSIZ);
+
+ crc = nvme_mi_crc32_update(crc, peer->tx_buf, peer->tx_buf_len);
+ crc_le = cpu_to_le32(~crc);
+ memcpy(peer->tx_buf + peer->tx_buf_len, &crc_le, sizeof(crc_le));
+ peer->tx_buf_len += sizeof(crc_le);
+}
+
+int __wrap_socket(int family, int type, int protocol)
+{
+ /* we do an open here to give the mi-mctp code something to close() */
+ test_peer.sd = open("/dev/null", 0);
+ return test_peer.sd;
+}
+
+ssize_t __wrap_sendmsg(int sd, const struct msghdr *hdr, int flags)
+{
+ size_t i, pos;
+
+ assert(sd == test_peer.sd);
+
+ test_peer.rx_buf[0] = NVME_MI_MSGTYPE_NVME;
+
+ /* gather iovec into buf */
+ for (i = 0, pos = 1; i < hdr->msg_iovlen; i++) {
+ struct iovec *iov = &hdr->msg_iov[i];
+
+ assert(pos + iov->iov_len < MAX_BUFSIZ - 1);
+ memcpy(test_peer.rx_buf + pos, iov->iov_base, iov->iov_len);
+ pos += iov->iov_len;
+ }
+
+ test_peer.rx_buf_len = pos;
+
+ errno = test_peer.rx_errno;
+
+ return test_peer.rx_rc ?: (pos - 1);
+}
+
+ssize_t __wrap_recvmsg(int sd, struct msghdr *hdr, int flags)
+{
+ size_t i, pos, len;
+
+ assert(sd == test_peer.sd);
+
+ if (test_peer.tx_fn) {
+ test_peer.tx_fn_res = test_peer.tx_fn(&test_peer,
+ test_peer.rx_buf,
+ test_peer.rx_buf_len);
+ } else {
+ /* set up a few default response fields; caller may have
+ * initialised the rest of the response */
+ test_peer.tx_buf[0] = NVME_MI_MSGTYPE_NVME;
+ test_peer.tx_buf[1] = test_peer.rx_buf[1] | (NVME_MI_ROR_RSP << 7);
+ test_set_tx_mic(&test_peer);
+ }
+
+ /* scatter buf into iovec */
+ for (i = 0, pos = 1; i < hdr->msg_iovlen && pos < test_peer.tx_buf_len;
+ i++) {
+ struct iovec *iov = &hdr->msg_iov[i];
+
+ len = iov->iov_len;
+ if (len > test_peer.tx_buf_len - pos)
+ len = test_peer.tx_buf_len - pos;
+
+ memcpy(iov->iov_base, test_peer.tx_buf + pos, len);
+ pos += len;
+ }
+
+ errno = test_peer.tx_errno;
+
+ return test_peer.tx_rc ?: (pos - 1);
+}
+
+int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ if (!test_peer.poll_fn)
+ return 1;
+
+ return test_peer.poll_fn(&test_peer, fds, nfds, timeout);
+}
+
+struct mctp_ioc_tag_ctl;
+
+#ifdef SIOCMCTPALLOCTAG
+int test_ioctl_tag(int sd, unsigned long req, struct mctp_ioc_tag_ctl *ctl)
+{
+ assert(sd == test_peer.sd);
+
+ switch (req) {
+ case SIOCMCTPALLOCTAG:
+ ctl->tag = 1 | MCTP_TAG_PREALLOC | MCTP_TAG_OWNER;
+ break;
+ case SIOCMCTPDROPTAG:
+ assert(tag == 1 | MCTP_TAG_PREALLOC | MCTP_TAG_OWNER);
+ break;
+ };
+
+ return 0;
+}
+#else
+int test_ioctl_tag(int sd, unsigned long req, struct mctp_ioc_tag_ctl *ctl)
+{
+ assert(sd == test_peer.sd);
+ return 0;
+}
+#endif
+
+static struct __mi_mctp_socket_ops ops = {
+ __wrap_socket,
+ __wrap_sendmsg,
+ __wrap_recvmsg,
+ __wrap_poll,
+ test_ioctl_tag,
+};
+
+/* tests */
+static void test_rx_err(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ peer->rx_rc = -1;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+static int tx_none(struct test_peer *peer, void *buf, size_t len)
+{
+ return 0;
+}
+
+static void test_tx_none(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ peer->tx_buf_len = 0;
+ peer->tx_fn = tx_none;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+static void test_tx_err(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ peer->tx_rc = -1;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+static void test_tx_short(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ peer->tx_buf_len = 11;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+static int poll_fn_err(struct test_peer *peer, struct pollfd *fds,
+ nfds_t nfds, int timeout)
+{
+ return -1;
+}
+
+static void test_poll_err(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ peer->poll_fn = poll_fn_err;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+static void test_read_mi_data(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ /* empty response data */
+ peer->tx_buf_len = 8 + 32;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc == 0);
+}
+
+static void test_mi_resp_err(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ /* simple error response */
+ peer->tx_buf[4] = 0x02; /* internal error */
+ peer->tx_buf_len = 8;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc == 0x2);
+}
+
+static void setup_unaligned_ctrl_list_resp(struct test_peer *peer)
+{
+ /* even number of controllers */
+ peer->tx_buf[8] = 0x02;
+ peer->tx_buf[9] = 0x00;
+
+ /* controller ID 1 */
+ peer->tx_buf[10] = 0x01;
+ peer->tx_buf[11] = 0x00;
+
+ /* controller ID 2 */
+ peer->tx_buf[12] = 0x02;
+ peer->tx_buf[13] = 0x00;
+
+ peer->tx_buf_len = 14;
+}
+
+/* Will call through the xfer/submit API expecting a full-sized list (so
+ * resp->data_len is set to sizeof(list)), but the endpoint will return an
+ * unaligned short list.
+ */
+static void test_mi_resp_unaligned(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_ctrl_list list;
+ int rc;
+
+ setup_unaligned_ctrl_list_resp(peer);
+
+ memset(&list, 0, sizeof(list));
+
+ rc = nvme_mi_mi_read_mi_data_ctrl_list(ep, 0, &list);
+ assert(rc == 0);
+
+ assert(le16_to_cpu(list.num) == 2);
+ assert(le16_to_cpu(list.identifier[0]) == 1);
+ assert(le16_to_cpu(list.identifier[1]) == 2);
+}
+
+/* Will call through the xfer/submit API expecting an unaligned list,
+ * and get a response of exactly that size.
+ */
+static void test_mi_resp_unaligned_expected(nvme_mi_ep_t ep,
+ struct test_peer *peer)
+{
+ /* direct access to the raw submit() API */
+ extern int nvme_mi_submit(nvme_mi_ep_t ep, struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp);
+ struct nvme_mi_mi_resp_hdr resp_hdr;
+ struct nvme_mi_mi_req_hdr req_hdr;
+ struct nvme_ctrl_list list;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc;
+
+ setup_unaligned_ctrl_list_resp(peer);
+
+ memset(&list, 0, sizeof(list));
+
+ memset(&req_hdr, 0, sizeof(req_hdr));
+ req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME;
+ req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3);
+ req_hdr.opcode = nvme_mi_mi_opcode_mi_data_read;
+ req_hdr.cdw0 = cpu_to_le32(nvme_mi_dtyp_ctrl_list << 24);
+
+ memset(&req, 0, sizeof(req));
+ req.hdr = &req_hdr.hdr;
+ req.hdr_len = sizeof(req_hdr);
+
+ memset(&resp, 0, sizeof(resp));
+ resp.hdr = &resp_hdr.hdr;
+ resp.hdr_len = sizeof(resp_hdr);
+ resp.data = &list;
+ resp.data_len = peer->tx_buf_len;
+
+ rc = nvme_mi_submit(ep, &req, &resp);
+ assert(rc == 0);
+ assert(resp.data_len == 6); /* 2-byte length, 2*2 byte controller IDs */
+
+ assert(le16_to_cpu(list.num) == 2);
+ assert(le16_to_cpu(list.identifier[0]) == 1);
+ assert(le16_to_cpu(list.identifier[1]) == 2);
+}
+
+static void test_admin_resp_err(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_id_ctrl id;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ ctrl = nvme_mi_init_ctrl(ep, 1);
+ assert(ctrl);
+
+ /* Simple error response, will be shorter than the expected Admin
+ * command response header. */
+ peer->tx_buf[4] = 0x02; /* internal error */
+ peer->tx_buf_len = 8;
+
+ rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
+ assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI);
+ assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR);
+}
+
+/* test: all 4-byte aligned response sizes - should be decoded into the
+ * response status value. We use an admin command here as the header size will
+ * be larger than the minimum header size (it contains the completion
+ * doublewords), and we need to ensure that an error response is correctly
+ * interpreted, including having the MIC extracted from the message.
+ */
+static void test_admin_resp_sizes(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_id_ctrl id;
+ nvme_mi_ctrl_t ctrl;
+ unsigned int i;
+ int rc;
+
+ ctrl = nvme_mi_init_ctrl(ep, 1);
+ assert(ctrl);
+
+ peer->tx_buf[4] = 0x02; /* internal error */
+
+ for (i = 8; i <= 4096 + 8; i+=4) {
+ peer->tx_buf_len = i;
+ rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
+ assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI);
+ assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR);
+ }
+
+ nvme_mi_close_ctrl(ctrl);
+}
+
+/* test: timeout value passed to poll */
+static int poll_fn_timeout_value(struct test_peer *peer, struct pollfd *fds,
+ nfds_t nfds, int timeout)
+{
+ assert(timeout == 3141);
+ return 1;
+}
+
+static void test_poll_timeout_value(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ /* empty response data */
+ peer->tx_buf_len = 8 + 32;
+
+ peer->poll_fn = poll_fn_timeout_value;
+ nvme_mi_ep_set_timeout(ep, 3141);
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc == 0);
+}
+
+/* test: poll timeout expiry */
+static int poll_fn_timeout(struct test_peer *peer, struct pollfd *fds,
+ nfds_t nfds, int timeout)
+{
+ return 0;
+}
+
+static void test_poll_timeout(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ peer->poll_fn = poll_fn_timeout;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+ assert(errno == ETIMEDOUT);
+}
+
+/* test: send a More Processing Required response, then the actual response */
+struct mpr_tx_info {
+ int msg_no;
+ bool admin_quirk;
+ size_t final_len;
+};
+
+static int tx_mpr(struct test_peer *peer, void *buf, size_t len)
+{
+ struct mpr_tx_info *tx_info = peer->tx_data;
+
+ memset(peer->tx_buf, 0, sizeof(peer->tx_buf));
+ peer->tx_buf[0] = NVME_MI_MSGTYPE_NVME;
+ peer->tx_buf[1] = test_peer.rx_buf[1] | (NVME_MI_ROR_RSP << 7);
+
+ switch (tx_info->msg_no) {
+ case 1:
+ peer->tx_buf[4] = NVME_MI_RESP_MPR;
+ peer->tx_buf_len = 8;
+ if (tx_info->admin_quirk) {
+ peer->tx_buf_len = 20;
+ }
+ break;
+ case 2:
+ peer->tx_buf[4] = NVME_MI_RESP_SUCCESS;
+ peer->tx_buf_len = tx_info->final_len;
+ break;
+ default:
+ assert(0);
+ }
+
+ test_set_tx_mic(peer);
+
+ tx_info->msg_no++;
+
+ return 0;
+}
+
+static void test_mpr_mi(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ struct mpr_tx_info tx_info;
+ int rc;
+
+ tx_info.msg_no = 1;
+ tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
+ tx_info.admin_quirk = false;
+
+ peer->tx_fn = tx_mpr;
+ peer->tx_data = &tx_info;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc == 0);
+}
+
+static void test_mpr_admin(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct mpr_tx_info tx_info;
+ struct nvme_id_ctrl id;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ tx_info.msg_no = 1;
+ tx_info.final_len = sizeof(struct nvme_mi_admin_resp_hdr) + sizeof(id);
+ tx_info.admin_quirk = false;
+
+ peer->tx_fn = tx_mpr;
+ peer->tx_data = &tx_info;
+
+ ctrl = nvme_mi_init_ctrl(ep, 1);
+
+ rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
+ assert(rc == 0);
+
+ nvme_mi_close_ctrl(ctrl);
+}
+
+/* We have seen drives that send a MPR response as a full Admin message,
+ * rather than a MI message; these have a larger message body
+ */
+static void test_mpr_admin_quirked(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct mpr_tx_info tx_info;
+ struct nvme_id_ctrl id;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ tx_info.msg_no = 1;
+ tx_info.final_len = sizeof(struct nvme_mi_admin_resp_hdr) + sizeof(id);
+ tx_info.admin_quirk = true;
+
+ peer->tx_fn = tx_mpr;
+ peer->tx_data = &tx_info;
+
+ ctrl = nvme_mi_init_ctrl(ep, 1);
+
+ rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
+ assert(rc == 0);
+
+ nvme_mi_close_ctrl(ctrl);
+}
+
+/* helpers for the MPR + poll tests */
+struct mpr_poll_info {
+ int poll_no;
+ uint16_t mprt;
+ unsigned int timeouts[2];
+};
+
+static int poll_fn_mpr_poll(struct test_peer *peer, struct pollfd *fds,
+ nfds_t nfds, int timeout)
+{
+ struct mpr_poll_info *info = peer->poll_data;
+
+ switch (info->poll_no) {
+ case 1:
+ case 2:
+ assert(timeout == info->timeouts[info->poll_no - 1]);
+ break;
+ default:
+ assert(0);
+ }
+
+ info->poll_no++;
+ return 1;
+}
+
+static int tx_fn_mpr_poll(struct test_peer *peer, void *buf, size_t len)
+{
+ struct mpr_tx_info *tx_info = peer->tx_data;
+ struct mpr_poll_info *poll_info = peer->poll_data;
+ unsigned int mprt;
+
+ memset(peer->tx_buf, 0, sizeof(peer->tx_buf));
+ peer->tx_buf[0] = NVME_MI_MSGTYPE_NVME;
+ peer->tx_buf[1] = test_peer.rx_buf[1] | (NVME_MI_ROR_RSP << 7);
+
+ switch (tx_info->msg_no) {
+ case 1:
+ peer->tx_buf[4] = NVME_MI_RESP_MPR;
+ peer->tx_buf_len = 8;
+ mprt = poll_info->mprt;
+ peer->tx_buf[7] = mprt >> 8;
+ peer->tx_buf[6] = mprt & 0xff;
+ break;
+ case 2:
+ peer->tx_buf[4] = NVME_MI_RESP_SUCCESS;
+ peer->tx_buf_len = tx_info->final_len;
+ break;
+ default:
+ assert(0);
+ }
+
+ test_set_tx_mic(peer);
+
+ tx_info->msg_no++;
+
+ return 0;
+}
+
+/* test: correct timeout value used from MPR response */
+static void test_mpr_timeouts(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ struct mpr_poll_info poll_info;
+ struct mpr_tx_info tx_info;
+ int rc;
+
+ nvme_mi_ep_set_timeout(ep, 3141);
+
+ tx_info.msg_no = 1;
+ tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
+
+ poll_info.poll_no = 1;
+ poll_info.mprt = 1234;
+ poll_info.timeouts[0] = 3141;
+ poll_info.timeouts[1] = 1234 * 100;
+
+ peer->tx_fn = tx_fn_mpr_poll;
+ peer->tx_data = &tx_info;
+
+ peer->poll_fn = poll_fn_mpr_poll;
+ peer->poll_data = &poll_info;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc == 0);
+}
+
+/* test: MPR value is limited to the max mpr */
+static void test_mpr_timeout_clamp(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ struct mpr_poll_info poll_info;
+ struct mpr_tx_info tx_info;
+ int rc;
+
+ nvme_mi_ep_set_timeout(ep, 3141);
+ nvme_mi_ep_set_mprt_max(ep, 123400);
+
+ tx_info.msg_no = 1;
+ tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
+
+ poll_info.poll_no = 1;
+ poll_info.mprt = 1235;
+ poll_info.timeouts[0] = 3141;
+ poll_info.timeouts[1] = 1234 * 100;
+
+ peer->tx_fn = tx_fn_mpr_poll;
+ peer->tx_data = &tx_info;
+
+ peer->poll_fn = poll_fn_mpr_poll;
+ peer->poll_data = &poll_info;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc == 0);
+}
+
+/* test: MPR value of zero doesn't result in poll with zero timeout */
+static void test_mpr_mprt_zero(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ struct mpr_poll_info poll_info;
+ struct mpr_tx_info tx_info;
+ int rc;
+
+ nvme_mi_ep_set_timeout(ep, 3141);
+ nvme_mi_ep_set_mprt_max(ep, 123400);
+
+ tx_info.msg_no = 1;
+ tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
+
+ poll_info.poll_no = 1;
+ poll_info.mprt = 0;
+ poll_info.timeouts[0] = 3141;
+ poll_info.timeouts[1] = 3141;
+
+ peer->tx_fn = tx_fn_mpr_poll;
+ peer->tx_data = &tx_info;
+
+ peer->poll_fn = poll_fn_mpr_poll;
+ peer->poll_data = &poll_info;
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc == 0);
+}
+
+#define DEFINE_TEST(name) { #name, test_ ## name }
+struct test {
+ const char *name;
+ void (*fn)(nvme_mi_ep_t, struct test_peer *);
+} tests[] = {
+ DEFINE_TEST(rx_err),
+ DEFINE_TEST(tx_none),
+ DEFINE_TEST(tx_err),
+ DEFINE_TEST(tx_short),
+ DEFINE_TEST(read_mi_data),
+ DEFINE_TEST(poll_err),
+ DEFINE_TEST(mi_resp_err),
+ DEFINE_TEST(mi_resp_unaligned),
+ DEFINE_TEST(mi_resp_unaligned_expected),
+ DEFINE_TEST(admin_resp_err),
+ DEFINE_TEST(admin_resp_sizes),
+ DEFINE_TEST(poll_timeout_value),
+ DEFINE_TEST(poll_timeout),
+ DEFINE_TEST(mpr_mi),
+ DEFINE_TEST(mpr_admin),
+ DEFINE_TEST(mpr_admin_quirked),
+ DEFINE_TEST(mpr_timeouts),
+ DEFINE_TEST(mpr_timeout_clamp),
+ DEFINE_TEST(mpr_mprt_zero),
+};
+
+static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep,
+ struct test_peer *peer)
+{
+ printf("Running test %s...", test->name);
+ fflush(stdout);
+ test->fn(ep, peer);
+ printf(" OK\n");
+ test_print_log_buf(logfd);
+}
+
+int main(void)
+{
+ nvme_root_t root;
+ nvme_mi_ep_t ep;
+ unsigned int i;
+ FILE *fd;
+
+ fd = test_setup_log();
+
+ __nvme_mi_mctp_set_ops(&ops);
+
+ root = nvme_mi_create_root(fd, DEFAULT_LOGLEVEL);
+ assert(root);
+
+ ep = nvme_mi_open_mctp(root, 0, 0);
+ assert(ep);
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ reset_test_peer();
+ run_test(&tests[i], fd, ep, &test_peer);
+ }
+
+ nvme_mi_close(ep);
+ nvme_mi_free_root(root);
+
+ test_close_log(fd);
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/mi.c b/test/mi.c
new file mode 100644
index 0000000..a86ba15
--- /dev/null
+++ b/test/mi.c
@@ -0,0 +1,1937 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2022 Code Construct
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <ccan/array_size/array_size.h>
+#include <ccan/endian/endian.h>
+
+/* we define a custom transport, so need the internal headers */
+#include "nvme/private.h"
+
+#include "libnvme-mi.h"
+
+#include "utils.h"
+
+typedef int (*test_submit_cb)(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data);
+
+struct test_transport_data {
+ unsigned int magic;
+ bool named;
+ test_submit_cb submit_cb;
+ void *submit_cb_data;
+};
+
+static const int test_transport_magic = 0x74657374;
+
+static int test_transport_submit(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp)
+{
+ struct test_transport_data *tpd = ep->transport_data;
+
+ assert(tpd->magic == test_transport_magic);
+
+ /* start from a minimal response: zeroed data, nmp to match request */
+ memset(resp->hdr, 0, resp->hdr_len);
+ if (resp->data_len)
+ memset(resp->data, 0, resp->data_len);
+ resp->hdr->type = NVME_MI_MSGTYPE_NVME;
+ resp->hdr->nmp = req->hdr->nmp | (NVME_MI_ROR_RSP << 7);
+
+ if (tpd->submit_cb)
+ return tpd->submit_cb(ep, req, resp, tpd->submit_cb_data);
+
+ return 0;
+}
+
+static void test_transport_close(struct nvme_mi_ep *ep)
+{
+ struct test_transport_data *tpd = ep->transport_data;
+ assert(tpd->magic == test_transport_magic);
+ free(tpd);
+}
+
+static int test_transport_desc_ep(struct nvme_mi_ep *ep,
+ char *buf, size_t len)
+{
+ struct test_transport_data *tpd = ep->transport_data;
+
+ assert(tpd->magic == test_transport_magic);
+
+ if (!tpd->named)
+ return -1;
+
+ snprintf(buf, len, "test endpoint 0x%x", tpd->magic);
+
+ return 0;
+}
+
+/* internal test helper to generate correct response crc */
+static void test_transport_resp_calc_mic(struct nvme_mi_resp *resp)
+{
+ extern __u32 nvme_mi_crc32_update(__u32 crc, void *data, size_t len);
+ __u32 crc = 0xffffffff;
+
+ crc = nvme_mi_crc32_update(crc, resp->hdr, resp->hdr_len);
+ crc = nvme_mi_crc32_update(crc, resp->data, resp->data_len);
+
+ resp->mic = ~crc;
+}
+
+static const struct nvme_mi_transport test_transport = {
+ .name = "test-mi",
+ .mic_enabled = true,
+ .submit = test_transport_submit,
+ .close = test_transport_close,
+ .desc_ep = test_transport_desc_ep,
+};
+
+static void test_set_transport_callback(nvme_mi_ep_t ep, test_submit_cb cb,
+ void *data)
+{
+ struct test_transport_data *tpd = ep->transport_data;
+ assert(tpd->magic == test_transport_magic);
+
+ tpd->submit_cb = cb;
+ tpd->submit_cb_data = data;
+}
+
+nvme_mi_ep_t nvme_mi_open_test(nvme_root_t root)
+{
+ struct test_transport_data *tpd;
+ struct nvme_mi_ep *ep;
+
+ ep = nvme_mi_init_ep(root);
+ assert(ep);
+
+ tpd = malloc(sizeof(*tpd));
+ assert(tpd);
+
+ tpd->magic = test_transport_magic;
+ tpd->named = true;
+
+ ep->transport = &test_transport;
+ ep->transport_data = tpd;
+
+ return ep;
+}
+
+unsigned int count_root_eps(nvme_root_t root)
+{
+ unsigned int i = 0;
+ nvme_mi_ep_t ep;
+
+ nvme_mi_for_each_endpoint(root, ep)
+ i++;
+
+ return i;
+}
+
+/* test that the root->endpoints list is updated on endpoint
+ * creation/destruction */
+static void test_endpoint_lifetime(nvme_mi_ep_t ep)
+{
+ nvme_root_t root = ep->root;
+ unsigned int count;
+ nvme_mi_ep_t ep2;
+
+ count = count_root_eps(root);
+ assert(count == 1);
+
+ ep2 = nvme_mi_open_test(root);
+ count = count_root_eps(root);
+ assert(count == 2);
+
+ nvme_mi_close(ep2);
+ count = count_root_eps(root);
+ assert(count == 1);
+}
+
+unsigned int count_ep_controllers(nvme_mi_ep_t ep)
+{
+ unsigned int i = 0;
+ nvme_mi_ctrl_t ctrl;
+
+ nvme_mi_for_each_ctrl(ep, ctrl)
+ i++;
+
+ return i;
+}
+
+/* test that the ep->controllers list is updated on controller
+ * creation/destruction */
+static void test_ctrl_lifetime(nvme_mi_ep_t ep)
+{
+ nvme_mi_ctrl_t c1, c2;
+ int count;
+
+ ep->controllers_scanned = true;
+
+ count = count_ep_controllers(ep);
+ assert(count == 0);
+
+ c1 = nvme_mi_init_ctrl(ep, 1);
+ count = count_ep_controllers(ep);
+ assert(count == 1);
+
+ c2 = nvme_mi_init_ctrl(ep, 2);
+ count = count_ep_controllers(ep);
+ assert(count == 2);
+
+ nvme_mi_close_ctrl(c1);
+ count = count_ep_controllers(ep);
+ assert(count == 1);
+
+ nvme_mi_close_ctrl(c2);
+ count = count_ep_controllers(ep);
+ assert(count == 0);
+}
+
+
+/* test: basic read MI datastructure command */
+static int test_read_mi_data_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 ror, mt, *hdr, *buf;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_MI);
+
+ /* do we have enough for a mi header? */
+ assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
+
+ /* inspect response as raw bytes */
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_mi_mi_opcode_mi_data_read);
+
+ /* create basic response */
+ assert(resp->hdr_len >= sizeof(struct nvme_mi_mi_resp_hdr));
+ assert(resp->data_len >= 4);
+
+ hdr = (__u8 *)resp->hdr;
+ hdr[4] = 0; /* status */
+
+ buf = (__u8 *)resp->data;
+ memset(buf, 0, resp->data_len);
+ buf[0] = 1; /* NUMP */
+ buf[1] = 1; /* MJR */
+ buf[2] = 2; /* MNR */
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_read_mi_data(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ test_set_transport_callback(ep, test_read_mi_data_cb, NULL);
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc == 0);
+}
+
+/* test: failed transport */
+static int test_transport_fail_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ return -1;
+}
+
+static void test_transport_fail(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ test_set_transport_callback(ep, test_transport_fail_cb, NULL);
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+static void test_transport_describe(nvme_mi_ep_t ep)
+{
+ struct test_transport_data *tpd;
+ char *str;
+
+ tpd = (struct test_transport_data *)ep->transport_data;
+
+ tpd->named = false;
+ str = nvme_mi_endpoint_desc(ep);
+ assert(str);
+ assert(!strcmp(str, "test-mi endpoint"));
+ free(str);
+
+ tpd->named = true;
+ str = nvme_mi_endpoint_desc(ep);
+ assert(str);
+ assert(!strcmp(str, "test-mi: test endpoint 0x74657374"));
+ free(str);
+}
+
+/* test: invalid crc */
+static int test_invalid_crc_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ resp->mic = 0;
+ return 0;
+}
+
+static void test_invalid_crc(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ test_set_transport_callback(ep, test_invalid_crc_cb, NULL);
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc < 0);
+}
+
+/* test: test that the controller list populates the endpoint's list of
+ * controllers */
+static int test_scan_ctrl_list_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 ror, mt, *hdr, *buf;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_MI);
+
+ /* do we have enough for a mi header? */
+ assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
+
+ /* inspect response as raw bytes */
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_mi_mi_opcode_mi_data_read);
+ assert(hdr[11] == nvme_mi_dtyp_ctrl_list);
+
+ /* create basic response */
+ assert(resp->hdr_len >= sizeof(struct nvme_mi_mi_resp_hdr));
+ assert(resp->data_len >= 4);
+
+ hdr = (__u8 *)resp->hdr;
+ hdr[4] = 0; /* status */
+
+ buf = (__u8 *)resp->data;
+ memset(buf, 0, resp->data_len);
+ buf[0] = 3; buf[1] = 0; /* num controllers */
+ buf[2] = 1; buf[3] = 0; /* id 1 */
+ buf[4] = 4; buf[5] = 0; /* id 4 */
+ buf[6] = 5; buf[7] = 0; /* id 5 */
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_scan_ctrl_list(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_ctrl *ctrl;
+
+ ep->controllers_scanned = false;
+
+ test_set_transport_callback(ep, test_scan_ctrl_list_cb, NULL);
+
+ nvme_mi_scan_ep(ep, false);
+
+ ctrl = nvme_mi_first_ctrl(ep);
+ assert(ctrl);
+ assert(ctrl->id == 1);
+
+ ctrl = nvme_mi_next_ctrl(ep, ctrl);
+ assert(ctrl);
+ assert(ctrl->id == 4);
+
+ ctrl = nvme_mi_next_ctrl(ep, ctrl);
+ assert(ctrl);
+ assert(ctrl->id == 5);
+
+ ctrl = nvme_mi_next_ctrl(ep, ctrl);
+ assert(ctrl == NULL);
+}
+
+/* test: simple NVMe admin request/response */
+static int test_admin_id_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 ror, mt, *hdr;
+ __u32 dlen, cdw10;
+ __u16 ctrl_id;
+ __u8 flags;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_ADMIN);
+
+ /* do we have enough for a mi header? */
+ assert(req->hdr_len == sizeof(struct nvme_mi_admin_req_hdr));
+
+ /* inspect response as raw bytes */
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+ flags = hdr[5];
+
+ ctrl_id = hdr[7] << 8 | hdr[6];
+ assert(ctrl_id == 0x5); /* controller id */
+
+ /* we requested a full id; if we've set the length flag,
+ * ensure the length matches */
+ dlen = hdr[35] << 24 | hdr[34] << 16 | hdr[33] << 8 | hdr[32];
+ if (flags & 0x1) {
+ assert(dlen == sizeof(struct nvme_id_ctrl));
+ }
+ assert(!(flags & 0x2));
+
+ /* CNS value of 1 in cdw10 field */
+ cdw10 = hdr[47] << 24 | hdr[46] << 16 | hdr[45] << 8 | hdr[44];
+ assert(cdw10 == 0x1);
+
+ /* create valid (but somewhat empty) response */
+ hdr = (__u8 *)resp->hdr;
+ hdr[4] = 0x00; /* status: success */
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id(nvme_mi_ep_t ep)
+{
+ struct nvme_id_ctrl id;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_id_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
+ assert(rc == 0);
+}
+
+/* test: simple NVMe error response, error reported in the MI header */
+static int test_admin_err_mi_resp_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 ror, mt, *hdr;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_ADMIN);
+
+ /* do we have enough for a mi header? */
+ assert(req->hdr_len == sizeof(struct nvme_mi_admin_req_hdr));
+
+ /* inspect response as raw bytes */
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ /* we need at least 8 bytes for error information */
+ assert(resp->hdr_len >= 8);
+
+ /* create error response */
+ hdr = (__u8 *)resp->hdr;
+ hdr[4] = 0x02; /* status: internal error */
+ hdr[5] = 0;
+ hdr[6] = 0;
+ hdr[7] = 0;
+ resp->hdr_len = 8;
+ resp->data_len = 0;
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_err_mi_resp(nvme_mi_ep_t ep)
+{
+ struct nvme_id_ctrl id;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_err_mi_resp_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 1);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
+ assert(rc != 0);
+ assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI);
+ assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR);
+}
+
+/* test: NVMe Admin error, with the error reported in the Admin response */
+static int test_admin_err_nvme_resp_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 ror, mt, *hdr;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_ADMIN);
+
+ /* do we have enough for a mi header? */
+ assert(req->hdr_len == sizeof(struct nvme_mi_admin_req_hdr));
+
+ /* inspect response as raw bytes */
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ /* we need at least 8 bytes for error information */
+ assert(resp->hdr_len >= sizeof(struct nvme_mi_admin_resp_hdr));
+
+ /* create error response */
+ hdr = (__u8 *)resp->hdr;
+ hdr[4] = 0; /* MI status: success */
+ hdr[5] = 0;
+ hdr[6] = 0;
+ hdr[7] = 0;
+
+ hdr[16] = 0; /* cdw3: SC: internal, SCT: generic, DNR */
+ hdr[17] = 0;
+ hdr[18] = 0x0c;
+ hdr[19] = 0x80;
+
+ resp->hdr_len = sizeof(struct nvme_mi_admin_resp_hdr);
+ resp->data_len = 0;
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_err_nvme_resp(nvme_mi_ep_t ep)
+{
+ struct nvme_id_ctrl id;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_err_nvme_resp_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 1);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
+ assert(rc != 0);
+ assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_NVME);
+ assert(nvme_status_get_value(rc) ==
+ (NVME_SC_INTERNAL | (NVME_SCT_GENERIC << NVME_SCT_SHIFT)
+ | NVME_SC_DNR));
+}
+
+/* invalid Admin command transfers */
+static int test_admin_invalid_formats_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ /* none of the tests should result in message transfer */
+ assert(0);
+ return -1;
+}
+
+static void test_admin_invalid_formats(nvme_mi_ep_t ep)
+{
+ struct {
+ struct nvme_mi_admin_req_hdr hdr;
+ uint8_t data[4];
+ } req = { 0 };
+ struct nvme_mi_admin_resp_hdr resp = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ size_t len;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_invalid_formats_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 1);
+ assert(ctrl);
+
+ /* unaligned req size */
+ len = 0;
+ rc = nvme_mi_admin_xfer(ctrl, &req.hdr, 1, &resp, 0, &len);
+ assert(rc != 0);
+
+ /* unaligned resp size */
+ len = 1;
+ rc = nvme_mi_admin_xfer(ctrl, &req.hdr, 0, &resp, 0, &len);
+ assert(rc != 0);
+
+ /* unaligned resp offset */
+ len = 4;
+ rc = nvme_mi_admin_xfer(ctrl, &req.hdr, 0, &resp, 1, &len);
+ assert(rc != 0);
+
+ /* resp too large */
+ len = 4096 + 4;
+ rc = nvme_mi_admin_xfer(ctrl, &req.hdr, 0, &resp, 0, &len);
+ assert(rc != 0);
+
+ /* resp offset too large */
+ len = 4;
+ rc = nvme_mi_admin_xfer(ctrl, &req.hdr, 0, &resp, (off_t)1 << 32, &len);
+ assert(rc != 0);
+
+ /* resp offset with no len */
+ len = 0;
+ rc = nvme_mi_admin_xfer(ctrl, &req.hdr, 0, &resp, 4, &len);
+ assert(rc != 0);
+
+ /* req and resp payloads */
+ len = 4;
+ rc = nvme_mi_admin_xfer(ctrl, &req.hdr, 4, &resp, 0, &len);
+ assert(rc != 0);
+}
+
+/* test: header length too small */
+static int test_resp_hdr_small_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ resp->hdr_len = 2;
+ test_transport_resp_calc_mic(resp);
+ return 0;
+}
+
+static void test_resp_hdr_small(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ test_set_transport_callback(ep, test_resp_hdr_small_cb, NULL);
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+/* test: respond with a request message */
+static int test_resp_req_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ resp->hdr->nmp &= ~(NVME_MI_ROR_RSP << 7);
+ test_transport_resp_calc_mic(resp);
+ return 0;
+}
+
+static void test_resp_req(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ test_set_transport_callback(ep, test_resp_req_cb, NULL);
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+/* test: invalid MCTP type in response */
+static int test_resp_invalid_type_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ resp->hdr->type = 0x3;
+ test_transport_resp_calc_mic(resp);
+ return 0;
+}
+
+static void test_resp_invalid_type(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ test_set_transport_callback(ep, test_resp_invalid_type_cb, NULL);
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+/* test: response with mis-matching command slot */
+static int test_resp_csi_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ resp->hdr->nmp ^= 0x1;
+ test_transport_resp_calc_mic(resp);
+ return 0;
+}
+
+static void test_resp_csi(nvme_mi_ep_t ep)
+{
+ struct nvme_mi_read_nvm_ss_info ss_info;
+ int rc;
+
+ test_set_transport_callback(ep, test_resp_csi_cb, NULL);
+
+ rc = nvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
+ assert(rc != 0);
+}
+
+/* test: config get MTU request & response layout, ensure we're handling
+ * endianness in the 3-byte NMRESP field correctly */
+static int test_mi_config_get_mtu_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_mi_mi_resp_hdr *mi_resp;
+ uint8_t *buf;
+
+ assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
+ assert(req->data_len == 0);
+
+ /* validate req as raw bytes */
+ buf = (void *)req->hdr;
+ assert(buf[4] == nvme_mi_mi_opcode_configuration_get);
+ /* dword 0: port and config id */
+ assert(buf[11] == 0x5);
+ assert(buf[8] == NVME_MI_CONFIG_MCTP_MTU);
+
+ /* set MTU in response */
+ mi_resp = (void *)resp->hdr;
+ mi_resp->nmresp[1] = 0x12;
+ mi_resp->nmresp[0] = 0x34;
+ resp->hdr_len = sizeof(*mi_resp);
+ resp->data_len = 0;
+
+ test_transport_resp_calc_mic(resp);
+ return 0;
+}
+
+static void test_mi_config_get_mtu(nvme_mi_ep_t ep)
+{
+ uint16_t mtu;
+ int rc;
+
+ test_set_transport_callback(ep, test_mi_config_get_mtu_cb, NULL);
+
+ rc = nvme_mi_mi_config_get_mctp_mtu(ep, 5, &mtu);
+ assert(rc == 0);
+ assert(mtu == 0x1234);
+}
+
+/* test: config set SMBus freq, both valid and invalid */
+static int test_mi_config_set_freq_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_mi_mi_resp_hdr *mi_resp;
+ uint8_t *buf;
+
+ assert(req->hdr_len == sizeof(struct nvme_mi_mi_req_hdr));
+ assert(req->data_len == 0);
+
+ /* validate req as raw bytes */
+ buf = (void *)req->hdr;
+ assert(buf[4] == nvme_mi_mi_opcode_configuration_set);
+ /* dword 0: port and config id */
+ assert(buf[11] == 0x5);
+ assert(buf[8] == NVME_MI_CONFIG_SMBUS_FREQ);
+
+ mi_resp = (void *)resp->hdr;
+ resp->hdr_len = sizeof(*mi_resp);
+ resp->data_len = 0;
+
+ /* accept 100 & 400, reject others */
+ switch (buf[9]) {
+ case NVME_MI_CONFIG_SMBUS_FREQ_100kHz:
+ case NVME_MI_CONFIG_SMBUS_FREQ_400kHz:
+ mi_resp->status = 0;
+ break;
+ case NVME_MI_CONFIG_SMBUS_FREQ_1MHz:
+ default:
+ mi_resp->status = 0x4;
+ break;
+ }
+
+ test_transport_resp_calc_mic(resp);
+ return 0;
+}
+
+static void test_mi_config_set_freq(nvme_mi_ep_t ep)
+{
+ int rc;
+
+ test_set_transport_callback(ep, test_mi_config_set_freq_cb, NULL);
+
+ rc = nvme_mi_mi_config_set_smbus_freq(ep, 5,
+ NVME_MI_CONFIG_SMBUS_FREQ_100kHz);
+ assert(rc == 0);
+}
+
+static void test_mi_config_set_freq_invalid(nvme_mi_ep_t ep)
+{
+ int rc;
+
+ test_set_transport_callback(ep, test_mi_config_set_freq_cb, NULL);
+
+ rc = nvme_mi_mi_config_set_smbus_freq(ep, 5,
+ NVME_MI_CONFIG_SMBUS_FREQ_1MHz);
+ assert(rc == 4);
+}
+
+/* Get Features callback, implementing Arbitration (which doesn't return
+ * additional data) and Timestamp (which does).
+ */
+static int test_admin_get_features_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 sel, fid, ror, mt, *rq_hdr, *rs_hdr, *rs_data;
+ __u16 ctrl_id;
+ int i;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_ADMIN);
+
+ /* do we have enough for a mi header? */
+ assert(req->hdr_len == sizeof(struct nvme_mi_admin_req_hdr));
+
+ /* inspect response as raw bytes */
+ rq_hdr = (__u8 *)req->hdr;
+
+ /* opcode */
+ assert(rq_hdr[4] == nvme_admin_get_features);
+
+ /* controller */
+ ctrl_id = rq_hdr[7] << 8 | rq_hdr[6];
+ assert(ctrl_id == 0x5); /* controller id */
+
+ /* sel & fid from lower bytes of cdw10 */
+ fid = rq_hdr[44];
+ sel = rq_hdr[45] & 0x7;
+
+ /* reserved fields */
+ assert(!(rq_hdr[46] || rq_hdr[47] || rq_hdr[45] & 0xf8));
+
+ assert(sel == 0x00);
+
+ rs_hdr = (__u8 *)resp->hdr;
+ rs_hdr[4] = 0x00; /* status: success */
+ rs_data = resp->data;
+
+ /* feature-id specific checks, and response generation */
+ switch (fid) {
+ case NVME_FEAT_FID_ARBITRATION:
+ /* arbitrary (hah!) arbitration value in cdw0 of response */
+ rs_hdr[8] = 1;
+ rs_hdr[9] = 2;
+ rs_hdr[10] = 3;
+ rs_hdr[11] = 4;
+ resp->data_len = 0;
+ break;
+
+ case NVME_FEAT_FID_TIMESTAMP:
+ resp->data_len = 8;
+ for (i = 0; i < 6; i++)
+ rs_data[i] = i;
+ rs_data[6] = 1;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_get_features_nodata(nvme_mi_ep_t ep)
+{
+ struct nvme_get_features_args args = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ uint32_t res;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_get_features_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ args.args_size = sizeof(args);
+ args.fid = NVME_FEAT_FID_ARBITRATION;
+ args.sel = 0;
+ args.result = &res;
+
+ rc = nvme_mi_admin_get_features(ctrl, &args);
+ assert(rc == 0);
+ assert(args.data_len == 0);
+ assert(res == 0x04030201);
+}
+
+static void test_get_features_data(nvme_mi_ep_t ep)
+{
+ struct nvme_get_features_args args = { 0 };
+ struct nvme_timestamp tstamp;
+ nvme_mi_ctrl_t ctrl;
+ uint8_t exp[6];
+ uint32_t res;
+ int rc, i;
+
+ test_set_transport_callback(ep, test_admin_get_features_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ args.args_size = sizeof(args);
+ args.fid = NVME_FEAT_FID_TIMESTAMP;
+ args.sel = 0;
+ args.result = &res;
+ args.data = &tstamp;
+ args.data_len = sizeof(tstamp);
+
+ /* expected timestamp value */
+ for (i = 0; i < sizeof(tstamp.timestamp); i++)
+ exp[i] = i;
+
+ rc = nvme_mi_admin_get_features(ctrl, &args);
+ assert(rc == 0);
+ assert(args.data_len == sizeof(tstamp));
+ assert(tstamp.attr == 1);
+ assert(!memcmp(tstamp.timestamp, exp, sizeof(tstamp.timestamp)));
+}
+
+/* Set Features callback for timestamp */
+static int test_admin_set_features_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 save, fid, ror, mt, *rq_hdr, *rq_data, *rs_hdr;
+ __u16 ctrl_id;
+ uint8_t ts[6];
+ int i;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_ADMIN);
+ assert(req->hdr_len == sizeof(struct nvme_mi_admin_req_hdr));
+ assert(req->data_len == 8);
+
+ rq_hdr = (__u8 *)req->hdr;
+ rq_data = req->data;
+
+ /* opcode */
+ assert(rq_hdr[4] == nvme_admin_set_features);
+
+ /* controller */
+ ctrl_id = rq_hdr[7] << 8 | rq_hdr[6];
+ assert(ctrl_id == 0x5); /* controller id */
+
+ /* fid from lower bytes of cdw10, save from top bit */
+ fid = rq_hdr[44];
+ save = rq_hdr[47] & 0x80;
+
+ /* reserved fields */
+ assert(!(rq_hdr[45] || rq_hdr[46]));
+
+ assert(fid == NVME_FEAT_FID_TIMESTAMP);
+ assert(save == 0x80);
+
+ for (i = 0; i < sizeof(ts); i++)
+ ts[i] = i;
+ assert(!memcmp(ts, rq_data, sizeof(ts)));
+
+ rs_hdr = (__u8 *)resp->hdr;
+ rs_hdr[4] = 0x00;
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_set_features(nvme_mi_ep_t ep)
+{
+ struct nvme_set_features_args args = { 0 };
+ struct nvme_timestamp tstamp = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ uint32_t res;
+ int rc, i;
+
+ test_set_transport_callback(ep, test_admin_set_features_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ for (i = 0; i < sizeof(tstamp.timestamp); i++)
+ tstamp.timestamp[i] = i;
+
+ args.args_size = sizeof(args);
+ args.fid = NVME_FEAT_FID_TIMESTAMP;
+ args.save = 1;
+ args.result = &res;
+ args.data = &tstamp;
+ args.data_len = sizeof(tstamp);
+
+ rc = nvme_mi_admin_set_features(ctrl, &args);
+ assert(rc == 0);
+ assert(args.data_len == 0);
+}
+
+enum ns_type {
+ NS_ACTIVE,
+ NS_ALLOC,
+};
+
+static int test_admin_id_ns_list_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_ns_list *list;
+ enum ns_type type;
+ int offset;
+ __u8 *hdr;
+ __u16 cns;
+
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ assert(req->data_len == 0);
+
+ cns = hdr[45] << 8 | hdr[44];
+
+ /* NSID */
+ assert(hdr[8] == 1 && !hdr[9] && !hdr[10] && !hdr[11]);
+
+ type = *(enum ns_type *)data;
+ resp->data_len = sizeof(*list);
+ list = resp->data;
+
+ switch (type) {
+ case NS_ALLOC:
+ assert(cns == NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST);
+ offset = 2;
+ break;
+ case NS_ACTIVE:
+ assert(cns == NVME_IDENTIFY_CNS_NS_ACTIVE_LIST);
+ offset = 4;
+ break;
+ default:
+ assert(0);
+ }
+
+ list->ns[0] = cpu_to_le32(offset);
+ list->ns[1] = cpu_to_le32(offset + 1);
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id_alloc_ns_list(struct nvme_mi_ep *ep)
+{
+ struct nvme_ns_list list;
+ nvme_mi_ctrl_t ctrl;
+ enum ns_type type;
+ int rc;
+
+ type = NS_ALLOC;
+ test_set_transport_callback(ep, test_admin_id_ns_list_cb, &type);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_allocated_ns_list(ctrl, 1, &list);
+ assert(!rc);
+
+ assert(le32_to_cpu(list.ns[0]) == 2);
+ assert(le32_to_cpu(list.ns[1]) == 3);
+ assert(le32_to_cpu(list.ns[2]) == 0);
+}
+
+static void test_admin_id_active_ns_list(struct nvme_mi_ep *ep)
+{
+ struct nvme_ns_list list;
+ nvme_mi_ctrl_t ctrl;
+ enum ns_type type;
+ int rc;
+
+ type = NS_ACTIVE;
+ test_set_transport_callback(ep, test_admin_id_ns_list_cb, &type);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_active_ns_list(ctrl, 1, &list);
+ assert(!rc);
+
+ assert(le32_to_cpu(list.ns[0]) == 4);
+ assert(le32_to_cpu(list.ns[1]) == 5);
+ assert(le32_to_cpu(list.ns[2]) == 0);
+}
+
+static int test_admin_id_ns_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_id_ns *id;
+ enum ns_type type;
+ __u16 nsid, cns;
+ __u8 *hdr;
+
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ assert(req->data_len == 0);
+
+ cns = hdr[45] << 8 | hdr[44];
+
+ /* NSID */
+ nsid = hdr[8];
+ assert(!hdr[9] && !hdr[10] && !hdr[11]);
+
+ type = *(enum ns_type *)data;
+ resp->data_len = sizeof(*id);
+ id = resp->data;
+ id->nsze = cpu_to_le64(nsid);
+
+ switch (type) {
+ case NS_ALLOC:
+ assert(cns == NVME_IDENTIFY_CNS_ALLOCATED_NS);
+ break;
+ case NS_ACTIVE:
+ assert(cns == NVME_IDENTIFY_CNS_NS);
+ break;
+ default:
+ assert(0);
+ }
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id_alloc_ns(struct nvme_mi_ep *ep)
+{
+ struct nvme_id_ns id;
+ nvme_mi_ctrl_t ctrl;
+ enum ns_type type;
+ int rc;
+
+ type = NS_ALLOC;
+ test_set_transport_callback(ep, test_admin_id_ns_cb, &type);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_allocated_ns(ctrl, 1, &id);
+ assert(!rc);
+ assert(le64_to_cpu(id.nsze) == 1);
+}
+
+static void test_admin_id_active_ns(struct nvme_mi_ep *ep)
+{
+ struct nvme_id_ns id;
+ nvme_mi_ctrl_t ctrl;
+ enum ns_type type;
+ int rc;
+
+ type = NS_ACTIVE;
+ test_set_transport_callback(ep, test_admin_id_ns_cb, &type);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_ns(ctrl, 1, &id);
+ assert(!rc);
+ assert(le64_to_cpu(id.nsze) == 1);
+}
+
+static int test_admin_id_nsid_ctrl_list_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u16 cns, ctrlid;
+ __u32 nsid;
+ __u8 *hdr;
+
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ assert(req->data_len == 0);
+
+ cns = hdr[45] << 8 | hdr[44];
+ assert(cns == NVME_IDENTIFY_CNS_NS_CTRL_LIST);
+
+ nsid = hdr[11] << 24 | hdr[10] << 16 | hdr[9] << 8 | hdr[8];
+ assert(nsid == 0x01020304);
+
+ ctrlid = hdr[47] << 8 | hdr[46];
+ assert(ctrlid == 5);
+
+ resp->data_len = sizeof(struct nvme_ctrl_list);
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id_nsid_ctrl_list(struct nvme_mi_ep *ep)
+{
+ struct nvme_ctrl_list list;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_id_nsid_ctrl_list_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_nsid_ctrl_list(ctrl, 0x01020304, 5, &list);
+ assert(!rc);
+}
+
+static int test_admin_id_secondary_ctrl_list_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u16 cns, ctrlid;
+ __u8 *hdr;
+
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ assert(req->data_len == 0);
+
+ cns = hdr[45] << 8 | hdr[44];
+ assert(cns == NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST);
+
+ ctrlid = hdr[47] << 8 | hdr[46];
+ assert(ctrlid == 5);
+
+ resp->data_len = sizeof(struct nvme_secondary_ctrl_list);
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id_secondary_ctrl_list(struct nvme_mi_ep *ep)
+{
+ struct nvme_secondary_ctrl_list list;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_id_secondary_ctrl_list_cb,
+ NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_secondary_ctrl_list(ctrl, 5, &list);
+ assert(!rc);
+}
+
+static int test_admin_ns_mgmt_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 *rq_hdr, *rs_hdr, sel, csi;
+ struct nvme_ns_mgmt_host_sw_specified *create_data;
+ __u32 nsid;
+
+ rq_hdr = (__u8 *)req->hdr;
+ assert(rq_hdr[4] == nvme_admin_ns_mgmt);
+
+ sel = rq_hdr[44];
+ csi = rq_hdr[45];
+ nsid = rq_hdr[11] << 24 | rq_hdr[10] << 16 | rq_hdr[9] << 8 | rq_hdr[8];
+
+ rs_hdr = (__u8 *)resp->hdr;
+
+ switch (sel) {
+ case NVME_NS_MGMT_SEL_CREATE:
+ assert(req->data_len == sizeof(struct nvme_ns_mgmt_host_sw_specified));
+ create_data = req->data;
+
+ /* No NSID on created namespaces */
+ assert(nsid == 0);
+ assert(csi == 0);
+
+ /* allow operations on nsze == 42, reject others */
+ if (le64_to_cpu(create_data->nsze) != 42) {
+ rs_hdr[4] = 0;
+ /* response cdw0 is created NSID */
+ rs_hdr[8] = 0x04;
+ rs_hdr[9] = 0x03;
+ rs_hdr[10] = 0x02;
+ rs_hdr[11] = 0x01;
+ } else {
+ rs_hdr[4] = NVME_MI_RESP_INVALID_PARAM;
+ }
+ break;
+
+ case NVME_NS_MGMT_SEL_DELETE:
+ assert(req->data_len == 0);
+ /* NSID required on delete */
+ assert(nsid == 0x05060708);
+ break;
+
+ default:
+ assert(0);
+ }
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_ns_mgmt_create(struct nvme_mi_ep *ep)
+{
+ struct nvme_ns_mgmt_host_sw_specified data = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ __u32 ns;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_ns_mgmt_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_ns_mgmt_create(ctrl, NULL, 0, &ns, &data);
+ assert(!rc);
+ assert(ns == 0x01020304);
+
+ data.nsze = cpu_to_le64(42);
+ rc = nvme_mi_admin_ns_mgmt_create(ctrl, NULL, 0, &ns, &data);
+ assert(rc);
+}
+
+static void test_admin_ns_mgmt_delete(struct nvme_mi_ep *ep)
+{
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_ns_mgmt_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_ns_mgmt_delete(ctrl, 0x05060708);
+ assert(!rc);
+}
+
+struct attach_op {
+ enum {
+ NS_ATTACH,
+ NS_DETACH,
+ } op;
+ struct nvme_ctrl_list *list;
+};
+
+static int test_admin_ns_attach_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct attach_op *op = data;
+ __u8 *rq_hdr, sel;
+ __u32 nsid;
+
+ rq_hdr = (__u8 *)req->hdr;
+ assert(rq_hdr[4] == nvme_admin_ns_attach);
+
+ sel = rq_hdr[44];
+ nsid = rq_hdr[11] << 24 | rq_hdr[10] << 16 | rq_hdr[9] << 8 | rq_hdr[8];
+
+ assert(req->data_len == sizeof(*op->list));
+
+ assert(nsid == 0x02030405);
+ switch (op->op) {
+ case NS_ATTACH:
+ assert(sel == NVME_NS_ATTACH_SEL_CTRL_ATTACH);
+ break;
+ case NS_DETACH:
+ assert(sel == NVME_NS_ATTACH_SEL_CTRL_DEATTACH);
+ break;
+ default:
+ assert(0);
+ }
+
+ assert(!memcmp(req->data, op->list, sizeof(*op->list)));
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_ns_attach(struct nvme_mi_ep *ep)
+{
+ struct nvme_ctrl_list list = { 0 };
+ struct attach_op aop;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ list.num = cpu_to_le16(2);
+ list.identifier[0] = 4;
+ list.identifier[1] = 5;
+
+ aop.op = NS_ATTACH;
+ aop.list = &list;
+
+ test_set_transport_callback(ep, test_admin_ns_attach_cb, &aop);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_ns_attach_ctrls(ctrl, 0x02030405, &list);
+ assert(!rc);
+}
+
+static void test_admin_ns_detach(struct nvme_mi_ep *ep)
+{
+ struct nvme_ctrl_list list = { 0 };
+ struct attach_op aop;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ list.num = cpu_to_le16(2);
+ list.identifier[0] = 6;
+ list.identifier[1] = 7;
+
+ aop.op = NS_DETACH;
+ aop.list = &list;
+
+ test_set_transport_callback(ep, test_admin_ns_attach_cb, &aop);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_ns_detach_ctrls(ctrl, 0x02030405, &list);
+ assert(!rc);
+}
+
+struct fw_download_info {
+ uint32_t offset;
+ uint32_t len;
+ void *data;
+};
+
+static int test_admin_fw_download_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct fw_download_info *info = data;
+ __u32 len, off;
+ __u8 *rq_hdr;
+
+ rq_hdr = (__u8 *)req->hdr;
+ assert(rq_hdr[4] == nvme_admin_fw_download);
+
+ len = rq_hdr[47] << 24 | rq_hdr[46] << 16 | rq_hdr[45] << 8 | rq_hdr[44];
+ off = rq_hdr[51] << 24 | rq_hdr[50] << 16 | rq_hdr[49] << 8 | rq_hdr[48];
+
+ assert(off << 2 == info->offset);
+ assert(((len+1) << 2) == info->len);
+
+ /* ensure that the request len matches too */
+ assert(req->data_len == info->len);
+
+ assert(!memcmp(req->data, info->data, len));
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_fw_download(struct nvme_mi_ep *ep)
+{
+ struct nvme_fw_download_args args;
+ struct fw_download_info info;
+ unsigned char fw[4096];
+ nvme_mi_ctrl_t ctrl;
+ int rc, i;
+
+ for (i = 0; i < sizeof(fw); i++)
+ fw[i] = i % 0xff;
+
+ info.offset = 0;
+ info.len = 0;
+ info.data = fw;
+ args.data = fw;
+ args.args_size = sizeof(args);
+
+ test_set_transport_callback(ep, test_admin_fw_download_cb, &info);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ /* invalid (zero) len */
+ args.data_len = info.len = 1;
+ args.offset = info.offset = 0;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(rc);
+
+ /* invalid (unaligned) len */
+ args.data_len = info.len = 1;
+ args.offset = info.offset = 0;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(rc);
+
+ /* invalid offset */
+ args.data_len = info.len = 4;
+ args.offset = info.offset = 1;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(rc);
+
+ /* smallest len */
+ args.data_len = info.len = 4;
+ args.offset = info.offset = 0;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(!rc);
+
+ /* largest len */
+ args.data_len = info.len = 4096;
+ args.offset = info.offset = 0;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(!rc);
+
+ /* offset value */
+ args.data_len = info.len = 4096;
+ args.offset = info.offset = 4096;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(!rc);
+}
+
+struct fw_commit_info {
+ __u8 bpid;
+ __u8 action;
+ __u8 slot;
+};
+
+static int test_admin_fw_commit_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct fw_commit_info *info = data;
+ __u8 bpid, action, slot;
+ __u8 *rq_hdr;
+
+ rq_hdr = (__u8 *)req->hdr;
+ assert(rq_hdr[4] == nvme_admin_fw_commit);
+
+ bpid = (rq_hdr[47] >> 7) & 0x1;
+ slot = rq_hdr[44] & 0x7;
+ action = (rq_hdr[44] >> 3) & 0x7;
+
+ assert(!!bpid == !!info->bpid);
+ assert(slot == info->slot);
+ assert(action == info->action);
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_fw_commit(struct nvme_mi_ep *ep)
+{
+ struct nvme_fw_commit_args args;
+ struct fw_commit_info info;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ args.args_size = sizeof(args);
+ info.bpid = args.bpid = 0;
+
+ test_set_transport_callback(ep, test_admin_fw_commit_cb, &info);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ /* all zeros */
+ info.bpid = args.bpid = 0;
+ info.slot = args.slot = 0;
+ info.action = args.action = 0;
+ rc = nvme_mi_admin_fw_commit(ctrl, &args);
+ assert(!rc);
+
+ /* all ones */
+ info.bpid = args.bpid = 1;
+ info.slot = args.slot = 0x7;
+ info.action = args.action = 0x7;
+ rc = nvme_mi_admin_fw_commit(ctrl, &args);
+ assert(!rc);
+
+ /* correct fields */
+ info.bpid = args.bpid = 1;
+ info.slot = args.slot = 2;
+ info.action = args.action = 3;
+ rc = nvme_mi_admin_fw_commit(ctrl, &args);
+ assert(!rc);
+}
+
+struct format_data {
+ __u32 nsid;
+ __u8 lbafu;
+ __u8 ses;
+ __u8 pil;
+ __u8 pi;
+ __u8 mset;
+ __u8 lbafl;
+};
+
+static int test_admin_format_nvm_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_format_nvm_args *args = data;
+ __u8 *rq_hdr;
+ __u32 nsid;
+
+ assert(req->data_len == 0);
+
+ rq_hdr = (__u8 *)req->hdr;
+
+ assert(rq_hdr[4] == nvme_admin_format_nvm);
+
+ nsid = (__u32)rq_hdr[11] << 24
+ | rq_hdr[10] << 16
+ | rq_hdr[9] << 8
+ | rq_hdr[8];
+ assert(nsid == args->nsid);
+
+ assert(((rq_hdr[44] >> 0) & 0xf) == args->lbaf);
+ assert(((rq_hdr[44] >> 4) & 0x1) == args->mset);
+ assert(((rq_hdr[44] >> 5) & 0x7) == args->pi);
+
+ assert(((rq_hdr[45] >> 0) & 0x1) == args->pil);
+ assert(((rq_hdr[45] >> 1) & 0x7) == args->ses);
+ assert(((rq_hdr[45] >> 4) & 0x3) == args->lbafu);
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_format_nvm(struct nvme_mi_ep *ep)
+{
+ struct nvme_format_nvm_args args = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ test_set_transport_callback(ep, test_admin_format_nvm_cb, &args);
+
+ /* ensure we have the cdw0 bit field encoding correct, by testing twice
+ * with inverted bit values */
+ args.args_size = sizeof(args);
+ args.nsid = 0x04030201;
+ args.lbafu = 0x3;
+ args.ses = 0x0;
+ args.pil = 0x1;
+ args.pi = 0x0;
+ args.mset = 0x1;
+ args.lbaf = 0x0;
+
+ rc = nvme_mi_admin_format_nvm(ctrl, &args);
+ assert(!rc);
+
+ args.nsid = ~args.nsid;
+ args.lbafu = 0;
+ args.ses = 0x7;
+ args.pil = 0x0;
+ args.pi = 0x7;
+ args.mset = 0x0;
+ args.lbaf = 0xf;
+
+ rc = nvme_mi_admin_format_nvm(ctrl, &args);
+ assert(!rc);
+}
+
+static int test_admin_sanitize_nvm_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_sanitize_nvm_args *args = data;
+ __u8 *rq_hdr;
+ __u32 ovrpat;
+
+ assert(req->data_len == 0);
+
+ rq_hdr = (__u8 *)req->hdr;
+
+ assert(rq_hdr[4] == nvme_admin_sanitize_nvm);
+
+ assert(((rq_hdr[44] >> 0) & 0x7) == args->sanact);
+ assert(((rq_hdr[44] >> 3) & 0x1) == args->ause);
+ assert(((rq_hdr[44] >> 4) & 0xf) == args->owpass);
+
+ assert(((rq_hdr[45] >> 0) & 0x1) == args->oipbp);
+ assert(((rq_hdr[45] >> 1) & 0x1) == args->nodas);
+
+ ovrpat = (__u32)rq_hdr[51] << 24 | rq_hdr[50] << 16 |
+ rq_hdr[49] << 8 | rq_hdr[48];
+ assert(ovrpat == args->ovrpat);
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_sanitize_nvm(struct nvme_mi_ep *ep)
+{
+ struct nvme_sanitize_nvm_args args = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ test_set_transport_callback(ep, test_admin_sanitize_nvm_cb, &args);
+
+ args.args_size = sizeof(args);
+ args.sanact = 0x7;
+ args.ause = 0x0;
+ args.owpass = 0xf;
+ args.oipbp = 0x0;
+ args.nodas = 0x1;
+ args.ovrpat = ~0x04030201;
+
+ rc = nvme_mi_admin_sanitize_nvm(ctrl, &args);
+ assert(!rc);
+
+ args.sanact = 0x0;
+ args.ause = 0x1;
+ args.owpass = 0x0;
+ args.oipbp = 0x1;
+ args.nodas = 0x0;
+ args.ovrpat = 0x04030201;
+
+ rc = nvme_mi_admin_sanitize_nvm(ctrl, &args);
+ assert(!rc);
+}
+
+/* test that we set the correct offset and size on get_log() calls that
+ * are split into multiple requests */
+struct log_data {
+ int n;
+};
+
+static int test_admin_get_log_split_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ uint32_t log_page_offset_lower;
+ struct log_data *ldata = data;
+ uint32_t len, off;
+ __u8 *rq_hdr;
+
+ assert(req->data_len == 0);
+
+ rq_hdr = (__u8 *)req->hdr;
+
+ assert(rq_hdr[4] == nvme_admin_get_log_page);
+
+ /* from the MI message's DOFST/DLEN fields */
+ off = rq_hdr[31] << 24 | rq_hdr[30] << 16 | rq_hdr[29] << 8 | rq_hdr[28];
+ len = rq_hdr[35] << 24 | rq_hdr[34] << 16 | rq_hdr[33] << 8 | rq_hdr[32];
+
+ /* From the MI message's Command Dword 12 */
+ log_page_offset_lower = rq_hdr[55] << 24 | rq_hdr[54] << 16 | rq_hdr[53] << 8 | rq_hdr[52];
+
+ /* we should have a full-sized start and middle, and a short end */
+ switch (ldata->n) {
+ case 0:
+ assert(log_page_offset_lower == 0);
+ assert(len == 4096);
+ assert(off == 0);
+ break;
+ case 1:
+ assert(log_page_offset_lower == 4096);
+ assert(len == 4096);
+ assert(off == 0);
+ break;
+ case 2:
+ assert(log_page_offset_lower == 8192);
+ assert(len == 4);
+ assert(off == 0);
+ break;
+ default:
+ assert(0);
+ }
+
+ /* ensure we've sized the expected response correctly */
+ assert(resp->data_len == len);
+ memset(resp->data, ldata->n & 0xff, len);
+
+ test_transport_resp_calc_mic(resp);
+
+ ldata->n++;
+
+ return 0;
+}
+
+static void test_admin_get_log_split(struct nvme_mi_ep *ep)
+{
+ struct nvme_get_log_args args = { 0 };
+ unsigned char buf[4096 * 2 + 4];
+ struct log_data ldata;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ ldata.n = 0;
+ test_set_transport_callback(ep, test_admin_get_log_split_cb, &ldata);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+
+ args.args_size = sizeof(args);
+ args.lid = 1;
+ args.log = buf;
+ args.len = sizeof(buf);
+ args.lpo = 0;
+ args.ot = false;
+
+ rc = nvme_mi_admin_get_log(ctrl, &args);
+
+ assert(!rc);
+
+ /* we should have sent three commands */
+ assert(ldata.n == 3);
+}
+
+#define DEFINE_TEST(name) { #name, test_ ## name }
+struct test {
+ const char *name;
+ void (*fn)(nvme_mi_ep_t);
+} tests[] = {
+ DEFINE_TEST(endpoint_lifetime),
+ DEFINE_TEST(ctrl_lifetime),
+ DEFINE_TEST(read_mi_data),
+ DEFINE_TEST(transport_fail),
+ DEFINE_TEST(transport_describe),
+ DEFINE_TEST(scan_ctrl_list),
+ DEFINE_TEST(invalid_crc),
+ DEFINE_TEST(admin_id),
+ DEFINE_TEST(admin_err_mi_resp),
+ DEFINE_TEST(admin_err_nvme_resp),
+ DEFINE_TEST(admin_invalid_formats),
+ DEFINE_TEST(resp_req),
+ DEFINE_TEST(resp_hdr_small),
+ DEFINE_TEST(resp_invalid_type),
+ DEFINE_TEST(resp_csi),
+ DEFINE_TEST(mi_config_get_mtu),
+ DEFINE_TEST(mi_config_set_freq),
+ DEFINE_TEST(mi_config_set_freq_invalid),
+ DEFINE_TEST(get_features_nodata),
+ DEFINE_TEST(get_features_data),
+ DEFINE_TEST(set_features),
+ DEFINE_TEST(admin_id_alloc_ns_list),
+ DEFINE_TEST(admin_id_active_ns_list),
+ DEFINE_TEST(admin_id_alloc_ns),
+ DEFINE_TEST(admin_id_active_ns),
+ DEFINE_TEST(admin_id_nsid_ctrl_list),
+ DEFINE_TEST(admin_id_secondary_ctrl_list),
+ DEFINE_TEST(admin_ns_mgmt_create),
+ DEFINE_TEST(admin_ns_mgmt_delete),
+ DEFINE_TEST(admin_ns_attach),
+ DEFINE_TEST(admin_ns_detach),
+ DEFINE_TEST(admin_fw_download),
+ DEFINE_TEST(admin_fw_commit),
+ DEFINE_TEST(admin_format_nvm),
+ DEFINE_TEST(admin_sanitize_nvm),
+ DEFINE_TEST(admin_get_log_split),
+};
+
+static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep)
+{
+ printf("Running test %s...", test->name);
+ fflush(stdout);
+ test->fn(ep);
+ /* tests will assert on failure; if we're here, we're OK */
+ printf(" OK\n");
+ test_print_log_buf(logfd);
+}
+
+int main(void)
+{
+ nvme_root_t root;
+ nvme_mi_ep_t ep;
+ unsigned int i;
+ FILE *fd;
+
+ fd = test_setup_log();
+
+ root = nvme_mi_create_root(fd, DEFAULT_LOGLEVEL);
+ assert(root);
+
+ ep = nvme_mi_open_test(root);
+ assert(ep);
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ run_test(&tests[i], fd, ep);
+ }
+
+ nvme_mi_close(ep);
+ nvme_mi_free_root(root);
+
+ test_close_log(fd);
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/mock-ifaddrs.c b/test/mock-ifaddrs.c
new file mode 100644
index 0000000..87a2e50
--- /dev/null
+++ b/test/mock-ifaddrs.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2023 Martin Belanger, Dell Technologies Inc.
+ */
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+struct ifaddrs_storage {
+ struct ifaddrs ifa;
+ union {
+ /* Reserve space for the biggest of the sockaddr types */
+ struct sockaddr_in s4;
+ struct sockaddr_in6 s6;
+ } addr, netmask, broadaddr;
+ char name[IF_NAMESIZE + 1];
+};
+
+static void init_entry(struct ifaddrs_storage *storage,
+ const char *ifname,
+ int family,
+ uint32_t addr1,
+ uint32_t addr2,
+ uint32_t addr3,
+ uint32_t addr4,
+ bool last)
+{
+ struct ifaddrs *p;
+
+ p = &storage->ifa;
+ p->ifa_next = last ? NULL : &storage[1].ifa;
+ p->ifa_name = storage->name;
+ strcpy(p->ifa_name, ifname);
+ p->ifa_flags = 0;
+
+ if (family == AF_INET) {
+ struct sockaddr_in *ipv4;
+
+ ipv4 = &storage->addr.s4;
+ ipv4->sin_family = family;
+ ipv4->sin_port = 0;
+ ipv4->sin_addr.s_addr = htonl(addr1);
+ p->ifa_addr = (struct sockaddr *)ipv4;
+
+ ipv4 = &storage->netmask.s4;
+ ipv4->sin_family = family;
+ ipv4->sin_port = 0;
+ ipv4->sin_addr.s_addr = 0xffffff00;
+ p->ifa_netmask = (struct sockaddr *)ipv4;
+
+ ipv4 = &storage->broadaddr.s4;
+ ipv4->sin_family = family;
+ ipv4->sin_port = 0;
+ ipv4->sin_addr.s_addr = 0;
+ p->ifa_broadaddr = (struct sockaddr *)ipv4;;
+ } else {
+ struct sockaddr_in6 *ipv6;
+
+ ipv6 = &storage->addr.s6;
+ ipv6->sin6_family = family;
+ ipv6->sin6_port = 0;
+ ipv6->sin6_flowinfo = 0;
+ ipv6->sin6_addr.s6_addr32[0] = htonl(addr1);
+ ipv6->sin6_addr.s6_addr32[1] = htonl(addr2);
+ ipv6->sin6_addr.s6_addr32[2] = htonl(addr3);
+ ipv6->sin6_addr.s6_addr32[3] = htonl(addr4);
+ ipv6->sin6_scope_id = 0;
+ p->ifa_addr = (struct sockaddr *)ipv6;
+
+ ipv6 = &storage->netmask.s6;
+ ipv6->sin6_family = family;
+ ipv6->sin6_port = 0;
+ ipv6->sin6_flowinfo = 0;
+ ipv6->sin6_addr.s6_addr32[0] = 0xffffffff;
+ ipv6->sin6_addr.s6_addr32[1] = 0xffffffff;
+ ipv6->sin6_addr.s6_addr32[2] = 0xffffffff;
+ ipv6->sin6_addr.s6_addr32[3] = 0;
+ ipv6->sin6_scope_id = 0;
+ p->ifa_netmask = (struct sockaddr *)ipv6;
+
+ ipv6 = &storage->broadaddr.s6;
+ ipv6->sin6_family = family;
+ ipv6->sin6_port = 0;
+ ipv6->sin6_flowinfo = 0;
+ ipv6->sin6_addr.s6_addr32[0] = 0;
+ ipv6->sin6_addr.s6_addr32[1] = 0;
+ ipv6->sin6_addr.s6_addr32[2] = 0;
+ ipv6->sin6_addr.s6_addr32[3] = 0;
+ ipv6->sin6_scope_id = 0;
+ p->ifa_broadaddr = (struct sockaddr *)ipv6;
+ }
+
+ p->ifa_data = NULL;
+}
+
+int getifaddrs(struct ifaddrs **ifap) {
+ struct ifaddrs_storage *storage;
+
+ /* Allocate memory for 4 interfaces */
+ storage = (struct ifaddrs_storage *)calloc(4, sizeof(struct ifaddrs_storage));
+ *ifap = &storage[0].ifa;
+
+ init_entry(&storage[0], "eth0", AF_INET, 0xc0a80114, 0, 0, 0, false); /* 192.168.1.20 */
+ init_entry(&storage[1], "eth0", AF_INET6, 0xfe800000, 0, 0, 0xdeadbeef, false); /* fe80::dead:beef */
+
+ /* Loopback interface */
+ init_entry(&storage[2], "lo", AF_INET, 0x7f000001, 0, 0, 0, false); /* 127.0.0.1 */
+ init_entry(&storage[3], "lo", AF_INET6, 0, 0, 0, 1, true); /* ::1 */
+
+ return 0;
+}
+
+void freeifaddrs(struct ifaddrs *ifa) {
+ free(ifa);
+}
+
diff --git a/test/nbft/README b/test/nbft/README
new file mode 100644
index 0000000..0f252a5
--- /dev/null
+++ b/test/nbft/README
@@ -0,0 +1,17 @@
+This is a simple testcase to verify the NBFT parser output over a set
+of provided ACPI NBFT tables.
+
+The 'nbft-dump' test utility is a simple tool to print out all elements
+and nested arrays of the nbft_info structs, bearing only minimal logic.
+
+The 'tables' directory contains sample binary files taken from
+/sys/firmware/acpi/tables. The 'tables_bad' then contains experiments, older
+table revisions or malformed data to test the parser error path.
+
+The "diffs" directory contains reference output generated by the 'nbft-dump'
+utility that is being compared against actual testcase run output. Everytime
+'nbft-dump.c' is modified these reference output files need to be regenerated
+by calling `ninja -C .build nbft-diffs` over the configured meson project.
+
+The tests are typically ran as part of the standard `meson test -C .build`
+invocation.
diff --git a/test/nbft/diffs/NBFT-Dell.PowerEdge.R660-fw1.5.5-single b/test/nbft/diffs/NBFT-Dell.PowerEdge.R660-fw1.5.5-single
new file mode 100644
index 0000000..72a34de
--- /dev/null
+++ b/test/nbft/diffs/NBFT-Dell.PowerEdge.R660-fw1.5.5-single
@@ -0,0 +1,54 @@
+raw_nbft_size=930
+host.id=44454c4c44010448030b8c04f445833
+host.nqn=nqn.2014-08.org.nvmexpress:uuid:4c4c4544-0044-4410-8030-b8c04f445833
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=27136
+hfi_list[0]->tcp_info.mac_addr=062bcbeb70
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=94
+hfi_list[0]->tcp_info.ipaddr=172.18.240.1
+hfi_list[0]->tcp_info.subnet_mask_prefix=24
+hfi_list[0]->tcp_info.gateway_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.route_metric=500
+hfi_list[0]->tcp_info.primary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=
+hfi_list[0]->tcp_info.host_name=(null)
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=0
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=172.18.240.60
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=270
+subsystem_ns_list[0]->nid_type=2
+subsystem_ns_list[0]->nid=5380b42fc0c5de718ccf9680be3ca7
+subsystem_ns_list[0]->subsys_nqn=nqn.1988-11.com.dell:powerstore:00:88b402df2d762AA7AF94
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=4105
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
+subsystem_ns_list[1]->index=2
+subsystem_ns_list[1]->num_hfis=1
+subsystem_ns_list[1]->hfis[0]->index=1
+subsystem_ns_list[1]->transport=tcp
+subsystem_ns_list[1]->traddr=172.18.240.60
+subsystem_ns_list[1]->trsvcid=4420
+subsystem_ns_list[1]->subsys_port_id=0
+subsystem_ns_list[1]->nsid=1671
+subsystem_ns_list[1]->nid_type=2
+subsystem_ns_list[1]->nid=f4c66fce74afdb8ccf96807eaeae
+subsystem_ns_list[1]->subsys_nqn=nqn.1988-11.com.dell:powerstore:00:88b402df2d762AA7AF94
+subsystem_ns_list[1]->pdu_header_digest_required=0
+subsystem_ns_list[1]->data_digest_required=0
+subsystem_ns_list[1]->controller_id=4105
+subsystem_ns_list[1]->asqsz=0
+subsystem_ns_list[1]->dhcp_root_path_string=(null)
diff --git a/test/nbft/diffs/NBFT-Dell.PowerEdge.R760 b/test/nbft/diffs/NBFT-Dell.PowerEdge.R760
new file mode 100644
index 0000000..d7fab3f
--- /dev/null
+++ b/test/nbft/diffs/NBFT-Dell.PowerEdge.R760
@@ -0,0 +1,60 @@
+raw_nbft_size=1017
+host.id=44454c4c34010368038b2c04f313233
+host.nqn=nqn.1988-11.com.dell:PowerEdge.R760.1234567
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=16384
+hfi_list[0]->tcp_info.mac_addr=b02628e87ce
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=82
+hfi_list[0]->tcp_info.ipaddr=100.71.245.232
+hfi_list[0]->tcp_info.subnet_mask_prefix=24
+hfi_list[0]->tcp_info.gateway_ipaddr=100.71.245.254
+hfi_list[0]->tcp_info.route_metric=500
+hfi_list[0]->tcp_info.primary_dns_ipaddr=100.64.0.5
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=100.64.0.6
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=100.71.245.254
+hfi_list[0]->tcp_info.host_name=(null)
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=1
+discovery_list[0]->index=1
+discovery_list[0]->hfi->index=1
+discovery_list[0]->uri=nvme+tcp://100.71.103.50:8009/
+discovery_list[0]->nqn=nqn.2014-08.org.nvmexpress.discovery
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->discovery->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=100.71.103.48
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=148
+subsystem_ns_list[0]->nid_type=2
+subsystem_ns_list[0]->nid=c8244ed9c15f53b8ccf96802efca
+subsystem_ns_list[0]->subsys_nqn=nqn.1988-11.com.dell:powerstore:00:2a64abf1c5b81F6C4549
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=5
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
+subsystem_ns_list[1]->index=2
+subsystem_ns_list[1]->discovery->index=1
+subsystem_ns_list[1]->num_hfis=1
+subsystem_ns_list[1]->hfis[0]->index=1
+subsystem_ns_list[1]->transport=tcp
+subsystem_ns_list[1]->traddr=100.71.103.49
+subsystem_ns_list[1]->trsvcid=4420
+subsystem_ns_list[1]->subsys_port_id=0
+subsystem_ns_list[1]->nsid=148
+subsystem_ns_list[1]->nid_type=2
+subsystem_ns_list[1]->nid=c8244ed9c15f53b8ccf96802efca
+subsystem_ns_list[1]->subsys_nqn=nqn.1988-11.com.dell:powerstore:00:2a64abf1c5b81F6C4549
+subsystem_ns_list[1]->pdu_header_digest_required=0
+subsystem_ns_list[1]->data_digest_required=0
+subsystem_ns_list[1]->controller_id=4166
+subsystem_ns_list[1]->asqsz=0
+subsystem_ns_list[1]->dhcp_root_path_string=(null)
diff --git a/test/nbft/diffs/NBFT-auto-ipv6 b/test/nbft/diffs/NBFT-auto-ipv6
new file mode 100644
index 0000000..83ee643
--- /dev/null
+++ b/test/nbft/diffs/NBFT-auto-ipv6
@@ -0,0 +1,38 @@
+raw_nbft_size=721
+host.id=1ee8b170eb4c864fb7957d179e201a
+host.nqn=nqn.2014-08.org.nvmexpress:uuid:70b1e81e-4ceb-4f86-b709-57d1079e201a
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=512
+hfi_list[0]->tcp_info.mac_addr=525409e201a
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=1
+hfi_list[0]->tcp_info.ipaddr=fd09:9a46:b5c1:1ff:5054:ff:fe9e:201a
+hfi_list[0]->tcp_info.subnet_mask_prefix=64
+hfi_list[0]->tcp_info.gateway_ipaddr=::
+hfi_list[0]->tcp_info.route_metric=0
+hfi_list[0]->tcp_info.primary_dns_ipaddr=::
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=::
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=
+hfi_list[0]->tcp_info.host_name=nvmepoc-sles15-sp5
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=0
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=fd09:9a46:b5c1:1ff:5054:ff:fefd:9e66
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=1
+subsystem_ns_list[0]->nid_type=3
+subsystem_ns_list[0]->nid=848f4dc06d394968bf180569b8eea97
+subsystem_ns_list[0]->subsys_nqn=nqn.2022-12.de.suse.mwilck:zeus.vagrant-nvmet.subsys04
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=1
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
diff --git a/test/nbft/diffs/NBFT-dhcp-ipv4 b/test/nbft/diffs/NBFT-dhcp-ipv4
new file mode 100644
index 0000000..067079d
--- /dev/null
+++ b/test/nbft/diffs/NBFT-dhcp-ipv4
@@ -0,0 +1,43 @@
+raw_nbft_size=825
+host.id=e359b7a15d37747b3cc1754b8b819b9
+host.nqn=nqn.2014-08.org.nvmexpress:uuid:a1b759e3-035d-4777-b3cc-1754b8b819b9
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=8
+hfi_list[0]->tcp_info.mac_addr=52540b819b9
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=3
+hfi_list[0]->tcp_info.ipaddr=192.168.49.155
+hfi_list[0]->tcp_info.subnet_mask_prefix=24
+hfi_list[0]->tcp_info.gateway_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.route_metric=0
+hfi_list[0]->tcp_info.primary_dns_ipaddr=192.168.49.1
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=192.168.49.1
+hfi_list[0]->tcp_info.host_name=nvmeof-sles
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=1
+discovery_list[0]->index=1
+discovery_list[0]->hfi->index=1
+discovery_list[0]->uri=nvme+tcp://192.168.49.10:4420/
+discovery_list[0]->nqn=nqn.2014-08.org.nvmexpress.discovery
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->discovery->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=192.168.49.10
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=1
+subsystem_ns_list[0]->nid_type=3
+subsystem_ns_list[0]->nid=df669a88bd6f4dd68a505f97eb55c835
+subsystem_ns_list[0]->subsys_nqn=nqn.2022-12.org.nvmexpress.boot.poc:bremer.vagrant-nvmet.subsys02
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=7
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
diff --git a/test/nbft/diffs/NBFT-dhcp-ipv6 b/test/nbft/diffs/NBFT-dhcp-ipv6
new file mode 100644
index 0000000..11c974f
--- /dev/null
+++ b/test/nbft/diffs/NBFT-dhcp-ipv6
@@ -0,0 +1,38 @@
+raw_nbft_size=725
+host.id=e359b7a15d37747b3cc1754b8b819b9
+host.nqn=nqn.2014-08.org.nvmexpress:uuid:a1b759e3-035d-4777-b3cc-1754b8b819b9
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=8
+hfi_list[0]->tcp_info.mac_addr=52540b819b9
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=3
+hfi_list[0]->tcp_info.ipaddr=fddf:d:f:49::eb
+hfi_list[0]->tcp_info.subnet_mask_prefix=64
+hfi_list[0]->tcp_info.gateway_ipaddr=::
+hfi_list[0]->tcp_info.route_metric=0
+hfi_list[0]->tcp_info.primary_dns_ipaddr=::
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=::
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=::
+hfi_list[0]->tcp_info.host_name=nvmeof-sles
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=1
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=fddf:d:f:49::10
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=1
+subsystem_ns_list[0]->nid_type=3
+subsystem_ns_list[0]->nid=df669a88bd6f4dd68a505f97eb55c835
+subsystem_ns_list[0]->subsys_nqn=nqn.2022-12.org.nvmexpress.boot.poc:bremer.vagrant-nvmet.subsys02
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=34
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
diff --git a/test/nbft/diffs/NBFT-rhpoc b/test/nbft/diffs/NBFT-rhpoc
new file mode 100644
index 0000000..d849b6e
--- /dev/null
+++ b/test/nbft/diffs/NBFT-rhpoc
@@ -0,0 +1,38 @@
+raw_nbft_size=724
+host.id=b4bb164e7f9be448c7f77d8b4fc9f39
+host.nqn=nqn.2014-08.org.nvmexpress:uuid:f8131bac-cdef-4165-866b-5998c1e67890
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=40
+hfi_list[0]->tcp_info.mac_addr=eaebd3588958
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=1
+hfi_list[0]->tcp_info.ipaddr=192.168.101.30
+hfi_list[0]->tcp_info.subnet_mask_prefix=24
+hfi_list[0]->tcp_info.gateway_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.route_metric=0
+hfi_list[0]->tcp_info.primary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=
+hfi_list[0]->tcp_info.host_name=host-vm
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=0
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=192.168.101.20
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=1
+subsystem_ns_list[0]->nid_type=3
+subsystem_ns_list[0]->nid=bee9c2b7176144b5a4e6f69498a94b
+subsystem_ns_list[0]->subsys_nqn=nqn.2014-08.org.nvmexpress:uuid:0c468c4d-a385-47e0-8299-6e95051277db
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=12
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
diff --git a/test/nbft/diffs/NBFT-static-ipv4 b/test/nbft/diffs/NBFT-static-ipv4
new file mode 100644
index 0000000..a6f3859
--- /dev/null
+++ b/test/nbft/diffs/NBFT-static-ipv4
@@ -0,0 +1,38 @@
+raw_nbft_size=725
+host.id=e359b7a15d37747b3cc1754b8b819b9
+host.nqn=nqn.2014-08.org.nvmexpress:uuid:a1b759e3-035d-4777-b3cc-1754b8b819b9
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=8
+hfi_list[0]->tcp_info.mac_addr=52540b819b9
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=1
+hfi_list[0]->tcp_info.ipaddr=192.168.49.50
+hfi_list[0]->tcp_info.subnet_mask_prefix=24
+hfi_list[0]->tcp_info.gateway_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.route_metric=0
+hfi_list[0]->tcp_info.primary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=
+hfi_list[0]->tcp_info.host_name=nvmeof-sles
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=0
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=192.168.49.10
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=1
+subsystem_ns_list[0]->nid_type=3
+subsystem_ns_list[0]->nid=df669a88bd6f4dd68a505f97eb55c835
+subsystem_ns_list[0]->subsys_nqn=nqn.2022-12.org.nvmexpress.boot.poc:bremer.vagrant-nvmet.subsys02
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=38
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
diff --git a/test/nbft/diffs/NBFT-static-ipv4-discovery b/test/nbft/diffs/NBFT-static-ipv4-discovery
new file mode 100644
index 0000000..5bf0e46
--- /dev/null
+++ b/test/nbft/diffs/NBFT-static-ipv4-discovery
@@ -0,0 +1,43 @@
+raw_nbft_size=825
+host.id=e359b7a15d37747b3cc1754b8b819b9
+host.nqn=nqn.2014-08.org.nvmexpress:uuid:a1b759e3-035d-4777-b3cc-1754b8b819b9
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=8
+hfi_list[0]->tcp_info.mac_addr=52540b819b9
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=1
+hfi_list[0]->tcp_info.ipaddr=192.168.49.50
+hfi_list[0]->tcp_info.subnet_mask_prefix=24
+hfi_list[0]->tcp_info.gateway_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.route_metric=0
+hfi_list[0]->tcp_info.primary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=0.0.0.0
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=
+hfi_list[0]->tcp_info.host_name=nvmeof-sles
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=0
+discovery_list[0]->index=1
+discovery_list[0]->hfi->index=1
+discovery_list[0]->uri=nvme+tcp://192.168.49.10:4420/
+discovery_list[0]->nqn=nqn.2014-08.org.nvmexpress.discovery
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->discovery->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=192.168.49.10
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=1
+subsystem_ns_list[0]->nid_type=3
+subsystem_ns_list[0]->nid=df669a88bd6f4dd68a505f97eb55c835
+subsystem_ns_list[0]->subsys_nqn=nqn.2022-12.org.nvmexpress.boot.poc:bremer.vagrant-nvmet.subsys02
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=13
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
diff --git a/test/nbft/diffs/NBFT-static-ipv6 b/test/nbft/diffs/NBFT-static-ipv6
new file mode 100644
index 0000000..c6ad844
--- /dev/null
+++ b/test/nbft/diffs/NBFT-static-ipv6
@@ -0,0 +1,38 @@
+raw_nbft_size=721
+host.id=1ee8b170eb4c864fb7957d179e201a
+host.nqn=nqn.2014-08.org.nvmexpress:uuid:70b1e81e-4ceb-4f86-b709-57d1079e201a
+host.host_id_configured=1
+host.host_nqn_configured=1
+host.primary=0
+hfi_list[0]->index=1
+hfi_list[0]->transport=tcp
+hfi_list[0]->tcp_info.pci_sbdf=512
+hfi_list[0]->tcp_info.mac_addr=525409e201a
+hfi_list[0]->tcp_info.vlan=0
+hfi_list[0]->tcp_info.ip_origin=1
+hfi_list[0]->tcp_info.ipaddr=fd09:9a46:b5c1:1fe::10
+hfi_list[0]->tcp_info.subnet_mask_prefix=64
+hfi_list[0]->tcp_info.gateway_ipaddr=::
+hfi_list[0]->tcp_info.route_metric=0
+hfi_list[0]->tcp_info.primary_dns_ipaddr=::
+hfi_list[0]->tcp_info.secondary_dns_ipaddr=::
+hfi_list[0]->tcp_info.dhcp_server_ipaddr=
+hfi_list[0]->tcp_info.host_name=nvmepoc-sles15-sp5
+hfi_list[0]->tcp_info.this_hfi_is_default_route=1
+hfi_list[0]->tcp_info.dhcp_override=0
+subsystem_ns_list[0]->index=1
+subsystem_ns_list[0]->num_hfis=1
+subsystem_ns_list[0]->hfis[0]->index=1
+subsystem_ns_list[0]->transport=tcp
+subsystem_ns_list[0]->traddr=fd09:9a46:b5c1:1fe::13f
+subsystem_ns_list[0]->trsvcid=4420
+subsystem_ns_list[0]->subsys_port_id=0
+subsystem_ns_list[0]->nsid=1
+subsystem_ns_list[0]->nid_type=3
+subsystem_ns_list[0]->nid=aab2c3c8444c47c599f23632e6364528
+subsystem_ns_list[0]->subsys_nqn=nqn.2022-12.de.suse.mwilck:zeus.vagrant-nvmet.subsys04
+subsystem_ns_list[0]->pdu_header_digest_required=0
+subsystem_ns_list[0]->data_digest_required=0
+subsystem_ns_list[0]->controller_id=9
+subsystem_ns_list[0]->asqsz=0
+subsystem_ns_list[0]->dhcp_root_path_string=(null)
diff --git a/test/nbft/gen-nbft-diffs.sh.in b/test/nbft/gen-nbft-diffs.sh.in
new file mode 100755
index 0000000..8b0b982
--- /dev/null
+++ b/test/nbft/gen-nbft-diffs.sh.in
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+for T in "@TABLES_DIR@"/*; do
+ "@NBFT_DUMP_PATH@" "$T" > "@DIFF_DIR@/`basename $T`"
+done
diff --git a/test/nbft/meson.build b/test/nbft/meson.build
new file mode 100644
index 0000000..919bf83
--- /dev/null
+++ b/test/nbft/meson.build
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2023 Red Hat Inc.
+#
+# Authors: Tomas Bzatek <tbzatek@redhat.com>
+
+# NBFT parser tests over supplied NBFT ACPI table dumps
+
+tables_dir = 'tables'
+tables_bad_dir = 'tables_bad'
+diff_dir = 'diffs'
+
+tables = [
+ 'NBFT-auto-ipv6',
+ 'NBFT-dhcp-ipv6',
+ 'NBFT-rhpoc',
+ 'NBFT-static-ipv4',
+ 'NBFT-static-ipv4-discovery',
+ 'NBFT-static-ipv6',
+ 'NBFT-Dell.PowerEdge.R760',
+ 'NBFT-Dell.PowerEdge.R660-fw1.5.5-single'
+]
+
+tables_bad = [
+ 'NBFT-bad-oldspec',
+ 'NBFT-random-noise',
+]
+
+nbft_dump = executable(
+ 'nbft-dump',
+ ['nbft-dump.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+
+helper_data = configuration_data()
+helper_data.set('NBFT_DUMP_PATH', nbft_dump.full_path())
+helper_data.set('TABLES_DIR', meson.current_source_dir()/tables_dir)
+helper_data.set('DIFF_DIR', meson.current_source_dir()/diff_dir)
+
+dump_helper = configure_file(
+ input: 'nbft-dump-diff.sh.in',
+ output: '@BASENAME@',
+ configuration: helper_data
+)
+
+gen_diffs_helper = configure_file(
+ input: 'gen-nbft-diffs.sh.in',
+ output: '@BASENAME@',
+ configuration: helper_data
+)
+
+
+run_target(
+ 'nbft-diffs',
+ depends: nbft_dump,
+ command: [gen_diffs_helper]
+)
+
+
+diffcmd = find_program(
+ 'diff',
+ required: false
+)
+if diffcmd.found()
+ foreach table: tables
+ test(
+ table,
+ dump_helper,
+ args: [files(tables_dir/table),
+ files(diff_dir/table)]
+ )
+ endforeach
+endif
+
+foreach table: tables_bad
+ test(
+ table,
+ nbft_dump,
+ args: [files(tables_bad_dir/table)],
+ should_fail: true
+ )
+endforeach
diff --git a/test/nbft/nbft-dump-diff.sh.in b/test/nbft/nbft-dump-diff.sh.in
new file mode 100755
index 0000000..f697bce
--- /dev/null
+++ b/test/nbft/nbft-dump-diff.sh.in
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+if [ $# -ne 2 ]; then
+ echo "Usage: $0 TABLE DIFF" >&2
+ exit 255
+fi
+
+"@NBFT_DUMP_PATH@" "$1" | diff --unified "$2" -
diff --git a/test/nbft/nbft-dump.c b/test/nbft/nbft-dump.c
new file mode 100644
index 0000000..3ff5efa
--- /dev/null
+++ b/test/nbft/nbft-dump.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2023 Red Hat Inc.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include "libnvme.h"
+
+static void print_hex(unsigned char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++, buf++)
+ printf("%x", *buf);
+}
+
+static void print_nbft(struct nbft_info *table)
+{
+ unsigned int i, j;
+ struct nbft_info_hfi **hfi;
+ struct nbft_info_security **sec;
+ struct nbft_info_discovery **disc;
+ struct nbft_info_subsystem_ns **ssns;
+
+ printf("raw_nbft_size=%zd\n", table->raw_nbft_size);
+
+ printf("host.id=");
+ print_hex(table->host.id, NVME_UUID_LEN);
+ printf("\n");
+ printf("host.nqn=%s\n", table->host.nqn);
+ printf("host.host_id_configured=%d\n", table->host.host_id_configured);
+ printf("host.host_nqn_configured=%d\n", table->host.host_nqn_configured);
+ printf("host.primary=%d\n", table->host.primary);
+
+ for (hfi = table->hfi_list, i = 0; hfi && *hfi; hfi++, i++) {
+ printf("hfi_list[%u]->index=%d\n", i, (*hfi)->index);
+ printf("hfi_list[%u]->transport=%.*s\n", i, (int)sizeof((*hfi)->transport), (*hfi)->transport);
+ printf("hfi_list[%u]->tcp_info.pci_sbdf=%"PRIu32"\n", i, (*hfi)->tcp_info.pci_sbdf);
+ printf("hfi_list[%u]->tcp_info.mac_addr=", i);
+ print_hex((*hfi)->tcp_info.mac_addr, sizeof((*hfi)->tcp_info.mac_addr));
+ printf("\n");
+ printf("hfi_list[%u]->tcp_info.vlan=%"PRIu16"\n", i, (*hfi)->tcp_info.vlan);
+ printf("hfi_list[%u]->tcp_info.ip_origin=%u\n", i, (*hfi)->tcp_info.ip_origin);
+ printf("hfi_list[%u]->tcp_info.ipaddr=%s\n", i, (*hfi)->tcp_info.ipaddr);
+ printf("hfi_list[%u]->tcp_info.subnet_mask_prefix=%u\n", i, (*hfi)->tcp_info.subnet_mask_prefix);
+ printf("hfi_list[%u]->tcp_info.gateway_ipaddr=%s\n", i, (*hfi)->tcp_info.gateway_ipaddr);
+ printf("hfi_list[%u]->tcp_info.route_metric=%"PRIu16"\n", i, (*hfi)->tcp_info.route_metric);
+ printf("hfi_list[%u]->tcp_info.primary_dns_ipaddr=%s\n", i, (*hfi)->tcp_info.primary_dns_ipaddr);
+ printf("hfi_list[%u]->tcp_info.secondary_dns_ipaddr=%s\n", i, (*hfi)->tcp_info.secondary_dns_ipaddr);
+ printf("hfi_list[%u]->tcp_info.dhcp_server_ipaddr=%s\n", i, (*hfi)->tcp_info.dhcp_server_ipaddr);
+ printf("hfi_list[%u]->tcp_info.host_name=%s\n", i, (*hfi)->tcp_info.host_name);
+ printf("hfi_list[%u]->tcp_info.this_hfi_is_default_route=%d\n", i, (*hfi)->tcp_info.this_hfi_is_default_route);
+ printf("hfi_list[%u]->tcp_info.dhcp_override=%d\n", i, (*hfi)->tcp_info.dhcp_override);
+ }
+
+ for (sec = table->security_list, i = 0; sec && *sec; sec++, i++) {
+ printf("security_list[%u]->index=%d\n", i, (*sec)->index);
+ }
+
+ for (disc = table->discovery_list, i = 0; disc && *disc; disc++, i++) {
+ printf("discovery_list[%u]->index=%d\n", i, (*disc)->index);
+ if ((*disc)->security)
+ printf("discovery_list[%u]->security->index=%d\n", i, (*disc)->security->index);
+ if ((*disc)->hfi)
+ printf("discovery_list[%u]->hfi->index=%d\n", i, (*disc)->hfi->index);
+ printf("discovery_list[%u]->uri=%s\n", i, (*disc)->uri);
+ printf("discovery_list[%u]->nqn=%s\n", i, (*disc)->nqn);
+ }
+
+ for (ssns = table->subsystem_ns_list, i = 0; ssns && *ssns; ssns++, i++) {
+ printf("subsystem_ns_list[%u]->index=%d\n", i, (*ssns)->index);
+ if ((*ssns)->discovery)
+ printf("subsystem_ns_list[%u]->discovery->index=%d\n", i, (*ssns)->discovery->index);
+ if ((*ssns)->security)
+ printf("subsystem_ns_list[%u]->security->index=%d\n", i, (*ssns)->security->index);
+ printf("subsystem_ns_list[%u]->num_hfis=%d\n", i, (*ssns)->num_hfis);
+ for (hfi = (*ssns)->hfis, j = 0; hfi && *hfi; hfi++, j++)
+ printf("subsystem_ns_list[%u]->hfis[%u]->index=%d\n", i, j, (*hfi)->index);
+ printf("subsystem_ns_list[%u]->transport=%s\n", i, (*ssns)->transport);
+ printf("subsystem_ns_list[%u]->traddr=%s\n", i, (*ssns)->traddr);
+ printf("subsystem_ns_list[%u]->trsvcid=%s\n", i, (*ssns)->trsvcid);
+ printf("subsystem_ns_list[%u]->subsys_port_id=%"PRIu16"\n", i, (*ssns)->subsys_port_id);
+ printf("subsystem_ns_list[%u]->nsid=%"PRIu32"\n", i, (*ssns)->nsid);
+ printf("subsystem_ns_list[%u]->nid_type=%d\n", i, (*ssns)->nid_type);
+ printf("subsystem_ns_list[%u]->nid=", i);
+ print_hex((*ssns)->nid, 16);
+ printf("\n");
+ printf("subsystem_ns_list[%u]->subsys_nqn=%s\n", i, (*ssns)->subsys_nqn);
+ printf("subsystem_ns_list[%u]->pdu_header_digest_required=%d\n", i, (*ssns)->pdu_header_digest_required);
+ printf("subsystem_ns_list[%u]->data_digest_required=%d\n", i, (*ssns)->data_digest_required);
+ printf("subsystem_ns_list[%u]->controller_id=%d\n", i, (*ssns)->controller_id);
+ printf("subsystem_ns_list[%u]->asqsz=%d\n", i, (*ssns)->asqsz);
+ printf("subsystem_ns_list[%u]->dhcp_root_path_string=%s\n", i, (*ssns)->dhcp_root_path_string);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct nbft_info *table = NULL;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s TABLE\n", argv[0]);
+ return 1;
+ }
+
+ if (nvme_nbft_read(&table, argv[1]) != 0) {
+ fprintf(stderr, "Error parsing the NBFT table %s: %m\n",
+ argv[1]);
+ return 2;
+ }
+
+ print_nbft(table);
+
+ nvme_nbft_free(table);
+ return 0;
+}
diff --git a/test/nbft/tables/NBFT-Dell.PowerEdge.R660-fw1.5.5-single b/test/nbft/tables/NBFT-Dell.PowerEdge.R660-fw1.5.5-single
new file mode 100644
index 0000000..845a8e2
--- /dev/null
+++ b/test/nbft/tables/NBFT-Dell.PowerEdge.R660-fw1.5.5-single
Binary files differ
diff --git a/test/nbft/tables/NBFT-Dell.PowerEdge.R760 b/test/nbft/tables/NBFT-Dell.PowerEdge.R760
new file mode 120000
index 0000000..2e5c8dc
--- /dev/null
+++ b/test/nbft/tables/NBFT-Dell.PowerEdge.R760
@@ -0,0 +1 @@
+../../../libnvme/tests/NBFT \ No newline at end of file
diff --git a/test/nbft/tables/NBFT-auto-ipv6 b/test/nbft/tables/NBFT-auto-ipv6
new file mode 100644
index 0000000..64457d7
--- /dev/null
+++ b/test/nbft/tables/NBFT-auto-ipv6
Binary files differ
diff --git a/test/nbft/tables/NBFT-dhcp-ipv4 b/test/nbft/tables/NBFT-dhcp-ipv4
new file mode 100644
index 0000000..1af159d
--- /dev/null
+++ b/test/nbft/tables/NBFT-dhcp-ipv4
Binary files differ
diff --git a/test/nbft/tables/NBFT-dhcp-ipv6 b/test/nbft/tables/NBFT-dhcp-ipv6
new file mode 100644
index 0000000..20715ee
--- /dev/null
+++ b/test/nbft/tables/NBFT-dhcp-ipv6
Binary files differ
diff --git a/test/nbft/tables/NBFT-rhpoc b/test/nbft/tables/NBFT-rhpoc
new file mode 100644
index 0000000..5d0a6cc
--- /dev/null
+++ b/test/nbft/tables/NBFT-rhpoc
Binary files differ
diff --git a/test/nbft/tables/NBFT-static-ipv4 b/test/nbft/tables/NBFT-static-ipv4
new file mode 100644
index 0000000..bf3f840
--- /dev/null
+++ b/test/nbft/tables/NBFT-static-ipv4
Binary files differ
diff --git a/test/nbft/tables/NBFT-static-ipv4-discovery b/test/nbft/tables/NBFT-static-ipv4-discovery
new file mode 100644
index 0000000..7ebb40e
--- /dev/null
+++ b/test/nbft/tables/NBFT-static-ipv4-discovery
Binary files differ
diff --git a/test/nbft/tables/NBFT-static-ipv6 b/test/nbft/tables/NBFT-static-ipv6
new file mode 100644
index 0000000..07b09cf
--- /dev/null
+++ b/test/nbft/tables/NBFT-static-ipv6
Binary files differ
diff --git a/test/nbft/tables_bad/NBFT-bad-oldspec b/test/nbft/tables_bad/NBFT-bad-oldspec
new file mode 100644
index 0000000..e09d6ad
--- /dev/null
+++ b/test/nbft/tables_bad/NBFT-bad-oldspec
Binary files differ
diff --git a/test/nbft/tables_bad/NBFT-random-noise b/test/nbft/tables_bad/NBFT-random-noise
new file mode 100644
index 0000000..296bcfe
--- /dev/null
+++ b/test/nbft/tables_bad/NBFT-random-noise
Binary files differ
diff --git a/test/register.c b/test/register.c
new file mode 100644
index 0000000..8a41628
--- /dev/null
+++ b/test/register.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+/**
+ * Prints the values of the nvme register map. Use the nvme controller resource
+ * for your pci device found in /sys/class/nvme/nvmeX/device/resource0
+ */
+
+#define __SANE_USERSPACE_TYPES__
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libnvme.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+
+#include <ccan/endian/endian.h>
+
+static inline uint32_t nvme_mmio_read32(volatile void *addr)
+{
+ uint32_t *p = (__le32 *)addr;
+
+ return le32_to_cpu(*p);
+}
+
+static inline uint64_t nvme_mmio_read64(volatile void *addr)
+{
+ volatile __u32 *p = (__u32 *)addr;
+ uint32_t low, high;
+
+ low = nvme_mmio_read32(p);
+ high = nvme_mmio_read32(p + 1);
+
+ return low + ((uint64_t)high << 32);
+}
+
+void nvme_print_registers(void *regs)
+{
+ __u64 cap = nvme_mmio_read64(regs + NVME_REG_CAP);
+ __u32 vs = nvme_mmio_read32(regs + NVME_REG_VS);
+ __u32 intms = nvme_mmio_read32(regs + NVME_REG_INTMS);
+ __u32 intmc = nvme_mmio_read32(regs + NVME_REG_INTMC);
+ __u32 cc = nvme_mmio_read32(regs + NVME_REG_CC);
+ __u32 csts = nvme_mmio_read32(regs + NVME_REG_CSTS);
+ __u32 nssr = nvme_mmio_read32(regs + NVME_REG_NSSR);
+ __u32 aqa = nvme_mmio_read32(regs + NVME_REG_AQA);
+ __u64 asq = nvme_mmio_read64(regs + NVME_REG_ASQ);
+ __u64 acq = nvme_mmio_read64(regs + NVME_REG_ACQ);
+ __u32 cmbloc = nvme_mmio_read32(regs + NVME_REG_CMBLOC);
+ __u32 cmbsz = nvme_mmio_read32(regs + NVME_REG_CMBSZ);
+ __u32 bpinfo = nvme_mmio_read32(regs + NVME_REG_BPINFO);
+ __u32 bprsel = nvme_mmio_read32(regs + NVME_REG_BPRSEL);
+ __u64 bpmbl = nvme_mmio_read64(regs + NVME_REG_BPMBL);
+ __u64 cmbmsc = nvme_mmio_read64(regs + NVME_REG_CMBMSC);
+ __u32 cmbsts = nvme_mmio_read32(regs + NVME_REG_CMBSTS);
+ __u32 pmrcap = nvme_mmio_read32(regs + NVME_REG_PMRCAP);
+ __u32 pmrctl = nvme_mmio_read32(regs + NVME_REG_PMRCTL);
+ __u32 pmrsts = nvme_mmio_read32(regs + NVME_REG_PMRSTS);
+ __u32 pmrebs = nvme_mmio_read32(regs + NVME_REG_PMREBS);
+ __u32 pmrswtp = nvme_mmio_read32(regs + NVME_REG_PMRSWTP);
+ __u64 pmrmsc = nvme_mmio_read32(regs + NVME_REG_PMRMSCL) |
+ (__u64)nvme_mmio_read64(regs + NVME_REG_PMRMSCU) << 32;
+
+ printf("%-10s : %llx\n", "CAP", cap);
+ printf(" %-8s : %llx\n", "MQES", NVME_CAP_MQES(cap));
+ printf(" %-8s : %llx\n", "CQRS", NVME_CAP_CQR(cap));
+ printf(" %-8s : %llx\n", "AMS", NVME_CAP_AMS(cap));
+ printf(" %-8s : %llx\n", "TO", NVME_CAP_TO(cap));
+ printf(" %-8s : %llx\n", "DSTRD", NVME_CAP_DSTRD(cap));
+ printf(" %-8s : %llx\n", "NSSRC", NVME_CAP_NSSRC(cap));
+ printf(" %-8s : %llx\n", "CSS", NVME_CAP_CSS(cap));
+ printf(" %-8s : %llx\n", "BPS", NVME_CAP_BPS(cap));
+ printf(" %-8s : %llx\n", "MPSMIN", NVME_CAP_MPSMIN(cap));
+ printf(" %-8s : %llx\n", "MPSMAX", NVME_CAP_MPSMAX(cap));
+ printf(" %-8s : %llx\n", "CMBS", NVME_CAP_CMBS(cap));
+ printf(" %-8s : %llx\n", "PMRS", NVME_CAP_PMRS(cap));
+
+ printf("%-10s : %x\n", "VS", vs);
+ printf(" %-8s : %x\n", "MJR", NVME_VS_TER(vs));
+ printf(" %-8s : %x\n", "MNR", NVME_VS_MNR(vs));
+ printf(" %-8s : %x\n", "TER", NVME_VS_MJR(vs));
+
+ printf("%-10s : %x\n", "INTMS", intms);
+ printf("%-10s : %x\n", "INTMC", intmc);
+
+ printf("%-10s : %x\n", "CC", cc);
+ printf(" %-8s : %x\n", "EN", NVME_CC_EN(cc));
+ printf(" %-8s : %x\n", "CSS", NVME_CC_CSS(cc));
+ printf(" %-8s : %x\n", "MPS", NVME_CC_MPS(cc));
+ printf(" %-8s : %x\n", "AMS", NVME_CC_AMS(cc));
+ printf(" %-8s : %x\n", "SHN", NVME_CC_SHN(cc));
+ printf(" %-8s : %x\n", "IOSQES", NVME_CC_IOSQES(cc));
+ printf(" %-8s : %x\n", "IOCQES", NVME_CC_IOCQES(cc));
+
+ printf("%-10s : %x\n", "CSTS", csts);
+ printf(" %-8s : %x\n", "RDY", NVME_CSTS_RDY(csts));
+ printf(" %-8s : %x\n", "CFS", NVME_CSTS_CFS(csts));
+ printf(" %-8s : %x\n", "SHST", NVME_CSTS_SHST(csts));
+ printf(" %-8s : %x\n", "NSSRO", NVME_CSTS_NSSRO(csts));
+ printf(" %-8s : %x\n", "PP", NVME_CSTS_PP(csts));
+
+ printf("%-10s : %x\n", "NSSR", nssr);
+
+ printf("%-10s : %x\n", "AQA", aqa);
+ printf(" %-8s : %x\n", "ASQS", NVME_AQA_ASQS(aqa));
+ printf(" %-8s : %x\n", "ACQS", NVME_AQA_ACQS(aqa));
+
+ printf("%-10s : %llx\n", "ASQ", asq);
+ printf("%-10s : %llx\n", "ACQ", acq);
+
+ printf("%-10s : %x\n", "CMBLOC", cmbloc);
+ printf(" %-8s : %x\n", "BIR", NVME_CMBLOC_BIR(cmbloc));
+ printf(" %-8s : %x\n", "CQMMS", NVME_CMBLOC_CQMMS(cmbloc));
+ printf(" %-8s : %x\n", "CQPDS", NVME_CMBLOC_CQPDS(cmbloc));
+ printf(" %-8s : %x\n", "CDPLMS", NVME_CMBLOC_CDPLMS(cmbloc));
+ printf(" %-8s : %x\n", "CDPCILS", NVME_CMBLOC_CDPCILS(cmbloc));
+ printf(" %-8s : %x\n", "CDMMMS", NVME_CMBLOC_CDMMMS(cmbloc));
+ printf(" %-8s : %x\n", "CQDA", NVME_CMBLOC_CQDA(cmbloc));
+ printf(" %-8s : %x\n", "OFST", NVME_CMBLOC_OFST(cmbloc));
+
+ printf("%-10s : %x\n", "CMBSZ", cmbsz);
+ printf(" %-8s : %x\n", "SQS", NVME_CMBSZ_SQS(cmbsz));
+ printf(" %-8s : %x\n", "CQS", NVME_CMBSZ_CQS(cmbsz));
+ printf(" %-8s : %x\n", "LISTS", NVME_CMBSZ_LISTS(cmbsz));
+ printf(" %-8s : %x\n", "RDS", NVME_CMBSZ_RDS(cmbsz));
+ printf(" %-8s : %x\n", "WDS", NVME_CMBSZ_WDS(cmbsz));
+ printf(" %-8s : %x\n", "SZU", NVME_CMBSZ_SZU(cmbsz));
+ printf(" %-8s : %x\n", "SZ", NVME_CMBSZ_SZ(cmbsz));
+ printf(" %-8s : %llx\n", "bytes", nvme_cmb_size(cmbsz));
+
+ printf("%-10s : %x\n", "BPINFO", bpinfo);
+ printf(" %-8s : %x\n", "BPSZ", NVME_BPINFO_BPSZ(bpinfo));
+ printf(" %-8s : %x\n", "BRS", NVME_BPINFO_BRS(bpinfo));
+ printf(" %-8s : %x\n", "ABPID", NVME_BPINFO_ABPID(bpinfo));
+
+ printf("%-10s : %x\n", "BPRSEL", bprsel);
+ printf(" %-8s : %x\n", "BPRSZ", NVME_BPRSEL_BPRSZ(bprsel));
+ printf(" %-8s : %x\n", "BPROF", NVME_BPRSEL_BPROF(bprsel));
+ printf(" %-8s : %x\n", "BPID", NVME_BPRSEL_BPID(bprsel));
+
+ printf("%-10s : %llx\n", "BPMBL", bpmbl);
+
+ printf("%-10s : %llx\n", "CMBMSC", cmbmsc);
+ printf(" %-8s : %llx\n", "CRE", NVME_CMBMSC_CRE(cmbmsc));
+ printf(" %-8s : %llx\n", "CMSE", NVME_CMBMSC_CMSE(cmbmsc));
+ printf(" %-8s : %llx\n", "CBA", NVME_CMBMSC_CBA(cmbmsc));
+
+ printf("%-10s : %x\n", "CMBSTS", cmbsts);
+ printf(" %-8s : %x\n", "CBAI", NVME_CMBSTS_CBAI(cmbsts));
+
+ printf("%-10s : %x\n", "PMRCAP", pmrcap);
+ printf(" %-8s : %x\n", "RDS", NVME_PMRCAP_RDS(pmrcap));
+ printf(" %-8s : %x\n", "WDS", NVME_PMRCAP_WDS(pmrcap));
+ printf(" %-8s : %x\n", "BIR", NVME_PMRCAP_BIR(pmrcap));
+ printf(" %-8s : %x\n", "PMRTU", NVME_PMRCAP_PMRTU(pmrcap));
+ printf(" %-8s : %x\n", "PMRWMB", NVME_PMRCAP_PMRWMB(pmrcap));
+ printf(" %-8s : %x\n", "PMRTO", NVME_PMRCAP_PMRTO(pmrcap));
+ printf(" %-8s : %x\n", "CMSS", NVME_PMRCAP_CMSS(pmrcap));
+
+ printf("%-10s : %x\n", "PMRCTL", pmrctl);
+ printf(" %-8s : %x\n", "EN", NVME_PMRCTL_EN(pmrctl));
+
+ printf("%-10s : %x\n", "PMRSTS", pmrsts);
+ printf(" %-8s : %x\n", "ERR", NVME_PMRSTS_ERR(pmrsts));
+ printf(" %-8s : %x\n", "NRDY", NVME_PMRSTS_NRDY(pmrsts));
+ printf(" %-8s : %x\n", "HSTS", NVME_PMRSTS_HSTS(pmrsts));
+ printf(" %-8s : %x\n", "CBAI", NVME_PMRSTS_CBAI(pmrsts));
+
+ printf("%-10s : %x\n", "PMREBS", pmrebs);
+ printf(" %-8s : %x\n", "PMRSZU", NVME_PMREBS_PMRSZU(pmrebs));
+ printf(" %-8s : %x\n", "RBB", NVME_PMREBS_RBB(pmrebs));
+ printf(" %-8s : %x\n", "PMRWBZ", NVME_PMREBS_PMRWBZ(pmrebs));
+ printf(" %-8s : %llx\n", "bytes", nvme_pmr_size(pmrebs));
+
+ printf("%-10s : %x\n", "PMRSWTP", pmrswtp);
+ printf(" %-8s : %x\n", "PMRSWTU", NVME_PMRSWTP_PMRSWTU(pmrswtp));
+ printf(" %-8s : %x\n", "PMRSWTV", NVME_PMRSWTP_PMRSWTV(pmrswtp));
+ printf(" %-8s : %llx\n", "tput", nvme_pmr_throughput(pmrswtp));
+
+ printf("%-10s : %llx\n", "PMRMSC", pmrmsc);
+ printf(" %-8s : %llx\n", "CMSE", NVME_PMRMSC_CMSE(pmrmsc));
+ printf(" %-8s : %llx\n", "CBA", NVME_PMRMSC_CBA(pmrmsc));
+}
+
+int main(int argc, char **argv)
+{
+ int ret, fd;
+ char *path;
+ void *regs;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s nvme<X>\n", argv[0]);
+ return 1;
+ }
+
+ ret = asprintf(&path, "/sys/class/nvme/%s/device/resource0", argv[1]);
+ if (ret < 0)
+ return 0;
+
+ printf("open %s\n", path);
+ fd = open(path, O_RDONLY | O_SYNC);
+ if (fd < 0) {
+ fprintf(stderr, "failed to open %s\n", path);
+ free(path);
+ return 1;
+ }
+
+ regs = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0);
+ if (regs == MAP_FAILED) {
+ fprintf(stderr, "failed to map device BAR\n");
+ fprintf(stderr, "did your kernel enable CONFIG_IO_STRICT_DEVMEM?\n");
+ free(path);
+ close(fd);
+ return 1;
+ }
+
+ nvme_print_registers(regs);
+ munmap(regs, getpagesize());
+ free(path);
+ close(fd);
+
+ return 0;
+}
+
diff --git a/test/sysfs/data/nvme-sysfs-tw-carbon-6.8.0-rc1+.out b/test/sysfs/data/nvme-sysfs-tw-carbon-6.8.0-rc1+.out
new file mode 100644
index 0000000..1cb6de4
--- /dev/null
+++ b/test/sysfs/data/nvme-sysfs-tw-carbon-6.8.0-rc1+.out
@@ -0,0 +1,32 @@
+{
+ "hosts":[
+ {
+ "hostnqn":"nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6",
+ "hostid":"ce4fee3e-c02c-11ee-8442-830d068a36c6",
+ "subsystems":[
+ {
+ "name":"nvme-subsys1",
+ "nqn":"nqn.2019-08.org.qemu:nvme-0",
+ "controllers":[
+ {
+ "name":"nvme1",
+ "transport":"pcie",
+ "traddr":"0000:00:05.0"
+ }
+ ]
+ },
+ {
+ "name":"nvme-subsys0",
+ "nqn":"nqn.2019-08.org.qemu:subsys1",
+ "controllers":[
+ {
+ "name":"nvme0",
+ "transport":"pcie",
+ "traddr":"0000:0f:00.0"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/test/sysfs/data/nvme-sysfs-tw-carbon-6.8.0-rc1+.tar.xz b/test/sysfs/data/nvme-sysfs-tw-carbon-6.8.0-rc1+.tar.xz
new file mode 100644
index 0000000..ee11fde
--- /dev/null
+++ b/test/sysfs/data/nvme-sysfs-tw-carbon-6.8.0-rc1+.tar.xz
Binary files differ
diff --git a/test/sysfs/meson.build b/test/sysfs/meson.build
new file mode 100644
index 0000000..c004fc0
--- /dev/null
+++ b/test/sysfs/meson.build
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of libnvme.
+# Copyright (c) 2024 SUSE LLC.
+#
+# Authors: Daniel Wagner <dwagner@suse.de>
+
+
+sysfs = executable(
+ 'test-sysfs',
+ ['sysfs.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+sysfs_files= [
+ 'nvme-sysfs-tw-carbon-6.8.0-rc1+'
+]
+
+setup = find_program('setup.sh')
+
+foreach t_file : sysfs_files
+ r = run_command(setup, files('data'/t_file + '.tar.xz'), meson.current_build_dir(), check: true)
+ i = r.stdout().strip()
+ e0 = 'LIBNVME_SYSFS_PATH=' + i
+ e1 = 'LIBNVME_HOSTNQN=nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6'
+ e2 = 'LIBNVME_HOSTID=ce4fee3e-c02c-11ee-8442-830d068a36c6'
+ test('sysfs', sysfs, args : [ i, t_file + '.out', files('data'/t_file + '.out') ], env : [ e0, e1, e2 ])
+endforeach
diff --git a/test/sysfs/setup.sh b/test/sysfs/setup.sh
new file mode 100755
index 0000000..3437d4e
--- /dev/null
+++ b/test/sysfs/setup.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+TARFILE=$1
+BASEDIR=$2
+TESTDIR="$BASEDIR/$(basename -s .tar.xz ${TARFILE})"
+
+mkdir -p "${TESTDIR}"
+tar -x -f "${TARFILE}" -C "${TESTDIR}" || exit 1
+
+echo "${TESTDIR}"
diff --git a/test/sysfs/sysfs.c b/test/sysfs/sysfs.c
new file mode 100644
index 0000000..c2df178
--- /dev/null
+++ b/test/sysfs/sysfs.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2024 Daniel Wagner, SUSE LLC
+ */
+
+#include "nvme/tree.h"
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#include <ccan/array_size/array_size.h>
+
+#include <libnvme.h>
+#include <nvme/private.h>
+
+static bool test_sysfs(const char *path, const char *filename)
+{
+ FILE *f;
+ nvme_root_t r;
+ int err;
+
+ f = fopen(filename, "w");
+ if (!f)
+ return false;
+
+ r = nvme_create_root(f, LOG_ERR);
+ assert(r);
+
+ err = nvme_scan_topology(r, NULL, NULL);
+ if (!err)
+ nvme_dump_tree(r);
+ fprintf(f, "\n");
+
+ nvme_free_tree(r);
+ fclose(f);
+
+ return err == 0;
+}
+
+static bool compare_content(const char *filename1, const char *filename2)
+{
+ FILE *f1, *f2;
+ char c1, c2;
+ bool pass = false;
+
+ f1 = fopen(filename1, "r");
+ if (!f1)
+ return false;
+
+ f2 = fopen(filename2, "r");
+ if (!f2) {
+ fclose(f1);
+ return false;
+ }
+
+ do {
+ c1 = getc(f1);
+ c2 = getc(f2);
+ if (c1 != c2)
+ goto out;
+ } while (c1 != EOF || c2 != EOF);
+
+ if (c1 == c2)
+ pass = true;
+out:
+ fclose(f1);
+ fclose(f2);
+
+ return pass;
+}
+
+int main(int argc, char *argv[])
+{
+ bool pass = true;
+
+ if (argc < 4) {
+ fprintf(stderr, "usage: test-sysfs SYSFS_DIR OUTPUT_FILE COMPARE_FILE\n");
+ return EXIT_FAILURE;
+ }
+
+ pass &= test_sysfs(argv[1], argv[2]);
+ pass &= compare_content(argv[2], argv[3]);
+
+ exit(pass ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/test/test-util.c b/test/test-util.c
new file mode 100644
index 0000000..88a3f42
--- /dev/null
+++ b/test/test-util.c
@@ -0,0 +1,121 @@
+/**
+ SPDX-License-Identifier: LGPL-2.1-or-later
+
+ This file is part of libnvme.
+ Copyright (c) 2023 Dell Inc.
+
+ Authors: Martin Belanger <Martin.Belanger@dell.com>
+*/
+
+/**
+ * In this file we test private and public functions found in
+ * "src/nvme/util.c". Note that the source files are included
+ * directly because the private functions are not available from
+ * the libnvme.so.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <netdb.h>
+#include <string.h>
+
+#include "nvme/log.c" /* to resolve __nvme_msg() */
+#include "nvme/util.c"
+
+static size_t safe_strlen(const char *p) {
+ return p ? strlen(p) : strlen("null");
+}
+
+static bool test_nvme_get_version(enum nvme_version type, const char * exp_str) {
+ const char * str;
+ str = nvme_get_version(type);
+ return !strcmp(str, exp_str);
+}
+
+static bool test_ipaddrs_eq() {
+ int test_success = true;
+ static const char *x = "1.1.1.1";
+ struct {
+ const char *a;
+ const char *b;
+ bool exp_result;
+ } addrs[] = {
+ {"192.168.56.101", "192.168.56.101", true},
+ {"2001:0db8:0000:0000:0000:ff00:0042:8329", "2001:0db8::ff00:0042:8329", true},
+ {NULL, NULL, true},
+ {x, x, true},
+ {"::ffff:192.168.56.101", "::ffff:192.168.56.101", true},
+ {"::ffff:192.168.56.101", "192.168.56.101", true},
+ {"192.168.56.101", "::ffff:192.168.56.101", true},
+ {"::ffff:192.168.56.222", "192.168.56.101", false},
+ {"192.168.56.101", "::ffff:192.168.56.222", false},
+ {"1.2.3.4", "192.168.56.101", false},
+ {"!@#$", "192.168.56.101", false},
+ {"192.168.56.101", "!@#$", false},
+ {"2001:0db8:0001:0000:0000:ff00:0042:8329", "2001:0db8::ff00:0042:8329", false},
+ {"2001:0db8:0001:0000:0000:ff00:0042:8329", NULL, false},
+ };
+
+ size_t i;
+ size_t n = sizeof(addrs) / sizeof(addrs[0]);
+ size_t longest_a = 0, longest_b = 0;
+
+ for (i = 0; i < n; i++) {
+ size_t l;
+ l = safe_strlen(addrs[i].a);
+ if (l > longest_a) longest_a = l;
+ l = safe_strlen(addrs[i].b);
+ if (l > longest_b) longest_b = l;
+ }
+
+ for (i = 0; i < n; i++) {
+ bool result = nvme_ipaddrs_eq(addrs[i].a, addrs[i].b);
+ bool pass = result == addrs[i].exp_result;
+ int pad_a = longest_a - safe_strlen(addrs[i].a);
+ int pad_b = longest_b - safe_strlen(addrs[i].b);
+ printf("%s %*.*s %s %*.*s -> %-10s %s\n",
+ addrs[i].a ? addrs[i].a : "null",
+ pad_a, pad_a, "",
+ addrs[i].b ? addrs[i].b : "null",
+ pad_b, pad_b, "",
+ result ? "equal/same" : "different",
+ pass ? "[PASS]" : "[FAIL]");
+
+ if (!pass)
+ test_success = false;
+ }
+
+ return test_success;
+}
+
+int main(int argc, char *argv[]) {
+ int exit_val = EXIT_SUCCESS;
+ bool pass;
+
+ printf("\n------------------------------------------------------------------------------\n");
+ pass = test_nvme_get_version(NVME_VERSION_PROJECT, PROJECT_VERSION);
+ printf("nvme_get_version(NVME_VERSION_PROJECT) %s\n", pass ? "[PASS]" : "[FAIL]");
+ if (!pass)
+ exit_val = EXIT_FAILURE;
+
+ printf("\n------------------------------------------------------------------------------\n");
+ pass = test_nvme_get_version(NVME_VERSION_GIT, GIT_VERSION);
+ printf("nvme_get_version(NVME_VERSION_GIT) %s\n", pass ? "[PASS]" : "[FAIL]");
+ if (!pass)
+ exit_val = EXIT_FAILURE;
+
+ printf("\n------------------------------------------------------------------------------\n");
+ pass = test_nvme_get_version(-1, "n/a");
+ printf("nvme_get_version(-1) %s\n", pass ? "[PASS]" : "[FAIL]");
+ if (!pass)
+ exit_val = EXIT_FAILURE;
+
+ printf("\n------------------------------------------------------------------------------\n");
+ pass = test_ipaddrs_eq();
+ printf("nvme_ipaddrs_eq() %s", pass ? "[PASS]" : "[FAIL]");
+ if (!pass)
+ exit_val = EXIT_FAILURE;
+
+ exit(exit_val);
+}
diff --git a/test/test.c b/test/test.c
new file mode 100644
index 0000000..23036bb
--- /dev/null
+++ b/test/test.c
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+/**
+ * Basic libnvme test: uses scan filters, single controllers, and many admin
+ * command APIs for identifications, logs, and features. No verification for
+ * specific values are performed: the test will only report which commands
+ * executed were completed successfully or with an error. User inspection of
+ * the output woould be required to know if everything is working when the
+ * program exists successfully; an ungraceful exit means a bug exists
+ * somewhere.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <libnvme.h>
+
+#include <ccan/endian/endian.h>
+
+static bool nvme_match_subsysnqn_filter(nvme_subsystem_t s,
+ nvme_ctrl_t c, nvme_ns_t ns, void *f_args)
+{
+ char *nqn_match = f_args;
+
+ if (s)
+ return strcmp(nvme_subsystem_get_nqn(s), nqn_match) == 0;
+ return true;
+}
+
+static int test_ctrl(nvme_ctrl_t c)
+{
+ static __u8 buf[0x1000];
+
+ enum nvme_get_features_sel sel = NVME_GET_FEATURES_SEL_CURRENT;
+ int ret, temp, fd = nvme_ctrl_get_fd(c);
+ struct nvme_error_log_page error[64];
+ struct nvme_smart_log smart = { 0 };
+ struct nvme_firmware_slot fw = { 0 };
+ struct nvme_ns_list ns_list = { 0 };
+ struct nvme_cmd_effects_log cfx = { 0 };
+ struct nvme_self_test_log st = { 0 };
+ struct nvme_telemetry_log *telem = (void *)buf;
+ struct nvme_endurance_group_log eglog = { 0 };
+ struct nvme_ana_group_desc *analog = (void *)buf;
+ struct nvme_resv_notification_log resvnotify = { 0 };
+ struct nvme_sanitize_log_page sanlog = { 0 };
+ struct nvme_id_uuid_list uuid = { 0 };
+ struct nvme_id_ns_granularity_list gran = { 0 };
+ struct nvme_secondary_ctrl_list sec = { 0 };
+ struct nvme_primary_ctrl_cap prim = { 0 };
+ struct nvme_ctrl_list ctrlist = { 0 };
+ struct nvme_id_ctrl id = { 0 };
+
+ __u32 result;
+
+ ret = nvme_ctrl_identify(c, &id);
+ if (ret) {
+ printf("ERROR: no identify for:%s\n", nvme_ctrl_get_name(c));
+ return ret;
+ }
+ else {
+ printf("PASSED: Identify controller\n");
+ }
+
+ ret = nvme_get_log_smart(fd, NVME_NSID_ALL, true, &smart);
+ if (ret) {
+ printf("ERROR: no smart log for:%s %#x\n", nvme_ctrl_get_name(c), ret);
+ return ret;
+ }
+ else {
+ printf("PASSED: smart log\n");
+ }
+
+ temp = ((smart.temperature[1] << 8) | smart.temperature[0]) - 273;
+ printf("Controller:%s\n", nvme_ctrl_get_name(c));
+ printf("\nIdentify:\n");
+ printf(" vid:%#04x\n", le16_to_cpu(id.vid));
+ printf(" ssvid:%#04x\n", le16_to_cpu(id.ssvid));
+ printf(" oacs:%#x\n", id.oacs);
+ printf(" lpa:%#x\n", id.lpa);
+ printf(" sn:%-.20s\n", id.sn);
+ printf(" model:%-.40s\n", id.mn);
+
+ ret = nvme_identify_allocated_ns_list(fd, 0, &ns_list);
+ if (!ret)
+ printf(" PASSED: Allocated NS List\n");
+ else
+ printf(" ERROR: Allocated NS List:%x\n", ret);
+ ret = nvme_identify_active_ns_list(fd, 0, &ns_list);
+ if (!ret)
+ printf(" PASSED: Active NS List\n");
+ else
+ printf(" ERROR: Active NS List:%x\n", ret);
+ ret = nvme_identify_ctrl_list(fd, 0, &ctrlist);
+ if (!ret)
+ printf(" PASSED: Ctrl List\n");
+ else
+ printf(" ERROR: CtrlList:%x\n", ret);
+ ret = nvme_identify_nsid_ctrl_list(fd, 1, 0, &ctrlist);
+ if (!ret)
+ printf(" PASSED: NSID Ctrl List\n");
+ else
+ printf(" ERROR: NSID CtrlList:%x\n", ret);
+ ret = nvme_identify_primary_ctrl(fd, 0, &prim);
+ if (!ret)
+ printf(" PASSED: Identify Primary\n");
+ else
+ printf(" ERROR: Identify Primary:%x\n", ret);
+ ret = nvme_identify_secondary_ctrl_list(fd, 0, &sec);
+ if (!ret)
+ printf(" PASSED: Identify Secondary\n");
+ else
+ printf(" ERROR: Identify Secondary:%x\n", ret);
+ ret = nvme_identify_ns_granularity(fd, &gran);
+ if (!ret)
+ printf(" PASSED: Identify NS granularity\n");
+ else
+ printf(" ERROR: Identify NS granularity:%x\n", ret);
+ ret = nvme_identify_uuid(fd, &uuid);
+ if (!ret)
+ printf(" PASSED: Identify UUID List\n");
+ else
+ printf(" ERROR: Identify UUID List:%x\n", ret);
+
+ printf("\nLogs\n");
+ printf(" SMART: Current temperature:%d percent used:%d%%\n", temp,
+ smart.percent_used);
+ ret = nvme_get_log_sanitize(fd, true, &sanlog);
+ if (!ret)
+ printf(" Sanitize Log:\n");
+ else
+ printf(" ERROR: Sanitize Log:%x\n", ret);
+ ret = nvme_get_log_reservation(fd, true, &resvnotify);
+ if (!ret)
+ printf(" Reservation Log\n");
+ else
+ printf(" ERROR: Reservation Log:%x\n", ret);
+ ret = nvme_get_log_ana_groups(fd, true, sizeof(buf), analog);
+ if (!ret)
+ printf(" ANA Groups\n");
+ else
+ printf(" ERROR: ANA Groups:%x\n", ret);
+ ret = nvme_get_log_endurance_group(fd, 0, &eglog);
+ if (!ret)
+ printf(" Endurance Group\n");
+ else
+ printf(" ERROR: Endurance Group:%x\n", ret);
+ ret = nvme_get_log_telemetry_ctrl(fd, true, 0, sizeof(buf), telem);
+ if (!ret)
+ printf(" Telemetry Controller\n");
+ else
+ printf(" ERROR: Telemetry Controller:%x\n", ret);
+ ret = nvme_get_log_device_self_test(fd, &st);
+ if (!ret)
+ printf(" Device Self Test\n");
+ else
+ printf(" ERROR: Device Self Test:%x\n", ret);
+ ret = nvme_get_log_cmd_effects(fd, NVME_CSI_NVM, &cfx);
+ if (!ret)
+ printf(" Command Effects\n");
+ else
+ printf(" ERROR: Command Effects:%x\n", ret);
+ ret = nvme_get_log_changed_ns_list(fd, true, &ns_list);
+ if (!ret)
+ printf(" Change NS List\n");
+ else
+ printf(" ERROR: Change NS List:%x\n", ret);
+ ret = nvme_get_log_fw_slot(fd, true, &fw);
+ if (!ret)
+ printf(" FW Slot\n");
+ else
+ printf(" ERROR: FW Slot%x\n", ret);
+ ret = nvme_get_log_error(fd, 64, true, error);
+ if (!ret)
+ printf(" Error Log\n");
+ else
+ printf(" ERROR: Error Log:%x\n", ret);
+ printf("\nFeatures\n");
+ ret = nvme_get_features_arbitration(fd, sel, &result);
+ if (!ret)
+ printf(" Arbitration:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Arbitration:%x\n", ret);
+ ret = nvme_get_features_power_mgmt(fd, sel, &result);
+ if (!ret)
+ printf(" Power Management:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Power Management:%x\n", ret);
+ ret = nvme_get_features_temp_thresh(fd, sel, &result);
+ if (!ret)
+ printf(" Temperature Threshold:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Temperature Threshold:%x\n", ret);
+ ret = nvme_get_features_err_recovery2(fd, sel, 0, &result);
+ if (!ret)
+ printf(" Error Recovery:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Error Recovery:%x\n", ret);
+ ret = nvme_get_features_volatile_wc(fd, sel, &result);
+ if (!ret)
+ printf(" Volatile Write Cache:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Volatile Write Cache:%x\n", ret);
+ ret = nvme_get_features_num_queues(fd, sel, &result);
+ if (!ret)
+ printf(" Number of Queues:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Number of Queues:%x\n", ret);
+ ret = nvme_get_features_irq_coalesce(fd, sel, &result);
+ if (!ret)
+ printf(" IRQ Coalescing:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: IRQ Coalescing:%x\n", ret);
+ ret = nvme_get_features_write_atomic(fd, sel, &result);
+ if (!ret)
+ printf(" Write Atomic:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Write Atomic:%x\n", ret);
+ ret = nvme_get_features_async_event(fd, sel, &result);
+ if (!ret)
+ printf(" Asycn Event Config:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Asycn Event Config:%x\n", ret);
+ ret = nvme_get_features_hctm(fd, sel, &result);
+ if (!ret)
+ printf(" HCTM:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: HCTM:%x\n", ret);
+ ret = nvme_get_features_nopsc(fd, sel, &result);
+ if (!ret)
+ printf(" NOP Power State Config:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: NOP Power State Configrbitration:%x\n", ret);
+ ret = nvme_get_features_rrl(fd, sel, &result);
+ if (!ret)
+ printf(" Read Recover Levels:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Read Recover Levels:%x\n", ret);
+ ret = nvme_get_features_lba_sts_interval(fd, sel, &result);
+ if (!ret)
+ printf(" LBA Status Interval:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: LBA Status Interval:%x\n", ret);
+ ret = nvme_get_features_sanitize(fd, sel, &result);
+ if (!ret)
+ printf(" Sanitize:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: SW Progress Marker:%x\n", ret);
+ ret = nvme_get_features_sw_progress(fd, sel, &result);
+ if (!ret)
+ printf(" SW Progress Marker:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Sanitize:%x\n", ret);
+ ret = nvme_get_features_resv_mask2(fd, sel, 0, &result);
+ if (!ret)
+ printf(" Reservation Mask:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Reservation Mask:%x\n", ret);
+ ret = nvme_get_features_resv_persist2(fd, sel, 0, &result);
+ if (!ret)
+ printf(" Reservation Persistence:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Reservation Persistence:%x\n", ret);
+ return 0;
+}
+
+static int test_namespace(nvme_ns_t n)
+{
+ int ret, nsid = nvme_ns_get_nsid(n), fd = nvme_ns_get_fd(n);
+ struct nvme_id_ns ns = { 0 }, allocated = { 0 };
+ struct nvme_ns_id_desc *descs;
+ __u32 result = 0;
+ __u8 flbas;
+
+ ret = nvme_ns_identify(n, &ns);
+ if (ret)
+ return ret;
+
+ nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &flbas);
+ printf("%s: nsze:%" PRIu64 " lba size:%d\n",
+ nvme_ns_get_name(n), le64_to_cpu(ns.nsze),
+ 1 << ns.lbaf[flbas].ds);
+
+ ret = nvme_identify_allocated_ns(fd, nsid, &allocated);
+ if (!ret)
+ printf(" Identify allocated ns\n");
+ else
+ printf(" ERROR: Identify allocated ns:%x\n", ret);
+ descs = malloc(NVME_IDENTIFY_DATA_SIZE);
+ if (!descs)
+ return -1;
+
+ ret = nvme_identify_ns_descs(fd, nsid, descs);
+ if (!ret)
+ printf(" Identify NS Descriptors\n");
+ else
+ printf(" ERROR: Identify NS Descriptors:%x\n", ret);
+ free(descs);
+ ret = nvme_get_features_write_protect(fd, nsid,
+ NVME_GET_FEATURES_SEL_CURRENT, &result);
+ if (!ret)
+ printf(" Write Protect:%x\n", result);
+ else if (ret > 0)
+ printf(" ERROR: Write Protect:%x\n", ret);
+ return 0;
+}
+
+static void print_hex(const uint8_t *x, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ printf("%02x", x[i]);
+}
+
+int main(int argc, char **argv)
+{
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ nvme_path_t p;
+ nvme_ns_t n;
+ const char *ctrl = "nvme4";
+ const char *nqn_match = "testnqn";
+
+ printf("Test filter for common loop back target\n");
+ r = nvme_create_root(NULL, DEFAULT_LOGLEVEL);
+ if (!r)
+ return 1;
+ nvme_scan_topology(r, nvme_match_subsysnqn_filter, (void *)nqn_match);
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ printf("%s - NQN=%s\n", nvme_subsystem_get_name(s),
+ nvme_subsystem_get_nqn(s));
+ nvme_subsystem_for_each_ctrl(s, c) {
+ printf(" %s %s %s %s\n", nvme_ctrl_get_name(c),
+ nvme_ctrl_get_transport(c),
+ nvme_ctrl_get_address(c),
+ nvme_ctrl_get_state(c));
+ }
+ }
+ }
+ printf("\n");
+
+ if (argc > 1)
+ ctrl = argv[1];
+
+ printf("Test scan specific controller\n");
+ c = nvme_scan_ctrl(r, ctrl);
+ if (c) {
+ printf("%s %s %s %s\n", nvme_ctrl_get_name(c),
+ nvme_ctrl_get_transport(c),
+ nvme_ctrl_get_address(c),
+ nvme_ctrl_get_state(c));
+ nvme_free_ctrl(c);
+ }
+ printf("\n");
+ nvme_free_tree(r);
+
+ r = nvme_scan(NULL);
+ if (!r)
+ return -1;
+
+ printf("Test walking the topology\n");
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ printf("%s - NQN=%s\n", nvme_subsystem_get_name(s),
+ nvme_subsystem_get_nqn(s));
+ nvme_subsystem_for_each_ctrl(s, c) {
+ printf(" `- %s %s %s %s\n",
+ nvme_ctrl_get_name(c),
+ nvme_ctrl_get_transport(c),
+ nvme_ctrl_get_address(c),
+ nvme_ctrl_get_state(c));
+
+ nvme_ctrl_for_each_ns(c, n) {
+ char uuid_str[NVME_UUID_LEN_STRING];
+ unsigned char uuid[NVME_UUID_LEN];
+ printf(" `- %s lba size:%d lba max:%" PRIu64 "\n",
+ nvme_ns_get_name(n),
+ nvme_ns_get_lba_size(n),
+ nvme_ns_get_lba_count(n));
+ printf(" eui:");
+ print_hex(nvme_ns_get_eui64(n), 8);
+ printf(" nguid:");
+ print_hex(nvme_ns_get_nguid(n), 16);
+ nvme_ns_get_uuid(n, uuid);
+ nvme_uuid_to_string(uuid, uuid_str);
+ printf(" uuid:%s csi:%d\n", uuid_str,
+ nvme_ns_get_csi(n));
+ }
+
+ nvme_ctrl_for_each_path(c, p)
+ printf(" `- %s %s\n",
+ nvme_path_get_name(p),
+ nvme_path_get_ana_state(p));
+ }
+
+ nvme_subsystem_for_each_ns(s, n) {
+ printf(" `- %s lba size:%d lba max:%" PRIu64 "\n",
+ nvme_ns_get_name(n),
+ nvme_ns_get_lba_size(n),
+ nvme_ns_get_lba_count(n));
+ }
+ }
+ printf("\n");
+ }
+
+ printf("Test identification, logs, and features\n");
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ nvme_subsystem_for_each_ctrl(s, c) {
+ test_ctrl(c);
+ printf("\n");
+ nvme_ctrl_for_each_ns(c, n) {
+ test_namespace(n);
+ printf("\n");
+ }
+ }
+ nvme_subsystem_for_each_ns(s, n) {
+ test_namespace(n);
+ printf("\n");
+ }
+ }
+ }
+ nvme_free_tree(r);
+
+ return 0;
+}
diff --git a/test/tree.c b/test/tree.c
new file mode 100644
index 0000000..c9370f9
--- /dev/null
+++ b/test/tree.c
@@ -0,0 +1,1184 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2023 Daniel Wagner, SUSE LLC
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#include <ccan/array_size/array_size.h>
+
+#include <libnvme.h>
+#include <nvme/private.h>
+
+struct test_data {
+ /* input data */
+ const char *subsysname;
+ const char *subsysnqn;
+ const char *transport;
+ const char *traddr;
+ const char *host_traddr;
+ const char *host_iface;
+ const char *trsvcid;
+
+ /* track controller generated by input data */
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ int ctrl_id;
+};
+
+#define DEFAULT_SUBSYSNAME "subsysname"
+#define DEFAULT_SUBSYSNQN "subsysnqn"
+#define SRC_ADDR4 "192.168.56.100"
+#define SRC_ADDR6 "1234:5678:abcd:EF01:1234:5678:abcd:EF01"
+
+struct test_data test_data[] = {
+ { DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN, "tcp", "192.168.1.1", "192.168.1.20", NULL, "4420" },
+ { DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN, "tcp", "192.168.1.1", "192.168.1.20", NULL, "4421" },
+ { DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN, "tcp", "192.168.1.2", "192.168.1.20", "eth1", "4420" },
+ { DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN, "tcp", "192.168.1.2", "192.168.1.20", "eth1", "4421" },
+ { DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN, "rdma", "192.168.1.3", "192.168.1.20", NULL, NULL },
+ { DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN, "rdma", "192.168.1.4", "192.168.1.20", NULL, NULL },
+ { DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN, "fc",
+ "nn-0x201700a09890f5bf:pn-0x201900a09890f5bf",
+ "nn-0x200000109b579ef3:pn-0x100000109b579ef3"
+ },
+ { DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN, "fc",
+ "nn-0x201700a09890f5bf:pn-0x201900a09890f5bf",
+ "nn-0x200000109b579ef6:pn-0x100000109b579ef6",
+ },
+};
+
+static struct test_data *find_test_data(nvme_ctrl_t c)
+{
+ for (int i = 0; i < ARRAY_SIZE(test_data); i++) {
+ struct test_data *d = &test_data[i];
+
+ if (d->c == c)
+ return d;
+ }
+
+ return NULL;
+}
+
+static void show_ctrl(nvme_ctrl_t c)
+{
+ struct test_data *d = find_test_data(c);
+
+ if (d)
+ printf("ctrl%d ", d->ctrl_id);
+ else
+ printf(" ");
+
+ printf("0x%p: %s %s %s %s %s ",
+ c,
+ nvme_ctrl_get_transport(c),
+ nvme_ctrl_get_traddr(c),
+ nvme_ctrl_get_host_traddr(c),
+ nvme_ctrl_get_host_iface(c),
+ nvme_ctrl_get_trsvcid(c));
+}
+
+static bool match_ctrl(struct test_data *d, nvme_ctrl_t c)
+{
+ bool pass = true;
+ const char *trsvid, *host_traddr, *host_iface;
+
+ if (d->c != c)
+ pass = false;
+
+ if (strcmp(d->transport, nvme_ctrl_get_transport(d->c)))
+ pass = false;
+
+ if (strcmp(d->traddr, nvme_ctrl_get_traddr(d->c)))
+ pass = false;
+
+
+ host_traddr = nvme_ctrl_get_host_traddr(c);
+ if (d->host_traddr &&
+ (!host_traddr || strcmp(d->host_traddr, host_traddr)))
+ pass = false;
+
+ host_iface = nvme_ctrl_get_host_iface(c);
+ if (d->host_iface &&
+ (!host_iface || strcmp(d->host_iface, host_iface)))
+ pass = false;
+
+ trsvid = nvme_ctrl_get_trsvcid(c);
+ if (d->trsvcid &&
+ (!trsvid || strcmp(d->trsvcid, trsvid)))
+ pass = false;
+
+ printf("[%s]", pass? "PASS" : "FAILED");
+
+ return pass;
+}
+
+static nvme_root_t create_tree()
+{
+ nvme_root_t r;
+ nvme_host_t h;
+
+ r = nvme_create_root(stdout, LOG_DEBUG);
+ assert(r);
+ h = nvme_default_host(r);
+ assert(h);
+
+ printf(" ctrls created:\n");
+ for (int i = 0; i < ARRAY_SIZE(test_data); i++) {
+ struct test_data *d = &test_data[i];
+
+ d->s = nvme_lookup_subsystem(h, d->subsysname, d->subsysnqn);
+ assert(d->s);
+ d->c = nvme_lookup_ctrl(d->s, d->transport, d->traddr,
+ d->host_traddr, d->host_iface,
+ d->trsvcid, NULL);
+ assert(d->c);
+ d->ctrl_id = i;
+
+ printf(" ");
+ show_ctrl(d->c);
+ match_ctrl(d, d->c);
+ printf("\n");
+ }
+ printf("\n");
+
+ return r;
+}
+
+static unsigned int count_entries(nvme_root_t r)
+{
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ unsigned int i = 0;
+
+ nvme_for_each_host(r, h)
+ nvme_for_each_subsystem(h, s)
+ nvme_subsystem_for_each_ctrl(s, c)
+ i++;
+
+ return i;
+}
+
+static bool tcp_ctrl_lookup(nvme_subsystem_t s, struct test_data *d)
+{
+ nvme_ctrl_t c;
+ bool pass = true;
+
+ c = nvme_lookup_ctrl(s, d->transport, d->traddr, NULL,
+ NULL, d->trsvcid, NULL);
+ printf("%10s %12s %10s -> ", d->trsvcid, "", "");
+ show_ctrl(c);
+ pass &= match_ctrl(d, c);
+ printf("\n");
+
+ if (d->host_traddr) {
+ c = nvme_lookup_ctrl(s, d->transport, d->traddr, d->host_traddr,
+ NULL, d->trsvcid, NULL);
+ printf("%10s %12s %10s -> ", d->trsvcid, d->host_traddr, "");
+ show_ctrl(c);
+ pass &= match_ctrl(d, c);
+ printf("\n");
+ }
+
+ if (d->host_iface) {
+ c = nvme_lookup_ctrl(s, d->transport, d->traddr, NULL,
+ d->host_iface, d->trsvcid, NULL);
+ printf("%10s %12s %10s -> ", d->trsvcid, "", d->host_iface);
+ show_ctrl(c);
+ pass &= match_ctrl(d, c);
+ printf("\n");
+ }
+
+ if (d->host_iface && d->traddr) {
+ c = nvme_lookup_ctrl(s, d->transport, d->traddr, d->host_traddr,
+ d->host_iface, d->trsvcid, NULL);
+ printf("%10s %12s %10s -> ", d->trsvcid, d->host_traddr, d->host_iface);
+ show_ctrl(c);
+ pass &= match_ctrl(d, c);
+ printf("\n");
+ }
+
+ return pass;
+}
+
+static bool default_ctrl_lookup(nvme_subsystem_t s, struct test_data *d)
+{
+ nvme_ctrl_t c;
+ bool pass = true;
+
+ c = nvme_lookup_ctrl(s, d->transport, d->traddr, d->host_traddr,
+ NULL, NULL, NULL);
+ printf("%10s %12s %10s -> ", "", "", "");
+ show_ctrl(c);
+ pass &= match_ctrl(d, c);
+ printf("\n");
+
+ return pass;
+}
+
+static bool ctrl_lookups(nvme_root_t r)
+{
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ bool pass = true;
+
+ h = nvme_first_host(r);
+ s = nvme_lookup_subsystem(h, DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN);
+
+ printf(" lookup controller:\n");
+ for (int i = 0; i < ARRAY_SIZE(test_data); i++) {
+ struct test_data *d = &test_data[i];
+
+ printf("%10s %12s %10s ", "", "", "");
+ show_ctrl(d->c);
+ printf("\n");
+
+ if (!strcmp("tcp", d->transport))
+ pass &= tcp_ctrl_lookup(s, d);
+ else
+ pass &= default_ctrl_lookup(s, d);
+
+ printf("\n");
+ }
+
+ return pass;
+}
+
+static bool test_lookup(void)
+{
+ nvme_root_t r;
+ bool pass;
+
+ printf("test_lookup:\n");
+
+ r = create_tree();
+ pass = count_entries(r) == ARRAY_SIZE(test_data);
+ pass &= ctrl_lookups(r);
+
+ nvme_free_tree(r);
+
+ return pass;
+}
+
+static bool test_src_addr()
+{
+ bool pass = true;
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_ctrl_t c;
+ nvme_subsystem_t s;
+ char *src_addr, buffer[100]; /* big enough for IPv6 max length */
+
+ printf("\n"
+ "test_src_addr:\n");
+
+ r = nvme_create_root(stdout, LOG_DEBUG);
+ assert(r);
+
+ h = nvme_default_host(r);
+ assert(h);
+
+ s = nvme_lookup_subsystem(h, DEFAULT_SUBSYSNAME, DEFAULT_SUBSYSNQN);
+ assert(s);
+
+ c = nvme_lookup_ctrl(s, "tcp", "192.168.56.1", NULL, NULL, "8009", NULL);
+ assert(c);
+
+ c->address = NULL;
+ printf(" - Test c->address = NULL : src_addr = NULL ");
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (src_addr != NULL) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address=NULL should return src_addr=NULL\n");
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = "";
+ printf(" - Test c->address = \"\" : src_addr = NULL ");
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (src_addr != NULL) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address="" should return src_addr=NULL\n");
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = "traddr=192.168.56.1,trsvcid=8009";
+ printf(" - Test c->address = \"%s\" : src_addr = NULL ", c->address);
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (src_addr != NULL) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address=%s should return src_addr=NULL\n",
+ c->address);
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = "traddr=192.168.56.1,trsvcid=8009,src_addr=" SRC_ADDR4;
+ printf(" - Test c->address = \"%s\" : src_addr = \"" SRC_ADDR4 "\" ", c->address);
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (!src_addr || strcmp(src_addr, SRC_ADDR4)) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address=%s should return src_addr=" SRC_ADDR4 "\n",
+ c->address);
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = "traddr=192.168.56.1,src_addr=" SRC_ADDR4 ",trsvcid=8009";
+ printf(" - Test c->address = \"%s\" : src_addr = \"" SRC_ADDR4 "\" ", c->address);
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (!src_addr || strcmp(src_addr, SRC_ADDR4)) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address=%s should return src_addr=" SRC_ADDR4 "\n",
+ c->address);
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = "traddr=1234::abcd,trsvcid=8009,src_addr=" SRC_ADDR6;
+ printf(" - Test c->address = \"%s\" : src_addr = \"" SRC_ADDR6 "\" ", c->address);
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (!src_addr || strcmp(src_addr, SRC_ADDR6)) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address=%s should return src_addr=" SRC_ADDR6 "\n",
+ c->address);
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = "traddr=1234::abcd,src_addr=" SRC_ADDR6 ",trsvcid=8009";
+ printf(" - Test c->address = \"%s\" : src_addr = \"" SRC_ADDR6 "\" ", c->address);
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (!src_addr || strcmp(src_addr, SRC_ADDR6)) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address=%s should return src_addr=" SRC_ADDR6 "\n",
+ c->address);
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = "traddr=1234::abcd,trsvcid=8009,src_addr=" SRC_ADDR6 "%scope";
+ printf(" - Test c->address = \"%s\" : src_addr = \"" SRC_ADDR6 "\" ", c->address);
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (!src_addr || strcmp(src_addr, SRC_ADDR6)) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address=%s should return src_addr=" SRC_ADDR6 "\n",
+ c->address);
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = "traddr=1234::abcd,src_addr=" SRC_ADDR6 "%scope,trsvcid=8009";
+ printf(" - Test c->address = \"%s\" : src_addr = \"" SRC_ADDR6 "\" ", c->address);
+ src_addr = nvme_ctrl_get_src_addr(c, buffer, sizeof(buffer));
+ if (!src_addr || strcmp(src_addr, SRC_ADDR6)) {
+ printf("[FAIL]\n");
+ fprintf(stderr,
+ "nvme_ctrl_get_src_addr() c->address=%s should return src_addr=" SRC_ADDR6 "\n",
+ c->address);
+ pass = false;
+ } else {
+ printf("[PASS]\n");
+ }
+
+ c->address = NULL; /* Needed to avoid freeing non-malloced memory (see above) */
+
+ nvme_free_tree(r);
+
+ return pass;
+}
+
+struct ctrl_args {
+ const char *transport;
+ const char *traddr;
+ const char *trsvcid;
+ const char *host_traddr;
+ const char *host_iface;
+ const char *address;
+ const char *subsysnqn;
+};
+
+static void set_ctrl_args(struct ctrl_args *args,
+ const char *transport,
+ const char *traddr,
+ const char *trsvcid,
+ const char *host_traddr,
+ const char *host_iface,
+ const char *address,
+ const char *subsysnqn)
+{
+ args->transport = transport;
+ args->traddr = traddr;
+ args->trsvcid = trsvcid;
+ args->host_traddr = host_traddr;
+ args->host_iface = host_iface;
+ args->address = address;
+ args->subsysnqn = subsysnqn;
+}
+
+static bool ctrl_match(const char *tag,
+ int reference_id,
+ int candidate_id,
+ struct ctrl_args *reference,
+ struct ctrl_args *candidate,
+ bool should_match)
+{
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_ctrl_t reference_ctrl; /* Existing controller (from sysfs) */
+ nvme_ctrl_t candidate_ctrl;
+ nvme_ctrl_t found_ctrl;
+ nvme_subsystem_t s;
+
+ r = nvme_create_root(stdout, LOG_INFO);
+ assert(r);
+
+ h = nvme_default_host(r);
+ assert(h);
+
+ s = nvme_lookup_subsystem(h, DEFAULT_SUBSYSNAME, reference->subsysnqn ? reference->subsysnqn : DEFAULT_SUBSYSNQN);
+ assert(s);
+
+ reference_ctrl = nvme_lookup_ctrl(s, reference->transport, reference->traddr,
+ reference->host_traddr, reference->host_iface,
+ reference->trsvcid, NULL);
+ assert(reference_ctrl);
+ reference_ctrl->name = "nvme1"; /* fake the device name */
+ if (reference->address) {
+ reference_ctrl->address = (char *)reference->address;
+ }
+
+ /* nvme_ctrl_find() MUST BE RUN BEFORE nvme_lookup_ctrl() */
+ found_ctrl = nvme_ctrl_find(s, candidate->transport, candidate->traddr,
+ candidate->trsvcid, candidate->subsysnqn,
+ candidate->host_traddr,
+ candidate->host_iface);
+
+ candidate_ctrl = nvme_lookup_ctrl(s, candidate->transport, candidate->traddr,
+ candidate->host_traddr, candidate->host_iface,
+ candidate->trsvcid, NULL);
+
+ if (should_match) {
+ if (candidate_ctrl != reference_ctrl) {
+ printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s, %s) failed to match (%s, %s, %s, %s, %s, %s, %s)\n",
+ tag, reference_id, candidate_id,
+ candidate->transport, candidate->traddr, candidate->trsvcid,
+ candidate->subsysnqn, candidate->host_traddr, candidate->host_iface,
+ reference->transport, reference->traddr, reference->trsvcid, reference->subsysnqn,
+ reference->host_traddr, reference->host_iface, reference->address);
+ return false;
+ }
+
+ if (!found_ctrl) {
+ printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s, %s) failed to find controller\n",
+ tag, reference_id, candidate_id,
+ candidate->transport, candidate->traddr, candidate->trsvcid,
+ candidate->subsysnqn, candidate->host_traddr, candidate->host_iface);
+ return false;
+ }
+ } else {
+ if (candidate_ctrl == reference_ctrl) {
+ printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s, %s) should not match (%s, %s, %s, %s, %s, %s, %s)\n",
+ tag, reference_id, candidate_id,
+ candidate->transport, candidate->traddr, candidate->trsvcid,
+ candidate->subsysnqn, candidate->host_traddr, candidate->host_iface,
+ reference->transport, reference->traddr, reference->trsvcid, reference->subsysnqn,
+ reference->host_traddr, reference->host_iface, reference->address);
+ return false;
+ }
+
+ if (found_ctrl) {
+ printf("%s-%d-%d: Candidate (%s, %s, %s, %s, %s, %s) should not have found controller. found_ctrl=%p reference=%p\n",
+ tag, reference_id, candidate_id,
+ candidate->transport, candidate->traddr, candidate->trsvcid, candidate->subsysnqn,
+ candidate->host_traddr, candidate->host_iface, found_ctrl, reference_ctrl);
+ return false;
+ }
+ }
+
+ /* Set the faked data back to NULL before freeing the tree */
+ reference_ctrl->name = NULL;
+ reference_ctrl->address = NULL;
+
+ nvme_free_tree(r);
+
+ return true;
+}
+
+/**
+ * test_ctrl_match_fc - Test that we can look up FC controllers
+ *
+ * @return true when all tests have passed. false otherwise.
+ */
+static bool test_ctrl_match_fc(void)
+{
+ bool pass = true;
+ struct ctrl_args reference = {0};
+ struct ctrl_args candidate = {0};
+
+ printf("test_ctrl_match_fc:\n");
+
+ /*******************************************************************/
+ /* Reference ID 1 */
+ set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 1, 0, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 1, 1, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("FC", 1, 2, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("FC", 1, 3, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 1, 4, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:21", NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 1, 5, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL, NULL);
+ pass &= ctrl_match("FC", 1, 6, &reference, &candidate, false);
+
+
+ /*******************************************************************/
+ /* Reference ID 2 */
+ set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 2, 0, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 2, 1, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("FC", 2, 2, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("FC", 2, 3, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 2, 4, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL, NULL);
+ pass &= ctrl_match("FC", 2, 5, &reference, &candidate, false);
+
+
+ /*******************************************************************/
+ /* Reference ID 3 */
+ set_ctrl_args(&reference, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL, NULL);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", "21:00:00:e0:8b:05:05:20", NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 3, 0, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 3, 1, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("FC", 3, 2, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "fc", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("FC", 3, 3, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("FC", 3, 4, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "fc", "21:00:00:e0:8b:05:05:01", "8009", NULL, "", NULL, NULL);
+ pass &= ctrl_match("FC", 3, 5, &reference, &candidate, false);
+
+ return pass;
+}
+
+/**
+ * test_ctrl_match_rdma - Test that we can look up RDMA controllers
+ *
+ * @return true when all tests have passed. false otherwise.
+ */
+static bool test_ctrl_match_rdma(void)
+{
+ bool pass = true;
+ struct ctrl_args reference = {0};
+ struct ctrl_args candidate = {0};
+
+ printf("test_ctrl_match_rdma:\n");
+
+ /*******************************************************************/
+ /* Reference ID 1 */
+ set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 1, 0, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 1, 1, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("RDMA", 1, 2, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("RDMA", 1, 3, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 1, 4, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 1, 5, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL, NULL);
+ pass &= ctrl_match("RDMA", 1, 6, &reference, &candidate, false);
+
+
+ /*******************************************************************/
+ /* Reference ID 2 */
+ set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 2, 0, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 2, 1, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("RDMA", 2, 2, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("RDMA", 2, 3, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 2, 4, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL, NULL);
+ pass &= ctrl_match("RDMA", 2, 5, &reference, &candidate, false);
+
+
+ /*******************************************************************/
+ /* Reference ID 3 */
+ set_ctrl_args(&reference, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL, NULL);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 3, 0, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 3, 1, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("RDMA", 3, 2, &reference, &candidate, true);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.2", "4420", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("RDMA", 3, 3, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("RDMA", 3, 4, &reference, &candidate, false);
+
+ set_ctrl_args(&candidate, "rdma", "192.168.2.1", "8009", NULL, "", NULL, NULL);
+ pass &= ctrl_match("RDMA", 3, 5, &reference, &candidate, false);
+
+ return pass;
+}
+
+/**
+ * test_ctrl_match_tcp - Test that we can look up TCP controllers
+ *
+ * @note: The mocked getifaddrs() returns 2 interface entries with the
+ * following addresses. Therefore the tests must use IP addresses
+ * that match these.
+ *
+ * eth0
+ * \_ 192.168.1.20
+ * \_ fe80::dead:beef
+ *
+ * lo
+ * \_ 127.0.0.1
+ * \_ ::1
+ *
+ * @return true when all tests have passed. false otherwise.
+ */
+static bool test_ctrl_match_tcp()
+{
+ bool pass = true;
+ struct ctrl_args reference = {0};
+ struct ctrl_args candidate = {0};
+
+ printf("\n"
+ "test_ctrl_match_tcp:\n");
+
+ /*******************************************************************/
+ /* IPv4: Reference ID 1 */
+ set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 4, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 5, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 6, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 7, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 1, 8, &reference, &candidate, true);
+
+ /*******************************************************************/
+ /* IPv4: Reference ID 2 */
+ set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 2, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv4: Reference ID 3 */
+ set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 3, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv4: Reference ID 4 */
+ set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 4, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv4: Reference ID 5 */
+ set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 1, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 2, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 3, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 4, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 5, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv4: Reference ID 6 */
+ set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, "traddr=123.123.123.123,trsvcid=8009,src_addr=192.168.1.20", NULL);
+
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 6, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv4: Reference ID 7 */
+ set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, "traddr=123.123.123.123,trsvcid=8009,src_addr=127.0.0.1", NULL);
+
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 1, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 2, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 3, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 6, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv4", 7, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv6: Reference ID 1 */
+ set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 4, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 5, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 6, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 7, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 1, 8, &reference, &candidate, true);
+
+ /*******************************************************************/
+ /* IPv6: Reference ID 2 */
+ set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 2, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv6: Reference ID 3 */
+ set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 3, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv6: Reference ID 4 */
+ set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 4, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv6: Reference ID 5 */
+ set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 1, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 2, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 3, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 4, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 5, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv6: Reference ID 6 */
+ set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, "traddr=aaaa::bbbb,trsvcid=8009,src_addr=fe80::dead:beef", NULL);
+
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 6, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 6, 8, &reference, &candidate, false);
+
+ /*******************************************************************/
+ /* IPv6: Reference ID 7 */
+ set_ctrl_args(&reference, "tcp", "aaaa::bbbb", "8009", NULL, NULL, "traddr=aaaa::bbbb,trsvcid=8009,src_addr=::1", NULL);
+
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 1, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 2, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 3, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", NULL, NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 4, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "eth0", NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 5, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 6, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:beef", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 7, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "aaaa::bbbb", "8009", "fe80::dead:cafe", "lo", NULL, NULL);
+ pass &= ctrl_match("IPv6", 7, 8, &reference, &candidate, false);
+
+ return pass;
+}
+
+static bool ctrl_config_match(const char *tag,
+ int reference_id,
+ int candidate_id,
+ struct ctrl_args *reference,
+ struct ctrl_args *candidate,
+ bool should_match)
+{
+ bool match;
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_ctrl_t reference_ctrl; /* Existing controller (from sysfs) */
+ nvme_subsystem_t s;
+
+ r = nvme_create_root(stdout, LOG_INFO);
+ assert(r);
+
+ h = nvme_default_host(r);
+ assert(h);
+
+ s = nvme_lookup_subsystem(h, DEFAULT_SUBSYSNAME, reference->subsysnqn ? reference->subsysnqn : DEFAULT_SUBSYSNQN);
+ assert(s);
+
+ reference_ctrl = nvme_lookup_ctrl(s, reference->transport, reference->traddr,
+ reference->host_traddr, reference->host_iface,
+ reference->trsvcid, NULL);
+ assert(reference_ctrl);
+ reference_ctrl->name = "nvme1"; /* fake the device name */
+ if (reference->address) {
+ reference_ctrl->address = (char *)reference->address;
+ }
+
+ match = nvme_ctrl_config_match(reference_ctrl, candidate->transport, candidate->traddr,
+ candidate->trsvcid, candidate->subsysnqn,
+ candidate->host_traddr, candidate->host_iface);
+
+ if (should_match) {
+ if (!match) {
+ printf("%s-%d-%d: Failed to match config for Candidate (%s, %s, %s, %s, %s, %s)\n",
+ tag, reference_id, candidate_id,
+ candidate->transport, candidate->traddr, candidate->trsvcid,
+ candidate->subsysnqn, candidate->host_traddr, candidate->host_iface);
+ return false;
+ }
+ } else {
+ if (match) {
+ printf("%s-%d-%d: Config should not have matched for Candidate (%s, %s, %s, %s, %s, %s)\n",
+ tag, reference_id, candidate_id,
+ candidate->transport, candidate->traddr, candidate->trsvcid,
+ candidate->subsysnqn, candidate->host_traddr, candidate->host_iface);
+ return false;
+ }
+ }
+
+ /* Set the faked data back to NULL before freeing the tree */
+ reference_ctrl->name = NULL;
+ reference_ctrl->address = NULL;
+
+ nvme_free_tree(r);
+
+ return true;
+}
+
+static bool test_ctrl_config_match()
+{
+ bool pass = true;
+ struct ctrl_args reference = {0};
+ struct ctrl_args candidate = {0};
+
+ printf("\n"
+ "test_ctrl_config_match:\n");
+
+ set_ctrl_args(&reference, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, NULL, NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 0, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", NULL, NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 1, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "eth0", NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 2, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "eth0", NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 3, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", NULL, NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 4, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "eth0", NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 5, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", NULL, "lo", NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 6, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.20", "lo", NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 7, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, NULL);
+ pass &= ctrl_config_match("IPv4", 1, 8, &reference, &candidate, true);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, "hello");
+ pass &= ctrl_config_match("IPv4", 1, 9, &reference, &candidate, false);
+ set_ctrl_args(&candidate, "tcp", "123.123.123.123", "8009", "192.168.1.21", "lo", NULL, DEFAULT_SUBSYSNQN);
+ pass &= ctrl_config_match("IPv4", 1, 9, &reference, &candidate, true);
+
+ return pass;
+}
+
+
+/**
+ * This test module uses a mocked ifaddrs library (mock-ifaddrs.c)
+ * such that there are 2 fake interfaces (eth0 and lo) with the
+ * following IP addresses:
+ *
+ * - eth0
+ * \_ IPv4: 192.168.1.20
+ * \_ IPv6: fe80::dead:beef
+ *
+ * - lo
+ * \_ IPv4: 127.0.0.1
+ * \_ IPv6: ::1
+ */
+int main(int argc, char *argv[])
+{
+ bool pass = true;
+
+ pass &= test_lookup();
+ pass &= test_src_addr();
+ pass &= test_ctrl_match_fc();
+ pass &= test_ctrl_match_rdma();
+ pass &= test_ctrl_match_tcp();
+ pass &= test_ctrl_config_match();
+
+ fflush(stdout);
+
+ exit(pass ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/test/tree.py b/test/tree.py
new file mode 100644
index 0000000..626a0aa
--- /dev/null
+++ b/test/tree.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python3
+'''
+SPDX-License-Identifier: LGPL-3.1-or-later
+
+This file is part of libnvme.
+Copyright (c) 2021 SUSE Software Solutions AG
+
+Authors: Hannes Reinecke <hare@suse.de>
+
+Scans the NVMe subsystem and prints out all found hosts,
+subsystems, and controllers
+'''
+
+import libnvme
+
+r = libnvme.nvme_root()
+for h in r.hosts():
+ print (h)
+ for s in h.subsystems():
+ print (s)
+ for c in s.controllers():
+ print (c)
+
diff --git a/test/utils.c b/test/utils.c
new file mode 100644
index 0000000..60665b8
--- /dev/null
+++ b/test/utils.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ *
+ * Common test utilities.
+ *
+ * Copyright (c) 2022 Code Construct
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+FILE *test_setup_log(void)
+{
+ FILE *fd;
+
+ fd = tmpfile();
+ if (!fd)
+ err(EXIT_FAILURE, "can't create temporary file for log buf");
+
+ return fd;
+}
+
+void test_close_log(FILE *fd)
+{
+ fclose(fd);
+}
+
+void test_print_log_buf(FILE *logfd)
+{
+ char buf[4096];
+ int rc;
+
+ if (!ftell(logfd))
+ return;
+
+ rewind(logfd);
+
+ printf("--- begin test output\n");
+
+ while (!feof(logfd) && !ferror(logfd)) {
+ size_t rlen, wlen, wpos;
+
+ rlen = fread(buf, 1, sizeof(buf), logfd);
+ if (rlen <= 0)
+ break;
+
+ for (wpos = 0; wpos < rlen;) {
+ wlen = fwrite(buf + wpos, 1, rlen - wpos, stdout);
+ if (wlen == 0)
+ break;
+ wpos += wlen;
+ }
+
+ if (feof(logfd) || ferror((logfd)))
+ break;
+ }
+
+ printf("--- end test output\n");
+ rewind(logfd);
+ rc = ftruncate(fileno(logfd), 0);
+ if (rc)
+ printf("failed to truncate log buf; further output may be invalid\n");
+}
+
diff --git a/test/utils.h b/test/utils.h
new file mode 100644
index 0000000..e86f6e6
--- /dev/null
+++ b/test/utils.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2022 Code Construct
+ *
+ * Common test utilities for libnvme tests. These have quite strict error
+ * handling, so the general pattern is to abort/exit on error.
+ */
+
+#ifndef _TEST_UTILS_H
+#define _TEST_UTILS_H
+
+#include <stdio.h>
+
+FILE *test_setup_log(void);
+void test_print_log_buf(FILE *logfd);
+void test_close_log(FILE *fd);
+
+#endif /* _TEST_UTILS_H */
+
diff --git a/test/uuid.c b/test/uuid.c
new file mode 100644
index 0000000..9146453
--- /dev/null
+++ b/test/uuid.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2022 Daniel Wagner, SUSE Software Solutions
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <ccan/array_size/array_size.h>
+
+#include <libnvme.h>
+
+static int test_rc;
+
+struct test_data {
+ unsigned char uuid[NVME_UUID_LEN];
+ const char *str;
+};
+
+static struct test_data test_data[] = {
+ { { 0 }, "00000000-0000-0000-0000-000000000000" },
+ { { [0 ... 15] = 0xff }, "ffffffff-ffff-ffff-ffff-ffffffffffff" },
+ { { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0f, 0x10 },
+ "00010203-0405-0607-0809-0a0b0c0d0f10" },
+};
+
+static void check_str(const char *exp, const char *res)
+{
+ if (!strcmp(res, exp))
+ return;
+
+ printf("ERROR: got '%s', expected '%s'\n", res, exp);
+
+ test_rc = 1;
+}
+
+static void print_uuid_hex(const unsigned char uuid[NVME_UUID_LEN])
+{
+ for (int i = 0; i < NVME_UUID_LEN; i++)
+ printf("%02x", uuid[i]);
+}
+
+static void check_uuid(unsigned char exp[NVME_UUID_LEN],
+ unsigned char res[NVME_UUID_LEN])
+{
+ if (!memcmp(exp, res, NVME_UUID_LEN))
+ return;
+
+ printf("ERROR: got '");
+ print_uuid_hex(exp);
+ printf("', expected '");
+ print_uuid_hex(res);
+ printf("'\n");
+}
+
+static void tostr_test(struct test_data *test)
+{
+ char str[NVME_UUID_LEN_STRING];
+
+ if (nvme_uuid_to_string(test->uuid, str)) {
+ test_rc = 1;
+ printf("ERROR: nvme_uuid_to_string() failed\n");
+ return;
+ }
+ check_str(test->str, str);
+}
+
+static void fromstr_test(struct test_data *test)
+{
+
+ unsigned char uuid[NVME_UUID_LEN];
+
+ if (nvme_uuid_from_string(test->str, uuid)) {
+ test_rc = 1;
+ printf("ERROR: nvme_uuid_from_string() failed\n");
+ return;
+ }
+ check_uuid(test->uuid, uuid);
+}
+
+static void random_uuid_test(void)
+{
+ unsigned char uuid1[NVME_UUID_LEN], uuid2[NVME_UUID_LEN];
+ char str1[NVME_UUID_LEN_STRING], str2[NVME_UUID_LEN_STRING];
+
+ if (nvme_uuid_random(uuid1) || nvme_uuid_random(uuid2)) {
+ test_rc = 1;
+ printf("ERROR: nvme_uuid_random() failed\n");
+ return;
+ }
+
+ if (!memcmp(uuid1, uuid2, NVME_UUID_LEN)) {
+ test_rc = 1;
+ printf("ERROR: generated random numbers are equal\n");
+ return;
+ }
+
+ if (nvme_uuid_to_string(uuid1, str1) ||
+ nvme_uuid_to_string(uuid2, str2)) {
+ test_rc = 1;
+ printf("ERROR: could not stringify randomly generated UUID\n");
+ return;
+ }
+ printf("PASS: generated UUIDs %s %s\n", str1, str2);
+}
+
+int main(void)
+{
+ for (int i = 0; i < ARRAY_SIZE(test_data); i++)
+ tostr_test(&test_data[i]);
+
+ for (int i = 0; i < ARRAY_SIZE(test_data); i++)
+ fromstr_test(&test_data[i]);
+
+ random_uuid_test();
+
+ return test_rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/test/zns.c b/test/zns.c
new file mode 100644
index 0000000..6d06d72
--- /dev/null
+++ b/test/zns.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors: Keith Busch <keith.busch@wdc.com>
+ */
+
+/**
+ * Search out for ZNS type namespaces, and if found, report their properties.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <libnvme.h>
+#include <inttypes.h>
+
+#include <ccan/endian/endian.h>
+
+static void show_zns_properties(nvme_ns_t n)
+{
+ struct nvme_zns_id_ns zns_ns;
+ struct nvme_zns_id_ctrl zns_ctrl;
+ struct nvme_zone_report *zr;
+ __u32 result;
+
+ zr = calloc(1, 0x1000);
+ if (!zr)
+ return;
+
+ if (nvme_zns_identify_ns(nvme_ns_get_fd(n), nvme_ns_get_nsid(n),
+ &zns_ns)) {
+ fprintf(stderr, "failed to identify zns ns\n");;
+ }
+
+ printf("zoc:%x ozcs:%x mar:%x mor:%x\n", le16_to_cpu(zns_ns.zoc),
+ le16_to_cpu(zns_ns.ozcs), le32_to_cpu(zns_ns.mar),
+ le32_to_cpu(zns_ns.mor));
+
+ if (nvme_zns_identify_ctrl(nvme_ns_get_fd(n), &zns_ctrl)) {
+ fprintf(stderr, "failed to identify zns ctrl\n");;
+ free(zr);
+ return;
+ }
+
+ printf("zasl:%u\n", zns_ctrl.zasl);
+
+ if (nvme_zns_report_zones(nvme_ns_get_fd(n), nvme_ns_get_nsid(n), 0,
+ NVME_ZNS_ZRAS_REPORT_ALL, false,
+ true, 0x1000, (void *)zr,
+ NVME_DEFAULT_IOCTL_TIMEOUT, &result)) {
+ fprintf(stderr, "failed to report zones, result %x\n",
+ le32_to_cpu(result));
+ free(zr);
+ return;
+ }
+
+ printf("nr_zones:%"PRIu64"\n", le64_to_cpu(zr->nr_zones));
+ free(zr);
+}
+
+int main()
+{
+ nvme_subsystem_t s;
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_ctrl_t c;
+ nvme_ns_t n;
+
+ r = nvme_scan(NULL);
+ if (!r)
+ return -1;
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ nvme_subsystem_for_each_ctrl(s, c) {
+ nvme_ctrl_for_each_ns(c, n) {
+ if (nvme_ns_get_csi(n) == NVME_CSI_ZNS)
+ show_zns_properties(n);
+ }
+ }
+ nvme_subsystem_for_each_ns(s, n) {
+ if (nvme_ns_get_csi(n) == NVME_CSI_ZNS)
+ show_zns_properties(n);
+ }
+ }
+ }
+ nvme_free_tree(r);
+}