#include "nvme_internal.h"
static int nvme_admin_submit_cmd(struct nvme_ctrlr *ctrlr,
struct nvme_cmd *cmd,
void *buf, uint32_t len,
nvme_cmd_cb cb_fn, void *cb_arg)
{
struct nvme_request *req;
if (buf)
req = nvme_request_allocate_contig(&ctrlr->adminq, buf, len,
cb_fn, cb_arg);
else
req = nvme_request_allocate_null(&ctrlr->adminq, cb_fn, cb_arg);
if (!req)
return ENOMEM;
memcpy(&req->cmd, cmd, sizeof(req->cmd));
return nvme_qpair_submit_request(&ctrlr->adminq, req);
}
static int nvme_admin_wait_cmd(struct nvme_ctrlr *ctrlr,
struct nvme_completion_poll_status *status)
{
while (status->done == false)
nvme_qpair_poll(&ctrlr->adminq, 0);
if (nvme_cpl_is_error(&status->cpl)) {
nvme_notice("Admin command failed\n");
return ENXIO;
}
return 0;
}
static int nvme_admin_exec_cmd(struct nvme_ctrlr *ctrlr,
struct nvme_cmd *cmd,
void *buf, uint32_t len)
{
struct nvme_completion_poll_status status;
int ret;
status.done = false;
ret = nvme_admin_submit_cmd(ctrlr, cmd, buf, len,
nvme_request_completion_poll_cb,
&status);
if (ret != 0)
return ret;
return nvme_admin_wait_cmd(ctrlr, &status);
}
int nvme_admin_identify_ctrlr(struct nvme_ctrlr *ctrlr,
struct nvme_ctrlr_data *cdata)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_IDENTIFY;
cmd.cdw10 = NVME_IDENTIFY_CTRLR;
return nvme_admin_exec_cmd(ctrlr, &cmd,
cdata, sizeof(struct nvme_ctrlr_data));
}
int nvme_admin_get_feature(struct nvme_ctrlr *ctrlr,
enum nvme_feat_sel sel,
enum nvme_feat feature,
uint32_t cdw11,
uint32_t *attributes)
{
struct nvme_completion_poll_status status;
struct nvme_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_GET_FEATURES;
cmd.cdw10 = (sel << 8) | feature;
cmd.cdw11 = cdw11;
status.done = false;
ret = nvme_admin_submit_cmd(ctrlr, &cmd, NULL, 0,
nvme_request_completion_poll_cb,
&status);
if (ret == 0) {
ret = nvme_admin_wait_cmd(ctrlr, &status);
if (ret == 0 && attributes)
*attributes = status.cpl.cdw0;
}
return ret;
}
int nvme_admin_set_feature(struct nvme_ctrlr *ctrlr,
bool save,
enum nvme_feat feature,
uint32_t cdw11, uint32_t cdw12,
uint32_t cdw13, uint32_t cdw14, uint32_t cdw15,
void *buf, uint32_t len,
uint32_t *attributes)
{
struct nvme_completion_poll_status status;
struct nvme_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_SET_FEATURES;
cmd.cdw10 = feature;
if (save)
cmd.cdw10 |= (1 << 31);
cmd.cdw11 = cdw11;
cmd.cdw12 = cdw12;
cmd.cdw13 = cdw13;
cmd.cdw14 = cdw14;
cmd.cdw15 = cdw15;
status.done = false;
ret = nvme_admin_submit_cmd(ctrlr, &cmd, buf, len,
nvme_request_completion_poll_cb,
&status);
if (ret == 0) {
ret = nvme_admin_wait_cmd(ctrlr, &status);
if (ret == 0 && attributes)
*attributes = status.cpl.cdw0;
}
return ret;
}
int nvme_admin_create_ioq(struct nvme_ctrlr *ctrlr,
struct nvme_qpair *qpair,
enum nvme_io_queue_type io_qtype)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
switch(io_qtype) {
case NVME_IO_SUBMISSION_QUEUE:
cmd.opc = NVME_OPC_CREATE_IO_SQ;
cmd.cdw11 = (qpair->id << 16) | (qpair->qprio << 1) | 0x1;
cmd.dptr.prp.prp1 = qpair->cmd_bus_addr;
break;
case NVME_IO_COMPLETION_QUEUE:
cmd.opc = NVME_OPC_CREATE_IO_CQ;
#ifdef __HAIKU__
cmd.cdw11 = 0x1 | 0x2;
#else
cmd.cdw11 = 0x1;
#endif
cmd.dptr.prp.prp1 = qpair->cpl_bus_addr;
break;
default:
return EINVAL;
}
cmd.cdw10 = ((qpair->entries - 1) << 16) | qpair->id;
return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
}
int nvme_admin_delete_ioq(struct nvme_ctrlr *ctrlr,
struct nvme_qpair *qpair,
enum nvme_io_queue_type io_qtype)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
switch(io_qtype) {
case NVME_IO_SUBMISSION_QUEUE:
cmd.opc = NVME_OPC_DELETE_IO_SQ;
break;
case NVME_IO_COMPLETION_QUEUE:
cmd.opc = NVME_OPC_DELETE_IO_CQ;
break;
default:
return EINVAL;
}
cmd.cdw10 = qpair->id;
return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
}
int nvme_admin_identify_ns(struct nvme_ctrlr *ctrlr,
uint16_t nsid,
struct nvme_ns_data *nsdata)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_IDENTIFY;
cmd.cdw10 = NVME_IDENTIFY_NS;
cmd.nsid = nsid;
return nvme_admin_exec_cmd(ctrlr, &cmd,
nsdata, sizeof(struct nvme_ns_data));
}
int nvme_admin_attach_ns(struct nvme_ctrlr *ctrlr,
uint32_t nsid,
struct nvme_ctrlr_list *clist)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_NS_ATTACHMENT;
cmd.nsid = nsid;
cmd.cdw10 = NVME_NS_CTRLR_ATTACH;
return nvme_admin_exec_cmd(ctrlr, &cmd,
clist, sizeof(struct nvme_ctrlr_list));
}
int nvme_admin_detach_ns(struct nvme_ctrlr *ctrlr,
uint32_t nsid,
struct nvme_ctrlr_list *clist)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_NS_ATTACHMENT;
cmd.nsid = nsid;
cmd.cdw10 = NVME_NS_CTRLR_DETACH;
return nvme_admin_exec_cmd(ctrlr, &cmd,
clist, sizeof(struct nvme_ctrlr_list));
}
int nvme_admin_create_ns(struct nvme_ctrlr *ctrlr,
struct nvme_ns_data *nsdata,
unsigned int *nsid)
{
struct nvme_completion_poll_status status;
struct nvme_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_NS_MANAGEMENT;
cmd.cdw10 = NVME_NS_MANAGEMENT_CREATE;
status.done = false;
ret = nvme_admin_submit_cmd(ctrlr, &cmd,
nsdata, sizeof(struct nvme_ns_data),
nvme_request_completion_poll_cb,
&status);
if (ret == 0)
ret = nvme_admin_wait_cmd(ctrlr, &status);
if (ret != 0)
return ret;
*nsid = status.cpl.cdw0;
return 0;
}
int nvme_admin_delete_ns(struct nvme_ctrlr *ctrlr,
unsigned int nsid)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_NS_MANAGEMENT;
cmd.cdw10 = NVME_NS_MANAGEMENT_DELETE;
cmd.nsid = nsid;
return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
}
int nvme_admin_format_nvm(struct nvme_ctrlr *ctrlr,
unsigned int nsid,
struct nvme_format *format)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_FORMAT_NVM;
cmd.nsid = nsid;
memcpy(&cmd.cdw10, format, sizeof(uint32_t));
return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
}
int nvme_admin_get_log_page(struct nvme_ctrlr *ctrlr,
uint8_t log_page,
uint32_t nsid,
void *payload,
uint32_t payload_size)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_GET_LOG_PAGE;
cmd.nsid = nsid;
cmd.cdw10 = ((payload_size / sizeof(uint32_t)) - 1) << 16;
cmd.cdw10 |= log_page;
return nvme_admin_exec_cmd(ctrlr, &cmd, payload, payload_size);
}
int nvme_admin_abort_cmd(struct nvme_ctrlr *ctrlr,
uint16_t cid, uint16_t sqid)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_ABORT;
cmd.cdw10 = (cid << 16) | sqid;
return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
}
int nvme_admin_fw_commit(struct nvme_ctrlr *ctrlr,
const struct nvme_fw_commit *fw_commit)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_FIRMWARE_COMMIT;
memcpy(&cmd.cdw10, fw_commit, sizeof(uint32_t));
return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
}
int nvme_admin_fw_image_dl(struct nvme_ctrlr *ctrlr,
void *fw, uint32_t size,
uint32_t offset)
{
struct nvme_cmd cmd;
memset(&cmd, 0, sizeof(struct nvme_cmd));
cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
cmd.cdw10 = (size >> 2) - 1;
cmd.cdw11 = offset >> 2;
return nvme_admin_exec_cmd(ctrlr, &cmd, fw, size);
}