/***************************************************************************
                             main.c  -  description
                           -------------------
    begin                : Mon Apr  7 12:05:22 CEST 2003
    copyright            : (C) 2003 by Intra2net AG
    email                : opensource@intra2net.com
    
    modified             : Thu Jan 25, 2007
                         : Craig Van Degrift
                         : craig@yosemitefoothills.com

												 : Again May 23, 2007 to specify serial nr when
												 :    opening
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <confuse.h>
#include <ftdi.h>

int main(int argc, char *argv[]) {
    /*
	configuration options
    */
    cfg_opt_t opts[] = {
	CFG_INT("current_vendor_id", 0, 0),
	CFG_INT("current_product_id", 0, 0),

	CFG_BOOL("chA_is_FIFO", cfg_true, 0),
	CFG_BOOL("chA_is_CPU_FIFO_target", cfg_false, 0),
	CFG_BOOL("chA_is_OPTO", cfg_false, 0),
	CFG_BOOL("chA_is_VCP", cfg_false, 0),
	CFG_BOOL("chA_is_high_current", cfg_false, 0),
	CFG_BOOL("byte_0_bit_5", cfg_false, 0),
	CFG_BOOL("byte_0_bit_6", cfg_false, 0),
	CFG_BOOL("byte_0_bit_7", cfg_false, 0),

	CFG_BOOL("chB_is_FIFO", cfg_true, 0),
	CFG_BOOL("chB_is_CPU_FIFO_target", cfg_false, 0),
	CFG_BOOL("chB_is_OPTO", cfg_false, 0),
	CFG_BOOL("chB_is_VCP", cfg_false, 0),
	CFG_BOOL("chB_is_high_current", cfg_false, 0),
	CFG_BOOL("byte_1_bit_5", cfg_false, 0),
	CFG_BOOL("byte_1_bit_6", cfg_false, 0),
	CFG_BOOL("byte_1_bit_7", cfg_false, 0),

	CFG_INT("vendor_id", 0, 0),
	CFG_INT("product_id", 0, 0),
	CFG_INT("device_id", 0, 0),

	CFG_INT("max_power", 0, 0),

	CFG_BOOL("byte_8_bit_0", cfg_false, 0),
	CFG_BOOL("byte_8_bit_1", cfg_false, 0),
	CFG_BOOL("byte_8_bit_2", cfg_false, 0),
	CFG_BOOL("byte_8_bit_3", cfg_false, 0),
	CFG_BOOL("battery_powered", cfg_false, 0),
	CFG_BOOL("remote_wakeup", cfg_true, 0),
	CFG_BOOL("self_powered", cfg_true, 0),
	CFG_BOOL("must_be_set", cfg_true, 0),

	CFG_BOOL("chA_in_is_isochronous", cfg_false, 0),
	CFG_BOOL("chA_out_is_isochronous", cfg_false, 0),
	CFG_BOOL("suspend_pull_downs", cfg_false, 0),
	CFG_BOOL("use_serial_nr", cfg_false, 0),
	CFG_BOOL("use_usb_version", cfg_false, 0),
	CFG_BOOL("chB_in_is_isochronous", cfg_false, 0),
	CFG_BOOL("chB_out_is_isochronous", cfg_false, 0),
	CFG_BOOL("byte_A_bit_7", cfg_false, 0),

	CFG_BOOL("byte_B_bit_0", cfg_false, 0),
	CFG_BOOL("byte_B_bit_1", cfg_false, 0),
	CFG_BOOL("byte_B_bit_2", cfg_false, 0),
	CFG_BOOL("byte_B_bit_3", cfg_false, 0),
	CFG_BOOL("byte_B_bit_4", cfg_false, 0),
	CFG_BOOL("byte_B_bit_5", cfg_false, 0),
	CFG_BOOL("byte_B_bit_6", cfg_false, 0),
	CFG_BOOL("byte_B_bit_7", cfg_false, 0),

	CFG_INT("byte_0x14", 0, 0),
	CFG_INT("byte_0x15", 0, 0),
	CFG_INT("byte_0x16", 0, 0),
	CFG_INT("byte_0x17", 0, 0),
	CFG_INT("byte_0x18", 0, 0),
	CFG_INT("byte_0x19", 0, 0),
	CFG_INT("byte_0x20", 0, 0),
	CFG_INT("byte_0x21", 0, 0),

	CFG_INT("usb_version", 0, 0),

	CFG_STR("manufacturer", "Acme Inc.", 0),
	CFG_STR("product", "USB Serial Converter", 0),
	CFG_STR("serial_nr", "08-15", 0),

	CFG_STR("filename", "", 0),
	CFG_END()
    };
    cfg_t *cfg;
    
    /*
	normal variables
    */ 
    int	_read = 0, _erase = 0, _flash = 0;
    unsigned char eeprom_buf[256];
    char *filename;
    int size_check, eeprom_size;
    int i, argc_filename;
    int current_vendor_id;
    int current_product_id;
    FILE *fp;

    struct ftdi_context ftdi;
    struct ftdi_eeprom eeprom;

    printf("\nFTDI eeprom generator v%s\n", VERSION);
    printf ("(c) Intra2net AG <opensource@intra2net.com>\n");
    
    if (argc != 2 && argc != 3) {
	printf("Syntax: %s [commands] config-file\n", argv[0]);
	printf("Valid commands:\n");
	printf("--read-eeprom		Read eeprom and write to -filename- from config-file\n");
	printf("--erase-eeprom		Erase eeprom\n");
	printf("--flash-eeprom		Flash eeprom\n");
	exit (-1);
    }
    
    if (argc == 3) {
	if (strcmp(argv[1], "--read-eeprom") == 0)
	    _read = 1;
	if (strcmp(argv[1], "--erase-eeprom") == 0)
	    _erase = 1;
	if (strcmp(argv[1], "--flash-eeprom") == 0)
	    _flash = 1;
	if (_read+_erase+_flash==0) {
	    printf("Bad command: Must be --read-eeprom, --erase-eeprom, or --flash-eeprom\n");
	    exit(-1);
	}
	
	argc_filename = 2;
    } else {
	argc_filename = 1;
    }

    if ((fp = fopen(argv[argc_filename], "r")) == NULL) {
	printf ("Can't open configuration file\n");
	exit (-1);
    }
    fclose (fp);

    cfg = cfg_init(opts, 0);
    cfg_parse(cfg, argv[argc_filename]);
    filename = cfg_getstr(cfg, "filename");

    if (cfg_getbool(cfg, "self_powered") && cfg_getint(cfg, "max_power") > 0)
	printf("Hint: Self powered devices should have a max_power setting of 0.\n");

    ftdi_eeprom_initdefaults (&eeprom);

    current_vendor_id = cfg_getint(cfg, "current_vendor_id");
    current_product_id = cfg_getint(cfg, "current_product_id");

    eeprom.chA_is_FIFO=cfg_getbool(cfg, "chA_is_FIFO");
    eeprom.chA_is_CPU_FIFO_target=cfg_getbool(cfg, "chA_is_CPU_FIFO_target");
    eeprom.chA_is_OPTO=cfg_getbool(cfg, "chA_is_OPTO");
    eeprom.chA_is_VCP=cfg_getbool(cfg, "chA_is_VCP");
    eeprom.chA_is_high_current= cfg_getbool(cfg, "chA_is_high_current");
    eeprom.byte_0_bit_5=cfg_getbool(cfg, "byte_0_bit_5");
    eeprom.byte_0_bit_6=cfg_getbool(cfg, "byte_0_bit_6");
    eeprom.byte_0_bit_7=cfg_getbool(cfg, "byte_0_bit_7");

    eeprom.chB_is_FIFO=cfg_getbool(cfg, "chB_is_FIFO");
    eeprom.chB_is_CPU_FIFO_target=cfg_getbool(cfg, "chB_is_CPU_FIFO_target");
    eeprom.chB_is_OPTO=cfg_getbool(cfg, "chB_is_OPTO");
    eeprom.chB_is_VCP=cfg_getbool(cfg, "chB_is_VCP");
    eeprom.chB_is_high_current= cfg_getbool(cfg, "chB_is_high_current");
    eeprom.byte_1_bit_5=cfg_getbool(cfg, "byte_1_bit_5");
    eeprom.byte_1_bit_6=cfg_getbool(cfg, "byte_1_bit_6");
    eeprom.byte_1_bit_7=cfg_getbool(cfg, "byte_1_bit_7");

    eeprom.vendor_id = cfg_getint(cfg, "vendor_id");
    eeprom.product_id = cfg_getint(cfg, "product_id");
    eeprom.device_id = cfg_getint(cfg, "device_id");

    eeprom.max_power = cfg_getint(cfg, "max_power");

    eeprom.byte_8_bit_0 = cfg_getbool(cfg, "byte_8_bit_0");
    eeprom.byte_8_bit_1 = cfg_getbool(cfg, "byte_8_bit_1");
    eeprom.byte_8_bit_2 = cfg_getbool(cfg, "byte_8_bit_2");
    eeprom.byte_8_bit_3 = cfg_getbool(cfg, "byte_8_bit_3");
    eeprom.battery_powered = cfg_getbool(cfg, "battery_powered");
    eeprom.remote_wakeup = cfg_getbool(cfg, "remote_wakeup");
    eeprom.self_powered = cfg_getbool(cfg, "self_powered");
    eeprom.must_be_set = cfg_getbool(cfg, "must_be_set");

    eeprom.chA_in_is_isochronous = cfg_getbool(cfg, "chA_in_is_isochronous");
    eeprom.chA_out_is_isochronous = cfg_getbool(cfg, "chA_out_is_isochronous");
    eeprom.suspend_pull_downs = cfg_getbool(cfg, "suspend_pull_downs");
    eeprom.use_serial_nr = cfg_getbool(cfg, "use_serial_nr");
    eeprom.use_usb_version = cfg_getbool(cfg, "use_usb_version");
    eeprom.chB_in_is_isochronous = cfg_getbool(cfg, "chB_in_is_isochronous");
    eeprom.chB_out_is_isochronous = cfg_getbool(cfg, "chB_out_is_isochronous");
    eeprom.byte_A_bit_7 = cfg_getbool(cfg, "byte_A_bit_7");

    eeprom.usb_version = cfg_getint(cfg, "usb_version");

    eeprom.manufacturer = cfg_getstr(cfg, "manufacturer");
    eeprom.product = cfg_getstr(cfg, "product");
    eeprom.serial_nr = cfg_getstr(cfg, "serial_nr");

    eeprom.byte_0x14 = 0xFF & cfg_getint(cfg, "byte_0x14");
    eeprom.byte_0x15 = 0xFF & cfg_getint(cfg, "byte_0x15");

    if (_read > 0 || _erase > 0 || _flash > 0) {
        printf("FTDI init: %d\n", ftdi_init(&ftdi));
	i = ftdi_usb_open_desc(&ftdi, current_vendor_id, current_product_id, NULL, eeprom.serial_nr);
	
	if (i != 0) {
	    printf("Unable to find FTDI devices under given vendor/product id/serial nr: 0x%X/0x%X/%s\n", eeprom.vendor_id, eeprom.product_id, eeprom.serial_nr);
            printf("May need to be run with root privileges.\n");
//	    printf("Retrying with default FTDI id.\n");

//    	    i = ftdi_usb_open_desc(&ftdi, 0x0403, 0x6010);
//	    if (i != 0) {
//		if (i == 5)
//			printf("Unable to claim device - maybe ftdi_sio driver is using it.");
//		else
//			printf("Sorry, unable to find FTDI USB chip\n");
		exit (-1);
//	    }
	}
    }

    if (ftdi.type == TYPE_2232C) // ftdi.type is set during ftdi_usb_open
	    eeprom_size = 256;
    else
	    eeprom_size = 128;

    if (_read > 0) {
        printf("FTDI read eeprom: %d\n", ftdi_read_eeprom(&ftdi, (char *)&eeprom_buf));
	if (filename != NULL && strlen(filename) > 0) {
	    FILE *fp = fopen (filename, "wb");
	    fwrite (&eeprom_buf, 1, eeprom_size, fp);
	    fclose (fp);
	} else {
	    printf("Warning: Not writing eeprom, you must supply a valid filename\n");
	}

	goto cleanup;
    }

    if (_erase > 0) {
	printf("FTDI erase eeprom: %d\n", ftdi_erase_eeprom(&ftdi));
    }    

    size_check = ftdi_eeprom_build(&ftdi, &eeprom, eeprom_buf);
    if (size_check == -1000)
	printf ("eeprom.FT_chip_type not set\n");
    else if (size_check < 0) {
	printf ("Sorry, the eeprom can only contain %d bytes (%d bytes for your Unicode strings).\n", eeprom_size, eeprom_size - 0x9C - 2*(ftdi.type == TYPE_2232C));
	printf ("You need to shorten your string by: %d Unicode characters (%d bytes)\n", -size_check/2, -size_check);
	goto cleanup;
    } else {
	printf ("Used eeprom space: %d bytes\n", eeprom_size - size_check); 
    }

    if (_flash > 0) {
	printf ("FTDI write eeprom: %d\n", ftdi_write_eeprom(&ftdi, (char *)&eeprom_buf));
    }

    // Write to file?
    if (filename != NULL && strlen(filename) > 0) {
        fp = fopen(filename, "w");
	if (fp == NULL) {
	    printf ("Can't write eeprom file.\n");
	    exit (-1);
	} else 
	    printf ("Writing to file: %s\n", filename);
	
	fwrite(eeprom_buf, eeprom_size, 1, fp);
	fclose(fp);
    }

cleanup:
    if (_read > 0 || _erase > 0 || _flash > 0) {
        printf("FTDI close: %d\n", ftdi_usb_close(&ftdi));
    }

    ftdi_deinit (&ftdi);
    
    cfg_free(cfg);
    
    printf("\n");
    return 0;
}

