diff --git a/kit/elektor507.c b/kit/elektor507.c new file mode 100644 index 000000000..e95e88014 --- /dev/null +++ b/kit/elektor507.c @@ -0,0 +1,1076 @@ +/* + * Hamlib KIT backend - Elektor SDR USB (5/07) receiver description + * Copyright (c) 2007 by Stephane Fillod + * + * $Id: elektor507.c,v 1.1 2007-10-07 20:30:40 fillods Exp $ + * + * This library 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "hamlib/rig.h" +#include "token.h" + +#include "kit.h" + +#ifdef _WIN32 +#define USE_FTDI_DLL +#elif defined(HAVE_LIBUSB) && defined(HAVE_USB_H) +#define USE_LIBUSB +#endif + +/* + * Compile this model only if libusb is available + * or if .DLL is available under Windows + */ +#if defined(USE_FTDI_DLL) || defined(USE_LIBUSB) + + +static int elektor507_init(RIG *rig); +static int elektor507_cleanup(RIG *rig); +static int elektor507_open(RIG *rig); +static int elektor507_set_freq(RIG *rig, vfo_t vfo, freq_t freq); +static int elektor507_get_freq(RIG *rig, vfo_t vfo, freq_t *freq); +static int elektor507_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val); +static int elektor507_set_ant(RIG * rig, vfo_t vfo, ant_t ant); +static int elektor507_set_conf(RIG *rig, token_t token, const char *val); +static int elektor507_get_conf(RIG *rig, token_t token, char *val); + + +static const char * elektor507_get_info(RIG *rig); + +/* + * I2C addresses + */ +#define CY_I2C_RAM_ADR 210 +#define CY_I2C_EEPROM_ADR 208 + +/* + * I2C registers + */ +#define CLKOE_REG 0x09 +#define DIV1_REG 0x0c +#define DIV2_REG 0x47 +#define XTALCTL_REG 0x12 +#define CAPLOAD_REG 0x13 +#define PUMPCOUNTERS_REG 0x40 +#define CLKSRC_REG 0x44 + + +static int cy_update_pll(RIG *rig, unsigned char IICadr); +static int i2c_write_regs(RIG *rig, unsigned char IICadr, int reg_count, unsigned char reg_adr, + unsigned char reg_val1, unsigned char reg_val2, unsigned char reg_val3); +#define i2c_write_reg(rig, IICadr, reg_adr, reg_val) \ + i2c_write_regs(rig, IICadr, 1, reg_adr, reg_val, 0, 0) + + +#ifdef USE_FTDI_DLL + +#ifdef HAVE_WINDOWS_H +#include +#endif +#ifdef HAVE_WINBASE_H +#include +#endif + +#include + +#define ELEKTOR507_DLL "FTD2XX.dll" + + +/* Some type definitions needed for dll access */ +typedef enum +{ + FT_OK = 0, + FT_INVALID_HANDLE = 1, + FT_DEVICE_NOT_FOUND = 2, + FT_DEVICE_NOT_OPENED = 3, + FT_IO_ERROR = 4, + FT_INSUFFICIENT_RESOURCES = 5, + FT_INVALID_PARAMETER = 6, + FT_SUCCESS = FT_OK, + FT_INVALID_BAUD_RATE = 7, + FT_DEVICE_NOT_OPENED_FOR_ERASE = 8, + FT_DEVICE_NOT_OPENED_FOR_WRITE = 9, + FT_FAILED_TO_WRITE_DEVICE = 10, + FT_EEPROM_READ_FAILED = 11, + FT_EEPROM_WRITE_FAILED = 12, + FT_EEPROM_ERASE_FAILED = 13, + FT_EEPROM_NOT_PRESENT = 14, + FT_EEPROM_NOT_PROGRAMMED = 15, + FT_INVALID_ARGS = 16, + FT_OTHER_ERROR = 17, +} FT_Result; + +typedef FT_Result (__stdcall *FNCFT_Open)(int Index, unsigned long *ftHandle); +typedef FT_Result (__stdcall *FNCFT_Close)(unsigned long ftHandle); +typedef FT_Result (__stdcall *FNCFT_SetBitMode)(unsigned long ftHandle, unsigned char Mask, unsigned char Enable); +typedef FT_Result (__stdcall *FNCFT_SetBaudRate)(unsigned long ftHandle, unsigned long BaudRate); +typedef FT_Result (__stdcall *FNCFT_Write)(unsigned long ftHandle, void *FTOutBuf, unsigned long BufferSize, int *ResultPtr); + + +struct elektor507_extra_priv_data { + HMODULE dll; + + FNCFT_Open FT_Open; + FNCFT_Close FT_Close; + FNCFT_SetBitMode FT_SetBitMode; + FNCFT_SetBaudRate FT_SetBaudRate; + FNCFT_Write FT_Write; + + unsigned long ftHandle; +}; + +#elif defined(USE_LIBUSB) + + +#include +#include + + +#define USB_VID_FTDI 0x0403 /* Future Technology Devices International */ +#define USB_PID_FTDI_FT232 0x6001 /* FT232R 8-bit FIFO */ + +#define FTDI_IN_EP 0x02 +#define FTDI_USB_WRITE_TIMEOUT 5000 + + +struct elektor507_extra_priv_data { + /* empty with libusb */ +}; + +#endif + + +/* defaults */ +#define OSCFREQ 10000 /* kHz unit -> MHz(10) */ +#define XTAL_CAL 128 + +#define TOK_OSCFREQ TOKEN_BACKEND(1) +#define TOK_XTALCAL TOKEN_BACKEND(2) + +static const struct confparams elektor507_cfg_params[] = { + { TOK_OSCFREQ, "osc_freq", "Oscillator freq", "Oscillator frequency in Hz", + "10000000", RIG_CONF_NUMERIC, { .n = { 0, MHz(30), 1 } } + }, + { TOK_XTALCAL, "xtal_cal", "Xtal Cal", "Cristal calibration", + "132", RIG_CONF_NUMERIC, { .n = { 0, 255, 1 } } + }, + { RIG_CONF_END, NULL, } +}; + + +/* + * Common data struct + */ +struct elektor507_priv_data { + struct elektor507_extra_priv_data extra_priv; + + unsigned xtal_cal; /* 0..255 (-150ppm..150ppm) */ + unsigned osc_freq; /* kHz */ + +#define ANT_AUTO RIG_ANT_1 +#define ANT_EXT RIG_ANT_2 +#define ANT_TEST_CLK RIG_ANT_3 + ant_t ant; /* current antenna */ + + /* CY PLL stuff. + * This is Qtotal & Ptotal values here. + */ + int P,Q,Div1N; + + /* FTDI comm stuff */ + unsigned char FT_port; + int Buf_adr; +#define FT_OUT_BUFFER_MAX 1024 + unsigned char FT_Out_Buffer[FT_OUT_BUFFER_MAX]; +}; + + + +#ifdef USE_FTDI_DLL + +int elektor507_init(RIG *rig) +{ + struct elektor507_priv_data *priv; + struct elektor507_extra_priv_data *extra_priv; + + priv = (struct elektor507_priv_data*)calloc(sizeof(struct elektor507_priv_data), 1); + if (!priv) { + /* whoops! memory shortage! */ + return -RIG_ENOMEM; + } + priv->xtal_cal = XTAL_CAL; + priv->osc_freq = OSCFREQ; + priv->ant = ANT_AUTO; + + extra_priv = &priv->extra_priv; + + /* Try to load required dll */ + extra_priv->dll = LoadLibrary(ELEKTOR507_DLL); + + if (!extra_priv->dll) { + rig_debug(RIG_DEBUG_ERR, "%s: Unable to LoadLibrary %s\n", + __FUNCTION__, ELEKTOR507_DLL); + free(priv); + return -RIG_EIO; /* huh! */ + } + + /* + * Get process addresses from dll for function access + */ + + /* Open_USB_Device */ + extra_priv->FT_Open = + (FNCFT_Open) GetProcAddress(extra_priv->dll, "FT_Open"); + /* Close_USB_Device */ + extra_priv->FT_Close = + (FNCFT_Close) GetProcAddress(extra_priv->dll, "FT_Close"); + /* Set_USB_Device_BitMode */ + extra_priv->FT_SetBitMode = + (FNCFT_SetBitMode) GetProcAddress(extra_priv->dll, "FT_SetBitMode"); + /* Set_USB_Device_BaudRate */ + extra_priv->FT_SetBaudRate = + (FNCFT_SetBaudRate) GetProcAddress(extra_priv->dll, "FT_SetBaudRate"); + /* Write_USB_Device_Buffer */ + extra_priv->FT_Write = + (FNCFT_Write) GetProcAddress(extra_priv->dll, "FT_Write"); + + rig->state.priv = (void*)priv; + + return RIG_OK; +} + +int elektor507_ftdi_write_data(RIG *rig, void *FTOutBuf, unsigned long BufferSize) +{ + struct elektor507_extra_priv_data *extra_priv = + &((struct elektor507_priv_data *)rig->state.priv)->extra_priv; + FT_Result ret; + int Result; + + rig_debug(RIG_DEBUG_TRACE,"%s called, %d bytes\n", __FUNCTION__, BufferSize); + + /* Open FTDI */ + ret = extra_priv->FT_Open(0, &extra_priv->ftHandle); + if (ret != FT_OK) + return -RIG_EIO; + + ret = extra_priv->FT_SetBitMode(extra_priv->ftHandle, 0xff, 1); + if (ret != FT_OK) + return -RIG_EIO; + + ret = extra_priv->FT_SetBaudRate(extra_priv->ftHandle, 38400); + if (ret != FT_OK) + return -RIG_EIO; + + ret = extra_priv->FT_Write(extra_priv->ftHandle, FTOutBuf, BufferSize, &Result); + if (ret != FT_OK) { + rig_debug(RIG_DEBUG_ERR,"FT_Write failed: %d, Result: %d\n", ret, Result); + return -RIG_EIO; + } + + ret = extra_priv->FT_Close(extra_priv->ftHandle); + if (ret != FT_OK) + return -RIG_EIO; + + return RIG_OK; +} + +int elektor507_cleanup(RIG *rig) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + + if (!rig) + return -RIG_EINVAL; + + /* Clean up the dll access */ + FreeLibrary(priv->extra_priv.dll); + + if (rig->state.priv) + free(rig->state.priv); + rig->state.priv = NULL; + + return RIG_OK; +} + +const char * elektor507_get_info(RIG *rig) +{ + static char buf[64]; + + sprintf(buf, "Elektor SDR USB w/ FTDI DLL"); + + return buf; +} + + + +#elif defined(USE_LIBUSB) + + +/* + * The libusb code is inspired by libftdi: + * http://www.intra2net.com/de/produkte/opensource/ftdi/ + */ +int elektor507_init(RIG *rig) +{ + hamlib_port_t *rp = &rig->state.rigport; + struct elektor507_priv_data *priv; + + priv = (struct elektor507_priv_data*)calloc(sizeof(struct elektor507_priv_data), 1); + if (!priv) { + /* whoops! memory shortage! */ + return -RIG_ENOMEM; + } + + priv->xtal_cal = XTAL_CAL; + priv->osc_freq = OSCFREQ; + priv->ant = ANT_AUTO; + + rp->parm.usb.vid = USB_VID_FTDI; + rp->parm.usb.pid = USB_PID_FTDI_FT232; + rp->parm.usb.conf = 1; + rp->parm.usb.iface = 0; + rp->parm.usb.alt = 0; /* necessary ? */ + + rig->state.priv = (void*)priv; + + return RIG_OK; +} + +int elektor507_cleanup(RIG *rig) +{ + if (!rig) + return -RIG_EINVAL; + + if (rig->state.priv) + free(rig->state.priv); + rig->state.priv = NULL; + + return RIG_OK; +} + +const char * elektor507_get_info(RIG *rig) +{ + static char buf[64]; + struct usb_dev_handle *udh = rig->state.rigport.handle; + struct usb_device *q = usb_device(udh); + + sprintf(buf, "USB dev %04d", q->descriptor.bcdDevice); + + return buf; +} + +int elektor507_libusb_setup(RIG *rig) +{ + struct usb_dev_handle *udh = rig->state.rigport.handle; + int ret; + unsigned short index=0, usb_val; + + rig_debug(RIG_DEBUG_TRACE,"%s called\n", __FUNCTION__); + + /* Reset the ftdi device */ +#if 1 + ret = usb_control_msg(udh, 0x40, 0, 0, index, NULL, 0, FTDI_USB_WRITE_TIMEOUT); + if (ret != 0) { + rig_debug (RIG_DEBUG_ERR, "%s: usb_control_msg reset failed: %s\n", + __FUNCTION__, + usb_strerror ()); + return -RIG_EIO; + } +#endif + + /* + * Enable bitbang mode + */ + usb_val = 0xff; /* low byte: bitmask */ + usb_val |= (0x01 << 8); /* Basic bitbang_mode: 0x01 */ + + ret = usb_control_msg(udh, 0x40, 0x0B, usb_val, index, NULL, 0, FTDI_USB_WRITE_TIMEOUT); + if (ret != 0) { + rig_debug (RIG_DEBUG_ERR, "%s: usb_control_msg bitbangmode failed: %s\n", + __FUNCTION__, + usb_strerror ()); + return -RIG_EIO; + } + + /* + * Set baudrate + * 9600 x4 because of bitbang mode + */ + usb_val = 49230; /* magic value for 38400 bauds */ + index = 0; + ret = usb_control_msg(udh, 0x40, 3, usb_val, index, NULL, 0, FTDI_USB_WRITE_TIMEOUT); + if (ret != 0) { + rig_debug (RIG_DEBUG_ERR, "%s: usb_control_msg baudrate failed: %s\n", + __FUNCTION__, + usb_strerror ()); + return -RIG_EIO; + } + + return RIG_OK; +} + +int elektor507_ftdi_write_data(RIG *rig, void *FTOutBuf, unsigned long BufferSize) +{ + struct usb_dev_handle *udh = rig->state.rigport.handle; + int ret; + + rig_debug(RIG_DEBUG_TRACE,"%s called, %d bytes\n", __FUNCTION__, BufferSize); + + ret = usb_bulk_write(udh, FTDI_IN_EP, FTOutBuf, BufferSize, FTDI_USB_WRITE_TIMEOUT); + if (ret < 0) { + /* we get EPIPE if the firmware stalls the endpoint. */ + if (errno != EPIPE) + rig_debug (RIG_DEBUG_ERR, + "usb_bulk_write failed: %s\n", + usb_strerror ()); + return -RIG_EIO; + } + + return RIG_OK; +} +#endif /* USE_LIBUSB */ + + + +#define ELEKTOR507_MODES (RIG_MODE_USB) /* USB is for SDR */ + +#define ELEKTOR507_FUNC (RIG_FUNC_NONE) + +#define ELEKTOR507_LEVEL_ALL (RIG_LEVEL_ATT) + +#define ELEKTOR507_PARM_ALL (RIG_PARM_NONE) + +#define ELEKTOR507_VFO (RIG_VFO_A) + +/* + * - Auto-filter antenna (K3) + * - External antenna (PC1) + * - Internal TEST_CLK (5 MHz) + */ +#define ELEKTOR507_ANT (RIG_ANT_1|RIG_ANT_2|RIG_ANT_3) + + +/* + * Elektor SDR USB (5/07) receiver description + * + * This kit is a QSD based on a CY27EE16ZE PLL. + * The receiver is controlled via USB (through FTDI FT232R). + */ + +const struct rig_caps elektor507_caps = { +.rig_model = RIG_MODEL_ELEKTOR507, +.model_name = "Elektor SDR-USB", +.mfg_name = "Elektor", +.version = "0.1", +.copyright = "LGPL", +.status = RIG_STATUS_BETA, +.rig_type = RIG_TYPE_TUNER, +.ptt_type = RIG_PTT_NONE, +.dcd_type = RIG_DCD_NONE, +#ifdef USE_LIBUSB +.port_type = RIG_PORT_USB, +#else +.port_type = RIG_PORT_NONE, +#endif +.serial_rate_min = 9600, +.serial_rate_max = 9600, +.serial_data_bits = 8, +.serial_stop_bits = 1, +.serial_parity = RIG_PARITY_NONE, +.serial_handshake = RIG_HANDSHAKE_NONE, +.write_delay = 0, +.post_write_delay = 0, +.timeout = 200, +.retry = 0, + +.has_get_func = ELEKTOR507_FUNC, +.has_set_func = ELEKTOR507_FUNC, +.has_get_level = ELEKTOR507_LEVEL_ALL, +.has_set_level = RIG_LEVEL_SET(ELEKTOR507_LEVEL_ALL), +.has_get_parm = ELEKTOR507_PARM_ALL, +.has_set_parm = RIG_PARM_SET(ELEKTOR507_PARM_ALL), +.level_gran = {}, +.parm_gran = {}, +.ctcss_list = NULL, +.dcs_list = NULL, +.preamp = { RIG_DBLST_END }, +.attenuator = { 0, 10, 20 }, +.max_rit = Hz(0), +.max_xit = Hz(0), +.max_ifshift = Hz(0), +.targetable_vfo = 0, +.transceive = RIG_TRN_OFF, +.bank_qty = 0, +.chan_desc_sz = 0, + +.chan_list = { RIG_CHAN_END, }, + +.rx_range_list1 = { + {kHz(30),MHz(30)-kHz(1),ELEKTOR507_MODES,-1,-1,ELEKTOR507_VFO}, + RIG_FRNG_END, + }, +.tx_range_list1 = { RIG_FRNG_END, }, +.rx_range_list2 = { + {kHz(30),MHz(30)-kHz(1),ELEKTOR507_MODES,-1,-1,ELEKTOR507_VFO}, + RIG_FRNG_END, + }, +.tx_range_list2 = { RIG_FRNG_END, }, +.tuning_steps = { + {ELEKTOR507_MODES,kHz(1)}, + RIG_TS_END, + }, + /* mode/filter list, remember: order matters! */ +.filters = { + {RIG_MODE_USB, kHz(24)}, /* bandpass may be more */ + RIG_FLT_END, + }, +.cfgparams = elektor507_cfg_params, + +.rig_init = elektor507_init, +.rig_cleanup = elektor507_cleanup, +.rig_open = elektor507_open, +.set_conf = elektor507_set_conf, +.get_conf = elektor507_get_conf, + + +.set_freq = elektor507_set_freq, +.get_freq = elektor507_get_freq, +.set_level = elektor507_set_level, +.set_ant = elektor507_set_ant, +.get_info = elektor507_get_info, + +}; + + +int elektor507_set_conf(RIG *rig, token_t token, const char *val) +{ + struct elektor507_priv_data *priv; + freq_t freq; + + priv = (struct elektor507_priv_data*)rig->state.priv; + + switch(token) { + case TOK_OSCFREQ: + sscanf(val, "%"SCNfreq, &freq); + priv->osc_freq = freq / kHz(1); + break; + case TOK_XTALCAL: + sscanf(val, "%u", &priv->xtal_cal); + break; + default: + return -RIG_EINVAL; + } + return RIG_OK; +} + +int elektor507_get_conf(RIG *rig, token_t token, char *val) +{ + struct elektor507_priv_data *priv; + + priv = (struct elektor507_priv_data*)rig->state.priv; + + switch(token) { + case TOK_OSCFREQ: + sprintf(val, "%"PRIfreq, priv->osc_freq*kHz(1)); + break; + case TOK_XTALCAL: + sprintf(val, "%u", priv->xtal_cal); + break; + default: + return -RIG_EINVAL; + } + return RIG_OK; +} + + + +int elektor507_open(RIG *rig) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + int ret; + + rig_debug(RIG_DEBUG_TRACE,"%s called\n", __FUNCTION__); + + /* + * Setup the FT232R. + */ +#ifdef USE_LIBUSB + ret = elektor507_libusb_setup(rig); + if (ret != RIG_OK) + return ret; +#endif + + /* Init the FT232R port to SCL/SDA high, Mux A0, Att 0 */ + priv->FT_port = 0x03; + + + /* + * Setup the CY27EE16ZE PLL. + */ + + /* Enable CLOCK3 & CLOCK5 */ + ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, CLKOE_REG, 0x24); + if (ret != 0) + return ret; + + /* DIV1N set to safe default */ + ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, DIV1_REG, 0x0a); + if (ret != 0) + return ret; + +#if 0 + /* Xtal gain setting */ + ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, XTALCTL_REG, 0x32); + if (ret != 0) + return ret; + + /* CapLoad set to middle */ + ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, CAPLOAD_REG, priv->xtal_cal); + if (ret != 0) + return ret; +#endif + /* CLKSRC: CLOCK3=DIV2CLK/2, CLOCK5=DIV1CLK/DIV1N */ + ret = i2c_write_regs(rig, CY_I2C_RAM_ADR, 3, CLKSRC_REG, 0x02, 0x8e, 0x47); + if (ret != 0) + return ret; + + /* DIV2SRC from REF */ + ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, DIV2_REG, 0x88); + if (ret != 0) + return ret; + + return RIG_OK; +} + + +static void find_P_Q(struct elektor507_priv_data *priv, freq_t freq) +{ + double Min, VCO; + int p, q, q_max; + + Min = priv->osc_freq; + freq /= kHz(1); + + /* + * Q:2..129 + * P:8..2055, best 16..1023 (because of Pump) + + For stable operation: + + REF/Qtotal must not fall below 250kHz ( + + P*(REF/Qtotal) must not be above 400 MHz or below 100 MHz + */ +#if 1 + q_max = priv->osc_freq/250; +#else + q_max = 100; +#endif + for (q = q_max; q >= 10; q--) { + for (p = 500; p <= 2000; p++) { + VCO = ((double)priv->osc_freq/q)*p; + + if (fabs(4*freq-VCO/priv->Div1N) < Min) { + Min = fabs(4*freq - VCO/priv->Div1N); + priv->Q = q; + priv->P = p; + } + } + } + + VCO = ((double)priv->osc_freq/priv->Q)*priv->P; + if (VCO < 100e3 || VCO > 400e3) + rig_debug(RIG_DEBUG_VERBOSE, "%s: Unstable parameters for VCO=%.1f\n", + __FUNCTION__, VCO); +} + +int elektor507_set_freq(RIG *rig, vfo_t vfo, freq_t freq) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + freq_t final_freq; + int Freq; + int ret=0; + int Mux; + + if (priv->ant == ANT_AUTO) { + /* Automatically select appropriate filter */ + if (freq <= kHz(1600)) { + /* Select A1, low pass, fc=1.6MHz */ + Mux = 1; + } else { + /* Select A2, high pass */ + Mux = 2; + } + priv->FT_port &= 0x63; //0,1 = I2C, 2,3,4=MUX, 5,6=Attenuator + priv->FT_port |= Mux << 2; + } + + /* + * Compute PLL parameters + */ + + Freq = freq / kHz(1); + + if (Freq > 19 && Freq < 60) + { + priv->Div1N = (2500 + Freq/2) / Freq + 128; + priv->P = 1000; + priv->Q = 40; + } + else + if (Freq > 59 && Freq < 801) + { + priv->Div1N = 125; + priv->P = Freq * 2; + priv->Q = 40; + } + else + if (Freq > 800 && Freq < 2001) + { + priv->Div1N = 50; + priv->P = Freq; + priv->Q = 50; + } + else + if (Freq > 2000 && Freq < 4001) + { + priv->Div1N = 25; + find_P_Q(priv, freq); + } + else + if (Freq > 4000 && Freq < 10001) + { + priv->Div1N = 10; + find_P_Q(priv, freq); + } + else + if (Freq > 10000 && Freq < 20001) + { + priv->Div1N = 5; + find_P_Q(priv, freq); + } + else + if (Freq > 20000 && Freq < 30001) + { + priv->Div1N = 4; + find_P_Q(priv, freq); + } + + + elektor507_get_freq(rig, vfo, &final_freq); + rig_debug(RIG_DEBUG_VERBOSE, "%s: Freq=%d kHz, delta=%d Hz, Div1N=%d, P=%d, Q=%d\n", + __FUNCTION__, Freq, (int)(final_freq-freq), priv->Div1N, priv->P, priv->Q); + + if ((double)priv->osc_freq/priv->Q < 250) + rig_debug(RIG_DEBUG_VERBOSE, + "%s: Unstable parameters for REF/Qtotal=%.1f\n", + __FUNCTION__, (double)priv->osc_freq/priv->Q); + + ret = cy_update_pll(rig, CY_I2C_RAM_ADR); + + return (ret != 0) ? -RIG_EIO : RIG_OK; +} + +int elektor507_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + double VCO; + + VCO = ((double)priv->osc_freq * kHz(1)) / priv->Q * priv->P; + + /* Div by 4 because of QSD */ + *freq = (VCO / priv->Div1N) / 4; + + return RIG_OK; +} + +int elektor507_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + int ret=0; + + switch(level) { + case RIG_LEVEL_ATT: + /* val.i */ + /* FTDI: DSR, DCD */ + + if (val.i != 0 && val.i != 1 && val.i != 2) + return -RIG_EINVAL; + + priv->FT_port &= 0x1f; + priv->FT_port |= (val.i&0x3) << 5; + + ret = elektor507_ftdi_write_data(rig, &priv->FT_port, 1); + + break; + + default: + return -RIG_EINVAL; + } + + return (ret != 0) ? -RIG_EIO : RIG_OK; +} + + +int elektor507_set_ant(RIG * rig, vfo_t vfo, ant_t ant) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + int ret, Mux; + + rig_debug(RIG_DEBUG_TRACE,"%s called\n", __FUNCTION__); + + /* + * FTDI: RTS, CTS, DTR + * + * A4,A5,A6 are not connected + * + * ANT1->A1/A2, ANT2->A3, ANT3->A7 + */ + + switch(ant) { + case RIG_ANT_1: Mux = 0; break; /* Mux will be updated upon next set_freq */ + case RIG_ANT_2: Mux = 3; break; /* ANT_EXT */ + case RIG_ANT_3: Mux = 7; break; /* ANT_TEST_CLK */ + default: + return -RIG_EINVAL; + } + priv->ant = ant; + + priv->FT_port &= 0x63; //0,1 = I2C, 2,3,4=MUX, 5,6=Attenuator + priv->FT_port |= Mux << 2; + + ret = elektor507_ftdi_write_data(rig, &priv->FT_port, 1); + + return (ret != 0) ? -RIG_EIO : RIG_OK; +} + + +/* + * Update the PLL counters + */ +static int cy_update_pll(RIG *rig, unsigned char IICadr) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + int P0, R40, R41, R42; + int Pump; + int ret; + + /* + * PLL Pump setting according to table 9 + */ + if (priv->P < 45) + Pump = 0; + else + if (priv->P < 480) + Pump = 1; + else + if (priv->P < 640) + Pump = 2; + else + if (priv->P < 800) + Pump = 3; + else + Pump = 4; + + P0 = priv->P & 0x01; + R40 = (((priv->P >> 1) - 4) >> 8) | (Pump << 2) | 0xc0; + R41 = ((priv->P >> 1) - 4) & 0xff; + R42 = (priv->Q - 2) | (P0<<7); + + + ret = i2c_write_regs(rig, IICadr, 3, PUMPCOUNTERS_REG, R40, R41, R42); + if (ret != 0) + return ret; + + ret = i2c_write_reg(rig, IICadr, DIV1_REG, priv->Div1N); + if (ret != 0) + return ret; + + return RIG_OK; +} + + +static void ftdi_SCL(RIG *rig, int d) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + + if (priv->Buf_adr >= FT_OUT_BUFFER_MAX) + return; + /* + * FTDI RXD->SCL + */ + + if (d == 0) + priv->FT_port &= ~0x02; + else + priv->FT_port |= 0x02; + + priv->FT_Out_Buffer[priv->Buf_adr++] = priv->FT_port; +} + +static void ftdi_SDA(RIG *rig, int d) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + + if (priv->Buf_adr >= FT_OUT_BUFFER_MAX) + return; + /* + * FTDI TXD->SDA + */ + + if (d == 0) + priv->FT_port &= ~0x01; + else + priv->FT_port |= 0x01; + + priv->FT_Out_Buffer[priv->Buf_adr++] = priv->FT_port; +} + +static void ftdi_I2C_Init(RIG *rig) +{ + ftdi_SCL(rig, 1); ftdi_SDA(rig, 1); /* SCL=1, SDA=1 */ +} + +static void ftdi_I2C_Start(RIG *rig) +{ + ftdi_SDA(rig, 0); /* SDA=0 */ + ftdi_SCL(rig, 0); /* SCL=0 */ +} + +static void ftdi_I2C_Stop(RIG *rig) +{ + ftdi_SCL(rig, 0); ftdi_SDA(rig, 0); /* SCL=0, SDA=0 */ + ftdi_SCL(rig, 1); /* SCL=1 */ + ftdi_SDA(rig, 1); /* SDA=1 */ +} + +/* + Acknowledge: + SCL=0, SDA=0 + SCL=1 + SCL=0 + + No Acknowledge: + SCL=0, SDA=1 + SCL=1 + SCL=0 + */ + +static void ftdi_I2C_Write_Byte(RIG *rig, unsigned char c) +{ + int i; + + for (i=7; i>=0; i--) + { + ftdi_SDA(rig, c & (1<state.priv; + int ret; + + /* Start with a new buffer */ + priv->Buf_adr = 0; + + ftdi_I2C_Init(rig); + ftdi_I2C_Start(rig); + ftdi_I2C_Write_Byte (rig, IICadr); + ftdi_I2C_Write_Byte (rig, reg_adr); + + if (reg_count >= 1) + ftdi_I2C_Write_Byte (rig, reg_val1); + if (reg_count >= 2) + ftdi_I2C_Write_Byte (rig, reg_val2); + if (reg_count >= 3) + ftdi_I2C_Write_Byte (rig, reg_val3); + + ftdi_I2C_Stop(rig); + //usleep(10000); + + ret = elektor507_ftdi_write_data(rig, priv->FT_Out_Buffer, priv->Buf_adr); + + if (ret != 0) + return -RIG_EIO; + return 0; +} + + +#if 0 +static const unsigned char ftdi_code[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x34, 0x08, 0x5a, 0x24/*0x6f*/, 0x00, 0x14, 0x0a, 0x00, 0x08, 0x88, + 0x50, 0x04, 0x32, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd1, 0x2b, 0x17, 0x00, 0xfe, 0xfe, 0x7f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, + 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, + 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, + 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, + 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, + 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, + 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, + 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32 +}; + +int load_ftdi_code(RIG *rig, unsigned char IICadr, const unsigned char code[]) +{ + struct elektor507_priv_data *priv = (struct elektor507_priv_data *)rig->state.priv; + int ret; + int i, j; + + rig_debug(RIG_DEBUG_TRACE,"%s called\n", __FUNCTION__); + + for (i = 0; i<16; i++) + { + /* Start with a new buffer */ + priv->Buf_adr = 0; + ftdi_I2C_Init(rig); + ftdi_I2C_Start(rig); + ftdi_I2C_Write_Byte (rig, IICadr); + ftdi_I2C_Write_Byte (rig, i*16); + + for (j = 0; j<16; j++) + { + ftdi_I2C_Write_Byte (rig, code[i*16+j]); + } + ftdi_I2C_Stop(rig); + + ret = elektor507_ftdi_write_data(rig, priv->FT_Out_Buffer, priv->Buf_adr); + if (ret != 0) + return -RIG_EIO; + } + + return RIG_OK; +} +#endif + +#endif /* defined(USE_FTDI_DLL) || defined(USE_LIBUSB) */