/* BEGIN_ICS_COPYRIGHT2 ****************************************

Copyright (c) 2015-2017, Intel Corporation

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

** END_ICS_COPYRIGHT2   ****************************************/

/* [ICS VERSION STRING: unknown] */

#ifndef __OPAMGT_PRIV_H__
#define __OPAMGT_PRIV_H__

#include <syslog.h>
/* Needed for getpid() */
#include <sys/types.h>
#include <unistd.h>
#include "opamgt.h"

#include "iba/ib_types.h"

#define OMGT_STL_OUI    (0x66A)

#include "iba/ib_generalServices.h"

#ifndef OMGT_OUTPUT_ERROR
#define OMGT_OUTPUT_ERROR(port, format, args...) \
	do { \
		FILE *current_log_file = port ? port->error_file : stderr; \
		if (port && current_log_file) { \
			if (current_log_file == OMGT_DBG_FILE_SYSLOG) { \
				syslog(LOG_ERR, "opamgt ERROR: [%d] %s: " format, \
					(int)getpid(), __func__, ##args); \
			} else { \
				fprintf(current_log_file, "opamgt ERROR: [%d] %s: " format, \
					(int)getpid(), __func__, ##args); \
			} \
		} \
	} while(0)
#endif

/* NOTE we keep this at LOG_INFO and reserve LOG_DEBUG for packet dump */
#ifndef OMGT_DBGPRINT
#define OMGT_DBGPRINT(port, format, args...) \
	do { \
		FILE *current_log_file = port ? port->dbg_file : NULL; \
		if (port && current_log_file) { \
			if (current_log_file == OMGT_DBG_FILE_SYSLOG) { \
				syslog(LOG_INFO, "opamgt: [%d] %s: " format, \
				    (int)getpid(), __func__, ##args); \
			} else { \
				fflush(current_log_file); fprintf(current_log_file, "opamgt: [%d] %s: " format, \
				    (int)getpid(), __func__, ##args); \
			} \
		} \
	} while(0)
#endif

#ifndef OMGT_OUTPUT_INFO
#define OMGT_OUTPUT_INFO(port, format, args...) \
	do { \
		FILE *current_log_file = port ? port->dbg_file : NULL; \
		if (port && current_log_file) { \
			if (current_log_file == OMGT_DBG_FILE_SYSLOG) { \
				syslog(LOG_INFO, "opamgt: [%d] %s: " format, \
				    (int)getpid(), __func__, ##args); \
			} else { \
				fprintf(current_log_file, "opamgt: [%d] %s: " format, \
				    (int)getpid(), __func__, ##args); \
			} \
		} \
	} while(0)
#endif


#ifndef DBG_ENTER_FUNC
#define DBG_ENTER_FUNC(port)  OMGT_DBGPRINT(port, "Entering %s\n",__func__)
#endif

#ifndef DBG_EXIT_FUNC
#define DBG_EXIT_FUNC(port)  OMGT_DBGPRINT(port, "Exiting %s\n",__func__)
#endif


/**
 * OMGT Port Accessor functions
 * Data is valid once port is opened and until port object is closed.
 */

/**
 * @brief Gets Port's SM LID for the given port.
 * Retrieves port's SM lid for an open in-band port.
 * @param port           previously initialized port object for an in-band
 *  					 connection
 * @param sm_lid         SM lid to be retruned
 * @return OMGT_STATUS_T
 */
OMGT_STATUS_T omgt_port_get_port_sm_lid(struct omgt_port *port, uint32_t *sm_lid);
/**
 * @brief Gets Port's LMC for the given port.
 * Retrieves port's LMC for an open in-band port.
 * @param port           previously initialized port object for an in-band
 *  					 connection
 * @param port_lmc       LMC to be retruned
 * @return OMGT_STATUS_T
 */
OMGT_STATUS_T omgt_port_get_port_lmc(struct omgt_port *port, uint32_t *port_lmc);
/**
 * @brief Gets Port's SM SL for the given port.
 * Retrieves port's SM sl for an open in-band port.
 * @param port           previously initialized port object for an in-band
 *  					 connection
 * @param sm_sl          SM sl to be retruned
 * @return OMGT_STATUS_T
 */
OMGT_STATUS_T omgt_port_get_port_sm_sl(struct omgt_port *port, uint8_t *sm_sl);

/**
 * @brief Gets Port's NodeType for the given port.
 * Retrieves port's NodeType for an open in-band port.
 * @param port           previously initialized port object for an in-band
 *  					 connection
 * @param node_type      Node Type of the omgt_port
 * @return OMGT_STATUS_T
 */
OMGT_STATUS_T omgt_port_get_node_type(struct omgt_port *port, uint8_t *node_type);

/* Create OPA GID from 32 bit LID */
static inline uint64_t omgt_create_gid(STL_LID lid)
{
	return (uint64_t) OMGT_STL_OUI << 40 | lid;
}

/* Check if lid is beyond the IB Unicast LID range */
static inline int omgt_is_ext_lid(STL_LID lid)
{
	return !!((lid > LID_UCAST_END) && lid != STL_LID_PERMISSIVE);
}

/**
 * Gets the ISSM device corresponding to the specified port.
 *
 * @param port      Pointer to the opened local port object.
 * @param path      Buffer in which to place the device path
 * @param path_max  Maximum length of the output buffer
 *
 * @return FSUCCESS on success, or UMad error code on failure
 */
FSTATUS omgt_get_issm_device(struct omgt_port *port, char *path, int path_max);

/**
 * Function to refresh the pkey table for the MAD interface
 * for a given hfi name and port.
 * To use with omgt_xxx_mad, and the umad OFED library. 
 * 
 * @param *port     pointer to port object 
 *  
 * @return 0    success 
 *         -1   Failure 
 */ 
extern int omgt_mad_refresh_port_pkey(struct omgt_port *port);

/**
 * Given a port and a pkey return if this pkey is in the ports local
 * pkey_table
 *
 * @param *port     pointer to port object
 * @param  pkey     pkey to search for
 *
 * @return pkey_idx   success
 *         -1         Failure
 */
extern int omgt_find_pkey(struct omgt_port *port, uint16_t pkey);


/**
 * Given a port, destination lid, hopcount, return the most 
 * appropriate management pkey to use. 
 *
 * @param *port     pointer to port object
 * @param  dlid     destination lid 
 * @param  hopCnt   hop count 
 *
 * @return pkey     success 
 *         0        Failure  (Not mgmt allowed for request)
 */
extern uint16_t omgt_get_mgmt_pkey(struct omgt_port *port, STL_LID dlid, uint8_t hopCnt);


/** =========================================================================
 * Send Recv interface
 */

/**
 * bind a list of managment classes/methods with the omgt_port object
 *
 * "bind" must be done if a client wants to respond to unsolicited MAD's
 * 
 * User may call this fn multiple times to register for different class/method
 * combinations.
 *
 * However, registering the same class/method combination twice will result in
 * an error.
 *
 * 3 special flags are used to ease registration for a simple responding
 * client, trap processing client or report processing client.  If these bits
 * are set, methods are set automatically as documented for the client.
 *
 * @param   port          port opened by omgt_open_port_*
 * @param  *mgmt_classes  pointer to a list of managment classes to be registered.
 *						  The list is terminated by a record that has 0 for the 
 *						  base_version.
 * 
 * Returns 0 if successful, +errno status
 */ 
#define OMGT_CLASS_ARG_MAX_METHODS 64
struct omgt_class_args {
	uint8_t  base_version;                       // IB_BASE_VERSION or STL_BASE_VERSION
	uint8_t  mgmt_class;                         // MCLASS_SUBN_ADM, STL_SA_CLASS, etc...
	uint8_t  class_version;                      // IB_BM_CLASS_VERSION, STL_SA_CLASS_VERSION, etc...
	int      is_responding_client;               // client responds to get's and sets
                                                 //     GET && SET
                                                 //        if (SA class)
												 //            GET && SET && GET_TABLE && DELETE && GET_TRACE_TABLE
	int      is_trap_client;                     // client processes traps
                                                 //     TRAP && TRAP_REPRESS
	int      is_report_client;                   // client processes report
                                                 //     REPORT
	int      kernel_rmpp;                         // nonzero if kernel rmpp coalescing support is desired.
	uint8_t *oui;                                // vendor-specific OUI for vendor classes.
	int      use_methods;                        // use the method list instead of default
	uint8_t  methods[OMGT_CLASS_ARG_MAX_METHODS]; // list of methods.
	uint8_t  res[64];
};
int omgt_bind_classes(struct omgt_port *port, struct omgt_class_args *mgmt_classes);


/**
 * Address vector for sending MAD's
 * Also used to return address information on received MAD's
 */
struct omgt_mad_addr {
	STL_LID lid;
	uint32_t qpn;
	uint32_t qkey;
	uint16_t pkey;
	uint8_t  sl;
	uint8_t  age;
	uint32_t flags;
	uint16_t entropy;
	uint8_t  res[18];
};
enum omgt_mad_addr_flags {
	OMGT_MAD_ADDR_SM = (1 << 0), /* use SM addr values defined in PortInfo (SMLID/SMSL)*/
	OMGT_MAD_ADDR_16B = (1 << 1), /* Force L2 layer to use 16B packets */
};
#define OMGT_DEFAULT_PKEY        0xffff
/**
 * Send and wait for response MAD
 *    (response is allocated and returned to user)
 * 
 * @param   port        port opened by omgt_open_port_*
 * @param  *send_mad    pointer to outbound MAD
 * @param   send_size   size of send_mad buffer
 * @param  *addr        destination address information
 * @param **recv_mad    pointer to inbound MAD (Allocated based on inbound size)
 * @param  *recv_size   size of recv_mad buffer returned
 * @param   timeout_ms  OFED send timeout in ms (if timeout_ms < 0 wait forever)
 * @param   retries     number of retries to attempt for MAD
 *
 * NOTE: function will wait up to timeout_ms * retries for a response
 *
 * @return 0 (SUCCESS) or error code 
 *           [Error codes are as specified in omgt_recv_mad_alloc]
 */
FSTATUS omgt_send_recv_mad_alloc(struct omgt_port *port,
			uint8_t *send_mad, size_t send_size,
			struct omgt_mad_addr *addr,
			uint8_t **recv_mad, size_t *recv_size,
			int timeout_ms, int retries);

/**
 * Send and wait for response MAD
 *     (response is allocated by user)
 * 
 * @param   port        port opened by omgt_open_port_*
 * @param  *send_mad    pointer to outbound mad
 * @param   send_size   size of send_mad buffer
 * @param  *addr        destination address information
 * @param  *recv_mad    pointer to buffer to hold response MAD
 * @param  *recv_size   IN size of recv_mad buffer
 *                      OUT sizeof actual data received.
 * @param   timeout_ms  OFED send timeout in ms (if timeout_ms < 0 wait forever)
 * @param   retries     number of retries to attempt for MAD
 *
 * NOTE: function will wait up to timeout_ms * retries for a response
 *
 * @return 0 (SUCCESS) or error code 
 *           [Error codes are as specified in omgt_recv_mad_no_alloc]
 */
FSTATUS omgt_send_recv_mad_no_alloc(struct omgt_port *port,
			uint8_t *send_mad, size_t send_size,
			struct omgt_mad_addr *addr,
			uint8_t *recv_mad, size_t *recv_size,
			int timeout_ms, int retries);

/**
 * Send a MAD
 * 
 * @param  port        port opened by omgt_open_port_*
 * @param *send_mad    pointer to mad to send                                        
 * @param  send_size   Length of buffer to send 
 * @param  addr        destination address information
 * @param  timeout_ms  OFED send timeout in ms (if timeout_ms < 0 wait forever)
 * @param  retries     number of retries to attempt for MAD
 * 
 * @return FSTATUS (0 if successful, else error code)
 */
FSTATUS omgt_send_mad2(struct omgt_port *port, uint8_t *send_mad, size_t send_size,
			struct omgt_mad_addr *addr, int timeout_ms, int retries);

/**
 * Receive a packet from the specified port
 *     (response is allocated by user)
 * 
 * Function will allocate an appropriately sized MAD to hold the status.
 * The entire MAD is returned, including all headers.
 * recv_size is initialized with the size of the returned buffer.
 *
 * @param    port        port opened by omgt_open_port_*
 * @param  **recv_mad    pointer to inbound MAD (Allocated based on inbound size)
 * @param   *recv_size   size of recv_mad buffer returned
 * @param    timeout_ms  OFED send timeout in ms (if timeout_ms < 0 wait forever)
 * @param    addr        if supplied recv'd MAD address information is filled in
 *                       here.
 * 
 * @return   0 if success, else error code
 *
 * FINVALID_PARAMETER - incorrect or missing arguments to function
 * FERROR - other atypical errors
 * FNOT_DONE - no packet available within timeout
 * FOVERRUN - error receiving a large rmpp MAD. *recv_size indicates length
 * 			reported by the failed umad_recv.
 * FINSUFFICIENT_MEMORY - unable to allocate memory (*recv_size is length of
 * 					MAD attempting to be allocated), but no MAD is returned
 *
 * For the FTIMEOUT, FREJECT and FSUCCESS return cases, *recv_mad will be
 * updated with pointer to the MAD (coallesced RMPP if applicable).  Caller
 * must free() this buffer.  *recv_size will indicate the length of the MAD.
 *
 * FTIMEOUT - a previous send's response exceeded timeout/retry, the sent MAD
 *  				is returned
 * FREJECT - unexpected error processing a previous send or its response, the
 * 					sent MAD is returned
 * FSUCCESS - mad successfully received
 */
FSTATUS omgt_recv_mad_alloc(struct omgt_port *port, uint8_t **recv_mad, size_t *recv_size,
			int timeout_ms, struct omgt_mad_addr *addr);
/**
 * Free a mad received from omgt_*
 *
 * @param    port        port opened by omgt_open_port_*
 * @param  **recv_mad    Pointer to rcv buffer
 */
void omgt_free_recv_mad(struct omgt_port *port, uint8_t *recv_mad);

/**
 * Receive a MAD into an allocated buffer
 *     (response is allocated by user)
 * 
 * @param    port        port opened by omgt_open_port_*
 * @param   *recv_mad    Allocated buffer to place MAD in
 * @param   *recv_size   IN size of recv_mad buffer
 *                       OUT sizeof actual data received.
 * @param    timeout_ms  OFED send timeout in ms (if timeout_ms < 0 wait forever)
 * @param    addr        if supplied recv'd MAD address information is filled in
 *                       here.
 * 
 * @return   0 if success, else error code
 *
 * FINVALID_PARAMETER - incorrect or missing arguments to function
 * FERROR - other atypical errors
 * FNOT_DONE - no packet available within timeout
 * FOVERRUN - large MAD (rmpp) found and discarded, 1st MAD in sequence
 * 				returned
 * FINSUFFICIENT_MEMORY - unable to allocate memory, no MAD returned
 *
 * For the FTIMEOUT, FREJECT and FSUCCESS return cases, recv_mad will be
 * filled in with MAD data.
 *
 * FTIMEOUT - a previous send's response exceeded timeout/retry, the sent MAD
 *  				is returned
 * FREJECT - unexpected error processing a previous send or its response, the
 * 					sent MAD is returned
 * FSUCCESS - MAD successfully received
 */
FSTATUS omgt_recv_mad_no_alloc(struct omgt_port *port, uint8_t *recv_mad, size_t *recv_size,
			int timeout_ms, struct omgt_mad_addr *addr);

/** =========================================================================
 * Need TBD...  Right now the global port has cached data for things like
 * pkey, smlid, smsl, portstate...
 * It is up to the user of the lib to "refresh" this data.
 * It would be nice if the lib could do that on it's own.
 * Regardless we could have a call to update this information if the user
 * feels the need.
 *
FSTATUS omgt_mad_refresh_port_details(struct omgt_port *port);
 */


/** =========================================================================
 * Generic HELPER FUNCTIONs
 */

/**
 * @brief Get the HFI number
 *
 * @param hfi_name       Name of the HFI device
 *
 * @return
 *  On Success  the HFI Number
 *  On Error    -1
 */
int omgt_get_hfi_num(char *hfi_name);

/**
 * MACRO used to get the specific record in a multi-record response
 * @param p      pointer to SA_MAD struct that contains the response
 * @param i      index of record
 */
#define GET_RESULT_OFFSET(p,i) (p->Data+(p->SaHdr.AttributeOffset*sizeof (uint64_t)*i))

/* translate ca/port number into a port Guid. Also return other useful structs
 * if non-null pointer passed-in.
 * Warning: Endian conversion not done
 *
 * INPUTS:
 * 	ca - system wide CA number 1-n, if 0 port is a system wide port #
 * 	port - 1-n, if ca is 0, system wide port number, otherwise port within CA
 * 			if 0, 1st active port
 * 	*pCaName - ca name for specified port
 * 	*omgtport - optional omgt_port, will use log targets set up for this port
 *
 * OUTPUTS:
 * 	*pCaGuid - ca guid for specified port
 * 	*pPortGuid - port guid for specified port (Warning: Endian conversion not done)
 * 	*pCaAttributes - attributes for CA,
 * 					If PortAttributesList not null, caller must MemoryDeallocate pCaAttributes->PortAttributesList
 * 	*ppPortAtributes - attributes for port, if ppPortAtributes not null, caller must MemoryDeallocate ppPortAtributes
 * 	*pCaCount - number of CA in system
 * 	*pPortCount - number of ports in system or CA (depends on ca input)
 *
 * RETURNS:
 * 	FNOT_FOUND - *pCaCount and *pPortCount still output
 * 				if ca == 0, *pPortCount = number of ports in system
 * 				if ca < *pCaCount, *pPortCount = number of ports in CA
 * 									otherwise *pPortCount will be 0
 *
 */
FSTATUS omgt_get_portguid(uint32 ca, uint32 port, char *pCaName, struct omgt_port *omgtport,
	EUI64 *pCaGuid, EUI64 *pPortGuid, IB_CA_ATTRIBUTES *pCaAttributes,
	IB_PORT_ATTRIBUTES **ppPortAttributes, uint32 *pCaCount, uint32 *pPortCount,
	char *pRetCaName, int *pRetPortNum, uint64 *pRetGIDPrefix);



#endif /* __OPAMGT_PRIV_H__ */
