#include "bioAuthDriver.h"

#include <tee_internal_api.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <driver.h>
#include <string.h>
#include <core/page.h>


//#define USE_DEBUG_MESSAGE
#define printI(...)        do { printf("bioAuthDrv: "); printf(__VA_ARGS__);  printf("\n"); } while (FALSE)
#ifdef USE_DEBUG_MESSAGE
#define printE(...)        do { printf("bioAuthDrv:[ERROR]%s: ", __func__); printf(__VA_ARGS__);  printf("\n"); } while (FALSE)
#define printD(...)        do { printf("bioAuthDrv:[DEBUG]%s: ", __func__); printf(__VA_ARGS__);  printf("\n"); } while (FALSE)
#else   //USE_DEBUG_MESSAGE
#define printE(...)        do { printf("bioAuthDrv:[ERROR]: "); printf(__VA_ARGS__);  printf("\n"); } while (FALSE)
#define printD(msg, ...)
#endif  //USE_DEBUG_MESSAGE 




enum cmd_ioctl
{
    CMD_IOCTL_NORMAL,
    CMD_IOCTL_PROCESS_DATA,
    CMD_IOCTL_MAX
};

struct ioctl_data
{
    unsigned int len;
    char buf[12];
};

static struct usr_drv_info *udrv_info = NULL;

#if defined v3
static char drv_name[] = "dev://bio_auth_driver";

#elif (defined v4_1 || defined v4_2)
static char drv_name[] = "bio_auth_driver"; //drv에 접근하려는 tl에서는 이름앞에 /dev/를 붙여서 "/dev/bio_auth_driver"; 로 써야함.

#endif

#define DRIVER_BUFFER_SIZE 1024*10 //공식적인 버퍼 최대 크기.
struct BioAuthDriver {
    char buffer[DRIVER_BUFFER_SIZE+1024];// 내부적으로 1024만큼 넉넉 잡자.
};

struct BioAuthDriver bioAuthDrv;

#define BLOWFISH_UUID_LEN 16

void __print16(const uint8_t* arr) {
    (void)arr;
    printD("%x %x %x %x, %x %x %x %x | %x %x %x %x, %x %x %x %x ||",
        *(arr+0),*(arr+1),*(arr+2),*(arr+3),*(arr+4),*(arr+5),*(arr+6),*(arr+7),
        *(arr+8),*(arr+9),*(arr+10),*(arr+11),*(arr+12),*(arr+13),*(arr+14),*(arr+15)
    );
}


typedef struct {
    uint8_t uuid[BLOWFISH_UUID_LEN];
} SAllowedList;

static SAllowedList allowedList[] = {
	//{{0x,0x,0x,0x, 0x,0x,0x,0x,  0x,0x,0x,0x, 0x,0x,0x,0x}}, // test client.
	//{{0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x67}}, // authnr
	//	{{0x11,0x11,0x11,0x11, 0x00,0x00,0x00,0x00,  0x63,0x6c,0x69,0x65, 0x6e,0x74,0x74,0x61}}, // test client.
	{{0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,  0x00,0x00,0x30,0x30, 0x49,0x46,0x41,0x41}}, // ifaa
	{{0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,  0x00,0x00,0x54,0x49, 0x47,0x45,0x52,0x46}}, // SOTER, 기존 Tigerfp와 동일
	{{0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,  0x00,0x00,0x46,0x49, 0x4e,0x47,0x45,0x52}}, // FP B0 Cap
	{{0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,  0x00,0x00,0x46,0x49, 0x4e,0x47,0x45,0x02}}, // FP B2 Indisplay
};
/*
const TEEC_UUID IFAA  = { 0x00000000,  0x0000,  0x0000,  {0x00,0x00,0x30,0x30, 0x49,0x46,0x41,0x41}  };
const TEEC_UUID SOTER = { 0x00000000,  0x0000,  0x0000,  {0x00,0x00,0x54,0x49, 0x47,0x45,0x52,0x46}  };
const TEEC_UUID FP_B0 = { 0x00000000,  0x0000,  0x0000,  {0x00,0x00,0x46,0x49, 0x4e,0x47,0x45,0x52}  }; // Cap
const TEEC_UUID FP_B2 = { 0x00000000,  0x0000,  0x0000,  {0x00,0x00,0x46,0x49, 0x4e,0x47,0x45,0x02}  }; // Indisplay
*/

bool haveRight( struct drv_info *info ){
	#if (defined v3 || defined v4_1)
	// make src uuid.
	uint8_t srcUuid[16] = {0};
	int uuidIdx=0;
    
    uint8_t *a;
    a = (uint8_t*)(&info->uuid.time_low);
	for(int i=0; i<4; i++){
		srcUuid[uuidIdx + i] = a[i];
	}
	uuidIdx +=4;
    
    a = (uint8_t*)(&info->uuid.time_mid);
	for(int i=0; i<2; i++){
		srcUuid[uuidIdx + i] = a[i];
	}
	uuidIdx +=2;
    
    a = (uint8_t*)(&info->uuid.time_hi_and_version);
	for(int i=0; i<2; i++){
		srcUuid[uuidIdx + i] = a[i];
	}
	uuidIdx +=2;
    
    a = info->uuid.clock_seq_and_node;
	for(int i=0; i<8; i++){
		srcUuid[uuidIdx + i] = a[i];
	}
	uuidIdx +=8;
	
    
    int arraySize = (int)(sizeof(allowedList) / sizeof(SAllowedList));
    //printD("haveRights : %d \n", arraySize );
    //__print16(srcUuid);
    
    //compare.
	for(int i=0; i < arraySize ; i++){
		int cmp = memcmp(srcUuid, allowedList[i].uuid, 16);
		//__print16(allowedList[i].uuid);
		if( cmp == 0 ) 
			return TRUE;
	}
    
    return FALSE;
	#else // v4_2
	
	int arraySize = (int)(sizeof(allowedList) / sizeof(SAllowedList));
	//compare.
	for(int i=0; i < arraySize ; i++){
		int cmp = memcmp(&info->uuid, allowedList[i].uuid, 16);
		//__print16(allowedList[i].uuid);
		if( cmp == 0 ) 
			return TRUE;
	}
	return FALSE;
	#endif
    
}

static int openDriver(struct drv_info *info, const char *drv_path, ...)
{
    (void) info;
    (void) drv_path;
    printD("openDriver success\n");

    return 0;
}

static int closeDriver(struct drv_info *info)
{
    (void) info;
    printD("closeDriver() exit\n");
    
    return 0;
}

static int ioctlDriver(struct drv_info *info, int ioctl_cmd, unsigned long ioctl_data)
{
    (void) info;
    (void) ioctl_cmd;
    (void) ioctl_data;
    printE("not supported \n");
    return -EINVAL;
}


static ssize_t readBuffer(struct drv_info *info, void *data, size_t dataLen)
{
    printD("readBuffer()\n");

    //check uuid,
    if( FALSE == haveRight(info) ){
        printE("no right \n");
        return -EINVAL;
	}
    
    if (dataLen > DRIVER_BUFFER_SIZE){
        printE("out of Buffer, read \n");
        return -EINVAL;
    }

#if defined v3
    char *vUsrAddr = NULL;
    vUsrAddr = TEES_AcquireUserBuffer(info, (uintptr_t)data, dataLen, PROT_READ | PROT_WRITE); //Pointer to userspace buffer, client의 버퍼(data)에 대한 주소(vUsrAddr)를 받음.
    TEE_MemMove(vUsrAddr, bioAuthDrv.buffer, dataLen); // 유저 영역 버퍼인 vUsrAddr에 드라이버 버퍼 내용인 bioAuthDrv를 써줌.    
    TEES_ReleaseUserBuffer(vUsrAddr, dataLen);

#elif (defined v4_1 || defined v4_2)
    TEE_MemMove(data, bioAuthDrv.buffer, dataLen); // 유저 영역 버퍼인 vUsrAddr에 드라이버 버퍼 내용인 bioAuthDrv를 써줌.    
    
#endif

    printD("readBuffer() exit\n");

    return dataLen;
}

static ssize_t writeBuffer(struct drv_info *info, void *data, size_t dataLen)
{
    printD("writeBuffer()\n");
    
    //check uuid,
    if( FALSE == haveRight(info) ){
        printE("no right \n");
        return -EINVAL;
	}
    
    if (dataLen > DRIVER_BUFFER_SIZE){
        printE("out of Buffer, write \n");
        return -EINVAL;
    }
    
#if defined v3
    char *vUsrAddr = NULL;
    vUsrAddr = TEES_AcquireUserBuffer(info, (uintptr_t)data, dataLen, PROT_READ | PROT_WRITE); //Pointer to userspace buffer    
    TEE_MemMove(bioAuthDrv.buffer, vUsrAddr, dataLen); //void TEE_MemMove(_OUTBUF_ void* dest, _INBUF_ const void* src, uint32_t size );
    // 드라이버에서 사용하는 버퍼인 bioAuthDrv에 저장.
    printD("writeBuffer() received buffer: '%s'\n", bioAuthDrv.buffer);
    TEES_ReleaseUserBuffer(vUsrAddr, dataLen);

#elif (defined v4_1 || defined v4_2)
    TEE_MemMove (bioAuthDrv.buffer, data, dataLen); //void TEE_MemMove(_OUTBUF_ void* dest, _INBUF_ const void* src, uint32_t size );
    // 드라이버에서 사용하는 버퍼인 bioAuthDrv에 저장.
    printD("writeBuffer() received buffer: '%s'\n", bioAuthDrv.buffer);
    
#endif

    
    printD("writeBuffer() exit\n");

    return dataLen;
}


static struct fops file_ops;

TEE_Result TA_CreateEntryPoint(void)
{
    int ret = 0;

    file_ops.open = openDriver;
    file_ops.close = closeDriver;
    file_ops.read = readBuffer;
    file_ops.write = writeBuffer;
    file_ops.ioctl  = ioctlDriver;

    ret = TEES_InitDriver(drv_name, &file_ops, ACC_PERM_SEC_DRV, &udrv_info);
    if (ret) {
        printE("TEES_InitDriver failed, ret = %d, errno = %d\n", ret, errno);
        return TEE_ERROR_GENERIC;
    }
    printD("Driver registered \n" );
    
    return TEE_SUCCESS;
}

void TA_DestroyEntryPoint(void)
{
    int ret = TEES_FiniDriver(udrv_info);

    if (ret) {
        printE("TEES_FiniDriver failed, ret = %d\n'", ret);
    }
    printD("Driver released\n");
}

TEE_Result TA_OpenSessionEntryPoint(uint32_t paramTypes,
    TEE_Param params[4], void **sessionContext)
{
    (void) paramTypes;
    (void) params;
    (void) sessionContext;
    
    if (paramTypes != TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE)) {
        printE("Bad parameter types");
        return TEE_ERROR_BAD_PARAMETERS;
    }

    return TEE_SUCCESS;
}

void TA_CloseSessionEntryPoint(void *sessionContext)
{
    (void) sessionContext;
}

TEE_Result TA_InvokeCommandEntryPoint(void *sessionContext,
    uint32_t commandID, uint32_t paramTypes, TEE_Param params[4])
{
    (void) sessionContext;
    (void) params;
    (void) paramTypes;
    (void) commandID;
    
    if (paramTypes != TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE)) {
        printE("Bad parameter types");
        return TEE_ERROR_BAD_PARAMETERS;
    }

    return TEE_SUCCESS;
}
