/*
 * Copyright (c) 2014 Red Hat, Inc.
 *
 * This file is licensed to you under your choice of the GNU Lesser
 * General Public License, version 2.1 or any later version (LGPLv2.1 or
 * later), or the Apache License 2.0.
 */

/*
 * APIs for both libtcmu users and tcmu-runner plugins to use.
 */

#ifndef __LIBTCMU_COMMON_H
#define __LIBTCMU_COMMON_H

#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

struct tcmu_device;
struct tgt_port;
struct tcmulib_cmd;

/*
 * TCMU return status codes
 */
enum {
	TCMU_STS_ASYNC_HANDLED = -2,
	TCMU_STS_NOT_HANDLED = -1,
	TCMU_STS_OK = 0,
	TCMU_STS_NO_RESOURCE,
	/* handler has setup sense. */
	TCMU_STS_PASSTHROUGH_ERR,
	TCMU_STS_BUSY,
	TCMU_STS_WR_ERR,
	TCMU_STS_RD_ERR,
	TCMU_STS_MISCOMPARE,
	TCMU_STS_INVALID_CMD,
	TCMU_STS_INVALID_CDB,
	TCMU_STS_INVALID_PARAM_LIST,
	TCMU_STS_INVALID_PARAM_LIST_LEN,
	TCMU_STS_TIMEOUT,
	TCMU_STS_FENCED,
	TCMU_STS_HW_ERR,
	TCMU_STS_RANGE,
	TCMU_STS_FRMT_IN_PROGRESS,
	TCMU_STS_CAPACITY_CHANGED,
	TCMU_STS_NOTSUPP_SAVE_PARAMS,
	TCMU_STS_WR_ERR_INCOMPAT_FRMT,
	TCMU_STS_TRANSITION,
	TCMU_STS_IMPL_TRANSITION_ERR,
	TCMU_STS_EXPL_TRANSITION_ERR,
	TCMU_STS_NO_LOCK_HOLDERS,
	/* xcopy specific errors */
	TCMU_STS_NOTSUPP_SEG_DESC_TYPE,
	TCMU_STS_NOTSUPP_TGT_DESC_TYPE,
	TCMU_STS_CP_TGT_DEV_NOTCONN,
	TCMU_STS_INVALID_CP_TGT_DEV_TYPE,
	TCMU_STS_TOO_MANY_SEG_DESC,
	TCMU_STS_TOO_MANY_TGT_DESC,
};

#define TCMU_THREAD_NAME_LEN 16

#define SENSE_BUFFERSIZE 96

#define CFGFS_ROOT "/sys/kernel/config/target"
#define CFGFS_CORE CFGFS_ROOT"/core"

#define CFGFS_TARGET_MOD "/sys/module/target_core_user"
#define CFGFS_MOD_PARAM CFGFS_TARGET_MOD"/parameters"

#define max(a, b) ({			\
	__typeof__ (a) _a = (a);	\
	__typeof__ (b) _b = (b);	\
	(void) (&_a == &_b);		\
	_a < _b ? _b : _a; })

#define min(a, b) ({			\
	__typeof__ (a) _a = (a);	\
	__typeof__ (b) _b = (b);	\
	(void) (&_a == &_b);		\
	_a < _b ? _a : _b; })

#define round_up(a, b) ({		\
	__typeof__ (a) _a = (a);	\
	__typeof__ (b) _b = (b);	\
	((_a + (_b - 1)) / _b) * _b; })

#define round_down(a, b) ({		\
	__typeof__ (a) _a = (a);	\
	__typeof__ (b) _b = (b);	\
	(_a - (_a % _b)); })

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

struct tcmulib_cmd {
	uint16_t cmd_id;
	uint8_t *cdb;
	struct iovec *iovec;
	size_t iov_cnt;
	uint8_t sense_buf[SENSE_BUFFERSIZE];
	void *hm_private;
};

/* Set/Get methods for the opaque tcmu_device */
void *tcmu_dev_get_private(struct tcmu_device *dev);
void tcmu_dev_set_private(struct tcmu_device *dev, void *priv);
const char *tcmu_dev_get_uio_name(struct tcmu_device *dev);
void tcmu_set_thread_name(const char *prefix, struct tcmu_device *dev);
int tcmu_dev_get_fd(struct tcmu_device *dev);
char *tcmu_dev_get_memory_info(struct tcmu_device *dev, void **base,
			       size_t *len, off_t *offset);
char *tcmu_dev_get_cfgstring(struct tcmu_device *dev);
void tcmu_dev_set_num_lbas(struct tcmu_device *dev, uint64_t num_lbas);
uint64_t tcmu_dev_get_num_lbas(struct tcmu_device *dev);
void tcmu_dev_set_block_size(struct tcmu_device *dev, uint32_t block_size);
uint32_t tcmu_dev_get_block_size(struct tcmu_device *dev);
uint64_t tcmu_lba_to_byte(struct tcmu_device *dev, uint64_t lba);
uint64_t tcmu_byte_to_lba(struct tcmu_device *dev, uint64_t byte);
void tcmu_dev_set_max_xfer_len(struct tcmu_device *dev, uint32_t len);
uint32_t tcmu_dev_get_max_xfer_len(struct tcmu_device *dev);
void tcmu_dev_set_opt_xcopy_rw_len(struct tcmu_device *dev, uint32_t len);
uint32_t tcmu_dev_get_opt_xcopy_rw_len(struct tcmu_device *dev);
void tcmu_dev_set_max_unmap_len(struct tcmu_device *dev, uint32_t len);
uint32_t tcmu_dev_get_max_unmap_len(struct tcmu_device *dev);
void tcmu_dev_set_opt_unmap_gran(struct tcmu_device *dev, uint32_t len,
				 bool split);
uint32_t tcmu_dev_get_opt_unmap_gran(struct tcmu_device *dev);
void tcmu_dev_set_unmap_gran_align(struct tcmu_device *dev, uint32_t len);
uint32_t tcmu_dev_get_unmap_gran_align(struct tcmu_device *dev);
void tcmu_dev_set_write_cache_enabled(struct tcmu_device *dev, bool enabled);
bool tcmu_dev_get_write_cache_enabled(struct tcmu_device *dev);
void tcmu_dev_set_solid_state_media(struct tcmu_device *dev, bool solid_state);
bool tcmu_dev_get_solid_state_media(struct tcmu_device *dev);
void tcmu_dev_set_unmap_enabled(struct tcmu_device *dev, bool enabled);
bool tcmu_dev_get_unmap_enabled(struct tcmu_device *dev);
struct tcmulib_handler *tcmu_dev_get_handler(struct tcmu_device *dev);
void tcmu_dev_flush_ring(struct tcmu_device *dev);
bool tcmu_dev_oooc_supported(struct tcmu_device* dev);

/* Set/Get methods for interacting with configfs */
char *tcmu_cfgfs_get_str(const char *path);
int tcmu_cfgfs_set_str(const char *path, const char *val, int val_len);
int tcmu_cfgfs_get_int(const char *path);
int tcmu_cfgfs_set_u32(const char *path, uint32_t val);
int tcmu_cfgfs_dev_get_attr_int(struct tcmu_device *dev, const char *name);
int tcmu_cfgfs_dev_exec_action(struct tcmu_device *dev, const char *name,
			       uint32_t val);
int tcmu_cfgfs_dev_set_ctrl_u64(struct tcmu_device *dev, const char *key,
			        uint64_t val);
uint64_t tcmu_cfgfs_dev_get_info_u64(struct tcmu_device *dev, const char *name,
                                     int *fn_ret);
char *tcmu_cfgfs_dev_get_wwn(struct tcmu_device *dev);
int tcmu_cfgfs_mod_param_set_u32(const char *name, uint32_t val);

/* Helper routines for processing commands */

/* SCSI CDB processing */
int tcmu_cdb_get_length(uint8_t *cdb);
uint64_t tcmu_cdb_get_lba(uint8_t *cdb);
uint32_t tcmu_cdb_get_xfer_length(uint8_t *cdb);
void tcmu_cdb_print_info(struct tcmu_device *dev, const struct tcmulib_cmd *cmd,
			 const char *info);
uint64_t tcmu_cdb_to_byte(struct tcmu_device *dev, uint8_t *cdb);

/* iovec processing */
off_t tcmu_iovec_compare(void *mem, struct iovec *iovec, size_t size);
size_t tcmu_iovec_seek(struct iovec *iovec, size_t count);
void tcmu_iovec_zero(struct iovec *iovec, size_t iov_cnt);
bool tcmu_iovec_zeroed(struct iovec *iovec, size_t iov_cnt);
size_t tcmu_iovec_length(struct iovec *iovec, size_t iov_cnt);

/* memory mangement */
size_t tcmu_memcpy_into_iovec(struct iovec *iovec, size_t iov_cnt, void *src,
			      size_t len);
size_t tcmu_memcpy_from_iovec(void *dest, size_t len, struct iovec *iovec,
			      size_t iov_cnt);

/* tcmulib_cmd processing */
void tcmu_cmd_seek(struct tcmulib_cmd *cmd, size_t count);

/* SCSI Sense */
int tcmu_sense_set_data(uint8_t *sense_buf, uint8_t key, uint16_t asc_ascq);
void tcmu_sense_set_info(uint8_t *sense_buf, uint32_t info);
void tcmu_sense_set_key_specific_info(uint8_t *sense_buf, uint16_t info);
void __tcmu_sense_set_data(uint8_t *sense_buf, uint8_t key, uint16_t asc_ascq);

/*
 * Misc
 */
void tcmu_thread_cancel(pthread_t thread);

#ifdef __cplusplus
}
#endif

#endif
