root/sys/dev/nvmf/host/nvmf_cmd.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2023-2024 Chelsio Communications, Inc.
 * Written by: John Baldwin <jhb@FreeBSD.org>
 */

#include <sys/types.h>
#include <sys/memdesc.h>
#include <sys/systm.h>
#include <dev/nvme/nvme.h>
#include <dev/nvmf/nvmf.h>
#include <dev/nvmf/nvmf_proto.h>
#include <dev/nvmf/host/nvmf_var.h>

bool
nvmf_cmd_get_property(struct nvmf_softc *sc, uint32_t offset, uint8_t size,
    nvmf_request_complete_t *cb, void *cb_arg, int how)
{
        struct nvmf_fabric_prop_get_cmd cmd;
        struct nvmf_request *req;

        memset(&cmd, 0, sizeof(cmd));
        cmd.opcode = NVME_OPC_FABRICS_COMMANDS;
        cmd.fctype = NVMF_FABRIC_COMMAND_PROPERTY_GET;
        switch (size) {
        case 4:
                cmd.attrib.size = NVMF_PROP_SIZE_4;
                break;
        case 8:
                cmd.attrib.size = NVMF_PROP_SIZE_8;
                break;
        default:
                panic("Invalid property size");
        }
        cmd.ofst = htole32(offset);

        req = nvmf_allocate_request(sc->admin, &cmd, cb, cb_arg, how);
        if (req != NULL)
                nvmf_submit_request(req);
        return (req != NULL);
}

bool
nvmf_cmd_set_property(struct nvmf_softc *sc, uint32_t offset, uint8_t size,
    uint64_t value, nvmf_request_complete_t *cb, void *cb_arg, int how)
{
        struct nvmf_fabric_prop_set_cmd cmd;
        struct nvmf_request *req;

        memset(&cmd, 0, sizeof(cmd));
        cmd.opcode = NVME_OPC_FABRICS_COMMANDS;
        cmd.fctype = NVMF_FABRIC_COMMAND_PROPERTY_SET;
        switch (size) {
        case 4:
                cmd.attrib.size = NVMF_PROP_SIZE_4;
                cmd.value.u32.low = htole32(value);
                break;
        case 8:
                cmd.attrib.size = NVMF_PROP_SIZE_8;
                cmd.value.u64 = htole64(value);
                break;
        default:
                panic("Invalid property size");
        }
        cmd.ofst = htole32(offset);

        req = nvmf_allocate_request(sc->admin, &cmd, cb, cb_arg, how);
        if (req != NULL)
                nvmf_submit_request(req);
        return (req != NULL);
}

bool
nvmf_cmd_keep_alive(struct nvmf_softc *sc, nvmf_request_complete_t *cb,
    void *cb_arg, int how)
{
        struct nvme_command cmd;
        struct nvmf_request *req;

        memset(&cmd, 0, sizeof(cmd));
        cmd.opc = NVME_OPC_KEEP_ALIVE;

        req = nvmf_allocate_request(sc->admin, &cmd, cb, cb_arg, how);
        if (req != NULL)
                nvmf_submit_request(req);
        return (req != NULL);
}

bool
nvmf_cmd_identify_active_namespaces(struct nvmf_softc *sc, uint32_t id,
    struct nvme_ns_list *nslist, nvmf_request_complete_t *req_cb,
    void *req_cb_arg, nvmf_io_complete_t *io_cb, void *io_cb_arg, int how)
{
        struct nvme_command cmd;
        struct memdesc mem;
        struct nvmf_request *req;

        memset(&cmd, 0, sizeof(cmd));
        cmd.opc = NVME_OPC_IDENTIFY;

        /* 5.15.1 Use CNS of 0x02 for namespace data. */
        cmd.cdw10 = htole32(2);
        cmd.nsid = htole32(id);

        req = nvmf_allocate_request(sc->admin, &cmd, req_cb, req_cb_arg, how);
        if (req == NULL)
                return (false);
        mem = memdesc_vaddr(nslist, sizeof(*nslist));
        nvmf_capsule_append_data(req->nc, &mem, sizeof(*nslist), false,
            io_cb, io_cb_arg);
        nvmf_submit_request(req);
        return (true);
}

bool
nvmf_cmd_identify_namespace(struct nvmf_softc *sc, uint32_t id,
    struct nvme_namespace_data *nsdata, nvmf_request_complete_t *req_cb,
    void *req_cb_arg, nvmf_io_complete_t *io_cb, void *io_cb_arg, int how)
{
        struct nvme_command cmd;
        struct memdesc mem;
        struct nvmf_request *req;

        memset(&cmd, 0, sizeof(cmd));
        cmd.opc = NVME_OPC_IDENTIFY;

        /* 5.15.1 Use CNS of 0x00 for namespace data. */
        cmd.cdw10 = htole32(0);
        cmd.nsid = htole32(id);

        req = nvmf_allocate_request(sc->admin, &cmd, req_cb, req_cb_arg, how);
        if (req == NULL)
                return (false);
        mem = memdesc_vaddr(nsdata, sizeof(*nsdata));
        nvmf_capsule_append_data(req->nc, &mem, sizeof(*nsdata), false,
            io_cb, io_cb_arg);
        nvmf_submit_request(req);
        return (true);
}

bool
nvmf_cmd_get_log_page(struct nvmf_softc *sc, uint32_t nsid, uint8_t lid,
    uint64_t offset, void *buf, size_t len, nvmf_request_complete_t *req_cb,
    void *req_cb_arg, nvmf_io_complete_t *io_cb, void *io_cb_arg, int how)
{
        struct nvme_command cmd;
        struct memdesc mem;
        struct nvmf_request *req;
        size_t numd;

        MPASS(len != 0 && len % 4 == 0);
        MPASS(offset % 4 == 0);

        numd = (len / 4) - 1;
        memset(&cmd, 0, sizeof(cmd));
        cmd.opc = NVME_OPC_GET_LOG_PAGE;
        cmd.nsid = htole32(nsid);
        cmd.cdw10 = htole32(numd << 16 | lid);
        cmd.cdw11 = htole32(numd >> 16);
        cmd.cdw12 = htole32(offset);
        cmd.cdw13 = htole32(offset >> 32);

        req = nvmf_allocate_request(sc->admin, &cmd, req_cb, req_cb_arg, how);
        if (req == NULL)
                return (false);
        mem = memdesc_vaddr(buf, len);
        nvmf_capsule_append_data(req->nc, &mem, len, false, io_cb, io_cb_arg);
        nvmf_submit_request(req);
        return (true);
}