/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2020  Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/


#include "header.h"

#include "cwdaemon.h"
#include "davac4.h"
#include "fifo.h"
#include "language2.h"
#include "rc.h"

//// for usb_info
//#include "cwdaemon.h"
//#include "rotar.h"
//#include "sdev.h"

#ifdef Z_HAVE_LIBFTDI
int davac4_init(struct cwdaemon *cwda){
    cwda->ftdi = NULL;
    MUTEX_LOCK(cwda->ftdi);
    davac4_open(cwda, 1);
    MUTEX_UNLOCK(cwda->ftdi);
    return 0;
}

int davac4_free(struct cwdaemon *cwda){
    dbg("davac4_free(%p)\n", cwda);

    if (!cwda) return 0;

    if (cwda->ftdi==NULL) return 0;
    
    ftdi_free(cwda->ftdi);
    cwda->ftdi=NULL;
    return 0;
}


int davac4_open(struct cwdaemon *cwda, int verbose){
    int ret;
    
    if (cwda->ftdi != NULL) {
        ret = 0;
        goto x;
    }

    cwda->ftdi = ftdi_new();
    if (!cwda->ftdi){
        if (verbose) log_addf(VTEXT(T_CANT_INIT_FTDI));
        dbg("davac4_open=%d\n", 1);
        ret = 1;
        goto x;
    }
    dbg("ftdi_new=%p\n", cwda->ftdi);
    ret=ftdi_usb_open(cwda->ftdi, cfg->cwda_vid, cfg->cwda_pid);
    dbg("ftdi_usb_open cwda->ftdi=%p ret=%d\n", cwda->ftdi, ret);
    if (ret){
        if (verbose) {
            log_addf(VTEXT(T_CANT_OPEN_DAVAC4), cfg->cwda_vid, cfg->cwda_pid, ret, ftdi_get_error_string(cwda->ftdi));
            if (ret==-8) log_addf("Maybe try to run as root: \"adduser %s davac4\" and relogin", getenv("USER"));
        }
        davac4_free(cwda);
        dbg("davac4_open=%d ret=%d\n", ret);
        goto x;
    }
    ret=ftdi_set_baudrate(cwda->ftdi, 115200);
    if (ret){
        if (verbose) log_addf(VTEXT(T_CANT_SET_BAUDRATE), ret, ftdi_get_error_string(cwda->ftdi));
        davac4_free(cwda);
        dbg("davac4_open=%d\n", ret);
        goto x;
    }
    //ret=ftdi_enable_bitbang(cwda->ftdi, 0xff);
    ret=ftdi_set_bitmode(cwda->ftdi, 0xff, BITMODE_BITBANG);
    if (ret){
        if (verbose) log_addf(VTEXT(T_CANT_ENABLE_BITBANG), ret, ftdi_get_error_string(cwda->ftdi));
        davac4_free(cwda);
        dbg("davac4_open=%d\n", ret);
        goto x;
    }
    cwda->ftdi_state=0xff;

    
x:;    
    dbg("davac4_open=%d\n", ret);
    return ret;
}


int davac4_reset(struct cwdaemon *cwda){
    davac4_ptt(cwda, 0);
    davac4_cw(cwda, 0);
    davac4_ssbway(cwda, 0);
    return 0;
}

int davac4_cw(struct cwdaemon *cwda, int onoff){
    int ret;
    
    MUTEX_LOCK(cwda->ftdi);
    if (!cwda || cwda->ftdi == NULL) {// function called from worker thread, exit silently
        ret = 1;
        goto x;
    }

    if (onoff)
        cwda->ftdi_state &= ~0x10;
    else
        cwda->ftdi_state |= 0x10;
    
    ret=ftdi_write_data(cwda->ftdi, &cwda->ftdi_state, 1);
    if (ret!=1){
        log_addf(VTEXT(T_CANT_WRITE_TO_DAVAC4), ret, ftdi_get_error_string(cwda->ftdi)); 
        davac4_free(cwda);
        goto x;
    }
x:;
    MUTEX_UNLOCK(cwda->ftdi);
    return ret;
}

int davac4_ptt(struct cwdaemon *cwda, int onoff){
    int ret;
    
    MUTEX_LOCK(cwda->ftdi);
    dbg("davac4_ptt cwda->ftdi=%p\n", cwda->ftdi);

    if (!cwda || cwda->ftdi == NULL){
        ret=davac4_open(cwda, 0);
        if (ret) goto x;
    }
    if (onoff)
        cwda->ftdi_state &= ~0x04;
    else
        cwda->ftdi_state |= 0x04;
    ret=ftdi_write_data(cwda->ftdi, &cwda->ftdi_state, 1);
    
    if (ret!=1){
        log_addf(VTEXT(T_CANT_WRITE_TO_DAVAC4), ret, ftdi_get_error_string(cwda->ftdi)); 
        davac4_free(cwda);
        goto x;
    }
x:;
    MUTEX_UNLOCK(cwda->ftdi);
    return ret;
}

/* 0=microphone, 1=soundcard */
int davac4_ssbway(struct cwdaemon *cwda, int onoff){
    int ret;
    
    MUTEX_LOCK(cwda->ftdi);
    if (!cwda || cwda->ftdi == NULL){
        ret=davac4_open(cwda, 0);
        if (ret) goto x;
    }
    if (onoff)
        cwda->ftdi_state |= 0x01;   /* soundcard */
    else
        cwda->ftdi_state &= ~0x01;  /* microphone */
    
    ret=ftdi_write_data(cwda->ftdi, &cwda->ftdi_state, 1);
    if (ret!=1){
        log_addf(VTEXT(T_CANT_WRITE_TO_DAVAC4), ret, ftdi_get_error_string(cwda->ftdi)); 
        davac4_free(cwda);
        goto x;
    }      
x:;
    MUTEX_UNLOCK(cwda->ftdi);
    return ret;
}

int davac4_monitor(struct cwdaemon *cwda, int onoff){
    int ret;
    
    MUTEX_LOCK(cwda->ftdi);
    if (!cwda || cwda->ftdi == NULL){
        ret=davac4_open(cwda, 0);
        if (ret) goto x;
    }
    if (onoff)
        cwda->ftdi_state &= ~0x08;
    else
        cwda->ftdi_state |= 0x08;
    
    ret=ftdi_write_data(cwda->ftdi, &cwda->ftdi_state, 1);
    if (ret!=1){
        log_addf(VTEXT(T_CANT_WRITE_TO_DAVAC4), ret, ftdi_get_error_string(cwda->ftdi)); 
        davac4_free(cwda);
        goto x;
    }
x:;    
    MUTEX_UNLOCK(cwda->ftdi);
    return ret;
}

int davac4_band (struct cwdaemon *cwda, int bandsw){
    int ret;
    
    MUTEX_LOCK(cwda->ftdi);
    if (!cwda || cwda->ftdi == NULL){
        ret=davac4_open(cwda, 0);
        if (ret) goto x;
    }
    cwda->ftdi_state &= ~0xe2;
    if (bandsw & 0x01) cwda->ftdi_state |= 0x02;
    if (bandsw & 0x02) cwda->ftdi_state |= 0x20;
    if (bandsw & 0x04) cwda->ftdi_state |= 0x40;
    if (bandsw & 0x08) cwda->ftdi_state |= 0x80;
    
    ret=ftdi_write_data(cwda->ftdi, &cwda->ftdi_state, 1);
    if (ret!=1){
        log_addf(VTEXT(T_CANT_WRITE_TO_DAVAC4), ret, ftdi_get_error_string(cwda->ftdi)); 
        davac4_free(cwda);
        goto x;
    }
x:;    
    MUTEX_UNLOCK(cwda->ftdi);
    return ret;
}

#define DUMP_EEPROMx

void usb_info(GString *gs){
    int ret;
    struct ftdi_context ftdi;
    struct ftdi_device_list *list, *dev;
	int ids[] = {0x0403, 0x6001, 
                 0xA600, 0xE110,
                 0xA600, 0xE111,
                 0xA600, 0xE112,
                 0xA600, 0xE113,
                 0xA600, 0xE114,
                 0x04D8, 0xFB56, // FUNcube Dongle
                 0x04D8, 0xFB31, // FUNcube Dongle Pro+
				 0x0bda, 0x2838, // RTL-SDR
                 0};
    int idi;
#ifdef Z_HAVE_FTDI_USB_GET_STRINGS
    char manufacturer[128], description[128];
	char serial[128];
#endif
#ifdef DUMP_EEPROM
    unsigned char eeprom_buf[128];
#endif
    
    g_string_append_printf(gs, "\n  usb_info:\n");
    ret = ftdi_init(&ftdi); 
    if (ret < 0) {
        g_string_append_printf(gs, "error calling ftdi_init, error=%d %s", ret, ftdi_get_error_string(&ftdi));
        return;
    }
	for (idi = 0; ids[idi] != 0; idi += 2){
		int vid = ids[idi];
		int pid = ids[idi + 1];

        ret = ftdi_usb_find_all(&ftdi, &list, vid, pid);
        if (ret < 0) {
            g_string_append_printf(gs, "error calling ftdi_usb_find_all, error=%d %s\n", ret, ftdi_get_error_string(&ftdi));
            return;
        }
        if (ret == 0) continue;
        for (dev = list; dev != NULL; dev = dev->next){
			g_string_append_printf(gs, "%04x:%04x  ", vid, pid);

			/*if (cwda != NULL && cwda->ftdi != NULL && vid == 0xa600 && pid == 0xe110){
				g_string_append_printf(gs, "Probably opened as Davac4\n");
				continue;
			}
			if (rotars != NULL){
				int i;
				MUTEX_LOCK(rotars);
				for (i = 0; i<rotars->len; i++){
					struct rotar * rot = (struct rotar *) g_ptr_array_index(rotars, i);
					if (rot != NULL && rot->type == ROT_OK1ZIA_FTDI && rot->sdev != NULL && rot->sdev->sconn != NULL && rot->sdev->sconn->opened){
						g_string_append_printf(gs, "Probably opened as Rotar %c", rot->rotchar);
					}
				}
				MUTEX_UNLOCK(rotars);
			}*/

#ifdef Z_HAVE_FTDI_USB_GET_STRINGS
            ret = ftdi_usb_get_strings(&ftdi, dev->dev, manufacturer, 128, description, 128, serial, 128);
            if (ret < 0){
                g_string_append_printf(gs, "Can't get strings, error=%d %s", ret, ftdi_get_error_string(&ftdi));
				//if (ret == -4) g_string_append_printf(gs, "  Maybe not handled by Winusb? Try to use Zadig to set Winusb for this device");
				g_string_append_printf(gs, "\n");
				continue;
            }else{
                g_string_append_printf(gs, "%s %s ", manufacturer, description);
                if (strlen(serial)>0 && serial[0]!='?')
                    g_string_append_printf(gs, "%s  ", serial);
            }
#endif
            if (vid == 0x04D8){
                g_string_append_printf(gs, "\n");
                continue;
            }

            ret = ftdi_usb_open_dev(&ftdi, dev->dev);
            if (ret < 0){
                g_string_append_printf(gs, "Can't open device, error=%d %s\n", ret, ftdi_get_error_string(&ftdi));
            }else{
                g_string_append(gs, "Open OK ");
#ifdef DUMP_EEPROM
                ret = ftdi_read_eeprom(&ftdi, eeprom_buf);
                if (ret < 0){
                    g_string_append_printf(gs, "Can't open device, error=%d %s\n", ret, ftdi_get_error_string(&ftdi));
                }else{
                    int i;
                    unsigned short chksum_in_eeprom, chksum_computed;
                    for (i=0; i<128; i++){
                        g_string_append_printf(gs, "%02x ", (unsigned char)eeprom_buf[i]);
                        if (i%16==15) g_string_append_printf(gs, "\n");
                        
                    }
                    chksum_in_eeprom = eeprom_buf[0x7e] + 256 * eeprom_buf[0x7f];
                    chksum_computed = ftdi_checksum(eeprom_buf, 0x80);
                    g_string_append_printf(gs, "Checksum eeprom=%04x computed=%04x ", chksum_in_eeprom, chksum_computed);
                }
#endif
                ftdi_usb_close(&ftdi);
            }
            g_string_append_printf(gs, "\n");
        }
    }
    ftdi_list_free(&list);
    ftdi_deinit(&ftdi);
}

// taken from libftdi
// calculate checksum
unsigned short ftdi_checksum(unsigned char *eeprom, int eeprom_size){
    int i;
    unsigned short value, checksum = 0xAAAA;

    for (i = 0; i < eeprom_size/2-1; i++) {
        value = eeprom[i*2];
        value += eeprom[(i*2)+1] << 8;

        checksum = value^checksum;
        checksum = (checksum << 1) | (checksum >> 15);
    }
    return checksum;
}
#endif
