#include "headers.h"


int InterfaceFileDownload( PVOID arg,
                        struct file *flp,
                        unsigned int on_chip_loc)
{
   // unsigned int    reg=0;
    mm_segment_t    oldfs={0};
    int             errno=0, len=0 /*,is_config_file = 0*/;
    loff_t          pos=0;
	PS_INTERFACE_ADAPTER psIntfAdapter = (PS_INTERFACE_ADAPTER)arg;
	//PMINI_ADAPTER Adapter = psIntfAdapter->psAdapter;
    char            *buff=kmalloc(MAX_TRANSFER_CTRL_BYTE_USB, GFP_KERNEL);

    if(!buff)
    {
        return -ENOMEM;
    }
    while(1)
    {
        oldfs=get_fs(); set_fs(get_ds());
        len=vfs_read(flp, (void __force __user *)buff, MAX_TRANSFER_CTRL_BYTE_USB, &pos);
        set_fs(oldfs);
        if(len<=0)
        {
            if(len<0)
            {
                BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "len < 0");
                errno=len;
            }
            else
            {
                errno = 0;
                BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Got end of file!");
            }
            break;
        }
        //BCM_DEBUG_PRINT_BUFFER(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, buff, MAX_TRANSFER_CTRL_BYTE_USB);
        errno = InterfaceWRM(psIntfAdapter, on_chip_loc, buff, len) ;
		if(errno)
		{
            BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_PRINTK, 0, 0, "WRM Failed! status: %d", errno);
			break;

		}
        on_chip_loc+=MAX_TRANSFER_CTRL_BYTE_USB;
	}/* End of for(;;)*/

	kfree(buff);
    return errno;
}

int InterfaceFileReadbackFromChip( PVOID arg,
                        struct file *flp,
                        unsigned int on_chip_loc)
{
    char            *buff, *buff_readback;
    unsigned int    reg=0;
    mm_segment_t    oldfs={0};
    int             errno=0, len=0, is_config_file = 0;
    loff_t          pos=0;
    static int fw_down = 0;
	INT				Status = STATUS_SUCCESS;
	PS_INTERFACE_ADAPTER psIntfAdapter = (PS_INTERFACE_ADAPTER)arg;

    buff=kmalloc(MAX_TRANSFER_CTRL_BYTE_USB, GFP_DMA);
    buff_readback=kmalloc(MAX_TRANSFER_CTRL_BYTE_USB , GFP_DMA);
    if(!buff || !buff_readback)
    {
        kfree(buff);
        kfree(buff_readback);

        return -ENOMEM;
    }

	is_config_file = (on_chip_loc == CONFIG_BEGIN_ADDR)? 1:0;

	memset(buff_readback, 0, MAX_TRANSFER_CTRL_BYTE_USB);
	memset(buff, 0, MAX_TRANSFER_CTRL_BYTE_USB);
    while(1)
    {
        oldfs=get_fs(); set_fs(get_ds());
        len=vfs_read(flp, (void __force __user *)buff, MAX_TRANSFER_CTRL_BYTE_USB, &pos);
        set_fs(oldfs);
        fw_down++;
        if(len<=0)
        {
            if(len<0)
            {
                BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "len < 0");
                errno=len;
            }
            else
            {
                errno = 0;
                BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Got end of file!");
            }
            break;
        }


		Status = InterfaceRDM(psIntfAdapter, on_chip_loc, buff_readback, len);
		if(Status)
		{
            BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "RDM of len %d Failed! %d", len, reg);
			goto exit;
		}
		reg++;
        if((len-sizeof(unsigned int))<4)
        {
            if(memcmp(buff_readback, buff, len))
            {
                BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Firmware Download is not proper %d", fw_down);
				BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT,DBG_LVL_ALL,"Length is: %d",len);
				Status = -EIO;
				goto exit;
            }
        }
        else
        {
            len-=4;
            while(len)
            {
                if(*(unsigned int*)&buff_readback[len] != *(unsigned int *)&buff[len])
                {
                    BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Firmware Download is not proper %d", fw_down);
                    BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Val from Binary %x, Val From Read Back %x ", *(unsigned int *)&buff[len], *(unsigned int*)&buff_readback[len]);
                    BCM_DEBUG_PRINT(psIntfAdapter->psAdapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "len =%x!!!", len);
					Status = -EIO;
					goto exit;
                }
                len-=4;
            }
        }
        on_chip_loc+=MAX_TRANSFER_CTRL_BYTE_USB;
    }/* End of while(1)*/
exit:
    kfree(buff);
    kfree(buff_readback);
	return Status;
}

static int bcm_download_config_file(PMINI_ADAPTER Adapter,
								FIRMWARE_INFO *psFwInfo)
{
	int retval = STATUS_SUCCESS;
	B_UINT32 value = 0;

	if(Adapter->pstargetparams == NULL)
    {
        if((Adapter->pstargetparams =
            kmalloc(sizeof(STARGETPARAMS), GFP_KERNEL)) == NULL)
        {
            return -ENOMEM;
        }
    }
	if(psFwInfo->u32FirmwareLength != sizeof(STARGETPARAMS))
	{
		return -EIO;
	}
	retval = copy_from_user(Adapter->pstargetparams,
			psFwInfo->pvMappedFirmwareAddress, psFwInfo->u32FirmwareLength);
	if(retval)
	{
		kfree(Adapter->pstargetparams);
		Adapter->pstargetparams = NULL;
		return -EFAULT;
	}
	/* Parse the structure and then Download the Firmware */
	beceem_parse_target_struct(Adapter);

	//Initializing the NVM.
	BcmInitNVM(Adapter);

	retval = InitLedSettings (Adapter);

	if(retval)
	{
		BCM_DEBUG_PRINT (Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "INIT LED Failed\n");
		return retval;
	}

	if(Adapter->LEDInfo.led_thread_running & BCM_LED_THREAD_RUNNING_ACTIVELY)
	{
		Adapter->LEDInfo.bLedInitDone = FALSE;
		Adapter->DriverState = DRIVER_INIT;
		wake_up(&Adapter->LEDInfo.notify_led_event);
	}

	if(Adapter->LEDInfo.led_thread_running & BCM_LED_THREAD_RUNNING_ACTIVELY)
	{
		Adapter->DriverState = FW_DOWNLOAD;
		wake_up(&Adapter->LEDInfo.notify_led_event);
	}

	/* Initialize the DDR Controller */
	retval = ddr_init(Adapter);
	if(retval)
	{
		BCM_DEBUG_PRINT (Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "DDR Init Failed\n");
		return retval;
	}

	value = 0;
	wrmalt(Adapter, EEPROM_CAL_DATA_INTERNAL_LOC - 4, &value, sizeof(value));
	wrmalt(Adapter, EEPROM_CAL_DATA_INTERNAL_LOC - 8, &value, sizeof(value));

	if(Adapter->eNVMType == NVM_FLASH)
	{
		retval = PropagateCalParamsFromFlashToMemory(Adapter);
		if(retval)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL,"propagaion of cal param failed with status :%d", retval);
			return retval;
		}
	}


	retval =buffDnldVerify(Adapter,(PUCHAR)Adapter->pstargetparams,sizeof(STARGETPARAMS),CONFIG_BEGIN_ADDR);

	if(retval)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "configuration file not downloaded properly");
	}
	else
		Adapter->bCfgDownloaded = TRUE;


	return retval;
}
static int bcm_compare_buff_contents(unsigned char *readbackbuff,
	unsigned char *buff,unsigned int len)
{
	int retval = STATUS_SUCCESS;
    PMINI_ADAPTER Adapter = GET_BCM_ADAPTER(gblpnetdev);
    if((len-sizeof(unsigned int))<4)
	{
		if(memcmp(readbackbuff , buff, len))
		{
			retval=-EINVAL;
		}
	}
	else
	{
		len-=4;
		while(len)
		{
			if(*(unsigned int*)&readbackbuff[len] !=
					*(unsigned int *)&buff[len])
			{
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Firmware Download is not proper");
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Val from Binary %x, Val From Read Back %x ", *(unsigned int *)&buff[len], *(unsigned int*)&readbackbuff[len]);
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "len =%x!!!", len);
				retval=-EINVAL;
				break;
			}
			len-=4;
		}
	}
	return retval;
}
int bcm_ioctl_fw_download(PMINI_ADAPTER Adapter, FIRMWARE_INFO *psFwInfo)
{
	int retval = STATUS_SUCCESS;
	PUCHAR buff = NULL;

	/*  Config File is needed for the Driver to download the Config file and
		Firmware. Check for the Config file to be first to be sent from the
		Application
	*/
	atomic_set (&Adapter->uiMBupdate, FALSE);
	if(!Adapter->bCfgDownloaded &&
		psFwInfo->u32StartingAddress != CONFIG_BEGIN_ADDR)
	{
		/*Can't Download Firmware.*/
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL,"Download the config File first\n");
		return -EINVAL;
	}

	/* If Config File, Finish the DDR Settings and then Download CFG File */
    if(psFwInfo->u32StartingAddress == CONFIG_BEGIN_ADDR)
    {
		retval = bcm_download_config_file (Adapter, psFwInfo);
	}
	else
	{

		buff = kzalloc(psFwInfo->u32FirmwareLength,GFP_KERNEL);
		if(buff==NULL)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL,"Failed in allocation memory");
			return -ENOMEM;
		}
		retval = copy_from_user(buff,psFwInfo->pvMappedFirmwareAddress, psFwInfo->u32FirmwareLength);
		if(retval != STATUS_SUCCESS)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "copying buffer from user space failed");
			retval = -EFAULT;
			goto error ;
		}

		retval = buffDnldVerify(Adapter,
					buff,
					psFwInfo->u32FirmwareLength,
					psFwInfo->u32StartingAddress);
		if(retval != STATUS_SUCCESS)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL,"f/w download failed status :%d", retval);
			goto error;
		}
	}
error:
	kfree(buff);
	return retval;
}

static INT buffDnld(PMINI_ADAPTER Adapter, PUCHAR mappedbuffer, UINT u32FirmwareLength,
		ULONG u32StartingAddress)
{

	unsigned int	len = 0;
	int retval = STATUS_SUCCESS;
	len = u32FirmwareLength;

	while(u32FirmwareLength)
	{
		len = MIN_VAL (u32FirmwareLength, MAX_TRANSFER_CTRL_BYTE_USB);
		retval = wrm (Adapter, u32StartingAddress, mappedbuffer, len);
		if(retval)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "wrm failed with status :%d", retval);
			break;
		}
		u32StartingAddress	+= len;
		u32FirmwareLength	-= len;
		mappedbuffer		+=len;
	}
	return retval;

}

static INT buffRdbkVerify(PMINI_ADAPTER Adapter,
			PUCHAR mappedbuffer, UINT u32FirmwareLength,
			ULONG u32StartingAddress)
{
	UINT len = u32FirmwareLength;
	INT retval = STATUS_SUCCESS;
	PUCHAR readbackbuff = kzalloc(MAX_TRANSFER_CTRL_BYTE_USB,GFP_KERNEL);

	if(NULL == readbackbuff)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "MEMORY ALLOCATION FAILED");
		return -ENOMEM;
	}
	while (u32FirmwareLength && !retval)
	{

		len = MIN_VAL (u32FirmwareLength, MAX_TRANSFER_CTRL_BYTE_USB);

		retval = rdm (Adapter, u32StartingAddress, readbackbuff, len);
		if(retval)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "rdm failed with status %d" ,retval);
			break;
		}

		if (STATUS_SUCCESS != (retval = bcm_compare_buff_contents (readbackbuff, mappedbuffer, len)))
		{
			break;
		}
		u32StartingAddress 	+= len;
		u32FirmwareLength  	-= len;
		mappedbuffer	   	+=len;
	}/* end of while (u32FirmwareLength && !retval) */
	kfree(readbackbuff);
	return retval;
}

INT buffDnldVerify(PMINI_ADAPTER Adapter, unsigned char *mappedbuffer, unsigned int u32FirmwareLength,
		unsigned long u32StartingAddress)
{
	INT status = STATUS_SUCCESS;

	status = buffDnld(Adapter,mappedbuffer,u32FirmwareLength,u32StartingAddress);
	if(status != STATUS_SUCCESS)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL,"Buffer download failed");
		goto error;
	}

	status= buffRdbkVerify(Adapter,mappedbuffer,u32FirmwareLength,u32StartingAddress);
	if(status != STATUS_SUCCESS)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL,"Buffer readback verifier failed");
		goto error;
	}
error:
	return status;
}