/*
*	Brief:	Implement IPMB send and receive function in this file, include primary IPMB and secondary IPMB.
*	Author:	Jimbo_Zhang@outlook.com
*	Date:	2019-9-9
*/

/* Standard includes. */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/socket.h>

#include "com_IPMI_LANIfc.h"
#include "com_IPMI_RMCP.h"
#include "RMCP.h"
#include "LANIfc.h"
#include "com_IPMIDefs.h"
#include "RMCP.h"
#include "MD.h"
#include <netdb.h>        /* getaddrinfo(3) et al.                       */
#include <netinet/in.h>   /* sockaddr_in & sockaddr_in6 definition.      */
#include  <net/if.h>
#include <sys/prctl.h>
#include "main.h"
#include "Session.h"
#include "com_IPMI_SDR.h"
#include "com_IPMI_Storage.h"
#include "SDR.h"
#include <sys/syscall.h> /*此头必须带上*/

pid_t gettid()
{
        return syscall(SYS_gettid); /*这才是内涵*/
}


//static int sendIPMBPkt(uint32_t ipmb_bus, uint8_t* i2c_send_buf, uint8_t size);
static uint8_t ValidateIPMBChkSums ( uint8_t* Data, uint8_t Size);
static void ProcessIPMBReq ( MsgPkt_T* pReq);
uint8_t IsResponseMatch (MsgPkt_T* pReq, MsgPkt_T* pRes);
static void* RecvIPMBPkt (void *pArg);

int gFd_PrimaryIpmbIfcQ, gFd_PrimaryIpmbResQ;
int gFd_SecondaryIpmbIfcQ, gFd_SecondaryIpmbResQ;
int gFd_Primary,  gFd_Secondary;
pthread_mutex_t primary_mutex, secondary_mutex;
/*!
    \brief      IPMB Task. Generating primary_IPMB task and secondary_IPMB task in this task.
    \param:		0: primary ipmb, 1: secondary ipmb
    \retval     none
*/
void *IPMBIfcTask(void *Param)
{
//	int i;
	MsgPkt_T RcvMsgPkt;
    uint8_t dev_name[10] = {0};
    uint8_t IPMB_IFC_Q[32] = {0};
    uint8_t IPMB_RES_Q[32] = {0};
    uint8_t OwnerAddr;
    int 	fd_IpmbDev;
    int 	fd_IpmbIfcQ, fd_IpmbResQ;
    uint8_t ipmbSelect = *(uint8_t*)Param;
    int RetVal;
    pthread_mutex_t *pMutex;
    
    //printf("ipmbSelect: %d\n", ipmbSelect);
    //Primary IPMB
    if((ipmbSelect == 0) && g_BMCInfo.IpmiConfig.PrimaryIPMBSupport)
    {
		printf("Primary IPMBIfcTask start...\n");
		strcpy(IPMB_IFC_Q, PRIMARY_IPMB_IFC_Q);	
		strcpy(IPMB_RES_Q, PRIMARY_IPMB_RES_Q);	
		sprintf(dev_name, "/dev/i2c%d", g_BMCInfo.IpmiConfig.PrimaryIPMBBus);
		OwnerAddr	=	g_BMCInfo.PrimaryIPMBAddr;
        pMutex = &primary_mutex;
	}
	//Secondary IPMB
	else if((ipmbSelect == 1) && g_BMCInfo.IpmiConfig.SecondaryIPMBSupport)
	{
		printf("Secondary IPMBIfcTask start...\n");
		strcpy(IPMB_IFC_Q, SECONDARY_IPMB_IFC_Q);	
		strcpy(IPMB_RES_Q, SECONDARY_IPMB_RES_Q);
		sprintf(dev_name, "/dev/i2c%d", g_BMCInfo.IpmiConfig.SecondaryIPMBBus);
		OwnerAddr	=	g_BMCInfo.SecondaryIPMBAddr;
        pMutex = &secondary_mutex;
	}
	else
	{
		printf("Invalid param! %d\n", ipmbSelect);
		return (void *)-1;
	}

	//create IPMB_IFC_Q
    if(-1 != access(IPMB_IFC_Q, F_OK))
    {
        remove(IPMB_IFC_Q);
    }
    if(0 != mkfifo (IPMB_IFC_Q, 0777))
    {
        printf("%s: Create %s fifo failed! %s\n", __FUNCTION__, IPMB_IFC_Q, strerror(errno));
        return (void*)-1;
    }
    fd_IpmbIfcQ = open (IPMB_IFC_Q, O_RDWR);
    if(-1 == fd_IpmbIfcQ)
    {
        printf("%s: Open %s fifo failed! %s\n", __FUNCTION__, IPMB_IFC_Q, strerror(errno));
        return (void*)-1;
    }

	//create IPMB_RES_Q
    if(-1 != access(IPMB_RES_Q, F_OK))
    {
        remove(IPMB_RES_Q);
    }
    if(0 != mkfifo (IPMB_RES_Q, 0777))
    {
        printf("%s: Create %s fifo failed! %s\n", __FUNCTION__, IPMB_RES_Q, strerror(errno));
        return (void*)-1;
    }
    fd_IpmbResQ = open (IPMB_RES_Q, O_RDWR);
    if(-1 == fd_IpmbResQ)
    {
        printf("%s: Open %s fifo failed! %s\n", __FUNCTION__, IPMB_RES_Q, strerror(errno));
        return (void*)-1;
    }

    fd_IpmbDev = open (dev_name, O_RDWR);
    if(-1 == fd_IpmbDev)
    {
        printf("%s: Open IPMB failed! %s\n", __FUNCTION__, strerror(errno));
        return (void*)-1;
    }
    //set IPMB address
    if(0 != stm32_i2c_set_addr(fd_IpmbDev, OwnerAddr))
    	printf("Set ipmb address fail.\n");

	if(ipmbSelect == 0)
    {
    	gFd_PrimaryIpmbIfcQ = fd_IpmbIfcQ;
    	gFd_PrimaryIpmbResQ = fd_IpmbResQ;
    	gFd_Primary 	=	fd_IpmbDev;
    }
    else if(ipmbSelect == 1)
    {
    	gFd_SecondaryIpmbIfcQ = fd_IpmbIfcQ;
    	gFd_SecondaryIpmbResQ = fd_IpmbResQ;
    	gFd_Secondary 	=	fd_IpmbDev;
    }

    pthread_mutex_init(pMutex, NULL);

    /*Create a thread to recv IPMB Pkt */
	gThreadIndex++;	
    if(0 != pthread_create(&gThreadIDs[gThreadIndex],NULL,RecvIPMBPkt,Param))
    {
        printf("%s: Create RecvIPMBPkt thread failed! %s\n", __FUNCTION__, strerror(errno));
        return (void *)-1; 
    }

	while(1)
	{		
		while(GetMsg(fd_IpmbIfcQ, &RcvMsgPkt, WAIT_INFINITE) != 0);
		
        switch(RcvMsgPkt.Param)
		{
			case PARAM_REQUEST:
                //printf("---> IPMBIfcTask/PARAM_REQUEST\n");
				ProcessIPMBReq (&RcvMsgPkt);
				break;
			case PARAM_BRIDGE:
                //printf("---> IPMBIfcTask/PARAM_BRIDGE, channel: %d\n", RcvMsgPkt.Channel);
                
                /* Send the response */
                if(RcvMsgPkt.Channel == PRIMARY_IPMB_CHANNEL)
                {
                    RetVal = stm32_i2c_master_write(gFd_Primary, RcvMsgPkt.Data[0], &RcvMsgPkt.Data[1], RcvMsgPkt.Size-1);
                    printf("PriTx: ");
                }
                else if(RcvMsgPkt.Channel == SECONDARY_IPMB_CHANNEL)
                {
                    RetVal = stm32_i2c_master_write(gFd_Secondary, RcvMsgPkt.Data[0], &RcvMsgPkt.Data[1], RcvMsgPkt.Size-1);
                    printf("SecTx: ");
                }
                else
                {
                    printf("IPMBIfc.c: IPMB channel error. %#x\r\n", RcvMsgPkt.Channel);
                }

                int i;
                for(i=0;i<RcvMsgPkt.Size;i++)
                    printf("%02x ", RcvMsgPkt.Data[i]);
                printf("\n");

                if (RetVal < 0)
                {
                    printf ("IPMBIfc.c : Unable to send a IPMI Bridge Message\n");
                    RespondSendMessage (&RcvMsgPkt, STATUS_FAIL);
                }
                else
                {
                    RespondSendMessage (&RcvMsgPkt, STATUS_OK);
                }
                
                break;
            default:
				printf("Unknow message param %#x\r\n", RcvMsgPkt.Param);
				break;
		}
		
	}
}



/**
 * @brief Process the request posted by IPMB driver.
 * @param pReq - Request message pointer.
**/
static void
ProcessIPMBReq ( MsgPkt_T* pReq)
{
    MsgPkt_T             	ResPkt;
    uint8_t              	ReqLen;
    IPMIMsgHdr_T* 			pIPMIMsgReq = (IPMIMsgHdr_T*) pReq->Data;
   
    int RetVal = 0;
    ReqLen = pReq->Size;
	
    /* Validate Checksums */
    if (TRUE != ValidateIPMBChkSums ((uint8_t*)pIPMIMsgReq, ReqLen))
    {
        printf ("IPMBIfc.c : IPMB Checksum validation failed\r\n");
        return;
    }

    pReq->Param			= PARAM_IFC;
    pReq->Cmd			= pIPMIMsgReq->Cmd;
    pReq->NetFnLUN		= pIPMIMsgReq->NetFnLUN;

    if(pReq->Channel == PRIMARY_IPMB_CHANNEL)
    	pReq->SrcQ 			= gFd_PrimaryIpmbResQ;
    else
    	pReq->SrcQ 			= gFd_SecondaryIpmbResQ;
	
    if(0 == ValidateMsgHdr(pReq))
    {
        /* Post the message to message handler Task */
        if (0 != PostMsg (gFd_MsgHndlrIfc, pReq))
        {
            printf("IPMB post message to MsgHndlrTask fail!\n");;
        }
    }
    else
    {
         //printf("Bridge response to Originator\n");
         return;
    }
    /* Post the message to message handler Task */
    //PostMsg (gFd_MsgHndlrIfc, pReq);
       
    if(pReq->ResTimeOut < 1)	pReq->ResTimeOut = 10;
    do
    {
    	if(pReq->Channel == PRIMARY_IPMB_CHANNEL)
        {
      		RetVal = GetMsg (gFd_PrimaryIpmbResQ, &ResPkt, pReq->ResTimeOut);  
            if((pReq->Cmd == CMD_GET_SDR) && (pReq->NetFnLUN == (NETFN_STORAGE<<2)) 
                && (pReq->Data[4+6] == 5))
            {
                ResPkt.Data[3+6] = g_BMCInfo.PrimaryIPMBAddr;
                ResPkt.Data[ResPkt.Size-1] = CalculateCheckSum (ResPkt.Data, ResPkt.Size-1);
            }
        }
      	else
        {
      		RetVal = GetMsg (gFd_SecondaryIpmbResQ, &ResPkt, pReq->ResTimeOut);  
            //为传感器的owner ID打的补丁 
            if((pReq->Cmd == CMD_GET_SDR) && (pReq->NetFnLUN == (NETFN_STORAGE<<2)) 
                && (pReq->Data[4+6] == 5))
            {
                ResPkt.Data[3+6] = g_BMCInfo.SecondaryIPMBAddr;
                ResPkt.Data[ResPkt.Size-1] = CalculateCheckSum (ResPkt.Data, ResPkt.Size-1);
            }
        }

    }while(!RetVal && !IsResponseMatch(pReq, &ResPkt) );
	
	if (0 != RetVal)
    {
		printf("Warning: Process IPMB request fail, return timeout.\r\n");
		ResPkt.Param	=	PARAM_NORMAL_RESPONSE;
		ResPkt.Size		=	sizeof(IPMIMsgHdr_T)+2;
		
		SwapIPMIMsgHdr(((IPMIMsgHdr_T*)pReq->Data), ((IPMIMsgHdr_T*)ResPkt.Data));
		ResPkt.Data[sizeof(IPMIMsgHdr_T)]	=	CC_TIMEOUT;
		ResPkt.Data[sizeof(IPMIMsgHdr_T)+1]	=	CalculateCheckSum2(ResPkt.Data, sizeof(IPMIMsgHdr_T)+1);
    }   

    /* If its not normal IPMI Response just return */
    if (PARAM_NO_RESPONSE == ResPkt.Param)
    {
        printf ("IPMBIfc.c : IPMB request packet dropped, not processed\n");
        return;
    }

   
    /* Send the response */
	if(pReq->Channel == PRIMARY_IPMB_CHANNEL)
	{
        pthread_mutex_lock(&primary_mutex);
		RetVal = stm32_i2c_master_write(gFd_Primary, ResPkt.Data[0], &ResPkt.Data[1], ResPkt.Size-1);
        pthread_mutex_unlock(&primary_mutex);
        printf("PriTx: ");
	}
	else if(pReq->Channel == SECONDARY_IPMB_CHANNEL)
	{
        pthread_mutex_lock(&secondary_mutex);
		RetVal = stm32_i2c_master_write(gFd_Secondary, ResPkt.Data[0], &ResPkt.Data[1], ResPkt.Size-1);
        pthread_mutex_unlock(&secondary_mutex);
        printf("SecTx: ");
	}
	else
	{
		printf("IPMBIfc.c: IPMB channel error. %#x\r\n", pReq->Channel);
	}

    int i;
    for(i=0;i<ResPkt.Size;i++)
        printf("%#x ", ResPkt.Data[i]);
    printf("\n");
	
    if (0 != RetVal)
    {
        printf ("IPMBIfc.c : Unable to send a IPMI Response\n");
    }
    return;
}

/**
 * @brief Validate IPMB Checksums.
 * @param Data - Buffer to be validated.
 * @param Size - Size of the buffer.
 * @return TRUE if valid, FALSE if error.
**/
static uint8_t ValidateIPMBChkSums ( uint8_t* Data, uint8_t Size)
{
    uint8_t i;
    uint8_t chksum;

    /* Check IPMB message min size */
    if (Size < MIN_IPMB_MSG_LENGTH)
    {
        printf("IPMBIfc.c: Invalid IPMB Message Length\r\n");
        return FALSE;
    }

    /* Validate the checksum1 */
    chksum = 0;
    for (i = 0; i < 3; i++)
    {
        chksum += *(Data + i);
    }

    if (chksum != 0)
    {
       
		printf ("IPMBIfc.c: Invalid checksum 1, size = %d\n",Size);
		for(i = 0; i < Size;i++)
			printf("%x ",Data[i]);
		printf("\n");
        return FALSE;
    }
    /* Validate the checksum2 */
    chksum = 0;
    for (i = 3; i < Size; i++)
    {
        chksum += *(Data + i);
    }

    if (chksum != 0)
    {
       
		printf("IPMBIfc.c: Invalid checksum 2, size = %d\n",Size);
		for(i = 0; i < Size;i++)
			printf("%x ",Data[i]);
		printf("\n");
        
        return FALSE;
    }

    return TRUE;
}


/*-----------------------------------------------------
 * @fn IsResponseMatch
 * @brief Checks if the Response Message corresponds to 
 *        the Request Message
 * 
 * @param pReq    : IPMI Request Message Packet
 * @param pRes    : IPMI Response Message Packet
 * 
 * @return  1 if match
 *          0 if mismatch
 *----------------------------------------------------*/
uint8_t IsResponseMatch (MsgPkt_T* pReq, MsgPkt_T* pRes)
{

    if( ( ((((IPMIMsgHdr_T*)pRes->Data)->RqSeqLUN) >> 2) != ((((IPMIMsgHdr_T*)pReq->Data)->RqSeqLUN) >> 2) ) && 
        ( (pReq->NetFnLUN & 0x4) && (PARAM_NO_RESPONSE != pRes->Param) ) )
    {
        return FALSE;
    }
  
    return TRUE;
}



/**
 * @fn RecvIPMBPkt
 * @brief This function receives IPMB request packet and post the message to
 *        IPMB interface queue
**/
static void* RecvIPMBPkt (void *Param)
{
    MsgPkt_T    IPMBReqPkt;
    int IPMBSlaveFd;
    int retval;
    uint8_t ipmbSelect = *(uint8_t*)Param;
    int i;

    prctl(PR_SET_NAME, __FUNCTION__, 0, 0, 0);
    int curThreadIndex = 0;
    pthread_mutex_t *pMutex;

    memset(&IPMBReqPkt,0,sizeof(MsgPkt_T));
    if(0 == ipmbSelect)
    {
    	printf ("Primary IPMB Receiver Started...\n");
        IPMBReqPkt.Data [0] = g_BMCInfo.PrimaryIPMBAddr;
        IPMBReqPkt.Channel = PRIMARY_IPMB_CHANNEL;
        IPMBSlaveFd = gFd_Primary;
        pMutex = &primary_mutex;
        //printf("Primary: %p\n", RecvIPMBPkt);
    }
    else if(1 == ipmbSelect)
    {
    	printf ("Secondary IPMB Receiver Started...\n");
        IPMBReqPkt.Data [0] = g_BMCInfo.SecondaryIPMBAddr;
        IPMBReqPkt.Channel = SECONDARY_IPMB_CHANNEL;
        IPMBSlaveFd = gFd_Secondary;
        pMutex = &secondary_mutex;
        //printf("Secondary: %p\n", RecvIPMBPkt);
    }



    /* Loop forever */
    while (1)
    {
        //printf("ppid: %d, pid: %d, tid: %d, i: %d, ipmbSelect: %d\n", getppid(), getpid(), gettid(), i++, ipmbSelect);
        pthread_mutex_lock(pMutex);
        pthread_mutex_unlock(pMutex);
        retval = stm32_i2c_slave_recv(IPMBSlaveFd, &IPMBReqPkt.Data[1]);
        if( retval > 5)
        {
            IPMBReqPkt.Param    = PARAM_REQUEST;
            IPMBReqPkt.Size     = retval + 1;       /* +1 to include BMC Slave address */

            int cnt;
            if(0 == ipmbSelect)
                printf("\nPriRx: ");
            else
                printf("\nSecRx: ");

            for(cnt=0;cnt < IPMBReqPkt.Size; cnt++)
                printf("%02x ", IPMBReqPkt.Data[cnt]);
            printf("\n");

            /* Post the IPMB Request message to IPMBIfc Queue */
            if(ipmbSelect == 0)
            {
                if (0 != PostMsg (gFd_PrimaryIpmbIfcQ, &IPMBReqPkt))
                {
                    printf ("IPMBIfc.c : Error posting message to IPMBIfc_Q\n");
                }
            }
            else
            {
                if (0 != PostMsg (gFd_SecondaryIpmbIfcQ, &IPMBReqPkt))
                {
                    printf ("IPMBIfc.c : Error posting message to IPMBIfc_Q\n");
                }
            }

        }

       
	}
    return (void*)-1;
}