From c426e94093ef4b30620f88236077706abe4b6832 Mon Sep 17 00:00:00 2001 From: Steve Markgraf Date: Thu, 17 May 2012 21:33:53 +0200 Subject: rename rtl-sdr.c to librtlsdr.c Having rtl_sdr.c for the application and rtl-sdr.c for the lib caused a bit of confusion... Signed-off-by: Steve Markgraf --- src/CMakeLists.txt | 4 +- src/Makefile.am | 2 +- src/librtlsdr.c | 1153 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rtl-sdr.c | 1153 ---------------------------------------------------- 4 files changed, 1156 insertions(+), 1156 deletions(-) create mode 100644 src/librtlsdr.c delete mode 100644 src/rtl-sdr.c (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0385e1f..8ffdd85 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,7 +21,7 @@ # Setup library ######################################################################## add_library(rtlsdr_shared SHARED - rtl-sdr.c + librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c @@ -37,7 +37,7 @@ set_target_properties(rtlsdr_shared PROPERTIES OUTPUT_NAME rtlsdr) set_target_properties(rtlsdr_shared PROPERTIES SOVERSION 0 VERSION 0.0.0) add_library(rtlsdr_static STATIC - rtl-sdr.c + librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c diff --git a/src/Makefile.am b/src/Makefile.am index 330f345..c44d869 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,7 @@ AM_CFLAGS = -fPIC -Wall lib_LTLIBRARIES = librtlsdr.la -librtlsdr_la_SOURCES = rtl-sdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c +librtlsdr_la_SOURCES = librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c librtlsdr_la_LDFLAGS = -version-info $(LIBVERSION) bin_PROGRAMS = rtl_sdr rtl_tcp diff --git a/src/librtlsdr.c b/src/librtlsdr.c new file mode 100644 index 0000000..6a9539b --- /dev/null +++ b/src/librtlsdr.c @@ -0,0 +1,1153 @@ +/* + * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver + * Copyright (C) 2012 by Steve Markgraf + * Copyright (C) 2012 by Dimitri Stolnikov + * + * 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. + * + * 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 program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#include + +/* + * All libusb callback functions should be marked with the LIBUSB_CALL macro + * to ensure that they are compiled with the same calling convention as libusb. + * + * If the macro isn't available in older libusb versions, we simply define it. + */ +#ifndef LIBUSB_CALL +#define LIBUSB_CALL +#endif + +#include "rtl-sdr.h" +#include "tuner_e4k.h" +#include "tuner_fc0012.h" +#include "tuner_fc0013.h" +#include "tuner_fc2580.h" + +typedef struct rtlsdr_tuner { + /* tuner interface */ + int (*init)(void *); + int (*exit)(void *); + int (*set_freq)(void *, uint32_t freq /* Hz */); + int (*set_bw)(void *, int bw /* Hz */); + int (*set_gain)(void *, int gain /* dB */); + int (*set_gain_mode)(void *, int manual); +} rtlsdr_tuner_t; + +enum rtlsdr_async_status { + RTLSDR_INACTIVE = 0, + RTLSDR_CANCELING, + RTLSDR_RUNNING +}; + +struct rtlsdr_dev { + libusb_context *ctx; + struct libusb_device_handle *devh; + uint32_t xfer_buf_num; + uint32_t xfer_buf_len; + struct libusb_transfer **xfer; + unsigned char **xfer_buf; + rtlsdr_read_async_cb_t cb; + void *cb_ctx; + enum rtlsdr_async_status async_status; + /* rtl demod context */ + uint32_t rate; /* Hz */ + uint32_t rtl_xtal; /* Hz */ + /* tuner context */ + rtlsdr_tuner_t *tuner; + uint32_t tun_xtal; /* Hz */ + uint32_t freq; /* Hz */ + int corr; /* ppm */ + int gain; /* dB */ + struct e4k_state e4k_s; +}; + +void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); + +/* generic tuner interface functions, shall be moved to the tuner implementations */ +int e4000_init(void *dev) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + devt->e4k_s.i2c_addr = E4K_I2C_ADDR; + devt->e4k_s.vco.fosc = devt->tun_xtal; + devt->e4k_s.rtl_dev = dev; + return e4k_init(&devt->e4k_s); +} +int e4000_exit(void *dev) { return 0; } +int e4000_set_freq(void *dev, uint32_t freq) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return e4k_tune_freq(&devt->e4k_s, freq); +} +int e4000_set_bw(void *dev, int bw) { + return 0; +} +int e4000_set_gain(void *dev, int gain) { + int rc; + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + int mixgain = (gain > 340) ? 12 : 4; + int enhgain = (gain - 420); + if(e4k_set_lna_gain(&devt->e4k_s, min(300, gain - 40)) == -EINVAL) + return -1; + if(e4k_mixer_gain_set(&devt->e4k_s, mixgain) == -EINVAL) + return -1; + if(enhgain >= 0) + if(e4k_set_enh_gain(&devt->e4k_s, enhgain) == -EINVAL) + return -1; + + return 0; +} + +int e4000_set_gain_mode(void *dev, int manual) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + e4k_enable_manual_gain(&devt->e4k_s, manual); + return 0; +} + +int fc0012_init(void *dev) { return FC0012_Open(dev); } +int fc0012_exit(void *dev) { return 0; } +int fc0012_set_freq(void *dev, uint32_t freq) { + /* select V-band/U-band filter */ + rtlsdr_set_gpio_bit(dev, 6, (freq > 300000000) ? 1 : 0); + return FC0012_SetFrequency(dev, freq/1000, 6); +} +int fc0012_set_bw(void *dev, int bw) { + return FC0012_SetFrequency(dev, ((rtlsdr_dev_t *) dev)->freq/1000, 6); +} +int fc0012_set_gain(void *dev, int gain) { return 0; } +int fc0012_set_gain_mode(void *dev, int manual) { return 0; } + +int fc0013_init(void *dev) { return FC0013_Open(dev); } +int fc0013_exit(void *dev) { return 0; } +int fc0013_set_freq(void *dev, uint32_t freq) { + return FC0013_SetFrequency(dev, freq/1000, 6); +} +int fc0013_set_bw(void *dev, int bw) { + return FC0013_SetFrequency(dev, ((rtlsdr_dev_t *) dev)->freq/1000, 6); +} +int fc0013_set_gain(void *dev, int gain) { return 0; } +int fc0013_set_gain_mode(void *dev, int manual) { return 0; } + +int fc2580_init(void *dev) { return fc2580_Initialize(dev); } +int fc2580_exit(void *dev) { return 0; } +int _fc2580_set_freq(void *dev, uint32_t freq) { + return fc2580_SetRfFreqHz(dev, freq); +} +int fc2580_set_bw(void *dev, int bw) { + return fc2580_SetBandwidthMode(dev, 1); +} +int fc2580_set_gain(void *dev, int gain) { return 0; } +int fc2580_set_gain_mode(void *dev, int manual) { return 0; } + +enum rtlsdr_tuners { + RTLSDR_TUNER_E4000, + RTLSDR_TUNER_FC0012, + RTLSDR_TUNER_FC0013, + RTLSDR_TUNER_FC2580 +}; + +static rtlsdr_tuner_t tuners[] = { + { + e4000_init, e4000_exit, + e4000_set_freq, e4000_set_bw, e4000_set_gain, + e4000_set_gain_mode + }, + { + fc0012_init, fc0012_exit, + fc0012_set_freq, fc0012_set_bw, fc0012_set_gain, + fc0012_set_gain_mode + }, + { + fc0013_init, fc0013_exit, + fc0013_set_freq, fc0013_set_bw, fc0013_set_gain, + fc0013_set_gain_mode + }, + { + fc2580_init, fc2580_exit, + _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, + fc2580_set_gain_mode + }, +}; + +typedef struct rtlsdr_dongle { + uint16_t vid; + uint16_t pid; + const char *name; +} rtlsdr_dongle_t; + +/* + * Please add your device here and send a patch to osmocom-sdr@lists.osmocom.org + */ +static rtlsdr_dongle_t known_devices[] = { + { 0x0bda, 0x2832, "Generic RTL2832U (e.g. hama nano)" }, + { 0x0bda, 0x2838, "ezcap USB 2.0 DVB-T/DAB/FM dongle" }, + { 0x0ccd, 0x00a9, "Terratec Cinergy T Stick Black (rev 1)" }, + { 0x0ccd, 0x00b3, "Terratec NOXON DAB/DAB+ USB dongle (rev 1)" }, + { 0x0ccd, 0x00d3, "Terratec Cinergy T Stick RC (Rev.3)" }, + { 0x0ccd, 0x00d7, "Terratec T Stick PLUS" }, + { 0x0ccd, 0x00e0, "Terratec NOXON DAB/DAB+ USB dongle (rev 2)" }, + { 0x185b, 0x0620, "Compro Videomate U620F"}, + { 0x185b, 0x0650, "Compro Videomate U650F"}, + { 0x185b, 0x0680, "Compro Videomate U680F"}, + { 0x1f4d, 0xb803, "GTek T803" }, + { 0x1f4d, 0xc803, "Lifeview LV5TDeluxe" }, + { 0x1b80, 0xd3a4, "Twintech UT-40" }, + { 0x1d19, 0x1101, "Dexatek DK DVB-T Dongle (Logilink VG0002A)" }, + { 0x1d19, 0x1102, "Dexatek DK DVB-T Dongle (MSI DigiVox mini II V3.0)" }, + { 0x1d19, 0x1103, "Dexatek Technology Ltd. DK 5217 DVB-T Dongle" }, + { 0x0458, 0x707f, "Genius TVGo DVB-T03 USB dongle (Ver. B)" }, + { 0x1b80, 0xd393, "GIGABYTE GT-U7300" }, + { 0x1b80, 0xd394, "DIKOM USB-DVBT HD" }, + { 0x1b80, 0xd395, "Peak 102569AGPK" }, + { 0x1b80, 0xd39d, "SVEON STV20 DVB-T USB & FM" }, +}; + +#define DEFAULT_BUF_NUMBER 32 +#define DEFAULT_BUF_LENGTH (16 * 32 * 512) + +#define DEF_RTL_XTAL_FREQ 28800000 +#define MIN_RTL_XTAL_FREQ (DEF_RTL_XTAL_FREQ - 1000) +#define MAX_RTL_XTAL_FREQ (DEF_RTL_XTAL_FREQ + 1000) + +#define MAX_SAMP_RATE 3200000 + +#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) +#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) +#define CTRL_TIMEOUT 300 +#define BULK_TIMEOUT 0 + +enum usb_reg { + USB_SYSCTL = 0x2000, + USB_CTRL = 0x2010, + USB_STAT = 0x2014, + USB_EPA_CFG = 0x2144, + USB_EPA_CTL = 0x2148, + USB_EPA_MAXPKT = 0x2158, + USB_EPA_MAXPKT_2 = 0x215a, + USB_EPA_FIFO_CFG = 0x2160, +}; + +enum sys_reg { + DEMOD_CTL = 0x3000, + GPO = 0x3001, + GPI = 0x3002, + GPOE = 0x3003, + GPD = 0x3004, + SYSINTE = 0x3005, + SYSINTS = 0x3006, + GP_CFG0 = 0x3007, + GP_CFG1 = 0x3008, + SYSINTE_1 = 0x3009, + SYSINTS_1 = 0x300a, + DEMOD_CTL_1 = 0x300b, + IR_SUSPEND = 0x300c, +}; + +enum blocks { + DEMODB = 0, + USBB = 1, + SYSB = 2, + TUNB = 3, + ROMB = 4, + IRB = 5, + IICB = 6, +}; + +int rtlsdr_read_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *array, uint8_t len) +{ + int r; + uint16_t index = (block << 8); + + r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, array, len, CTRL_TIMEOUT); + + return r; +} + +int rtlsdr_write_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *array, uint8_t len) +{ + int r; + uint16_t index = (block << 8) | 0x10; + + r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, array, len, CTRL_TIMEOUT); + + return r; +} + +int rtlsdr_i2c_write_reg(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t reg, uint8_t val) +{ + uint16_t addr = i2c_addr; + uint8_t data[2]; + + data[0] = reg; + data[1] = val; + return rtlsdr_write_array(dev, IICB, addr, (uint8_t *)&data, 2); +} + +uint8_t rtlsdr_i2c_read_reg(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t reg) +{ + uint16_t addr = i2c_addr; + uint8_t data; + + rtlsdr_write_array(dev, IICB, addr, ®, 1); + rtlsdr_read_array(dev, IICB, addr, &data, 1); + + return data; +} + +/* TODO clean this up again */ +int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val) +{ + return rtlsdr_i2c_write_reg((rtlsdr_dev_t*)e4k->rtl_dev, e4k->i2c_addr, reg, val);} + +uint8_t e4k_reg_read(struct e4k_state *e4k, uint8_t reg) +{ + return rtlsdr_i2c_read_reg((rtlsdr_dev_t*)e4k->rtl_dev, e4k->i2c_addr, reg); +} + +int rtlsdr_i2c_write(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int len) +{ + uint16_t addr = i2c_addr; + + if (!dev) + return -1; + + return rtlsdr_write_array(dev, IICB, addr, buffer, len); +} + +int rtlsdr_i2c_read(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int len) +{ + uint16_t addr = i2c_addr; + + if (!dev) + return -1; + + return rtlsdr_read_array(dev, IICB, addr, buffer, len); +} + +uint16_t rtlsdr_read_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t len) +{ + int r; + unsigned char data[2]; + uint16_t index = (block << 8); + uint16_t reg; + + r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); + + if (r < 0) + fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); + + reg = (data[1] << 8) | data[0]; + + return reg; +} + +void rtlsdr_write_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint16_t val, uint8_t len) +{ + int r; + unsigned char data[2]; + + uint16_t index = (block << 8) | 0x10; + + if (len == 1) + data[0] = val & 0xff; + else + data[0] = val >> 8; + + data[1] = val & 0xff; + + r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); + + if (r < 0) + fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); +} + +uint16_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint8_t len) +{ + int r; + unsigned char data[2]; + + uint16_t index = page; + uint16_t reg; + addr = (addr << 8) | 0x20; + + r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); + + if (r < 0) + fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); + + reg = (data[1] << 8) | data[0]; + + return reg; +} + +void rtlsdr_demod_write_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint16_t val, uint8_t len) +{ + int r; + unsigned char data[2]; + uint16_t index = 0x10 | page; + addr = (addr << 8) | 0x20; + + if (len == 1) + data[0] = val & 0xff; + else + data[0] = val >> 8; + + data[1] = val & 0xff; + + r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); + + if (r < 0) + fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); + + rtlsdr_demod_read_reg(dev, 0x0a, 0x01, 1); +} + +void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val) +{ + uint8_t r; + + gpio = 1 << gpio; + r = rtlsdr_read_reg(dev, SYSB, GPO, 1); + r = val ? (r | gpio) : (r & ~gpio); + rtlsdr_write_reg(dev, SYSB, GPO, r, 1); +} + +void rtlsdr_set_gpio_output(rtlsdr_dev_t *dev, uint8_t gpio) +{ + int r; + gpio = 1 << gpio; + + r = rtlsdr_read_reg(dev, SYSB, GPD, 1); + rtlsdr_write_reg(dev, SYSB, GPO, r & ~gpio, 1); + r = rtlsdr_read_reg(dev, SYSB, GPOE, 1); + rtlsdr_write_reg(dev, SYSB, GPOE, r | gpio, 1); +} + +void rtlsdr_set_i2c_repeater(rtlsdr_dev_t *dev, int on) +{ + rtlsdr_demod_write_reg(dev, 1, 0x01, on ? 0x18 : 0x10, 1); +} + +void rtlsdr_init_baseband(rtlsdr_dev_t *dev) +{ + unsigned int i; + + /* default FIR coefficients used for DAB/FM by the Windows driver, + * the DVB driver uses different ones */ + uint8_t fir_coeff[] = { + 0xca, 0xdc, 0xd7, 0xd8, 0xe0, 0xf2, 0x0e, 0x35, 0x06, 0x50, + 0x9c, 0x0d, 0x71, 0x11, 0x14, 0x71, 0x74, 0x19, 0x41, 0xa5, + }; + + /* initialize USB */ + rtlsdr_write_reg(dev, USBB, USB_SYSCTL, 0x09, 1); + rtlsdr_write_reg(dev, USBB, USB_EPA_MAXPKT, 0x0002, 2); + rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x1002, 2); + + /* poweron demod */ + rtlsdr_write_reg(dev, SYSB, DEMOD_CTL_1, 0x22, 1); + rtlsdr_write_reg(dev, SYSB, DEMOD_CTL, 0xe8, 1); + + /* reset demod (bit 3, soft_rst) */ + rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); + rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); + + /* disable spectrum inversion and adjacent channel rejection */ + rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); + rtlsdr_demod_write_reg(dev, 1, 0x16, 0x0000, 2); + + /* set IF-frequency to 0 Hz */ + rtlsdr_demod_write_reg(dev, 1, 0x19, 0x0000, 2); + + /* set FIR coefficients */ + for (i = 0; i < sizeof (fir_coeff); i++) + rtlsdr_demod_write_reg(dev, 1, 0x1c + i, fir_coeff[i], 1); + + rtlsdr_demod_write_reg(dev, 0, 0x19, 0x25, 1); + + /* init FSM state-holding register */ + rtlsdr_demod_write_reg(dev, 1, 0x93, 0xf0, 1); + + /* disable AGC (en_dagc, bit 0) */ + rtlsdr_demod_write_reg(dev, 1, 0x11, 0x00, 1); + + /* disable PID filter (enable_PID = 0) */ + rtlsdr_demod_write_reg(dev, 0, 0x61, 0x60, 1); + + /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ + rtlsdr_demod_write_reg(dev, 0, 0x06, 0x80, 1); + + /* Enable Zero-IF mode (en_bbin bit), DC cancellation (en_dc_est), + * IQ estimation/compensation (en_iq_comp, en_iq_est) */ + rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1b, 1); +} + +int rtlsdr_deinit_baseband(rtlsdr_dev_t *dev) +{ + int r = 0; + + if (!dev) + return -1; + + if (dev->tuner && dev->tuner->exit) { + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->exit(dev); /* deinitialize tuner */ + rtlsdr_set_i2c_repeater(dev, 0); + } + + /* poweroff demodulator and ADCs */ + rtlsdr_write_reg(dev, SYSB, DEMOD_CTL, 0x20, 1); + + return r; +} + +int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_freq) +{ + int r = 0; + + if (!dev) + return -1; + + if (rtl_freq > 0 && + (rtl_freq < MIN_RTL_XTAL_FREQ || rtl_freq > MAX_RTL_XTAL_FREQ)) + return -2; + + if (dev->rtl_xtal != rtl_freq) { + if (0 == rtl_freq) + rtl_freq = DEF_RTL_XTAL_FREQ; + + dev->rtl_xtal = rtl_freq; + + /* update xtal-dependent settings */ + if (dev->rate) + r = rtlsdr_set_sample_rate(dev, dev->rate); + } + + if (dev->tun_xtal != tuner_freq) { + if (0 == tuner_freq) + tuner_freq = dev->rtl_xtal; + + dev->tun_xtal = tuner_freq; + + /* update xtal-dependent settings */ + if (dev->freq) + r = rtlsdr_set_center_freq(dev, dev->freq); + } + + return r; +} + +int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_freq) +{ + if (!dev) + return -1; + + *rtl_freq = dev->rtl_xtal; + + if (!dev->tuner) + return -2; + + *tuner_freq = dev->tun_xtal; + + return 0; +} + +int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) +{ + int r = -1; + double f = (double) freq; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_freq) { + f *= 1.0 + dev->corr / 1e6; + + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->set_freq(dev, (uint32_t) f); + rtlsdr_set_i2c_repeater(dev, 0); + + if (!r) + dev->freq = freq; + else + dev->freq = 0; + } + + return r; +} + +uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) +{ + if (!dev || !dev->tuner) + return 0; + + return dev->freq; +} + +int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) +{ + int r; + + if (!dev || !dev->tuner) + return -1; + + if (dev->corr == ppm) + return -1; + + dev->corr = ppm; + + /* retune to apply new correction value */ + r = rtlsdr_set_center_freq(dev, dev->freq); + + return r; +} + +int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev) +{ + if (!dev || !dev->tuner) + return 0; + + return dev->corr; +} + +int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) +{ + int r = 0; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_gain) { + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->set_gain((void *)dev, gain); + rtlsdr_set_i2c_repeater(dev, 0); + } + + if (!r) + dev->gain = gain; + else + dev->gain = 0; + + return r; +} + +int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) +{ + if (!dev || !dev->tuner) + return 0; + + return dev->gain; +} + +int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) +{ + int r = 0; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_gain_mode) { + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->set_gain_mode((void *)dev, mode); + rtlsdr_set_i2c_repeater(dev, 0); + } + + return r; +} + +int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) +{ + uint16_t tmp; + uint32_t rsamp_ratio; + double real_rate; + + if (!dev) + return -1; + + /* check for the maximum rate the resampler supports */ + if (samp_rate > MAX_SAMP_RATE) + samp_rate = MAX_SAMP_RATE; + + rsamp_ratio = (dev->rtl_xtal * pow(2, 22)) / samp_rate; + rsamp_ratio &= ~3; + + real_rate = (dev->rtl_xtal * pow(2, 22)) / rsamp_ratio; + + if ( ((double)samp_rate) != real_rate ) + fprintf(stderr, "Exact sample rate is: %f Hz\n", real_rate); + + if (dev->tuner && dev->tuner->set_bw) { + rtlsdr_set_i2c_repeater(dev, 1); + dev->tuner->set_bw(dev, real_rate); + rtlsdr_set_i2c_repeater(dev, 0); + } + + dev->rate = samp_rate; + + tmp = (rsamp_ratio >> 16); + rtlsdr_demod_write_reg(dev, 1, 0x9f, tmp, 2); + tmp = rsamp_ratio & 0xffff; + rtlsdr_demod_write_reg(dev, 1, 0xa1, tmp, 2); + + /* reset demod (bit 3, soft_rst) */ + rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); + rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); + + return 0; +} + +uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) +{ + if (!dev) + return 0; + + return dev->rate; +} + +rtlsdr_dongle_t *find_known_device(uint16_t vid, uint16_t pid) +{ + unsigned int i; + rtlsdr_dongle_t *device = NULL; + + for (i = 0; i < sizeof(known_devices)/sizeof(rtlsdr_dongle_t); i++ ) { + if (known_devices[i].vid == vid && known_devices[i].pid == pid) { + device = &known_devices[i]; + break; + } + } + + return device; +} + +uint32_t rtlsdr_get_device_count(void) +{ + int i; + libusb_context *ctx; + libusb_device **list; + uint32_t device_count = 0; + struct libusb_device_descriptor dd; + ssize_t cnt; + + libusb_init(&ctx); + + cnt = libusb_get_device_list(ctx, &list); + + for (i = 0; i < cnt; i++) { + libusb_get_device_descriptor(list[i], &dd); + + if (find_known_device(dd.idVendor, dd.idProduct)) + device_count++; + } + + libusb_free_device_list(list, 1); + + libusb_exit(ctx); + + return device_count; +} + +const char *rtlsdr_get_device_name(uint32_t index) +{ + int i; + libusb_context *ctx; + libusb_device **list; + struct libusb_device_descriptor dd; + rtlsdr_dongle_t *device = NULL; + uint32_t device_count = 0; + ssize_t cnt; + + libusb_init(&ctx); + + cnt = libusb_get_device_list(ctx, &list); + + for (i = 0; i < cnt; i++) { + libusb_get_device_descriptor(list[i], &dd); + + device = find_known_device(dd.idVendor, dd.idProduct); + + if (device) { + device_count++; + + if (index == device_count - 1) + break; + } + } + + libusb_free_device_list(list, 1); + + libusb_exit(ctx); + + if (device) + return device->name; + else + return ""; +} + +int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) +{ + int r; + int i; + libusb_device **list; + rtlsdr_dev_t *dev = NULL; + libusb_device *device = NULL; + uint32_t device_count = 0; + struct libusb_device_descriptor dd; + uint8_t reg; + ssize_t cnt; + + dev = malloc(sizeof(rtlsdr_dev_t)); + if (NULL == dev) + return -ENOMEM; + + memset(dev, 0, sizeof(rtlsdr_dev_t)); + + libusb_init(&dev->ctx); + + cnt = libusb_get_device_list(dev->ctx, &list); + + for (i = 0; i < cnt; i++) { + device = list[i]; + + libusb_get_device_descriptor(list[i], &dd); + + if (find_known_device(dd.idVendor, dd.idProduct)) { + device_count++; + } + + if (index == device_count - 1) + break; + + device = NULL; + } + + if (!device) { + r = -1; + goto err; + } + + r = libusb_open(device, &dev->devh); + if (r < 0) { + libusb_free_device_list(list, 1); + fprintf(stderr, "usb_open error %d\n", r); + goto err; + } + + libusb_free_device_list(list, 1); + + r = libusb_claim_interface(dev->devh, 0); + if (r < 0) { + fprintf(stderr, "usb_claim_interface error %d\n", r); + goto err; + } + + dev->rtl_xtal = DEF_RTL_XTAL_FREQ; + + rtlsdr_init_baseband(dev); + + /* Probe tuners */ + rtlsdr_set_i2c_repeater(dev, 1); + + reg = rtlsdr_i2c_read_reg(dev, E4K_I2C_ADDR, E4K_CHECK_ADDR); + if (reg == E4K_CHECK_VAL) { + fprintf(stderr, "Found Elonics E4000 tuner\n"); + dev->tuner = &tuners[RTLSDR_TUNER_E4000]; + goto found; + } + + reg = rtlsdr_i2c_read_reg(dev, FC0013_I2C_ADDR, FC0013_CHECK_ADDR); + if (reg == FC0013_CHECK_VAL) { + fprintf(stderr, "Found Fitipower FC0013 tuner\n"); + dev->tuner = &tuners[RTLSDR_TUNER_FC0013]; + goto found; + } + + /* initialise GPIOs */ + rtlsdr_set_gpio_output(dev, 5); + + /* reset tuner before probing */ + rtlsdr_set_gpio_bit(dev, 5, 1); + rtlsdr_set_gpio_bit(dev, 5, 0); + + reg = rtlsdr_i2c_read_reg(dev, FC2580_I2C_ADDR, FC2580_CHECK_ADDR); + if ((reg & 0x7f) == FC2580_CHECK_VAL) { + fprintf(stderr, "Found FCI 2580 tuner\n"); + dev->tuner = &tuners[RTLSDR_TUNER_FC2580]; + goto found; + } + + reg = rtlsdr_i2c_read_reg(dev, FC0012_I2C_ADDR, FC0012_CHECK_ADDR); + if (reg == FC0012_CHECK_VAL) { + fprintf(stderr, "Found Fitipower FC0012 tuner\n"); + rtlsdr_set_gpio_output(dev, 6); + dev->tuner = &tuners[RTLSDR_TUNER_FC0012]; + goto found; + } + +found: + if (dev->tuner) { + dev->tun_xtal = dev->rtl_xtal; + + if (dev->tuner->init) + r = dev->tuner->init(dev); + } + + rtlsdr_set_i2c_repeater(dev, 0); + + *out_dev = dev; + + return 0; +err: + if (dev) { + if (dev->ctx) + libusb_exit(dev->ctx); + + free(dev); + } + + return r; +} + +int rtlsdr_close(rtlsdr_dev_t *dev) +{ + if (!dev) + return -1; + + rtlsdr_deinit_baseband(dev); + + libusb_release_interface(dev->devh, 0); + libusb_close(dev->devh); + + libusb_exit(dev->ctx); + + free(dev); + + return 0; +} + +int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) +{ + if (!dev) + return -1; + + rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x1002, 2); + rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x0000, 2); + + return 0; +} + +int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read) +{ + if (!dev) + return -1; + + return libusb_bulk_transfer(dev->devh, 0x81, buf, len, n_read, BULK_TIMEOUT); +} + +static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) +{ + rtlsdr_dev_t *dev = (rtlsdr_dev_t *)xfer->user_data; + + if (LIBUSB_TRANSFER_COMPLETED == xfer->status) { + if (dev->cb) + dev->cb(xfer->buffer, xfer->actual_length, dev->cb_ctx); + + libusb_submit_transfer(xfer); /* resubmit transfer */ + } else { + /*fprintf(stderr, "transfer status: %d\n", xfer->status);*/ + rtlsdr_cancel_async(dev); /* abort async loop */ + } +} + +int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx) +{ + return rtlsdr_read_async(dev, cb, ctx, 0, 0); +} + +static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) +{ + unsigned int i; + + if (!dev) + return -1; + + if (!dev->xfer) { + dev->xfer = malloc(dev->xfer_buf_num * + sizeof(struct libusb_transfer *)); + + for(i = 0; i < dev->xfer_buf_num; ++i) + dev->xfer[i] = libusb_alloc_transfer(0); + } + + if (!dev->xfer_buf) { + dev->xfer_buf = malloc(dev->xfer_buf_num * + sizeof(unsigned char *)); + + for(i = 0; i < dev->xfer_buf_num; ++i) + dev->xfer_buf[i] = malloc(dev->xfer_buf_len); + } + + return 0; +} + +static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev) +{ + unsigned int i; + + if (!dev) + return -1; + + if (dev->xfer) { + for(i = 0; i < dev->xfer_buf_num; ++i) { + if (dev->xfer[i]) { + libusb_free_transfer(dev->xfer[i]); + } + } + + free(dev->xfer); + dev->xfer = NULL; + } + + if (dev->xfer_buf) { + for(i = 0; i < dev->xfer_buf_num; ++i) { + if (dev->xfer_buf[i]) + free(dev->xfer_buf[i]); + } + + free(dev->xfer_buf); + dev->xfer_buf = NULL; + } + + return 0; +} + +int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, + uint32_t buf_num, uint32_t buf_len) +{ + unsigned int i; + int r; + struct timeval tv = { 1, 0 }; + + if (!dev) + return -1; + + dev->cb = cb; + dev->cb_ctx = ctx; + + if (buf_num > 0) + dev->xfer_buf_num = buf_num; + else + dev->xfer_buf_num = DEFAULT_BUF_NUMBER; + + if (buf_len > 0 && buf_len % 512 == 0) /* len must be multiple of 512 */ + dev->xfer_buf_len = buf_len; + else + dev->xfer_buf_len = DEFAULT_BUF_LENGTH; + + _rtlsdr_alloc_async_buffers(dev); + + for(i = 0; i < dev->xfer_buf_num; ++i) { + libusb_fill_bulk_transfer(dev->xfer[i], + dev->devh, + 0x81, + dev->xfer_buf[i], + dev->xfer_buf_len, + _libusb_callback, + (void *)dev, + BULK_TIMEOUT); + + libusb_submit_transfer(dev->xfer[i]); + } + + dev->async_status = RTLSDR_RUNNING; + + while (RTLSDR_INACTIVE != dev->async_status) { + r = libusb_handle_events_timeout(dev->ctx, &tv); + if (r < 0) { + /*fprintf(stderr, "handle_events returned: %d\n", r);*/ + if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */ + continue; + break; + } + + if (RTLSDR_CANCELING == dev->async_status) { + dev->async_status = RTLSDR_INACTIVE; + + if (!dev->xfer) + break; + + for(i = 0; i < dev->xfer_buf_num; ++i) { + if (!dev->xfer[i]) + continue; + + if (dev->xfer[i]->status == LIBUSB_TRANSFER_COMPLETED) { + libusb_cancel_transfer(dev->xfer[i]); + dev->async_status = RTLSDR_CANCELING; + } + } + + if (RTLSDR_INACTIVE == dev->async_status) + break; + } + } + + _rtlsdr_free_async_buffers(dev); + + return r; +} + +int rtlsdr_cancel_async(rtlsdr_dev_t *dev) +{ + if (!dev) + return -1; + + if (RTLSDR_RUNNING == dev->async_status) { + dev->async_status = RTLSDR_CANCELING; + return 0; + } + + return -2; +} + +uint32_t rtlsdr_get_tuner_clock(void *dev) +{ + if (!dev) + return 0; + + return ((rtlsdr_dev_t *)dev)->tun_xtal; +} + +int rtlsdr_i2c_write_fn(void *dev, uint8_t addr, uint8_t *buf, int len) +{ + if (dev) + return rtlsdr_i2c_write(((rtlsdr_dev_t *)dev), addr, buf, len); + + return -1; +} + +int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len) +{ + if (dev) + return rtlsdr_i2c_read(((rtlsdr_dev_t *)dev), addr, buf, len); + + return -1; +} diff --git a/src/rtl-sdr.c b/src/rtl-sdr.c deleted file mode 100644 index 6a9539b..0000000 --- a/src/rtl-sdr.c +++ /dev/null @@ -1,1153 +0,0 @@ -/* - * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver - * Copyright (C) 2012 by Steve Markgraf - * Copyright (C) 2012 by Dimitri Stolnikov - * - * 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. - * - * 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 program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#include - -/* - * All libusb callback functions should be marked with the LIBUSB_CALL macro - * to ensure that they are compiled with the same calling convention as libusb. - * - * If the macro isn't available in older libusb versions, we simply define it. - */ -#ifndef LIBUSB_CALL -#define LIBUSB_CALL -#endif - -#include "rtl-sdr.h" -#include "tuner_e4k.h" -#include "tuner_fc0012.h" -#include "tuner_fc0013.h" -#include "tuner_fc2580.h" - -typedef struct rtlsdr_tuner { - /* tuner interface */ - int (*init)(void *); - int (*exit)(void *); - int (*set_freq)(void *, uint32_t freq /* Hz */); - int (*set_bw)(void *, int bw /* Hz */); - int (*set_gain)(void *, int gain /* dB */); - int (*set_gain_mode)(void *, int manual); -} rtlsdr_tuner_t; - -enum rtlsdr_async_status { - RTLSDR_INACTIVE = 0, - RTLSDR_CANCELING, - RTLSDR_RUNNING -}; - -struct rtlsdr_dev { - libusb_context *ctx; - struct libusb_device_handle *devh; - uint32_t xfer_buf_num; - uint32_t xfer_buf_len; - struct libusb_transfer **xfer; - unsigned char **xfer_buf; - rtlsdr_read_async_cb_t cb; - void *cb_ctx; - enum rtlsdr_async_status async_status; - /* rtl demod context */ - uint32_t rate; /* Hz */ - uint32_t rtl_xtal; /* Hz */ - /* tuner context */ - rtlsdr_tuner_t *tuner; - uint32_t tun_xtal; /* Hz */ - uint32_t freq; /* Hz */ - int corr; /* ppm */ - int gain; /* dB */ - struct e4k_state e4k_s; -}; - -void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); - -/* generic tuner interface functions, shall be moved to the tuner implementations */ -int e4000_init(void *dev) { - rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - devt->e4k_s.i2c_addr = E4K_I2C_ADDR; - devt->e4k_s.vco.fosc = devt->tun_xtal; - devt->e4k_s.rtl_dev = dev; - return e4k_init(&devt->e4k_s); -} -int e4000_exit(void *dev) { return 0; } -int e4000_set_freq(void *dev, uint32_t freq) { - rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - return e4k_tune_freq(&devt->e4k_s, freq); -} -int e4000_set_bw(void *dev, int bw) { - return 0; -} -int e4000_set_gain(void *dev, int gain) { - int rc; - rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - int mixgain = (gain > 340) ? 12 : 4; - int enhgain = (gain - 420); - if(e4k_set_lna_gain(&devt->e4k_s, min(300, gain - 40)) == -EINVAL) - return -1; - if(e4k_mixer_gain_set(&devt->e4k_s, mixgain) == -EINVAL) - return -1; - if(enhgain >= 0) - if(e4k_set_enh_gain(&devt->e4k_s, enhgain) == -EINVAL) - return -1; - - return 0; -} - -int e4000_set_gain_mode(void *dev, int manual) { - rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - e4k_enable_manual_gain(&devt->e4k_s, manual); - return 0; -} - -int fc0012_init(void *dev) { return FC0012_Open(dev); } -int fc0012_exit(void *dev) { return 0; } -int fc0012_set_freq(void *dev, uint32_t freq) { - /* select V-band/U-band filter */ - rtlsdr_set_gpio_bit(dev, 6, (freq > 300000000) ? 1 : 0); - return FC0012_SetFrequency(dev, freq/1000, 6); -} -int fc0012_set_bw(void *dev, int bw) { - return FC0012_SetFrequency(dev, ((rtlsdr_dev_t *) dev)->freq/1000, 6); -} -int fc0012_set_gain(void *dev, int gain) { return 0; } -int fc0012_set_gain_mode(void *dev, int manual) { return 0; } - -int fc0013_init(void *dev) { return FC0013_Open(dev); } -int fc0013_exit(void *dev) { return 0; } -int fc0013_set_freq(void *dev, uint32_t freq) { - return FC0013_SetFrequency(dev, freq/1000, 6); -} -int fc0013_set_bw(void *dev, int bw) { - return FC0013_SetFrequency(dev, ((rtlsdr_dev_t *) dev)->freq/1000, 6); -} -int fc0013_set_gain(void *dev, int gain) { return 0; } -int fc0013_set_gain_mode(void *dev, int manual) { return 0; } - -int fc2580_init(void *dev) { return fc2580_Initialize(dev); } -int fc2580_exit(void *dev) { return 0; } -int _fc2580_set_freq(void *dev, uint32_t freq) { - return fc2580_SetRfFreqHz(dev, freq); -} -int fc2580_set_bw(void *dev, int bw) { - return fc2580_SetBandwidthMode(dev, 1); -} -int fc2580_set_gain(void *dev, int gain) { return 0; } -int fc2580_set_gain_mode(void *dev, int manual) { return 0; } - -enum rtlsdr_tuners { - RTLSDR_TUNER_E4000, - RTLSDR_TUNER_FC0012, - RTLSDR_TUNER_FC0013, - RTLSDR_TUNER_FC2580 -}; - -static rtlsdr_tuner_t tuners[] = { - { - e4000_init, e4000_exit, - e4000_set_freq, e4000_set_bw, e4000_set_gain, - e4000_set_gain_mode - }, - { - fc0012_init, fc0012_exit, - fc0012_set_freq, fc0012_set_bw, fc0012_set_gain, - fc0012_set_gain_mode - }, - { - fc0013_init, fc0013_exit, - fc0013_set_freq, fc0013_set_bw, fc0013_set_gain, - fc0013_set_gain_mode - }, - { - fc2580_init, fc2580_exit, - _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, - fc2580_set_gain_mode - }, -}; - -typedef struct rtlsdr_dongle { - uint16_t vid; - uint16_t pid; - const char *name; -} rtlsdr_dongle_t; - -/* - * Please add your device here and send a patch to osmocom-sdr@lists.osmocom.org - */ -static rtlsdr_dongle_t known_devices[] = { - { 0x0bda, 0x2832, "Generic RTL2832U (e.g. hama nano)" }, - { 0x0bda, 0x2838, "ezcap USB 2.0 DVB-T/DAB/FM dongle" }, - { 0x0ccd, 0x00a9, "Terratec Cinergy T Stick Black (rev 1)" }, - { 0x0ccd, 0x00b3, "Terratec NOXON DAB/DAB+ USB dongle (rev 1)" }, - { 0x0ccd, 0x00d3, "Terratec Cinergy T Stick RC (Rev.3)" }, - { 0x0ccd, 0x00d7, "Terratec T Stick PLUS" }, - { 0x0ccd, 0x00e0, "Terratec NOXON DAB/DAB+ USB dongle (rev 2)" }, - { 0x185b, 0x0620, "Compro Videomate U620F"}, - { 0x185b, 0x0650, "Compro Videomate U650F"}, - { 0x185b, 0x0680, "Compro Videomate U680F"}, - { 0x1f4d, 0xb803, "GTek T803" }, - { 0x1f4d, 0xc803, "Lifeview LV5TDeluxe" }, - { 0x1b80, 0xd3a4, "Twintech UT-40" }, - { 0x1d19, 0x1101, "Dexatek DK DVB-T Dongle (Logilink VG0002A)" }, - { 0x1d19, 0x1102, "Dexatek DK DVB-T Dongle (MSI DigiVox mini II V3.0)" }, - { 0x1d19, 0x1103, "Dexatek Technology Ltd. DK 5217 DVB-T Dongle" }, - { 0x0458, 0x707f, "Genius TVGo DVB-T03 USB dongle (Ver. B)" }, - { 0x1b80, 0xd393, "GIGABYTE GT-U7300" }, - { 0x1b80, 0xd394, "DIKOM USB-DVBT HD" }, - { 0x1b80, 0xd395, "Peak 102569AGPK" }, - { 0x1b80, 0xd39d, "SVEON STV20 DVB-T USB & FM" }, -}; - -#define DEFAULT_BUF_NUMBER 32 -#define DEFAULT_BUF_LENGTH (16 * 32 * 512) - -#define DEF_RTL_XTAL_FREQ 28800000 -#define MIN_RTL_XTAL_FREQ (DEF_RTL_XTAL_FREQ - 1000) -#define MAX_RTL_XTAL_FREQ (DEF_RTL_XTAL_FREQ + 1000) - -#define MAX_SAMP_RATE 3200000 - -#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) -#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) -#define CTRL_TIMEOUT 300 -#define BULK_TIMEOUT 0 - -enum usb_reg { - USB_SYSCTL = 0x2000, - USB_CTRL = 0x2010, - USB_STAT = 0x2014, - USB_EPA_CFG = 0x2144, - USB_EPA_CTL = 0x2148, - USB_EPA_MAXPKT = 0x2158, - USB_EPA_MAXPKT_2 = 0x215a, - USB_EPA_FIFO_CFG = 0x2160, -}; - -enum sys_reg { - DEMOD_CTL = 0x3000, - GPO = 0x3001, - GPI = 0x3002, - GPOE = 0x3003, - GPD = 0x3004, - SYSINTE = 0x3005, - SYSINTS = 0x3006, - GP_CFG0 = 0x3007, - GP_CFG1 = 0x3008, - SYSINTE_1 = 0x3009, - SYSINTS_1 = 0x300a, - DEMOD_CTL_1 = 0x300b, - IR_SUSPEND = 0x300c, -}; - -enum blocks { - DEMODB = 0, - USBB = 1, - SYSB = 2, - TUNB = 3, - ROMB = 4, - IRB = 5, - IICB = 6, -}; - -int rtlsdr_read_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *array, uint8_t len) -{ - int r; - uint16_t index = (block << 8); - - r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, array, len, CTRL_TIMEOUT); - - return r; -} - -int rtlsdr_write_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *array, uint8_t len) -{ - int r; - uint16_t index = (block << 8) | 0x10; - - r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, array, len, CTRL_TIMEOUT); - - return r; -} - -int rtlsdr_i2c_write_reg(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t reg, uint8_t val) -{ - uint16_t addr = i2c_addr; - uint8_t data[2]; - - data[0] = reg; - data[1] = val; - return rtlsdr_write_array(dev, IICB, addr, (uint8_t *)&data, 2); -} - -uint8_t rtlsdr_i2c_read_reg(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t reg) -{ - uint16_t addr = i2c_addr; - uint8_t data; - - rtlsdr_write_array(dev, IICB, addr, ®, 1); - rtlsdr_read_array(dev, IICB, addr, &data, 1); - - return data; -} - -/* TODO clean this up again */ -int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val) -{ - return rtlsdr_i2c_write_reg((rtlsdr_dev_t*)e4k->rtl_dev, e4k->i2c_addr, reg, val);} - -uint8_t e4k_reg_read(struct e4k_state *e4k, uint8_t reg) -{ - return rtlsdr_i2c_read_reg((rtlsdr_dev_t*)e4k->rtl_dev, e4k->i2c_addr, reg); -} - -int rtlsdr_i2c_write(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int len) -{ - uint16_t addr = i2c_addr; - - if (!dev) - return -1; - - return rtlsdr_write_array(dev, IICB, addr, buffer, len); -} - -int rtlsdr_i2c_read(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int len) -{ - uint16_t addr = i2c_addr; - - if (!dev) - return -1; - - return rtlsdr_read_array(dev, IICB, addr, buffer, len); -} - -uint16_t rtlsdr_read_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t len) -{ - int r; - unsigned char data[2]; - uint16_t index = (block << 8); - uint16_t reg; - - r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); - - if (r < 0) - fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); - - reg = (data[1] << 8) | data[0]; - - return reg; -} - -void rtlsdr_write_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint16_t val, uint8_t len) -{ - int r; - unsigned char data[2]; - - uint16_t index = (block << 8) | 0x10; - - if (len == 1) - data[0] = val & 0xff; - else - data[0] = val >> 8; - - data[1] = val & 0xff; - - r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); - - if (r < 0) - fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); -} - -uint16_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint8_t len) -{ - int r; - unsigned char data[2]; - - uint16_t index = page; - uint16_t reg; - addr = (addr << 8) | 0x20; - - r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); - - if (r < 0) - fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); - - reg = (data[1] << 8) | data[0]; - - return reg; -} - -void rtlsdr_demod_write_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint16_t val, uint8_t len) -{ - int r; - unsigned char data[2]; - uint16_t index = 0x10 | page; - addr = (addr << 8) | 0x20; - - if (len == 1) - data[0] = val & 0xff; - else - data[0] = val >> 8; - - data[1] = val & 0xff; - - r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); - - if (r < 0) - fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); - - rtlsdr_demod_read_reg(dev, 0x0a, 0x01, 1); -} - -void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val) -{ - uint8_t r; - - gpio = 1 << gpio; - r = rtlsdr_read_reg(dev, SYSB, GPO, 1); - r = val ? (r | gpio) : (r & ~gpio); - rtlsdr_write_reg(dev, SYSB, GPO, r, 1); -} - -void rtlsdr_set_gpio_output(rtlsdr_dev_t *dev, uint8_t gpio) -{ - int r; - gpio = 1 << gpio; - - r = rtlsdr_read_reg(dev, SYSB, GPD, 1); - rtlsdr_write_reg(dev, SYSB, GPO, r & ~gpio, 1); - r = rtlsdr_read_reg(dev, SYSB, GPOE, 1); - rtlsdr_write_reg(dev, SYSB, GPOE, r | gpio, 1); -} - -void rtlsdr_set_i2c_repeater(rtlsdr_dev_t *dev, int on) -{ - rtlsdr_demod_write_reg(dev, 1, 0x01, on ? 0x18 : 0x10, 1); -} - -void rtlsdr_init_baseband(rtlsdr_dev_t *dev) -{ - unsigned int i; - - /* default FIR coefficients used for DAB/FM by the Windows driver, - * the DVB driver uses different ones */ - uint8_t fir_coeff[] = { - 0xca, 0xdc, 0xd7, 0xd8, 0xe0, 0xf2, 0x0e, 0x35, 0x06, 0x50, - 0x9c, 0x0d, 0x71, 0x11, 0x14, 0x71, 0x74, 0x19, 0x41, 0xa5, - }; - - /* initialize USB */ - rtlsdr_write_reg(dev, USBB, USB_SYSCTL, 0x09, 1); - rtlsdr_write_reg(dev, USBB, USB_EPA_MAXPKT, 0x0002, 2); - rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x1002, 2); - - /* poweron demod */ - rtlsdr_write_reg(dev, SYSB, DEMOD_CTL_1, 0x22, 1); - rtlsdr_write_reg(dev, SYSB, DEMOD_CTL, 0xe8, 1); - - /* reset demod (bit 3, soft_rst) */ - rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); - rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); - - /* disable spectrum inversion and adjacent channel rejection */ - rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); - rtlsdr_demod_write_reg(dev, 1, 0x16, 0x0000, 2); - - /* set IF-frequency to 0 Hz */ - rtlsdr_demod_write_reg(dev, 1, 0x19, 0x0000, 2); - - /* set FIR coefficients */ - for (i = 0; i < sizeof (fir_coeff); i++) - rtlsdr_demod_write_reg(dev, 1, 0x1c + i, fir_coeff[i], 1); - - rtlsdr_demod_write_reg(dev, 0, 0x19, 0x25, 1); - - /* init FSM state-holding register */ - rtlsdr_demod_write_reg(dev, 1, 0x93, 0xf0, 1); - - /* disable AGC (en_dagc, bit 0) */ - rtlsdr_demod_write_reg(dev, 1, 0x11, 0x00, 1); - - /* disable PID filter (enable_PID = 0) */ - rtlsdr_demod_write_reg(dev, 0, 0x61, 0x60, 1); - - /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ - rtlsdr_demod_write_reg(dev, 0, 0x06, 0x80, 1); - - /* Enable Zero-IF mode (en_bbin bit), DC cancellation (en_dc_est), - * IQ estimation/compensation (en_iq_comp, en_iq_est) */ - rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1b, 1); -} - -int rtlsdr_deinit_baseband(rtlsdr_dev_t *dev) -{ - int r = 0; - - if (!dev) - return -1; - - if (dev->tuner && dev->tuner->exit) { - rtlsdr_set_i2c_repeater(dev, 1); - r = dev->tuner->exit(dev); /* deinitialize tuner */ - rtlsdr_set_i2c_repeater(dev, 0); - } - - /* poweroff demodulator and ADCs */ - rtlsdr_write_reg(dev, SYSB, DEMOD_CTL, 0x20, 1); - - return r; -} - -int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_freq) -{ - int r = 0; - - if (!dev) - return -1; - - if (rtl_freq > 0 && - (rtl_freq < MIN_RTL_XTAL_FREQ || rtl_freq > MAX_RTL_XTAL_FREQ)) - return -2; - - if (dev->rtl_xtal != rtl_freq) { - if (0 == rtl_freq) - rtl_freq = DEF_RTL_XTAL_FREQ; - - dev->rtl_xtal = rtl_freq; - - /* update xtal-dependent settings */ - if (dev->rate) - r = rtlsdr_set_sample_rate(dev, dev->rate); - } - - if (dev->tun_xtal != tuner_freq) { - if (0 == tuner_freq) - tuner_freq = dev->rtl_xtal; - - dev->tun_xtal = tuner_freq; - - /* update xtal-dependent settings */ - if (dev->freq) - r = rtlsdr_set_center_freq(dev, dev->freq); - } - - return r; -} - -int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_freq) -{ - if (!dev) - return -1; - - *rtl_freq = dev->rtl_xtal; - - if (!dev->tuner) - return -2; - - *tuner_freq = dev->tun_xtal; - - return 0; -} - -int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) -{ - int r = -1; - double f = (double) freq; - - if (!dev || !dev->tuner) - return -1; - - if (dev->tuner->set_freq) { - f *= 1.0 + dev->corr / 1e6; - - rtlsdr_set_i2c_repeater(dev, 1); - r = dev->tuner->set_freq(dev, (uint32_t) f); - rtlsdr_set_i2c_repeater(dev, 0); - - if (!r) - dev->freq = freq; - else - dev->freq = 0; - } - - return r; -} - -uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) -{ - if (!dev || !dev->tuner) - return 0; - - return dev->freq; -} - -int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) -{ - int r; - - if (!dev || !dev->tuner) - return -1; - - if (dev->corr == ppm) - return -1; - - dev->corr = ppm; - - /* retune to apply new correction value */ - r = rtlsdr_set_center_freq(dev, dev->freq); - - return r; -} - -int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev) -{ - if (!dev || !dev->tuner) - return 0; - - return dev->corr; -} - -int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) -{ - int r = 0; - - if (!dev || !dev->tuner) - return -1; - - if (dev->tuner->set_gain) { - rtlsdr_set_i2c_repeater(dev, 1); - r = dev->tuner->set_gain((void *)dev, gain); - rtlsdr_set_i2c_repeater(dev, 0); - } - - if (!r) - dev->gain = gain; - else - dev->gain = 0; - - return r; -} - -int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) -{ - if (!dev || !dev->tuner) - return 0; - - return dev->gain; -} - -int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) -{ - int r = 0; - - if (!dev || !dev->tuner) - return -1; - - if (dev->tuner->set_gain_mode) { - rtlsdr_set_i2c_repeater(dev, 1); - r = dev->tuner->set_gain_mode((void *)dev, mode); - rtlsdr_set_i2c_repeater(dev, 0); - } - - return r; -} - -int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) -{ - uint16_t tmp; - uint32_t rsamp_ratio; - double real_rate; - - if (!dev) - return -1; - - /* check for the maximum rate the resampler supports */ - if (samp_rate > MAX_SAMP_RATE) - samp_rate = MAX_SAMP_RATE; - - rsamp_ratio = (dev->rtl_xtal * pow(2, 22)) / samp_rate; - rsamp_ratio &= ~3; - - real_rate = (dev->rtl_xtal * pow(2, 22)) / rsamp_ratio; - - if ( ((double)samp_rate) != real_rate ) - fprintf(stderr, "Exact sample rate is: %f Hz\n", real_rate); - - if (dev->tuner && dev->tuner->set_bw) { - rtlsdr_set_i2c_repeater(dev, 1); - dev->tuner->set_bw(dev, real_rate); - rtlsdr_set_i2c_repeater(dev, 0); - } - - dev->rate = samp_rate; - - tmp = (rsamp_ratio >> 16); - rtlsdr_demod_write_reg(dev, 1, 0x9f, tmp, 2); - tmp = rsamp_ratio & 0xffff; - rtlsdr_demod_write_reg(dev, 1, 0xa1, tmp, 2); - - /* reset demod (bit 3, soft_rst) */ - rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); - rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); - - return 0; -} - -uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) -{ - if (!dev) - return 0; - - return dev->rate; -} - -rtlsdr_dongle_t *find_known_device(uint16_t vid, uint16_t pid) -{ - unsigned int i; - rtlsdr_dongle_t *device = NULL; - - for (i = 0; i < sizeof(known_devices)/sizeof(rtlsdr_dongle_t); i++ ) { - if (known_devices[i].vid == vid && known_devices[i].pid == pid) { - device = &known_devices[i]; - break; - } - } - - return device; -} - -uint32_t rtlsdr_get_device_count(void) -{ - int i; - libusb_context *ctx; - libusb_device **list; - uint32_t device_count = 0; - struct libusb_device_descriptor dd; - ssize_t cnt; - - libusb_init(&ctx); - - cnt = libusb_get_device_list(ctx, &list); - - for (i = 0; i < cnt; i++) { - libusb_get_device_descriptor(list[i], &dd); - - if (find_known_device(dd.idVendor, dd.idProduct)) - device_count++; - } - - libusb_free_device_list(list, 1); - - libusb_exit(ctx); - - return device_count; -} - -const char *rtlsdr_get_device_name(uint32_t index) -{ - int i; - libusb_context *ctx; - libusb_device **list; - struct libusb_device_descriptor dd; - rtlsdr_dongle_t *device = NULL; - uint32_t device_count = 0; - ssize_t cnt; - - libusb_init(&ctx); - - cnt = libusb_get_device_list(ctx, &list); - - for (i = 0; i < cnt; i++) { - libusb_get_device_descriptor(list[i], &dd); - - device = find_known_device(dd.idVendor, dd.idProduct); - - if (device) { - device_count++; - - if (index == device_count - 1) - break; - } - } - - libusb_free_device_list(list, 1); - - libusb_exit(ctx); - - if (device) - return device->name; - else - return ""; -} - -int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) -{ - int r; - int i; - libusb_device **list; - rtlsdr_dev_t *dev = NULL; - libusb_device *device = NULL; - uint32_t device_count = 0; - struct libusb_device_descriptor dd; - uint8_t reg; - ssize_t cnt; - - dev = malloc(sizeof(rtlsdr_dev_t)); - if (NULL == dev) - return -ENOMEM; - - memset(dev, 0, sizeof(rtlsdr_dev_t)); - - libusb_init(&dev->ctx); - - cnt = libusb_get_device_list(dev->ctx, &list); - - for (i = 0; i < cnt; i++) { - device = list[i]; - - libusb_get_device_descriptor(list[i], &dd); - - if (find_known_device(dd.idVendor, dd.idProduct)) { - device_count++; - } - - if (index == device_count - 1) - break; - - device = NULL; - } - - if (!device) { - r = -1; - goto err; - } - - r = libusb_open(device, &dev->devh); - if (r < 0) { - libusb_free_device_list(list, 1); - fprintf(stderr, "usb_open error %d\n", r); - goto err; - } - - libusb_free_device_list(list, 1); - - r = libusb_claim_interface(dev->devh, 0); - if (r < 0) { - fprintf(stderr, "usb_claim_interface error %d\n", r); - goto err; - } - - dev->rtl_xtal = DEF_RTL_XTAL_FREQ; - - rtlsdr_init_baseband(dev); - - /* Probe tuners */ - rtlsdr_set_i2c_repeater(dev, 1); - - reg = rtlsdr_i2c_read_reg(dev, E4K_I2C_ADDR, E4K_CHECK_ADDR); - if (reg == E4K_CHECK_VAL) { - fprintf(stderr, "Found Elonics E4000 tuner\n"); - dev->tuner = &tuners[RTLSDR_TUNER_E4000]; - goto found; - } - - reg = rtlsdr_i2c_read_reg(dev, FC0013_I2C_ADDR, FC0013_CHECK_ADDR); - if (reg == FC0013_CHECK_VAL) { - fprintf(stderr, "Found Fitipower FC0013 tuner\n"); - dev->tuner = &tuners[RTLSDR_TUNER_FC0013]; - goto found; - } - - /* initialise GPIOs */ - rtlsdr_set_gpio_output(dev, 5); - - /* reset tuner before probing */ - rtlsdr_set_gpio_bit(dev, 5, 1); - rtlsdr_set_gpio_bit(dev, 5, 0); - - reg = rtlsdr_i2c_read_reg(dev, FC2580_I2C_ADDR, FC2580_CHECK_ADDR); - if ((reg & 0x7f) == FC2580_CHECK_VAL) { - fprintf(stderr, "Found FCI 2580 tuner\n"); - dev->tuner = &tuners[RTLSDR_TUNER_FC2580]; - goto found; - } - - reg = rtlsdr_i2c_read_reg(dev, FC0012_I2C_ADDR, FC0012_CHECK_ADDR); - if (reg == FC0012_CHECK_VAL) { - fprintf(stderr, "Found Fitipower FC0012 tuner\n"); - rtlsdr_set_gpio_output(dev, 6); - dev->tuner = &tuners[RTLSDR_TUNER_FC0012]; - goto found; - } - -found: - if (dev->tuner) { - dev->tun_xtal = dev->rtl_xtal; - - if (dev->tuner->init) - r = dev->tuner->init(dev); - } - - rtlsdr_set_i2c_repeater(dev, 0); - - *out_dev = dev; - - return 0; -err: - if (dev) { - if (dev->ctx) - libusb_exit(dev->ctx); - - free(dev); - } - - return r; -} - -int rtlsdr_close(rtlsdr_dev_t *dev) -{ - if (!dev) - return -1; - - rtlsdr_deinit_baseband(dev); - - libusb_release_interface(dev->devh, 0); - libusb_close(dev->devh); - - libusb_exit(dev->ctx); - - free(dev); - - return 0; -} - -int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) -{ - if (!dev) - return -1; - - rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x1002, 2); - rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x0000, 2); - - return 0; -} - -int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read) -{ - if (!dev) - return -1; - - return libusb_bulk_transfer(dev->devh, 0x81, buf, len, n_read, BULK_TIMEOUT); -} - -static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) -{ - rtlsdr_dev_t *dev = (rtlsdr_dev_t *)xfer->user_data; - - if (LIBUSB_TRANSFER_COMPLETED == xfer->status) { - if (dev->cb) - dev->cb(xfer->buffer, xfer->actual_length, dev->cb_ctx); - - libusb_submit_transfer(xfer); /* resubmit transfer */ - } else { - /*fprintf(stderr, "transfer status: %d\n", xfer->status);*/ - rtlsdr_cancel_async(dev); /* abort async loop */ - } -} - -int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx) -{ - return rtlsdr_read_async(dev, cb, ctx, 0, 0); -} - -static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) -{ - unsigned int i; - - if (!dev) - return -1; - - if (!dev->xfer) { - dev->xfer = malloc(dev->xfer_buf_num * - sizeof(struct libusb_transfer *)); - - for(i = 0; i < dev->xfer_buf_num; ++i) - dev->xfer[i] = libusb_alloc_transfer(0); - } - - if (!dev->xfer_buf) { - dev->xfer_buf = malloc(dev->xfer_buf_num * - sizeof(unsigned char *)); - - for(i = 0; i < dev->xfer_buf_num; ++i) - dev->xfer_buf[i] = malloc(dev->xfer_buf_len); - } - - return 0; -} - -static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev) -{ - unsigned int i; - - if (!dev) - return -1; - - if (dev->xfer) { - for(i = 0; i < dev->xfer_buf_num; ++i) { - if (dev->xfer[i]) { - libusb_free_transfer(dev->xfer[i]); - } - } - - free(dev->xfer); - dev->xfer = NULL; - } - - if (dev->xfer_buf) { - for(i = 0; i < dev->xfer_buf_num; ++i) { - if (dev->xfer_buf[i]) - free(dev->xfer_buf[i]); - } - - free(dev->xfer_buf); - dev->xfer_buf = NULL; - } - - return 0; -} - -int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, - uint32_t buf_num, uint32_t buf_len) -{ - unsigned int i; - int r; - struct timeval tv = { 1, 0 }; - - if (!dev) - return -1; - - dev->cb = cb; - dev->cb_ctx = ctx; - - if (buf_num > 0) - dev->xfer_buf_num = buf_num; - else - dev->xfer_buf_num = DEFAULT_BUF_NUMBER; - - if (buf_len > 0 && buf_len % 512 == 0) /* len must be multiple of 512 */ - dev->xfer_buf_len = buf_len; - else - dev->xfer_buf_len = DEFAULT_BUF_LENGTH; - - _rtlsdr_alloc_async_buffers(dev); - - for(i = 0; i < dev->xfer_buf_num; ++i) { - libusb_fill_bulk_transfer(dev->xfer[i], - dev->devh, - 0x81, - dev->xfer_buf[i], - dev->xfer_buf_len, - _libusb_callback, - (void *)dev, - BULK_TIMEOUT); - - libusb_submit_transfer(dev->xfer[i]); - } - - dev->async_status = RTLSDR_RUNNING; - - while (RTLSDR_INACTIVE != dev->async_status) { - r = libusb_handle_events_timeout(dev->ctx, &tv); - if (r < 0) { - /*fprintf(stderr, "handle_events returned: %d\n", r);*/ - if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */ - continue; - break; - } - - if (RTLSDR_CANCELING == dev->async_status) { - dev->async_status = RTLSDR_INACTIVE; - - if (!dev->xfer) - break; - - for(i = 0; i < dev->xfer_buf_num; ++i) { - if (!dev->xfer[i]) - continue; - - if (dev->xfer[i]->status == LIBUSB_TRANSFER_COMPLETED) { - libusb_cancel_transfer(dev->xfer[i]); - dev->async_status = RTLSDR_CANCELING; - } - } - - if (RTLSDR_INACTIVE == dev->async_status) - break; - } - } - - _rtlsdr_free_async_buffers(dev); - - return r; -} - -int rtlsdr_cancel_async(rtlsdr_dev_t *dev) -{ - if (!dev) - return -1; - - if (RTLSDR_RUNNING == dev->async_status) { - dev->async_status = RTLSDR_CANCELING; - return 0; - } - - return -2; -} - -uint32_t rtlsdr_get_tuner_clock(void *dev) -{ - if (!dev) - return 0; - - return ((rtlsdr_dev_t *)dev)->tun_xtal; -} - -int rtlsdr_i2c_write_fn(void *dev, uint8_t addr, uint8_t *buf, int len) -{ - if (dev) - return rtlsdr_i2c_write(((rtlsdr_dev_t *)dev), addr, buf, len); - - return -1; -} - -int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len) -{ - if (dev) - return rtlsdr_i2c_read(((rtlsdr_dev_t *)dev), addr, buf, len); - - return -1; -} -- cgit v1.3.1