        case 's':                                       /* Save parameters (user mode) */
            vPut_finish(vSave_params(SAVE_CUSTOMER));
            break;
/**********************************************************/

#define SAVE_CUSTOMER   TRUE
/**********************************************************/

PRIVATE uint8_t vSave_params(Bool CUST)
{
    uint8_t b;
    /* Printer must be in steady state before launching saving so as to
       avoid overheating pb during flashing. Rmk : wait state only if no
       printer error because it could cause a blocking state while wish
       action is not a printing one but only a config one */
    appFlush_dotline(TRUE);

    if (0) comLock_host(ON);
    
    if (CUST){
        b = parSave_params_custom_mode();
    }else
    {
        b = parSave_params_custom_mode();
    }
    if (0) comLock_host(OFF);

    return b;
}
/*********************************************************/

Bool parSave_params_custom_mode(void)
{
    parTYPE_PARAMS_CUSTOMER_FILE *f = filGet_file(PARAMS_CUST, 0);
    parTYPE_PARAMS_FILE_HEADER h;
    uint16_t crc;

    /* function disabled if no customer file found */
    if (f == NULL)
        return FALSE;
	init_lpc17xx();

    /* compute file CRC and build file header */
    crc = crcCompute_crc((uint8_t *)&parCust_parameters, sizeof(parCust_parameters));
    crc = crcUpdate_crc((uint8_t *)&parCalib_parameters, sizeof(parCalib_parameters), crc);

    h.crc = crc;
    h.size = sizeof(parTYPE_PARAMS_CUSTOMER_FILE);

    /* program customer file */
    if (! fshProgram_flash_region((uint8_t *)f, (uint8_t *)&h, sizeof(h)))
        return FALSE;
    if (! fshProgram_flash_region((uint8_t *)&f->cust, (uint8_t *)&parCust_parameters, sizeof(parCust_parameters)))
        return FALSE;
    if (! fshProgram_flash_region((uint8_t *)&f->calib, (uint8_t *)&parCalib_parameters, sizeof(parCalib_parameters)))
        return FALSE;

    return TRUE;
}
/*********************************************************/

Bool fshProgram_flash_region(uint8_t *addr, uint8_t *p, uint16_t n)
{
   uint8_t status;
   /* use ahb sram bank 0 as a cache */
   int x, len;
   uint32_t * xbuf = (uint32_t *) 0x2007c000;

	x = get_sector_of_addr((uint32_t) addr);
	if (x >= 10)
		return FALSE;
	if (x == 9)
		len = 0x1000;
	else
		len = 0x1000 * 2;
	memcpy(xbuf, (uint32_t) addr & ~ (0x1000 - 1), len);
	if (len == 0x1000 * 2)
		lpc17xx_flash_erase_sector(x + 1);
	lpc17xx_flash_erase_sector(x);
	memcpy(((uint32_t) xbuf) + ((uint32_t) addr & (0x1000 - 1)), p, n);
	lpc17xx_flash_program_words(x * 0x1000, xbuf, len >> 2);
	return TRUE;
}
/*********************************************************/

int lpc17xx_flash_erase_sector(uint32_t sector_nr)
{
struct lpc17xx_flash_data * d;

	if (lpc17xx_prepare_sectors_for_writing(sector_nr, sector_nr))
		return -1;

	d = &flash_data;

	d->cmd[0] = ERASE_SECTORS;
	d->cmd[1] = sector_nr;
	d->cmd[2] = sector_nr;
	d->cmd[3] = d->cclk / 1000;

	if (lpc17xx_run_iap_routine())
		return -1;
	if (d->result[0] != CMD_SUCCESS)
		return -1;
	else
		return 0;
}
/**********************************************************/

int lpc17xx_prepare_sectors_for_writing(int first, int last)
{
struct lpc17xx_flash_data * d;

	d = &flash_data;

	d->cmd[0] = PREPARE_SECTORS_FOR_WRITING;
	d->cmd[1] = first;
	d->cmd[2] = last;

	if (lpc17xx_run_iap_routine())
		return -1;
	if (d->result[0] != CMD_SUCCESS)
		return -1;
	else
		return 0;

}
/**********************************************************/

static int lpc17xx_run_iap_routine(void)
{
	((void (*)(uint32_t *, uint32_t *))IAP_LOCATION)(flash_data.cmd, flash_data.result);
	return 0;
}
/**********************************************************/

const byte cmd[] = 
{
    0x00, // read 1
    0x50, // read 2
    0x00, // prog 1
    0x50, // prog 2
    0x00, // copy src
    0x8A, // copy dst
    0x60
};
/*********************************************************/

struct lpc17xx_flash_data
{
	uint32_t	cmd[5];
	uint32_t	result[5];
	/* computed target core clock frequency, in hertz, needed for passing as a parameter to some iap routines */
	uint32_t	cclk;
} flash_data;
/********************************************************/

int lpc17xx_flash_program_words(uint32_t dest, uint32_t * src, int wordcnt)
{
struct lpc17xx_flash_data * pdev;
int i, sector_nr, sector_idx;

	/* make sure the destination address falls on a 256 byte boundary */
	if (dest & (0xff))
	{
		return -1;
	}

	pdev = &flash_data;
	sector_nr = get_sector_of_addr(dest);
	{
		i = (wordcnt > LPC17XX_BUF_SIZE / sizeof(uint32_t)) ? LPC17XX_BUF_SIZE : wordcnt * sizeof(uint32_t);
		if (lpc17xx_prepare_sectors_for_writing(sector_nr, sector_nr))
			return -1;
		pdev->cmd[0] = COPY_RAM_TO_FLASH;
		pdev->cmd[1] = dest;
		pdev->cmd[2] = (uint32_t) src;
		pdev->cmd[3] = wordcnt * 4;
		pdev->cmd[4] = pdev->cclk / 1000;
		if (lpc17xx_run_iap_routine() || pdev->result[0] != CMD_SUCCESS)
			return -1;
	}

	return 0;
}
/*************************************************************/

int get_sector_of_addr(uint32_t addr)
{
uint32_t i, x;

	for (x = i = 0; flash_sector_sizes[i]; x += flash_sector_sizes[i ++])
		if (x <= addr && addr < x + flash_sector_sizes[i])
			break;
	return i;
}
/***********************************************************/

enum
{
	NR_SUPPORTED_DEVICES = 1,
	/* lpc17xx in-application-programming (iap) related constants */
	IAP_LOCATION			= 0x1fff1ff1,
	PREPARE_SECTORS_FOR_WRITING	= 50,
	COPY_RAM_TO_FLASH		= 51,
	ERASE_SECTORS			= 52,
	BLANK_CHECK_SECTORS		= 53,
	READ_PART_ID			= 54,
	READ_BOOT_CODE_VERSION		= 55,
	IAP_COMPARE			= 56,
	REINVOKE_ISP			= 57,
	READ_DEVICE_SERIAL_NUMBER	= 58,
	/* iap return codes */
	CMD_SUCCESS			= 0,
	/* pll0 register addresses, used for getting/setting target core clock settings */
	PLL0CON				= 0x400fc080,
	PLL0CFG				= 0x400fc084,
	PLL0STAT			= 0x400fc088,
	PLL0FEED			= 0x400fc08c,
	/* clock source selection register */
	CLKSRCSEL			= 0x400fc10c,
	CCLKCFG				= 0x400fc104,
	/* bit 0 of the MEMMAP register specifies what memory is mapped at
	 * address 0 - if this bit is 0 - a porion of the boot rom is mapped
	 * at address 0, if 1 - user memory is mapped at address 0 */
	MEMMAP				= 0x400fc040,

	/* parameters for running the target iap routines */
	LPC17XX_RAM_BASE		= 0x10000000,
	LPC17XX_CMD_ADDR		= LPC17XX_RAM_BASE,
	LPC17XX_RESULT_ADDR		= LPC17XX_RAM_BASE + 0x20,

	LPC17XX_BUF_ADDR_FOR_WRITE_OPERATION	= LPC17XX_RAM_BASE + 0x40,
	LPC17XX_BUF_SIZE		= 4 * 1024,

};
/**********************************************************************/

