/*
 * Copyright (c) 2007 Intel Corporation.  All rights reserved.
 *
 * This Software is licensed under one of the following licenses:
 *
 * 1) under the terms of the "Common Public License 1.0" a copy of which is
 *    available from the Open Source Initiative, see
 *    http://www.opensource.org/licenses/cpl.php.
 *
 * 2) under the terms of the "The BSD License" a copy of which is
 *    available from the Open Source Initiative, see
 *    http://www.opensource.org/licenses/bsd-license.php.
 *
 * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
 *    copy of which is available from the Open Source Initiative, see
 *    http://www.opensource.org/licenses/gpl-license.php.
 *
 * Licensee has the right to choose one of the above licenses.
 *
 * Redistributions of source code must retain the above copyright
 * notice and one of the license notices.
 *
 * Redistributions in binary form must reproduce both the above copyright
 * notice, one of the license notices in the documentation
 * and/or other materials provided with the distribution.
 */

/**********************************************************************
 * 
 * MODULE: dapl_ibal_extensions.c
 *
 * PURPOSE:  Extensions routines for OpenIB-windows IBAL provider
 *
 * $Id: $
 *
 **********************************************************************/

#include "dapl.h"
#include "dapl_adapter_util.h"
#include "dapl_evd_util.h"
#include "dapl_ibal_util.h"
#include "dapl_ep_util.h"
#include "dapl_cookie.h"
#include <stdarg.h>

DAT_RETURN
dapli_post_ext( IN       DAT_EP_HANDLE		ep_handle,
		IN       DAT_UINT64		cmp_add,	
		IN       DAT_UINT64		swap,
		IN	 DAT_UINT32		immed_data,
		IN	 DAT_COUNT		segments,
		IN       DAT_LMR_TRIPLET	*local_iov,
		IN       DAT_DTO_COOKIE		user_cookie,
		IN const DAT_RMR_TRIPLET	*remote_iov,
		IN	 int			op_type, 
		IN       DAT_COMPLETION_FLAGS	flags );


/*
 * dapl_extensions
 *
 * Process extension requests
 *
 * Input:
 *	ext_type,
 *	...
 *
 * Output:
 * 	Depends....
 *
 * Returns:
 * 	DAT_SUCCESS
 *	DAT_NOT_IMPLEMENTED
 *      .....
 *
 */
DAT_RETURN
dapl_extensions(IN DAT_HANDLE		dat_handle, 
		IN DAT_EXTENDED_OP	ext_op, 
		IN va_list		args)
{
	DAT_EP_HANDLE		ep;
	DAT_LMR_TRIPLET		*lmr_p;
	DAT_DTO_COOKIE		cookie;
	const DAT_RMR_TRIPLET	*rmr_p;
	DAT_UINT64		dat_uint64a, dat_uint64b;
	DAT_UINT32		dat_uint32;
	DAT_COUNT		segments = 1;
	DAT_COMPLETION_FLAGS	comp_flags;
	DAT_RETURN		status = DAT_NOT_IMPLEMENTED;

	dapl_dbg_log(DAPL_DBG_TYPE_API,
		     "dapl_extensions(hdl %p operation %d, ...)\n",
		     dat_handle, ext_op);

	switch ((int)ext_op)
	{
	
	case DAT_IB_RDMA_WRITE_IMMED_OP:
    		dapl_dbg_log(DAPL_DBG_TYPE_RTN, 
			     " WRITE_IMMED_DATA extension call\n");
		
		ep          = dat_handle;		 /* ep_handle */
		segments    = va_arg( args, DAT_COUNT);  /* num segments */
		lmr_p       = va_arg( args, DAT_LMR_TRIPLET*);  
		cookie      = va_arg( args, DAT_DTO_COOKIE);
		rmr_p       = va_arg( args, const DAT_RMR_TRIPLET*); 
		dat_uint32  = va_arg( args, DAT_UINT32); /* immed data */
		comp_flags  = va_arg( args, DAT_COMPLETION_FLAGS);   
		
		status = dapli_post_ext(ep, 0, 0, dat_uint32, segments, lmr_p,
    					cookie, rmr_p, OP_RDMA_WRITE_IMM,
    	    				comp_flags );
    		break;

	case DAT_IB_CMP_AND_SWAP_OP:
    		dapl_dbg_log(DAPL_DBG_TYPE_RTN, 
			     " CMP_AND_SWAP extension call\n");

		ep          = dat_handle;		/* ep_handle */
		dat_uint64a = va_arg( args, DAT_UINT64); /* cmp_value */
		dat_uint64b = va_arg( args, DAT_UINT64); /* swap_value */
		lmr_p       = va_arg( args, DAT_LMR_TRIPLET*); 
		cookie      = va_arg( args, DAT_DTO_COOKIE);  
		rmr_p       = va_arg( args, const DAT_RMR_TRIPLET*);
		comp_flags  = va_arg( args, DAT_COMPLETION_FLAGS);

		status = dapli_post_ext(ep, dat_uint64a, dat_uint64b,
					0, segments, lmr_p, cookie, rmr_p, 
					OP_COMP_AND_SWAP, comp_flags );
   		break;

	case DAT_IB_FETCH_AND_ADD_OP:
    		dapl_dbg_log(DAPL_DBG_TYPE_RTN, 
			     " FETCH_AND_ADD extension call\n");
		
		ep          = dat_handle;		/* ep_handle */
		dat_uint64a = va_arg( args, DAT_UINT64); /* add value */
		lmr_p       = va_arg( args, DAT_LMR_TRIPLET*);  
		cookie      = va_arg( args, DAT_DTO_COOKIE);
		rmr_p       = va_arg( args, const DAT_RMR_TRIPLET*); 
		comp_flags  = va_arg( args, DAT_COMPLETION_FLAGS);   
		
		status = dapli_post_ext(ep, dat_uint64a, 0, 0, segments, 
					lmr_p, cookie, rmr_p, 
					OP_FETCH_AND_ADD, comp_flags );

    		break;

	default:
    		dapl_dbg_log(DAPL_DBG_TYPE_ERR, 
			     "unsupported extension(%d)\n", (int)ext_op);
	}
	
	return(status);
}


DAT_RETURN
dapli_post_ext( IN       DAT_EP_HANDLE		ep_handle,
		IN       DAT_UINT64		cmp_add,	
		IN       DAT_UINT64		swap,
		IN	 DAT_UINT32		immed_data,
		IN	 DAT_COUNT		segments,
		IN       DAT_LMR_TRIPLET	*local_iov,
		IN       DAT_DTO_COOKIE		user_cookie,
		IN const DAT_RMR_TRIPLET	*remote_iov,
		IN	 int			op_type, 
		IN       DAT_COMPLETION_FLAGS	flags )
{
	DAPL_EP 	*ep_ptr;
	ib_qp_handle_t	qp_ptr;
	DAPL_COOKIE	*cookie;
	DAT_RETURN	dat_status = DAT_SUCCESS;

	dapl_dbg_log(DAPL_DBG_TYPE_API,
		     "--> post_ext_op: ep %p cmp_val %x swap_val %x cookie "
		     "0x%x\n    r_iov %p, flags 0x%x op %d segs %d\n",
		     ep_handle, (unsigned)cmp_add, (unsigned)swap, 
		     (unsigned)user_cookie.as_64, remote_iov, flags, op_type,
		     segments);

	if (DAPL_BAD_HANDLE(ep_handle, DAPL_MAGIC_EP))
		return(DAT_ERROR(DAT_INVALID_HANDLE, DAT_INVALID_HANDLE_EP));

	if ((NULL == remote_iov) || (NULL == local_iov))
		return DAT_INVALID_PARAMETER;

	ep_ptr = (DAPL_EP *) ep_handle;
	qp_ptr = ep_ptr->qp_handle;

	/*
	 * Synchronization ok since this buffer is only used for send
	 * requests, which aren't allowed to race with each other.
	 */
		dat_status = dapls_dto_cookie_alloc(
						&ep_ptr->req_buffer,
						DAPL_DTO_TYPE_EXTENSION,
						user_cookie,
						&cookie );

		if ( dat_status != DAT_SUCCESS )
		{
#ifdef DAPL_DBG
		dapl_dbg_log(DAPL_DBG_TYPE_ERR,"%s() cookie alloc faulure %x\n",
					__FUNCTION__,dat_status);
#endif
			goto bail;
		}
		
		/*
	 * Invoke provider specific routine to post DTO
	 */
	dat_status = dapls_ib_post_ext_send(ep_ptr,	
					    op_type,
					    cookie,	
					    segments,	/* data segments */
					    local_iov, 
					    remote_iov,
					    immed_data,	/* immed data */
					    cmp_add,	/* compare or add */
					    swap,	/* swap */
					    flags);

	if (dat_status != DAT_SUCCESS)
	{
			dapls_cookie_dealloc(&ep_ptr->req_buffer, cookie);
#ifdef DAPL_DBG
			dapl_dbg_log(DAPL_DBG_TYPE_ERR,
					"%s() post_ex_send err %d @ line %d\n",
					__FUNCTION__,dat_status,__LINE__);
#endif
		}

bail:
	return dat_status;

}


/* 
 * New provider routine to process extended DTO events 
 */
void
dapls_cqe_to_event_extension(IN DAPL_EP			*ep_ptr,
			     IN DAPL_COOKIE		*cookie,
			     IN ib_work_completion_t	*cqe_ptr,
			     IN DAT_EVENT		*event_ptr)
{
    uint32_t ibtype;
    DAT_DTO_COMPLETION_EVENT_DATA *dto = 
			&event_ptr->event_data.dto_completion_event_data;
    DAT_IB_EXTENSION_EVENT_DATA *ext_data = 
			(DAT_IB_EXTENSION_EVENT_DATA *)
			&event_ptr->event_extension_data[0];
    DAT_DTO_COMPLETION_STATUS dto_status;

    /* Get status from cqe */
    dto_status = dapls_ib_get_dto_status(cqe_ptr);
	
    dapl_dbg_log(DAPL_DBG_TYPE_EVD,
    	     " cqe_to_event_ext: dto_ptr %p ext_ptr %p status %d\n",
    	     dto, ext_data, dto_status);

    event_ptr->event_number = DAT_IB_DTO_EVENT;
    dto->ep_handle = cookie->ep;
    dto->user_cookie = cookie->val.dto.cookie;
    dto->operation = DAPL_GET_CQE_DTOS_OPTYPE(cqe_ptr); /* new for 2.0 */
    dto->status = ext_data->status = dto_status;

    if (dto_status != DAT_DTO_SUCCESS)
    	return;
	
    /* 
     * Get operation type from CQ work completion entry and
     * if extented operation then set extended event data
     */
    ibtype = DAPL_GET_CQE_OPTYPE(cqe_ptr);
        
    switch (ibtype)	{

    case OP_RDMA_WRITE_IMM:
    	dapl_dbg_log (DAPL_DBG_TYPE_EVD,
                      " cqe_to_event_ext: OP_RDMA_WRITE_IMMED\n");
		
    	/* type and outbound rdma write transfer size */
    	dto->transfered_length = cookie->val.dto.size;
    	ext_data->type = DAT_IB_RDMA_WRITE_IMMED;
    	break;

    case OP_RECEIVE_IMM:
    	dapl_dbg_log (DAPL_DBG_TYPE_EVD,
                                " cqe_to_event_ext: OP_RECEIVE_RDMA_IMMED\n");

    	/* immed recvd, type and inbound rdma write transfer size */
    	dto->transfered_length = DAPL_GET_CQE_BYTESNUM(cqe_ptr);
    	ext_data->type = DAT_IB_RDMA_WRITE_IMMED_DATA;
    	ext_data->val.immed.data = DAPL_GET_CQE_IMMED_DATA(cqe_ptr);
    	break;

    case OP_COMP_AND_SWAP:
        dapl_dbg_log (DAPL_DBG_TYPE_EVD,
                      " cqe_to_event_ext: COMP_AND_SWAP_RESP\n");

        /* original data is returned in LMR provided with post */
    	ext_data->type = DAT_IB_CMP_AND_SWAP;
    	dto->transfered_length = DAPL_GET_CQE_BYTESNUM(cqe_ptr);
    	break;

    case OP_FETCH_AND_ADD:
        dapl_dbg_log (DAPL_DBG_TYPE_EVD,
                      " cqe_to_event_ext: FETCH_AND_ADD_RESP\n");

    	/* original data is returned in LMR provided with post */
    	ext_data->type = DAT_IB_FETCH_AND_ADD;
    	dto->transfered_length = DAPL_GET_CQE_BYTESNUM(cqe_ptr);
    	break;

    default:
    	/* not extended operation */
    	ext_data->status = DAT_IB_OP_ERR;
    	dto->status = DAT_DTO_ERR_TRANSPORT;
    	break;
    }
}

