#!/bin/bash -eE
# Name: Expose libmlx5 headers
# Author: Majd Dibbiny - majd@mellanox.com

name=libmlx_expose_headers
author="Majd Dibbiny - Majd@Mellanox.com"
usage="./libmlx_expose_headers defines-file structures-file enumerations-file\nPlease provide the files in the exact order"
example="./libmlx_expose_headers defines.txt structs.txt enums.txt"
script_output="The script's output file is saved to $output_file"
SCRIPTPATH=$(cd `dirname "${BASH_SOURCE[0]}"` && pwd)
args=3
defines_file="$1"
structs_file="$2"
enums_file="$3"
prefix="$4"
output_file="$prefix/include/infiniband/mlx5_hw.h"
mkdir -p "$prefix/include/infiniband"
libmlx5_path="$SCRIPTPATH/../../src/*"
FILES="$libmlx5_path"

function add_header {
cat <<EOF > $output_file
/**
 * Copyright (C) Mellanox Technologies Ltd. 2001-2014.  ALL RIGHTS RESERVED.
 * This software product is a proprietary product of Mellanox Technologies Ltd.
 * (the "Company") and all right, title, and interest and to the software product,
 * including all associated intellectual property rights, are and shall
 * remain exclusively with the Company.
 *
 * This software product is governed by the End User License Agreement
 * provided with the software product.
 */

#ifndef MLX_HW_H_
#define MLX_HW_H_

#include <linux/types.h>
#include <stdint.h>
#include <pthread.h>
#include <infiniband/driver.h>
#include <infiniband/verbs.h>

#define MLX5_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#if MLX5_GCC_VERSION >= 403
#	define __MLX5_ALGN_F__ __attribute__((noinline, aligned(64)))
#	define __MLX5_ALGN_D__ __attribute__((aligned(64)))
#else
#	define __MLX5_ALGN_F__
#	define __MLX5_ALGN_D__
#endif

EOF
}

function add_footer {
	echo -e "\n#endif" >> $output_file
}

function expose_defines {
	#need to add support for define on multiple lines
	local expose_defines_res=0
	for f in $FILES ; do
		grep -F -f $defines_file $f | sed -n '/^#/p' >> $output_file
	done
	while read -r line
	do
		if [ "`grep $line $output_file`" = "" ]; then
			#echo "define: $line wasn't found."
			expose_defines_res=1
			break
		fi
	done < "$defines_file"
	echo -e "\n" >> $output_file
	echo $expose_defines_res
}

function expose_enums {
	local expose_enums_res=0

cat <<EOF >> $output_file
enum mlx5_alloc_type { MXM_MLX5_ALLOC_TYPE_DUMMY };
enum mlx5_rsc_type   { MXM_MLX5_RSC_TYPE_DUMMY };
enum mlx5_db_method { MXM_MLX5_DB_TYPE_DUMMY };
enum mlx5_lock_type { MXM_MLX5_LOCK_TYPE_DUMMY };
enum mlx5_lock_state { MXM_MLX5_LOCK_STATE_TYPE_DUMMY };
EOF
	echo "enum {" >> $output_file
	while read -r line
	do
		for f in $FILES ; do
			grep "$line" $f| while read -r gline ; do
				pat="(\t)*(\s)*$line(\t)*(\s)*="
				if [[ $gline =~ $pat ]] ;
				then
					grep_res="`echo $gline|sed -e 's/,.*//'`"
					echo -e "\t$grep_res," >> $output_file
					break
				fi
			done
		done
		if [ "`grep $line $output_file`" = "" ]; then
			#echo "enum: $line wasn't found."
			expose_enums_res=1
			break
		fi
	done < "$enums_file"
	echo -e "};\n" >> $output_file
	echo $expose_enums_res
}

function expose_structs {
	local expose_structs_res=0

	echo -e "struct mlx5_qp;\n" >> $output_file;

	while read -r line
	do
		struct_found=0
		for f in $FILES; do
			struct_line="struct $line {"
			grep_res=`grep "$struct_line" $f`
			if [ "$grep_res" != "" ] ; then
				struct_found=1
				counter=0
				flag=0
				while IFS='' read -r fline
					do
						if [ "$struct_line" == "$fline" ] ;
						then
							flag=1
						fi
						if [ "$flag" -gt "0" ] ;
						then
							if [[ $fline == *{* ]] ;
							then
								((counter++))
							elif [[ $fline == *}* ]] ;
							then
								((counter--))
							fi
							printf "%s\n" "$fline">> $output_file
							if [ "$counter" -eq "0" ] ;
							then
								flag=0
								echo -e "\n" >> $output_file
							fi
						fi
				done < "$f"
				break
			fi
		done
		if [ $struct_found -lt 1 ]; then
			#echo "struct: $line wasn't found."
			expose_structs_res=1
			break
		fi
	done < "$structs_file"
	echo $expose_structs_res
}

function add_aux_funcs {
cat <<EOF >> $output_file
#define to_mxxx(xxx, type)\\
	((struct mlx5_##type *)\\
	((void *) ((uintptr_t)ib##xxx - offsetof(struct mlx5_##type, ibv_##xxx))))

static inline struct mlx5_qp *to_mqp(struct ibv_qp *ibqp)
{
	struct verbs_qp *vqp = (struct verbs_qp *)ibqp;
	return container_of(vqp, struct mlx5_qp, verbs_qp);
}

static inline struct mlx5_cq *to_mcq(struct ibv_cq *ibcq)
{
	return to_mxxx(cq, cq);
}

EOF
}

function add_qp_info_struct {
cat <<EOF >> $output_file
struct ibv_mlx5_qp_info {
	uint32_t	qpn;
	volatile uint32_t	*dbrec;
	struct {
		void		*buf;
		unsigned	wqe_cnt;
		unsigned	stride;
	} sq, rq;
	struct {
		void		*reg;
		unsigned	size;
		int             need_lock;
	} bf;
};

EOF
}
function add_qp_info_func {
	add_qp_info_struct
cat <<EOF >> $output_file
static inline int ibv_mlx5_exp_get_qp_info(struct ibv_qp *qp, struct	ibv_mlx5_qp_info *qp_info)
{
	struct mlx5_qp *mqp = to_mqp(qp);

	if ((mqp->gen_data.scur_post != 0) || (mqp->rq.head != 0))
		return -1;

	qp_info->qpn = mqp->ctrl_seg.qp_num;
	qp_info->dbrec = mqp->gen_data.db;
	qp_info->sq.buf = (void *)((uintptr_t)mqp->buf.buf + mqp->sq.offset);
	qp_info->sq.wqe_cnt = mqp->sq.wqe_cnt;
	qp_info->sq.stride = 1 << mqp->sq.wqe_shift;
	qp_info->rq.buf = (void *)((uintptr_t)mqp->buf.buf + mqp->rq.offset);
	qp_info->rq.wqe_cnt = mqp->rq.wqe_cnt;
	qp_info->rq.stride = 1 << mqp->rq.wqe_shift;
	qp_info->bf.reg = mqp->gen_data.bf->reg;
	qp_info->bf.need_lock = mqp->gen_data.bf->need_lock;

	if (mqp->gen_data.bf->uuarn > 0)
		qp_info->bf.size = mqp->gen_data.bf->buf_size;
	else
		qp_info->bf.size = 0;

	return 0;
}

EOF
}

function add_cq_info_struct {
cat <<EOF >> $output_file
struct ibv_mlx5_cq_info {
	uint32_t	cqn;
	unsigned	cqe_cnt;
	void		*buf;
	volatile uint32_t	*dbrec;
	unsigned	cqe_size;
};

EOF
}

function add_cq_info_func {
	add_cq_info_struct
cat <<EOF >> $output_file
static inline int ibv_mlx5_exp_get_cq_info(struct ibv_cq *cq, struct	ibv_mlx5_cq_info *cq_info)
{
	struct mlx5_cq *mcq = to_mcq(cq);

	if (mcq->cons_index != 0)
		return -1;

	cq_info->cqn = mcq->cqn;
	cq_info->cqe_cnt = mcq->ibv_cq.cqe + 1;
	cq_info->cqe_size = mcq->cqe_sz;
	cq_info->buf = mcq->active_buf->buf;
	cq_info->dbrec = mcq->dbrec;

	return 0;
}

EOF
}

function add_srq_info_struct {
cat <<EOF >> $output_file
struct ibv_mlx5_srq_info {
	void		*buf;
	volatile uint32_t	*dbrec;
	unsigned	stride;
	unsigned	head;
	unsigned	tail;
};

EOF
}

function add_srq_info_func {
	add_srq_info_struct
cat <<EOF >> $output_file
static inline int ibv_mlx5_exp_get_srq_info(struct ibv_srq *srq, struct ibv_mlx5_srq_info *srq_info)
{
	struct mlx5_srq *msrq;

	if (srq->handle == LEGACY_XRC_SRQ_HANDLE)
	srq = (struct ibv_srq *)(((struct ibv_srq_legacy *)srq)->ibv_srq);

	msrq = container_of(srq, struct mlx5_srq, vsrq.srq);

	if (msrq->counter != 0)
		return -1;

	srq_info->buf = msrq->buf.buf;
	srq_info->dbrec = msrq->db;
	srq_info->stride = 1 << msrq->wqe_shift;
	srq_info->head = msrq->head;
	srq_info->tail = msrq->tail;

	return 0;
}

EOF
}

function add_cq_ci_func {
cat <<EOF >> $output_file
static inline void ibv_mlx5_exp_update_cq_ci(struct ibv_cq *cq, unsigned cq_ci)
{
	struct mlx5_cq *mcq = to_mcq(cq);

	mcq->cons_index = cq_ci;
}
EOF
}

##MAIN##

if [ $# -lt $args ] ; then
	echo "Wrong number of arguments!"
	echo -e "\n"
	echo -e "Usage: $usage"
	echo -e "\n"
	echo "Example: $example"
	echo -e "\n"
	echo "Output: $script_output"
	echo -e "\n\n"
	echo -e "For help please contact $author \nExiting..."
	exit 1
fi

add_header
expose_defines_res=$(expose_defines)
if [ $expose_defines_res -ne 0 ] ; then
	echo "expose_defines: Failed!"
	echo "Exiting..."
	rm -f $output_file
	exit 1
fi
expose_enums_res=$(expose_enums)
if [ $expose_enums_res -ne 0 ] ; then
        echo "expose_enums: Failed!"
        echo "Exiting..."
	rm -f $output_file
        exit 1
fi
expose_structs_res=$(expose_structs)
if [ $expose_structs_res -ne 0 ] ; then
        echo "expose_structs: Failed!"
        echo "Exiting..."
	rm -f $output_file
        exit 1
fi

add_aux_funcs
add_qp_info_func
add_cq_info_func
add_srq_info_func
add_cq_ci_func

add_footer

exit 0
