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

#ifndef __NVMFT_VAR_H__
#define	__NVMFT_VAR_H__

#include <sys/_callout.h>
#include <sys/refcount.h>
#include <sys/taskqueue.h>

#include <dev/nvmf/nvmf_proto.h>

#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl_frontend.h>

struct nvmf_capsule;
struct nvmft_controller;
struct nvmft_qpair;

#define	NVMFT_NUM_AER		16

struct nvmft_port {
	TAILQ_ENTRY(nvmft_port) link;
	u_int	refs;
	struct ctl_port port;
	struct nvme_controller_data cdata;
	struct nvme_firmware_page fp;
	uint64_t cap;
	uint32_t max_io_qsize;
	bool	online;

	struct sx lock;

	struct unrhdr *ids;
	TAILQ_HEAD(, nvmft_controller) controllers;

	uint32_t *active_ns;
	u_int	num_ns;
};

struct nvmft_io_qpair {
	struct nvmft_qpair *qp;

	bool shutdown;
};

struct nvmft_controller {
	struct nvmft_qpair *admin;
	struct nvmft_io_qpair *io_qpairs;
	u_int	num_io_queues;
	bool	shutdown;
	bool	admin_closed;
	uint16_t cntlid;
	uint32_t cc;
	uint32_t csts;

	struct nvmft_port *np;
	struct mtx lock;

	struct nvme_controller_data cdata;
	struct nvme_health_information_page hip;
	sbintime_t create_time;
	sbintime_t start_busy;
	sbintime_t busy_total;
	uint16_t partial_dur;
	uint16_t partial_duw;

	uint8_t	hostid[16];
	uint8_t	hostnqn[NVME_NQN_FIELD_SIZE];
	u_int	trtype;

	TAILQ_ENTRY(nvmft_controller) link;

	/*
	 * Each queue can have at most UINT16_MAX commands, so the total
	 * across all queues will fit in a uint32_t.
	 */
	uint32_t pending_commands;

	volatile int ka_active_traffic;
	struct callout ka_timer;
	sbintime_t ka_sbt;

	/* AER fields. */
	uint32_t aer_mask;
	uint16_t aer_cids[NVMFT_NUM_AER];
	uint8_t aer_pending;
	uint8_t aer_cidx;
	uint8_t aer_pidx;

	/* Changed namespace IDs. */
	struct nvme_ns_list *changed_ns;
	bool	changed_ns_reported;

	struct task shutdown_task;
	struct timeout_task terminate_task;
};

MALLOC_DECLARE(M_NVMFT);

/* ctl_frontend_nvmf.c */
void	nvmft_port_free(struct nvmft_port *np);
void	nvmft_populate_active_nslist(struct nvmft_port *np, uint32_t nsid,
    struct nvme_ns_list *nslist);
void	nvmft_dispatch_command(struct nvmft_qpair *qp,
    struct nvmf_capsule *nc, bool admin);
void	nvmft_terminate_commands(struct nvmft_controller *ctrlr);

/* nvmft_controller.c */
void	nvmft_controller_error(struct nvmft_controller *ctrlr,
    struct nvmft_qpair *qp, int error);
void	nvmft_controller_lun_changed(struct nvmft_controller *ctrlr,
    int lun_id);
void	nvmft_handle_admin_command(struct nvmft_controller *ctrlr,
    struct nvmf_capsule *nc);
void	nvmft_handle_io_command(struct nvmft_qpair *qp, uint16_t qid,
    struct nvmf_capsule *nc);
int	nvmft_handoff_admin_queue(struct nvmft_port *np,
    const struct nvmf_handoff_controller_qpair *handoff,
    const struct nvmf_fabric_connect_cmd *cmd,
    const struct nvmf_fabric_connect_data *data);
int	nvmft_handoff_io_queue(struct nvmft_port *np,
    const struct nvmf_handoff_controller_qpair *handoff,
    const struct nvmf_fabric_connect_cmd *cmd,
    const struct nvmf_fabric_connect_data *data);
int	nvmft_printf(struct nvmft_controller *ctrlr, const char *fmt, ...)
    __printflike(2, 3);

/* nvmft_qpair.c */
struct nvmft_qpair *nvmft_qpair_init(enum nvmf_trtype trtype,
    const struct nvmf_handoff_qpair_params *handoff, uint16_t qid,
    const char *name);
void	nvmft_qpair_shutdown(struct nvmft_qpair *qp);
void	nvmft_qpair_destroy(struct nvmft_qpair *qp);
struct nvmft_controller *nvmft_qpair_ctrlr(struct nvmft_qpair *qp);
uint16_t nvmft_qpair_id(struct nvmft_qpair *qp);
const char *nvmft_qpair_name(struct nvmft_qpair *qp);
void	nvmft_command_completed(struct nvmft_qpair *qp,
    struct nvmf_capsule *nc);
int	nvmft_send_response(struct nvmft_qpair *qp, const void *cqe);
void	nvmft_init_cqe(void *cqe, struct nvmf_capsule *nc, uint16_t status);
int	nvmft_send_error(struct nvmft_qpair *qp, struct nvmf_capsule *nc,
    uint8_t sc_type, uint8_t sc_status);
int	nvmft_send_generic_error(struct nvmft_qpair *qp,
    struct nvmf_capsule *nc, uint8_t sc_status);
int	nvmft_send_success(struct nvmft_qpair *qp,
    struct nvmf_capsule *nc);
void	nvmft_connect_error(struct nvmft_qpair *qp,
    const struct nvmf_fabric_connect_cmd *cmd, uint8_t sc_type,
    uint8_t sc_status);
void	nvmft_connect_invalid_parameters(struct nvmft_qpair *qp,
    const struct nvmf_fabric_connect_cmd *cmd, bool data, uint16_t offset);
int	nvmft_finish_accept(struct nvmft_qpair *qp,
    const struct nvmf_fabric_connect_cmd *cmd, struct nvmft_controller *ctrlr);

static __inline void
nvmft_port_ref(struct nvmft_port *np)
{
	refcount_acquire(&np->refs);
}

static __inline void
nvmft_port_rele(struct nvmft_port *np)
{
	if (refcount_release(&np->refs))
		nvmft_port_free(np);
}

#endif	/* !__NVMFT_VAR_H__ */