mirror of
https://github.com/Hamlib/Hamlib
synced 2026-08-13 17:32:05 -04:00
As reported in GitHub issue #1892, configure on FreeBSD failed to search for the name of the library correctly which is libusb. As pkg-config correctly holds the library name for linking, modify the search for libusb-1.0 to use the pkg-config capability. If pkg-config (now largely replaced by pkgconf) is not installed or found, the modules depending on it will be gracefull disabled, i.e. the configure script will complete with those modules disabled. Modify the output summary to use spaces instead of tabs as things were not lining up nicely in my FreeBSD terminal. Rename the Automake substituted variable 'HAVE_LIBUSB' to 'TESTS_HAVE_LIBUSB' to avoid confusion with identically named variable written to include/hamlib/config.h. Likewise, rename the AutoConf substituted name 'LIBUSB' to 'HAMLIB_PC_LIBUSB' to avoid confusion with the 'LIBUSB' string passed to PKG_CHECK_MODULES. Update hamlib.pc.in and tests/Makefile.am to use the new variable names. Make sure all source files no longer reference HAVE_LIBUSB_H or HAVE_LIBUSB_1_0_LIBUSB_H as these variables no longer exist. Fix src/Makefile.am to provide include path for usb_port.c.
954 lines
26 KiB
C
954 lines
26 KiB
C
/*
|
|
* Hamlib KIT backend - FUNcube Dongle USB tuner description
|
|
* Copyright (c) 2009-2011 by Stephane Fillod
|
|
*
|
|
* Derived from usbsoftrock-0.5:
|
|
* Copyright (C) 2009 Andrew Nilsson (andrew.nilsson@gmail.com)
|
|
*
|
|
* Author: Stefano Speretta, Innovative Solutions In Space BV
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include "hamlib/config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "hamlib/rig.h"
|
|
|
|
#define BACKEND_VER "20210830"
|
|
|
|
/*
|
|
* Compile this model only if libusb is available
|
|
*/
|
|
#if defined(HAVE_LIBUSB)
|
|
|
|
|
|
// LIBUSB_CFLAGS set by pkg-config should set the include path appropriately.
|
|
# include <libusb.h>
|
|
|
|
#include "funcube.h"
|
|
|
|
static int funcube_hid_cmd(RIG *rig, unsigned char *au8BufOut,
|
|
unsigned char *au8BufIn, int inputSize);
|
|
|
|
static int funcube_init(RIG *rig);
|
|
static int funcubeplus_init(RIG *rig);
|
|
static int funcube_cleanup(RIG *rig);
|
|
static int funcube_set_freq(RIG *rig, vfo_t vfo, freq_t freq);
|
|
static int funcube_get_freq(RIG *rig, vfo_t vfo, freq_t *freq);
|
|
static int funcube_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val);
|
|
static int funcube_get_level(RIG *rig, vfo_t vfo, setting_t level,
|
|
value_t *val);
|
|
|
|
static int funcubepro_set_level(RIG *rig, vfo_t vfo, setting_t level,
|
|
value_t val);
|
|
static int funcubepro_get_level(RIG *rig, vfo_t vfo, setting_t level,
|
|
value_t *val);
|
|
|
|
static const char *funcube_get_info(RIG *rig);
|
|
static int funcube_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode,
|
|
pbwidth_t *width);
|
|
|
|
static const struct confparams funcube_cfg_params[] =
|
|
{
|
|
{ RIG_CONF_END, NULL, }
|
|
};
|
|
|
|
// functions used set / read frequency, working on FUNcube version 0 and 1
|
|
int set_freq_v0(libusb_device_handle *udh, unsigned int f, int timeout);
|
|
int set_freq_v1(libusb_device_handle *udh, unsigned int f, int timeout);
|
|
|
|
/*
|
|
* Common data struct
|
|
*/
|
|
struct funcube_priv_data
|
|
{
|
|
|
|
freq_t freq; /* Hz */
|
|
};
|
|
|
|
/*
|
|
* FUNcube Dongle description
|
|
*
|
|
* Based on Jan Axelson HID examples
|
|
* http://www.lvr.com/
|
|
*
|
|
*/
|
|
|
|
struct rig_caps funcube_caps =
|
|
{
|
|
RIG_MODEL(RIG_MODEL_FUNCUBEDONGLE),
|
|
.model_name = "FUNcube Dongle",
|
|
.mfg_name = "AMSAT-UK",
|
|
.version = BACKEND_VER ".1",
|
|
.copyright = "LGPL",
|
|
.status = RIG_STATUS_STABLE,
|
|
.rig_type = RIG_TYPE_TUNER,
|
|
.ptt_type = RIG_PTT_RIG,
|
|
.dcd_type = RIG_DCD_NONE,
|
|
.port_type = RIG_PORT_USB,
|
|
.write_delay = 0,
|
|
.post_write_delay = 0,
|
|
.timeout = 1000,
|
|
.retry = 0,
|
|
|
|
.has_get_func = RIG_FUNC_NONE,
|
|
.has_set_func = RIG_FUNC_NONE,
|
|
.has_get_level = RIG_LEVEL_ATT | RIG_LEVEL_STRENGTH | RIG_LEVEL_PREAMP,
|
|
.has_set_level = RIG_LEVEL_ATT | RIG_LEVEL_PREAMP,
|
|
.has_get_parm = RIG_PARM_NONE,
|
|
.has_set_parm = RIG_PARM_NONE,
|
|
.level_gran = {},
|
|
.parm_gran = {},
|
|
.ctcss_list = NULL,
|
|
.dcs_list = NULL,
|
|
.preamp = { 5, 10, 15, 20, 25, 30, RIG_DBLST_END, },
|
|
.attenuator = { 2, 5, RIG_DBLST_END, },
|
|
.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 = {
|
|
{MHz(50), MHz(2500), RIG_MODE_USB, -1, -1, RIG_VFO_A},
|
|
RIG_FRNG_END,
|
|
},
|
|
.tuning_steps = {
|
|
{RIG_MODE_USB, kHz(1)},
|
|
RIG_TS_END,
|
|
},
|
|
.filters = {
|
|
{RIG_MODE_USB, kHz(192)},
|
|
RIG_FLT_END,
|
|
},
|
|
.cfgparams = funcube_cfg_params,
|
|
|
|
.rig_init = funcube_init,
|
|
.rig_cleanup = funcube_cleanup,
|
|
.set_freq = funcube_set_freq,
|
|
.get_freq = funcube_get_freq,
|
|
.get_level = funcube_get_level,
|
|
.set_level = funcube_set_level,
|
|
.get_info = funcube_get_info,
|
|
.get_mode = funcube_get_mode,
|
|
.hamlib_check_rig_caps = HAMLIB_CHECK_RIG_CAPS
|
|
};
|
|
|
|
|
|
struct rig_caps funcubeplus_caps =
|
|
{
|
|
RIG_MODEL(RIG_MODEL_FUNCUBEDONGLEPLUS),
|
|
.model_name = "FUNcube Dongle Pro+",
|
|
.mfg_name = "AMSAT-UK",
|
|
.version = BACKEND_VER ".1",
|
|
.copyright = "LGPL",
|
|
.status = RIG_STATUS_STABLE,
|
|
.rig_type = RIG_TYPE_TUNER,
|
|
.ptt_type = RIG_PTT_RIG,
|
|
.dcd_type = RIG_DCD_NONE,
|
|
.port_type = RIG_PORT_USB,
|
|
.write_delay = 0,
|
|
.post_write_delay = 0,
|
|
.timeout = 1000,
|
|
.retry = 0,
|
|
|
|
.has_get_func = RIG_FUNC_NONE,
|
|
.has_set_func = RIG_FUNC_NONE,
|
|
.has_get_level = RIG_LEVEL_PREAMP | RIG_LEVEL_RF,
|
|
// RIG_LEVEL_PREAMP: 10dB=LNAon MixGainOff. 20dB=LNAoff, MixGainOn. 30dB=LNAOn, MixGainOn
|
|
// RIG_LEVEL_RF 0..1 : IF gain 0 .. 59 dB
|
|
.has_set_level = RIG_LEVEL_PREAMP | RIG_LEVEL_RF,
|
|
.has_get_parm = RIG_PARM_NONE,
|
|
.has_set_parm = RIG_PARM_NONE,
|
|
.level_gran = {},
|
|
.parm_gran = {},
|
|
.ctcss_list = NULL,
|
|
.dcs_list = NULL,
|
|
.preamp = { 10, 20, 30, RIG_DBLST_END, },
|
|
.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(150), MHz(1900), RIG_MODE_IQ, -1, -1, RIG_VFO_A},
|
|
RIG_FRNG_END,
|
|
},
|
|
.tuning_steps = {
|
|
{RIG_MODE_IQ, kHz(1)},
|
|
RIG_TS_END,
|
|
},
|
|
.filters = {
|
|
{RIG_MODE_IQ, kHz(192)},
|
|
RIG_FLT_END,
|
|
},
|
|
.cfgparams = funcube_cfg_params,
|
|
|
|
.rig_init = funcubeplus_init,
|
|
.rig_cleanup = funcube_cleanup,
|
|
.set_freq = funcube_set_freq,
|
|
.get_freq = funcube_get_freq,
|
|
.get_level = funcubepro_get_level,
|
|
.set_level = funcubepro_set_level,
|
|
.get_info = funcube_get_info,
|
|
.get_mode = funcube_get_mode,
|
|
};
|
|
|
|
int funcube_init(RIG *rig)
|
|
{
|
|
hamlib_port_t *rp = RIGPORT(rig);
|
|
struct funcube_priv_data *priv;
|
|
|
|
STATE(rig)->priv = (struct funcube_priv_data *)calloc(sizeof(
|
|
struct funcube_priv_data), 1);
|
|
|
|
if (!STATE(rig)->priv)
|
|
{
|
|
/* whoops! memory shortage! */
|
|
return -RIG_ENOMEM;
|
|
}
|
|
|
|
priv = STATE(rig)->priv;
|
|
|
|
priv->freq = 0;
|
|
|
|
rp->parm.usb.vid = VID;
|
|
rp->parm.usb.pid = PID;
|
|
rp->parm.usb.conf = FUNCUBE_CONFIGURATION;
|
|
rp->parm.usb.iface = FUNCUBE_INTERFACE;
|
|
rp->parm.usb.alt = FUNCUBE_ALTERNATIVE_SETTING;
|
|
|
|
rp->parm.usb.vendor_name = VENDOR_NAME;
|
|
rp->parm.usb.product = PRODUCT_NAME;
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int funcubeplus_init(RIG *rig)
|
|
{
|
|
hamlib_port_t *rp = RIGPORT(rig);
|
|
struct funcube_priv_data *priv;
|
|
|
|
STATE(rig)->priv = (struct funcube_priv_data *)calloc(sizeof(
|
|
struct funcube_priv_data), 1);
|
|
|
|
if (!STATE(rig)->priv)
|
|
{
|
|
/* whoops! memory shortage! */
|
|
return -RIG_ENOMEM;
|
|
}
|
|
|
|
priv = STATE(rig)->priv;
|
|
|
|
priv->freq = 0;
|
|
|
|
rp->parm.usb.vid = VID;
|
|
rp->parm.usb.pid = PIDPLUS;
|
|
rp->parm.usb.conf = FUNCUBE_CONFIGURATION;
|
|
rp->parm.usb.iface = FUNCUBE_INTERFACE;
|
|
rp->parm.usb.alt = FUNCUBE_ALTERNATIVE_SETTING;
|
|
|
|
rp->parm.usb.vendor_name = VENDOR_NAME;
|
|
rp->parm.usb.product = PRODUCT_NAMEPLUS;
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int funcube_cleanup(RIG *rig)
|
|
{
|
|
if (!rig)
|
|
{
|
|
return -RIG_EINVAL;
|
|
}
|
|
|
|
if (STATE(rig)->priv)
|
|
{
|
|
free(STATE(rig)->priv);
|
|
}
|
|
|
|
STATE(rig)->priv = NULL;
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
/* Rem: not reentrant */
|
|
const char *funcube_get_info(RIG *rig)
|
|
{
|
|
static char buf[64];
|
|
libusb_device_handle *udh = RIGPORT(rig)->handle;
|
|
struct libusb_device_descriptor desc;
|
|
|
|
/* always succeeds since libusb-1.0.16 */
|
|
libusb_get_device_descriptor(libusb_get_device(udh), &desc);
|
|
|
|
SNPRINTF(buf, sizeof(buf), "Dev %04d", desc.bcdDevice);
|
|
|
|
return buf;
|
|
}
|
|
|
|
int set_freq_v0(libusb_device_handle *udh, unsigned int f, int timeout)
|
|
{
|
|
int ret;
|
|
int actual_length;
|
|
|
|
unsigned char au8BufOut[64]; // endpoint size
|
|
unsigned char au8BufIn[64]; // endpoint size
|
|
|
|
// frequency is in Hz, while the dongle expects it in kHz
|
|
f = f / 1000;
|
|
|
|
au8BufOut[0] = REQUEST_SET_FREQ; // Command to Set Frequency on dongle
|
|
au8BufOut[1] = (unsigned char)f;
|
|
au8BufOut[2] = (unsigned char)(f >> 8);
|
|
au8BufOut[3] = (unsigned char)(f >> 16);
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: HID packet set to %02x%02x%02x%02x\n",
|
|
__func__, au8BufOut[0] & 0xFF, au8BufOut[1] & 0xFF, au8BufOut[2] & 0xFF,
|
|
au8BufOut[3] & 0xFF);
|
|
|
|
ret = libusb_interrupt_transfer(udh, OUTPUT_ENDPOINT, au8BufOut,
|
|
sizeof(au8BufOut), &actual_length, timeout);
|
|
|
|
if (ret < 0)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret, libusb_error_name(ret));
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
ret = libusb_interrupt_transfer(udh, INPUT_ENDPOINT, au8BufIn, sizeof(au8BufIn),
|
|
&actual_length, timeout);
|
|
|
|
if (ret < 0 || actual_length != sizeof(au8BufIn))
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret, libusb_error_name(ret));
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Answer buf=%02x%02x\n",
|
|
__func__, au8BufIn[0] & 0xFF, au8BufIn[1] & 0xFF);
|
|
|
|
if (au8BufIn[1] != FUNCUBE_SUCCESS)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: REQUEST_SET_FREQ not supported\n",
|
|
__func__);
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int set_freq_v1(libusb_device_handle *udh, unsigned int f, int timeout)
|
|
{
|
|
int ret;
|
|
int actual_length;
|
|
|
|
unsigned char au8BufOut[64]; // endpoint size
|
|
unsigned char au8BufIn[64]; // endpoint size
|
|
|
|
au8BufOut[0] = REQUEST_SET_FREQ_HZ; // Command to Set Frequency in Hz on dongle
|
|
au8BufOut[1] = (unsigned char)f;
|
|
au8BufOut[2] = (unsigned char)(f >> 8);
|
|
au8BufOut[3] = (unsigned char)(f >> 16);
|
|
au8BufOut[4] = (unsigned char)(f >> 24);
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: HID packet set to %02x%02x%02x%02x%02x\n",
|
|
__func__, au8BufOut[0] & 0xFF, au8BufOut[1] & 0xFF, au8BufOut[2] & 0xFF,
|
|
au8BufOut[3] & 0xFF,
|
|
au8BufOut[4] & 0xFF);
|
|
|
|
ret = libusb_interrupt_transfer(udh, OUTPUT_ENDPOINT, au8BufOut,
|
|
sizeof(au8BufOut), &actual_length, timeout);
|
|
|
|
if (ret < 0)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
ret = libusb_interrupt_transfer(udh, INPUT_ENDPOINT, au8BufIn, sizeof(au8BufIn),
|
|
&actual_length, timeout);
|
|
|
|
if (ret < 0 || actual_length != sizeof(au8BufIn))
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Answer buf=%02x%02x%02x%02x%02x%02x\n",
|
|
__func__, au8BufIn[0] & 0xFF, au8BufIn[1] & 0xFF, au8BufIn[2] & 0xFF,
|
|
au8BufIn[3] & 0xFF,
|
|
au8BufIn[4] & 0xFF, au8BufIn[5] & 0xFF);
|
|
|
|
if (au8BufIn[1] != FUNCUBE_SUCCESS)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: REQUEST_SET_FREQ_HZ not supported\n",
|
|
__func__);
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int funcube_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
|
|
{
|
|
struct funcube_priv_data *priv = (struct funcube_priv_data *)STATE(rig)->priv;
|
|
hamlib_port_t *rp = RIGPORT(rig);
|
|
libusb_device_handle *udh = rp->handle;
|
|
|
|
int ret;
|
|
|
|
if ((ret = set_freq_v1(udh, freq, rp->timeout)) != RIG_OK)
|
|
{
|
|
if ((ret = set_freq_v0(udh, freq, rp->timeout)) == RIG_OK)
|
|
{
|
|
priv->freq = freq;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
priv->freq = freq;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int get_freq_v0(RIG *rig, vfo_t vfo, freq_t *freq)
|
|
{
|
|
const struct funcube_priv_data *priv = (struct funcube_priv_data *)
|
|
STATE(rig)->priv;
|
|
|
|
rig_debug(RIG_DEBUG_TRACE,
|
|
"%s: frequency is not read from the device, the value shown is the last successfully set.\n",
|
|
__func__);
|
|
*freq = priv->freq;
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int get_freq_v1(RIG *rig, vfo_t vfo, freq_t *freq)
|
|
{
|
|
hamlib_port_t *rp = RIGPORT(rig);
|
|
libusb_device_handle *udh = rp->handle;
|
|
int ret;
|
|
unsigned int f;
|
|
int actual_length;
|
|
|
|
unsigned char au8BufOut[64] = "\0\0\0\0"; // endpoint size
|
|
unsigned char au8BufIn[64] = "\0\0\0\0"; // endpoint size
|
|
|
|
au8BufOut[0] = REQUEST_GET_FREQ_HZ; // Command to Set Frequency on dongle
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: HID packet set to %02x%02x%02x%02x\n",
|
|
__func__, au8BufOut[0] & 0xFF, au8BufOut[1] & 0xFF, au8BufOut[2] & 0xFF,
|
|
au8BufOut[3] & 0xFF);
|
|
|
|
ret = libusb_interrupt_transfer(udh, OUTPUT_ENDPOINT, au8BufOut,
|
|
sizeof(au8BufOut), &actual_length, rp->timeout);
|
|
|
|
if (ret < 0)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
}
|
|
|
|
ret = libusb_interrupt_transfer(udh, INPUT_ENDPOINT, au8BufIn, sizeof(au8BufIn),
|
|
&actual_length, rp->timeout);
|
|
|
|
if (ret < 0 || actual_length != sizeof(au8BufIn))
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Answer buf=%02x%02x%02x%02x%02x%02x\n",
|
|
__func__, au8BufIn[0] & 0xFF, au8BufIn[1] & 0xFF, au8BufIn[2] & 0xFF,
|
|
au8BufIn[3] & 0xFF,
|
|
au8BufIn[4] & 0xFF, au8BufIn[5] & 0xFF);
|
|
|
|
if (au8BufIn[1] != FUNCUBE_SUCCESS)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: REQUEST_GET_FREQ_HZ not supported\n",
|
|
__func__);
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
f = (au8BufIn[2] & 0xFF) | ((au8BufIn[3] & 0xFF) << 8) |
|
|
((au8BufIn[4] & 0xFF) << 16) | ((au8BufIn[5] & 0xFF) << 24),
|
|
|
|
*freq = f;
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int funcube_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
|
|
{
|
|
int ret;
|
|
|
|
if ((ret = get_freq_v1(rig, vfo, freq)) != RIG_OK)
|
|
{
|
|
ret = get_freq_v0(rig, vfo, freq);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int funcube_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
|
|
{
|
|
hamlib_port_t *rp = RIGPORT(rig);
|
|
libusb_device_handle *udh = rp->handle;
|
|
int ret;
|
|
int actual_length;
|
|
unsigned char au8BufOut[64] = "\0\0\0\0"; // endpoint size
|
|
unsigned char au8BufIn[64] = "\0\0\0\0"; // endpoint size
|
|
|
|
switch (level)
|
|
{
|
|
case RIG_LEVEL_PREAMP:
|
|
au8BufOut[0] = REQUEST_SET_LNA_GAIN; // Command to Set LNA gain
|
|
|
|
switch (val.i)
|
|
{
|
|
case 5:
|
|
au8BufOut[1] = 6;
|
|
break;
|
|
|
|
case 10:
|
|
au8BufOut[1] = 8;
|
|
break;
|
|
|
|
case 15:
|
|
au8BufOut[1] = 10;
|
|
break;
|
|
|
|
case 20:
|
|
au8BufOut[1] = 12;
|
|
break;
|
|
|
|
case 25:
|
|
au8BufOut[1] = 13;
|
|
break;
|
|
|
|
case 30:
|
|
au8BufOut[1] = 14;
|
|
break;
|
|
|
|
default:
|
|
au8BufOut[1] = 4;
|
|
}
|
|
|
|
break;
|
|
|
|
case RIG_LEVEL_ATT:
|
|
au8BufOut[0] = REQUEST_SET_LNA_GAIN; // Command to Set LNA gain
|
|
|
|
switch (val.i)
|
|
{
|
|
case 2:
|
|
au8BufOut[1] = 1;
|
|
break;
|
|
|
|
case 5:
|
|
au8BufOut[1] = 0;
|
|
break;
|
|
|
|
default:
|
|
au8BufOut[1] = 4;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
rig_debug(RIG_DEBUG_ERR, "%s: unsupported level %s\n", __func__,
|
|
rig_strlevel(level));
|
|
return -RIG_EINVAL;
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: HID packet set to %02x%02x%02x%02x\n",
|
|
__func__, au8BufOut[0] & 0xFF, au8BufOut[1] & 0xFF, au8BufOut[2] & 0xFF,
|
|
au8BufOut[3] & 0xFF);
|
|
|
|
ret = libusb_interrupt_transfer(udh, OUTPUT_ENDPOINT, au8BufOut,
|
|
sizeof(au8BufOut), &actual_length, rp->timeout);
|
|
|
|
if (ret < 0)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
}
|
|
|
|
ret = libusb_interrupt_transfer(udh, INPUT_ENDPOINT, au8BufIn, sizeof(au8BufIn),
|
|
&actual_length, rp->timeout);
|
|
|
|
if (ret < 0 || actual_length != sizeof(au8BufIn))
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Answer buf=%02x%02x\n",
|
|
__func__, au8BufIn[0] & 0xFF, au8BufIn[1] & 0xFF);
|
|
|
|
if (au8BufIn[1] != FUNCUBE_SUCCESS)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: REQUEST_SET_LEVEL not supported\n",
|
|
__func__);
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int funcube_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
|
|
{
|
|
hamlib_port_t *rp = RIGPORT(rig);
|
|
libusb_device_handle *udh = rp->handle;
|
|
int ret;
|
|
int actual_length;
|
|
unsigned char au8BufOut[64] = "\0\0\0\0"; // endpoint size
|
|
unsigned char au8BufIn[64] = "\0\0\0\0"; // endpoint size
|
|
|
|
switch (level)
|
|
{
|
|
case RIG_LEVEL_ATT:
|
|
case RIG_LEVEL_PREAMP:
|
|
au8BufOut[0] = REQUEST_GET_LNA_GAIN; // Command to Get LNA / ATT gain
|
|
break;
|
|
|
|
case RIG_LEVEL_STRENGTH:
|
|
au8BufOut[0] = REQUEST_GET_RSSI;
|
|
break;
|
|
|
|
default:
|
|
rig_debug(RIG_DEBUG_ERR, "%s: unsupported level %s\n", __func__,
|
|
rig_strlevel(level));
|
|
return -RIG_EINVAL;
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: HID packet set to %02x%02x%02x%02x\n",
|
|
__func__, au8BufOut[0] & 0xFF, au8BufOut[1] & 0xFF, au8BufOut[2] & 0xFF,
|
|
au8BufOut[3] & 0xFF);
|
|
|
|
ret = libusb_interrupt_transfer(udh, OUTPUT_ENDPOINT, au8BufOut,
|
|
sizeof(au8BufOut), &actual_length, rp->timeout);
|
|
|
|
if (ret < 0)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
}
|
|
|
|
ret = libusb_interrupt_transfer(udh, INPUT_ENDPOINT, au8BufIn, sizeof(au8BufIn),
|
|
&actual_length, rp->timeout);
|
|
|
|
if (ret < 0 || actual_length != sizeof(au8BufIn))
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Answer buf=%02x%02x%02x\n",
|
|
__func__, au8BufIn[0] & 0xFF, au8BufIn[1] & 0xFF, au8BufIn[2] & 0xFF);
|
|
|
|
if (au8BufIn[1] != FUNCUBE_SUCCESS)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: REQUEST_GET_LEVEL_x not supported\n",
|
|
__func__);
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
switch (level)
|
|
{
|
|
case RIG_LEVEL_PREAMP:
|
|
switch (au8BufIn[2])
|
|
{
|
|
case 6:
|
|
val->i = 5;
|
|
break;
|
|
|
|
case 8:
|
|
val->i = 10;
|
|
break;
|
|
|
|
case 10:
|
|
val->i = 15;
|
|
break;
|
|
|
|
case 12:
|
|
val->i = 20;
|
|
break;
|
|
|
|
case 13:
|
|
val->i = 25;
|
|
break;
|
|
|
|
case 14:
|
|
val->i = 30;
|
|
break;
|
|
|
|
default:
|
|
val->i = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case RIG_LEVEL_ATT:
|
|
switch (au8BufIn[2])
|
|
{
|
|
case 0:
|
|
val->i = 5;
|
|
break;
|
|
|
|
case 1:
|
|
val->i = 2;
|
|
break;
|
|
|
|
default:
|
|
val->i = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case RIG_LEVEL_STRENGTH:
|
|
val->i = (int)((float)au8BufIn[2] * 2.8 - 35);
|
|
break;
|
|
|
|
default:
|
|
rig_debug(RIG_DEBUG_ERR, "%s: unsupported level %s\n", __func__,
|
|
rig_strlevel(level));
|
|
return -RIG_EINVAL;
|
|
}
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int funcube_hid_cmd(RIG *rig, unsigned char *au8BufOut, unsigned char *au8BufIn,
|
|
int inputSize)
|
|
{
|
|
hamlib_port_t *rp = RIGPORT(rig);
|
|
libusb_device_handle *udh = rp->handle;
|
|
int ret;
|
|
int actual_length;
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: HID packet set to %02x%02x%02x%02x\n",
|
|
__func__, au8BufOut[0] & 0xFF, au8BufOut[1] & 0xFF, au8BufOut[2] & 0xFF,
|
|
au8BufOut[3] & 0xFF);
|
|
|
|
ret = libusb_interrupt_transfer(udh, OUTPUT_ENDPOINT, au8BufOut,
|
|
sizeof(au8BufOut), &actual_length, rp->timeout);
|
|
|
|
if (ret < 0)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
}
|
|
|
|
ret = libusb_interrupt_transfer(udh, INPUT_ENDPOINT, au8BufIn, inputSize,
|
|
&actual_length, rp->timeout);
|
|
|
|
if (ret < 0 || actual_length != inputSize)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: libusb_interrupt_transfer failed (%d): %s\n",
|
|
__func__, ret,
|
|
libusb_error_name(ret));
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Answer buf=%02x%02x\n",
|
|
__func__, au8BufIn[0] & 0xFF, au8BufIn[1] & 0xFF);
|
|
|
|
if (au8BufIn[1] != FUNCUBE_SUCCESS)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: failed to perform FUNCube HID command %d.\n",
|
|
__func__, au8BufOut[0]);
|
|
return -RIG_EIO;
|
|
}
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int funcubepro_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
|
|
{
|
|
int ret;
|
|
unsigned char au8BufOut[64] = { 0 }; // endpoint size
|
|
unsigned char au8BufIn[64] = { 0 }; // endpoint size
|
|
|
|
switch (level)
|
|
{
|
|
case RIG_LEVEL_PREAMP:
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Setting PREAMP state to %d.\n",
|
|
__func__, val.i);
|
|
au8BufOut[0] = REQUEST_SET_LNA_GAIN; // Command to set LNA gain
|
|
|
|
if (val.i == 10 || val.i == 30)
|
|
{
|
|
au8BufOut[1] = 1;
|
|
}
|
|
else
|
|
{
|
|
au8BufOut[1] = 0;
|
|
}
|
|
|
|
ret = funcube_hid_cmd(rig, au8BufOut, au8BufIn, sizeof(au8BufIn));
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
au8BufOut[0] = REQUEST_SET_MIXER_GAIN; // Set mixer gain
|
|
|
|
if (val.i == 20 || val.i == 30)
|
|
{
|
|
au8BufOut[1] = 1;
|
|
}
|
|
else
|
|
{
|
|
au8BufOut[1] = 0;
|
|
}
|
|
|
|
return funcube_hid_cmd(rig, au8BufOut, au8BufIn, sizeof(au8BufIn));
|
|
|
|
case RIG_LEVEL_RF:
|
|
au8BufOut[0] = REQUEST_SET_IF_GAIN; // Command to set IF gain
|
|
au8BufOut[1] = (int)(val.f * 100) ;
|
|
|
|
if (au8BufOut[1] > 59)
|
|
{
|
|
au8BufOut[1] = 59;
|
|
}
|
|
|
|
return funcube_hid_cmd(rig, au8BufOut, au8BufIn, sizeof(au8BufIn));
|
|
|
|
default:
|
|
rig_debug(RIG_DEBUG_ERR, "%s: unsupported level %s\n", __func__,
|
|
rig_strlevel(level));
|
|
return -RIG_EINVAL;
|
|
}
|
|
}
|
|
|
|
int funcubepro_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
|
|
{
|
|
int ret;
|
|
int gain_state;
|
|
unsigned char au8BufOut[64] = { 0 }; // endpoint size
|
|
unsigned char au8BufIn[64] = { 0 }; // endpoint size
|
|
|
|
switch (level)
|
|
{
|
|
|
|
case RIG_LEVEL_PREAMP:
|
|
au8BufOut[0] = REQUEST_GET_MIXER_GAIN; // Command to get mixer gain enabled
|
|
ret = funcube_hid_cmd(rig, au8BufOut, au8BufIn, sizeof(au8BufIn));
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Mixer gain state returned %d.\n",
|
|
__func__, au8BufIn[2] & 0xFF);
|
|
|
|
gain_state = au8BufIn[2] & 0x1;
|
|
|
|
au8BufOut[0] = REQUEST_GET_LNA_GAIN; // Command to get LNA gain enabled
|
|
|
|
ret = funcube_hid_cmd(rig, au8BufOut, au8BufIn, sizeof(au8BufIn));
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: LNA gain state returned %d.\n",
|
|
__func__, au8BufIn[2] & 0xFF);
|
|
|
|
//Mixer gain is 20dB 0x2
|
|
gain_state *= 2;
|
|
|
|
//Add the LNA gain if present (10dB) 0x1
|
|
gain_state += (au8BufIn[2] & 0x1);
|
|
|
|
//Scale it to tens 1->10dB 2->20dB 3->30dB
|
|
gain_state *= 10;
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: Calculated gain state is %d.\n",
|
|
__func__, gain_state);
|
|
|
|
if (gain_state > 30 || gain_state < 0 || gain_state % 10 != 0)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: unrecognized composite gain: %d\n", __func__,
|
|
gain_state);
|
|
return -RIG_EINVAL;
|
|
}
|
|
|
|
val->i = gain_state;
|
|
|
|
return RIG_OK;
|
|
|
|
case RIG_LEVEL_RF:
|
|
au8BufOut[0] = REQUEST_GET_IF_GAIN;
|
|
ret = funcube_hid_cmd(rig, au8BufOut, au8BufIn, sizeof(au8BufIn));
|
|
val->f = ((float)au8BufIn[2]) / 100.;
|
|
return ret;
|
|
|
|
default:
|
|
rig_debug(RIG_DEBUG_ERR, "%s: unsupported level %s\n", __func__,
|
|
rig_strlevel(level));
|
|
return -RIG_EINVAL;
|
|
}
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
static int funcube_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode,
|
|
pbwidth_t *width)
|
|
{
|
|
if (rig->caps->rig_model == RIG_MODEL_FUNCUBEDONGLE)
|
|
{
|
|
*mode = RIG_MODE_USB;
|
|
}
|
|
else
|
|
{
|
|
*mode = RIG_MODE_IQ;
|
|
}
|
|
|
|
*width = 192000;
|
|
return RIG_OK;
|
|
}
|
|
|
|
#endif /* defined(HAVE_LIBUSB) */
|