/* $Id: $ * * Hamlib KIT backend - FiFi-SDR Receiver(/Tuner) description * Copyright (c) 2010 by Rolf Meeser * * Derived from si570avrusb backend: * Copyright (C) 2004-2010 Stephane Fillod * * * 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 #ifdef HAVE_STDINT_H #include #endif #include #include #include #include "hamlib/rig.h" #include "token.h" #include "kit.h" /* * Compile this model only if libusb is available */ #if defined(HAVE_LIBUSB) && defined(HAVE_USB_H) #include #include /* Selected request codes of the original AVR USB Si570 firmware */ #define REQUEST_SET_FREQ_BY_VALUE (0x32) #define REQUEST_SET_XTALL_FREQ (0x33) #define REQUEST_READ_MULTIPLY_LO (0x39) #define REQUEST_READ_FREQUENCY (0x3A) /* FiFi-SDR specific requests */ #define REQUEST_FIFISDR_READ (0xAB) #define REQUEST_FIFISDR_WRITE (0xAC) /* USB VID/PID, vendor name (idVendor), product name (idProduct). * Use obdev's generic shared VID/PID pair and follow the rules outlined * in firmware/usbdrv/USBID-License.txt. */ #define USBDEV_SHARED_VID 0x16C0 /* VOTI */ #define USBDEV_SHARED_PID 0x05DC /* Obdev's free shared PID */ #define FIFISDR_VENDOR_NAME "www.ov-lennestadt.de" #define FIFISDR_PRODUCT_NAME "FiFi-SDR" static int fifisdr_init(RIG *rig); static int fifisdr_cleanup(RIG *rig); static int fifisdr_open(RIG *rig); static int fifisdr_set_freq(RIG *rig, vfo_t vfo, freq_t freq); static int fifisdr_get_freq(RIG *rig, vfo_t vfo, freq_t *freq); static const char *fifisdr_get_info(RIG *rig); static int fifisdr_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width); static int fifisdr_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t * width); static int fifisdr_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val); static int fifisdr_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val); static int fifisdr_get_ext_level(RIG *rig, vfo_t vfo, token_t token, value_t *val); /* Private tokens. */ #define TOK_LVL_FMCENTER TOKEN_BACKEND(1) /* FM center frequency deviation */ /* Extra levels definitions */ static const struct confparams fifisdr_ext_levels[] = { { TOK_LVL_FMCENTER, "fmcenter", "FM center", "Center frequency deviation of FM signal", NULL, RIG_CONF_NUMERIC, { .n = { -kHz(50), kHz(50), Hz(1) } } }, { RIG_CONF_END, NULL, } }; /** Private instance data. */ struct fifisdr_priv_instance_data { double multiplier; }; /** FiFi-SDR receiver description. */ const struct rig_caps fifisdr_caps = { .rig_model = RIG_MODEL_FIFISDR, .model_name = "FiFi-SDR", .mfg_name = "FiFi", .version = "0.3", .copyright = "GPL", .status = RIG_STATUS_BETA, .rig_type = RIG_TYPE_RECEIVER, .ptt_type = RIG_PTT_NONE, .dcd_type = RIG_DCD_NONE, .port_type = RIG_PORT_USB, .write_delay = 0, .post_write_delay = 0, .timeout = 500, .retry = 0, .has_get_func = RIG_FUNC_NONE, .has_set_func = RIG_FUNC_NONE, .has_get_level = RIG_LEVEL_PREAMP | RIG_LEVEL_STRENGTH, .has_set_level = RIG_LEVEL_SET(RIG_LEVEL_PREAMP), .has_get_parm = RIG_PARM_NONE, .has_set_parm = RIG_PARM_SET(RIG_PARM_NONE), .level_gran = {}, .parm_gran = {}, .extparms = NULL, .extlevels = fifisdr_ext_levels, .preamp = { 6, RIG_DBLST_END }, .attenuator = { RIG_DBLST_END }, .max_rit = Hz(0), .max_xit = Hz(0), .max_ifshift = Hz(0), .vfo_ops = RIG_OP_NONE, .scan_ops = RIG_SCAN_NONE, .targetable_vfo = 0, .transceive = RIG_TRN_OFF, .bank_qty = 0, .chan_desc_sz = 0, .chan_list = { RIG_CHAN_END, }, .rx_range_list1 = { {.start = kHz(39.1), .end = MHz(175.0), .modes = RIG_MODE_AM | RIG_MODE_SSB | RIG_MODE_FM, .low_power = -1, .high_power = -1, .vfo = RIG_VFO_A, .ant = RIG_ANT_1}, RIG_FRNG_END, }, .tx_range_list1 = { RIG_FRNG_END, }, .rx_range_list2 = { {.start = kHz(39.1), .end = MHz(175.0), .modes = RIG_MODE_AM | RIG_MODE_SSB | RIG_MODE_FM, .low_power = -1, .high_power = -1, .vfo = RIG_VFO_A, .ant = RIG_ANT_1}, RIG_FRNG_END, }, .tx_range_list2 = { RIG_FRNG_END, }, .tuning_steps = { {RIG_MODE_AM | RIG_MODE_SSB | RIG_MODE_FM, Hz(1)}, RIG_TS_END, }, /* mode/filter list, remember: order matters! */ .filters = { {RIG_MODE_SSB, kHz(2.7)}, {RIG_MODE_AM, kHz(6.0)}, {RIG_MODE_FM, kHz(12.5)}, RIG_FLT_END, }, .cfgparams = NULL, .rig_init = fifisdr_init, .rig_cleanup = fifisdr_cleanup, .rig_open = fifisdr_open, .rig_close = NULL, .set_freq = fifisdr_set_freq, .get_freq = fifisdr_get_freq, .set_mode = fifisdr_set_mode, .get_mode = fifisdr_get_mode, .set_level = fifisdr_set_level, .get_level = fifisdr_get_level, .get_ext_level = fifisdr_get_ext_level, .get_info = fifisdr_get_info, }; /** Convert from host endianness to FiFi-SDR little endian. */ static uint32_t fifisdr_tole32 (uint32_t x) { return (((((x) / 1ul) % 256ul) << 0) | ((((x) / 256ul) % 256ul) << 8) | ((((x) / 65536ul) % 256ul) << 16) | ((((x) / 16777216ul) % 256ul) << 24)); } /** Convert FiFi-SDR little endian to host endianness. */ static uint32_t fifisdr_fromle32 (uint32_t x) { return (((((x) >> 24) & 0xFF) * 16777216ul) + ((((x) >> 16) & 0xFF) * 65536ul) + ((((x) >> 8) & 0xFF) * 256ul) + ((((x) >> 0) & 0xFF) * 1ul)); } /** USB OUT transfer via vendor device command. */ static int fifisdr_usb_write (RIG *rig, int request, int value, int index, char *bytes, int size) { int ret; struct usb_dev_handle *udh = rig->state.rigport.handle; ret = usb_control_msg(udh, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, request, value, index, bytes, size, rig->state.rigport.timeout); if (ret != size) { rig_debug (RIG_DEBUG_ERR, "%s: usb_control_msg (%d/%d) failed: %s\n", __func__, request, value, usb_strerror ()); return -RIG_EIO; } return RIG_OK; } /** USB IN transfer via vendor device command. */ static int fifisdr_usb_read (RIG *rig, int request, int value, int index, char *bytes, int size) { int ret; struct usb_dev_handle *udh = rig->state.rigport.handle; ret = usb_control_msg(udh, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, request, value, index, bytes, size, rig->state.rigport.timeout); if (ret != size) { rig_debug (RIG_DEBUG_ERR, "%s: usb_control_msg (%d/%d) failed: %s\n", __func__, request, value, usb_strerror ()); return -RIG_EIO; } return RIG_OK; } int fifisdr_init (RIG *rig) { hamlib_port_t *rp = &rig->state.rigport; struct fifisdr_priv_instance_data *priv; priv = (struct fifisdr_priv_instance_data*)calloc(sizeof(struct fifisdr_priv_instance_data), 1); if (!priv) { /* whoops! memory shortage! */ return -RIG_ENOMEM; } priv->multiplier = 4; rp->parm.usb.vid = USBDEV_SHARED_VID; rp->parm.usb.pid = USBDEV_SHARED_PID; /* no usb_set_configuration() and usb_claim_interface() */ rp->parm.usb.iface = -1; rp->parm.usb.conf = 1; rp->parm.usb.alt = 0; rp->parm.usb.vendor_name = FIFISDR_VENDOR_NAME; rp->parm.usb.product = FIFISDR_PRODUCT_NAME; rig->state.priv = (void*)priv; return RIG_OK; } int fifisdr_cleanup (RIG *rig) { if (!rig) return -RIG_EINVAL; if (rig->state.priv) free(rig->state.priv); rig->state.priv = NULL; return RIG_OK; } int fifisdr_open (RIG *rig) { int ret; uint32_t multiply; struct fifisdr_priv_instance_data *priv; priv = (struct fifisdr_priv_instance_data*)rig->state.priv; /* The VCO is a multiple of the RX frequency. Typically 4 */ ret = fifisdr_usb_read(rig, REQUEST_FIFISDR_READ, 0, 11, /* Read virtual VCO factor */ (char *)&multiply, sizeof(multiply)); if (ret == RIG_OK) { priv->multiplier = fifisdr_fromle32(multiply); } return RIG_OK; } const char * fifisdr_get_info (RIG *rig) { static char buf[64]; int ret; uint32_t svn_version; int version; ret = fifisdr_usb_read(rig, REQUEST_FIFISDR_READ, 0, 0, (char *)&svn_version, sizeof(svn_version)); if (ret != RIG_OK) { return NULL; } version = svn_version; snprintf(buf, sizeof(buf), "Firmware version: %d", svn_version); return buf; } int fifisdr_set_freq (RIG *rig, vfo_t vfo, freq_t freq) { struct fifisdr_priv_instance_data *priv = (struct fifisdr_priv_instance_data *)rig->state.priv; int ret; double mhz; uint32_t freq1121; /* Need frequency in 11.21 format */ mhz = (freq * priv->multiplier) / 1e6; freq1121 = fifisdr_tole32(round(mhz * 2097152.0)); ret = fifisdr_usb_write(rig, REQUEST_SET_FREQ_BY_VALUE, 0, 0, (char *)&freq1121, sizeof(freq1121)); if (ret != RIG_OK) { return -RIG_EIO; } return RIG_OK; } int fifisdr_get_freq (RIG *rig, vfo_t vfo, freq_t *freq) { struct fifisdr_priv_instance_data *priv = (struct fifisdr_priv_instance_data *)rig->state.priv; int ret; uint32_t freq1121; ret = fifisdr_usb_read(rig, REQUEST_READ_FREQUENCY, 0, 0, (char *)&freq1121, sizeof(freq1121)); if (ret == RIG_OK) { freq1121 = fifisdr_fromle32(freq1121); *freq = MHz(((double)freq1121 / (1ul << 21)) / priv->multiplier); } return ret; } static int fifisdr_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) { int ret; uint8_t fifi_mode; uint32_t fifi_width; /* Translate mode into FiFi-SDR language */ fifi_mode = 0; switch (mode) { case RIG_MODE_AM: fifi_mode = 2; break; case RIG_MODE_LSB: fifi_mode = 0; break; case RIG_MODE_USB: fifi_mode = 1; break; case RIG_MODE_FM: fifi_mode = 3; break; default: return -RIG_EINVAL; } ret = fifisdr_usb_write(rig, REQUEST_FIFISDR_WRITE, 0, 15, /* Demodulator mode */ (char *)&fifi_mode, sizeof(fifi_mode)); if (ret != RIG_OK) { return -RIG_EIO; } /* Set filter width */ fifi_width = fifisdr_tole32(width); ret = fifisdr_usb_write(rig, REQUEST_FIFISDR_WRITE, 0, 16, /* Filter width */ (char *)&fifi_width, sizeof(fifi_width)); if (ret != RIG_OK) { return -RIG_EIO; } return RIG_OK; } static int fifisdr_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t * width) { int ret; uint8_t fifi_mode; uint32_t fifi_width; /* Read current mode */ ret = fifisdr_usb_read(rig, REQUEST_FIFISDR_READ, 0, 15, /* Demodulator mode */ (char *)&fifi_mode, sizeof(fifi_mode)); if (ret != RIG_OK) { return -RIG_EIO; } /* Translate mode coding */ *mode = RIG_MODE_NONE; switch (fifi_mode) { case 0: *mode = RIG_MODE_LSB; break; case 1: *mode = RIG_MODE_USB; break; case 2: *mode = RIG_MODE_AM; break; case 3: *mode = RIG_MODE_FM; break; } /* Read current filter width */ ret = fifisdr_usb_read(rig, REQUEST_FIFISDR_READ, 0, 16, /* Filter width */ (char *)&fifi_width, sizeof(fifi_width)); if (ret != RIG_OK) { return -RIG_EIO; } *width = s_Hz(fifisdr_fromle32(fifi_width)); return RIG_OK; } static int fifisdr_set_level (RIG * rig, vfo_t vfo, setting_t level, value_t val) { int ret = RIG_OK; uint8_t fifi_preamp; switch (level) { /* Preamplifier (ADC 0/+6dB switch) */ case RIG_LEVEL_PREAMP: /* Value can be 0 (0 dB) or 1 (+6 dB) */ fifi_preamp = 0; if (val.i == 6) { fifi_preamp = 1; } ret = fifisdr_usb_write(rig, REQUEST_FIFISDR_WRITE, 0, 19, /* Preamp */ (char *)&fifi_preamp, sizeof(fifi_preamp)); break; /* Unsupported option */ default: ret = -RIG_ENIMPL; } return ret; } static int fifisdr_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) { int ret = RIG_OK; uint32_t fifi_meter = 0; uint8_t fifi_preamp = 0; switch (level) { /* Preamplifier (ADC 0/+6dB switch) */ case RIG_LEVEL_PREAMP: ret = fifisdr_usb_read(rig, REQUEST_FIFISDR_READ, 0, 19, /* Preamp */ (char *)&fifi_preamp, sizeof(fifi_preamp)); if (ret == RIG_OK) { /* Value can be 0 (0 dB) or 1 (+6 dB) */ val->i = 0; if (fifi_preamp != 0) { val->i = 6; } } break; /* Signal strength */ case RIG_LEVEL_STRENGTH: ret = fifisdr_usb_read(rig, REQUEST_FIFISDR_READ, 0, 17, /* S-Meter */ (char *)&fifi_meter, sizeof(fifi_meter)); if (ret == RIG_OK) { val->i = fifisdr_fromle32(fifi_meter); } break; /* Unsupported option */ default: ret = -RIG_ENIMPL; } return ret; } static int fifisdr_get_ext_level(RIG *rig, vfo_t vfo, token_t token, value_t *val) { int ret = RIG_OK; uint32_t u32; switch (token) { /* FM center frequency deviation */ case TOK_LVL_FMCENTER: ret = fifisdr_usb_read(rig, REQUEST_FIFISDR_READ, 0, 18, /* FM center frequency */ (char *)&u32, sizeof(u32)); if (ret == RIG_OK) { val->f = Hz((int32_t)fifisdr_fromle32(u32)); } break; /* Unsupported option */ default: ret = -RIG_ENIMPL; } return ret; } #endif /* defined(HAVE_LIBUSB) && defined(HAVE_USB_H) */