/* * 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 #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 /* two raised to the power of n */ #define TWO_POW(n) ((double)(1ULL<<(n))) #include "rtl-sdr.h" #include "tuner_e4k.h" #include "tuner_fc0012.h" #include "tuner_fc0013.h" #include "tuner_fc2580.h" #include "tuner_r820t.h" typedef struct rtlsdr_tuner_iface { /* 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 /* tenth dB */); int (*set_if_gain)(void *, int stage, int gain /* tenth dB */); int (*set_gain_mode)(void *, int manual); } rtlsdr_tuner_iface_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 */ int direct_sampling; /* tuner context */ enum rtlsdr_tuner tuner_type; rtlsdr_tuner_iface_t *tuner; uint32_t tun_xtal; /* Hz */ uint32_t freq; /* Hz */ uint32_t offs_freq; /* Hz */ int corr; /* ppm */ int gain; /* tenth 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; rtlsdr_get_xtal_freq(devt, NULL, &devt->e4k_s.vco.fosc); 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) { int r = 0; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; r |= e4k_if_filter_bw_set(&devt->e4k_s, E4K_IF_FILTER_MIX, bw); r |= e4k_if_filter_bw_set(&devt->e4k_s, E4K_IF_FILTER_RC, bw); r |= e4k_if_filter_bw_set(&devt->e4k_s, E4K_IF_FILTER_CHAN, bw); return r; } int e4000_set_gain(void *dev, int gain) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; int mixgain = (gain > 340) ? 12 : 4; #if 0 int enhgain = (gain - 420); #endif if(e4k_set_lna_gain(&devt->e4k_s, min(300, gain - mixgain * 10)) == -EINVAL) return -1; if(e4k_mixer_gain_set(&devt->e4k_s, mixgain) == -EINVAL) return -1; #if 0 /* enhanced mixer gain seems to have no effect */ if(enhgain >= 0) if(e4k_set_enh_gain(&devt->e4k_s, enhgain) == -EINVAL) return -1; #endif return 0; } int e4000_set_if_gain(void *dev, int stage, int gain) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return e4k_if_gain_set(&devt->e4k_s, (uint8_t)stage, (int8_t)(gain / 10)); } int e4000_set_gain_mode(void *dev, int manual) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return e4k_enable_manual_gain(&devt->e4k_s, manual); } int _fc0012_init(void *dev) { return fc0012_init(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_set_params(dev, freq, 6000000); } int fc0012_set_bw(void *dev, int bw) { return 0; } int _fc0012_set_gain(void *dev, int gain) { return fc0012_set_gain(dev, gain); } int fc0012_set_gain_mode(void *dev, int manual) { return 0; } int _fc0013_init(void *dev) { return fc0013_init(dev); } int fc0013_exit(void *dev) { return 0; } int fc0013_set_freq(void *dev, uint32_t freq) { return fc0013_set_params(dev, freq, 6000000); } int fc0013_set_bw(void *dev, int bw) { return 0; } int _fc0013_set_gain(void *dev, int gain) { return fc0013_set_lna_gain(dev, gain); } 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; } int r820t_init(void *dev) { int r = R828_Init(dev); r820t_SetStandardMode(dev, DVB_T_6M); return r; } int r820t_exit(void *dev) { return 0; } int r820t_set_freq(void *dev, uint32_t freq) { return r820t_SetRfFreqHz(dev, freq); } int r820t_set_bw(void *dev, int bw) { return 0; } int r820t_set_gain(void *dev, int gain) { return R828_SetRfGain(dev, gain); } int r820t_set_gain_mode(void *dev, int manual) { return R828_RfGainMode(dev, manual); } /* definition order must match enum rtlsdr_tuner */ static rtlsdr_tuner_iface_t tuners[] = { { NULL, NULL, NULL, NULL, NULL, NULL, NULL /* dummy for unknown tuners */ }, { e4000_init, e4000_exit, e4000_set_freq, e4000_set_bw, e4000_set_gain, e4000_set_if_gain, e4000_set_gain_mode }, { _fc0012_init, fc0012_exit, fc0012_set_freq, fc0012_set_bw, _fc0012_set_gain, NULL, fc0012_set_gain_mode }, { _fc0013_init, fc0013_exit, fc0013_set_freq, fc0013_set_bw, _fc0013_set_gain, NULL, fc0013_set_gain_mode }, { fc2580_init, fc2580_exit, _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, NULL, fc2580_set_gain_mode }, { r820t_init, r820t_exit, r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, r820t_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, 0x00b4, "Terratec NOXON DAB/DAB+ USB dongle (rev 1)" }, { 0x0ccd, 0x00b7, "Terratec NOXON DAB/DAB+ USB dongle (rev 1)" }, { 0x0ccd, 0x00c6, "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)" }, { 0x1554, 0x5020, "PixelView PV-DT235U(RN)" }, { 0x185b, 0x0620, "Compro Videomate U620F"}, { 0x185b, 0x0650, "Compro Videomate U650F"}, { 0x185b, 0x0680, "Compro Videomate U680F"}, { 0x1f4d, 0xa803, "Sweex DVB-T USB" }, { 0x1f4d, 0xb803, "GTek T803" }, { 0x1f4d, 0xc803, "Lifeview LV5TDeluxe" }, { 0x1f4d, 0xd286, "MyGica TD312" }, { 0x1f4d, 0xd803, "PROlectrix DV107669" }, { 0x1b80, 0xd398, "Zaapa ZT-MINDVBZP" }, { 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" }, { 0x1d19, 0x1104, "MSI DigiVox Micro HD" }, { 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 #define EEPROM_ADDR 0xa0 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); #if 0 if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); #endif 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); #if 0 if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); #endif 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 = 0; 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; } int 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); return 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; } int 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); return (r == len) ? 0 : -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); /* clear both DDC shift and IF frequency registers */ for (i = 0; i < 6; i++) rtlsdr_demod_write_reg(dev, 1, 0x16 + i, 0x00, 1); /* set FIR coefficients */ for (i = 0; i < sizeof (fir_coeff); i++) rtlsdr_demod_write_reg(dev, 1, 0x1c + i, fir_coeff[i], 1); /* enable SDR mode, disable DAGC (bit 5) */ rtlsdr_demod_write_reg(dev, 0, 0x19, 0x05, 1); /* init FSM state-holding register */ rtlsdr_demod_write_reg(dev, 1, 0x93, 0xf0, 1); rtlsdr_demod_write_reg(dev, 1, 0x94, 0x0f, 1); /* disable AGC (en_dagc, bit 0) (this seems to have no effect) */ rtlsdr_demod_write_reg(dev, 1, 0x11, 0x00, 1); /* disable RF and IF AGC loop */ rtlsdr_demod_write_reg(dev, 1, 0x04, 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); /* disable 4.096 MHz clock output on pin TP_CK0 */ rtlsdr_demod_write_reg(dev, 0, 0x0d, 0x83, 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_if_freq(rtlsdr_dev_t *dev, uint32_t freq) { uint32_t rtl_xtal; int32_t if_freq; uint8_t tmp; int r; if (!dev) return -1; /* read corrected clock value */ if (rtlsdr_get_xtal_freq(dev, &rtl_xtal, NULL)) return -2; if_freq = ((freq * TWO_POW(22)) / rtl_xtal) * (-1); tmp = (if_freq >> 16) & 0x3f; r = rtlsdr_demod_write_reg(dev, 1, 0x19, tmp, 1); tmp = (if_freq >> 8) & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x1a, tmp, 1); tmp = if_freq & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x1b, tmp, 1); return r; } int rtlsdr_set_sample_freq_correction(rtlsdr_dev_t *dev, int ppm) { int r = 0; uint8_t tmp; int16_t offs = ppm * (-1) * TWO_POW(24) / 1000000; tmp = offs & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x3f, tmp, 1); tmp = (offs >> 8) & 0x3f; r |= rtlsdr_demod_write_reg(dev, 1, 0x3e, tmp, 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 (rtl_freq > 0 && dev->rtl_xtal != rtl_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) dev->tun_xtal = dev->rtl_xtal; else dev->tun_xtal = tuner_freq; /* read corrected clock value into e4k structure */ if (rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc)) return -3; /* 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; #define APPLY_PPM_CORR(val,ppm) (((val) * (1.0 + (ppm) / 1e6))) if (rtl_freq) *rtl_freq = (uint32_t) APPLY_PPM_CORR(dev->rtl_xtal, dev->corr); if (tuner_freq) *tuner_freq = (uint32_t) APPLY_PPM_CORR(dev->tun_xtal, dev->corr); return 0; } int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, char *serial) { struct libusb_device_descriptor dd; libusb_device *device = NULL; const int buf_max = 256; int r = 0; if (!dev || !dev->devh) return -1; device = libusb_get_device(dev->devh); r = libusb_get_device_descriptor(device, &dd); if (r < 0) return -1; if (manufact) { memset(manufact, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iManufacturer, (unsigned char *)manufact, buf_max); } if (product) { memset(product, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iProduct, (unsigned char *)product, buf_max); } if (serial) { memset(serial, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iSerialNumber, (unsigned char *)serial, buf_max); } return 0; } int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len) { int r = 0; int i; uint8_t cmd[2]; if (!dev) return -1; if ((len + offset) > 256) return -2; for (i = 0; i < len; i++) { cmd[0] = i + offset; r = rtlsdr_write_array(dev, IICB, EEPROM_ADDR, cmd, 1); r = rtlsdr_read_array(dev, IICB, EEPROM_ADDR, &cmd[1], 1); /* only write the byte if it differs */ if (cmd[1] == data[i]) continue; cmd[1] = data[i]; r = rtlsdr_write_array(dev, IICB, EEPROM_ADDR, cmd, 2); if (r != sizeof(cmd)) return -3; /* for some EEPROMs (e.g. ATC 240LC02) we need a delay * between write operations, otherwise they will fail */ #ifdef _WIN32 Sleep(5); #else usleep(5000); #endif } return 0; } int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len) { int r = 0; int i; if (!dev) return -1; if ((len + offset) > 256) return -2; r = rtlsdr_write_array(dev, IICB, EEPROM_ADDR, &offset, 1); if (r < 0) return -3; for (i = 0; i < len; i++) { r = rtlsdr_read_array(dev, IICB, EEPROM_ADDR, data + i, 1); if (r < 0) return -3; } return r; } int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) { int r = -1; if (!dev || !dev->tuner) return -1; if (dev->direct_sampling) { r = rtlsdr_set_if_freq(dev, freq); } else if (dev->tuner && dev->tuner->set_freq) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_freq(dev, freq - dev->offs_freq); 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) return 0; return dev->freq; } int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) { int r = 0; if (!dev) return -1; if (dev->corr == ppm) return -2; dev->corr = ppm; r |= rtlsdr_set_sample_freq_correction(dev, ppm); /* read corrected clock value into e4k structure */ if (rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc)) return -3; if (dev->freq) /* 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) return 0; return dev->corr; } enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev) { if (!dev) return RTLSDR_TUNER_UNKNOWN; return dev->tuner_type; } int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains) { /* all gain values are expressed in tenths of a dB */ const int e4k_gains[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, 240, 290, 340, 420 }; const int fc0012_gains[] = { -99, -40, 71, 179, 192 }; const int fc0013_gains[] = { -99, -73, -65, -63, -60, -58, -54, 58, 61, 63, 65, 67, 68, 70, 71, 179, 181, 182, 184, 186, 188, 191, 197 }; const int fc2580_gains[] = { 0 /* no gain values */ }; const int r820t_gains[] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, 207, 229, 254, 280, 297, 328, 338, 364, 372, 386, 402, 421, 434, 439, 445, 480, 496 }; const int unknown_gains[] = { 0 /* no gain values */ }; const int *ptr = NULL; int len = 0; if (!dev) return -1; switch (dev->tuner_type) { case RTLSDR_TUNER_E4000: ptr = e4k_gains; len = sizeof(e4k_gains); break; case RTLSDR_TUNER_FC0012: ptr = fc0012_gains; len = sizeof(fc0012_gains); break; case RTLSDR_TUNER_FC0013: ptr = fc0013_gains; len = sizeof(fc0013_gains); break; case RTLSDR_TUNER_FC2580: ptr = fc2580_gains; len = sizeof(fc2580_gains); break; case RTLSDR_TUNER_R820T: ptr = r820t_gains; len = sizeof(r820t_gains); break; default: ptr = unknown_gains; len = sizeof(unknown_gains); break; } if (!gains) { /* no buffer provided, just return the count */ return len / sizeof(int); } else { if (len) memcpy(gains, ptr, len); return len / sizeof(int); } } 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) return 0; return dev->gain; } int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain) { int r = 0; if (!dev || !dev->tuner) return -1; if (dev->tuner->set_if_gain) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_if_gain(dev, stage, gain); rtlsdr_set_i2c_repeater(dev, 0); } return r; } 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) { int r = 0; uint16_t tmp; uint32_t rsamp_ratio; double real_rate; uint32_t rtl_freq = dev->rtl_xtal; 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 = (rtl_freq * TWO_POW(22)) / samp_rate; rsamp_ratio &= ~3; real_rate = (rtl_freq * TWO_POW(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, (int)real_rate); rtlsdr_set_i2c_repeater(dev, 0); } dev->rate = (uint32_t)real_rate; tmp = (rsamp_ratio >> 16); r |= rtlsdr_demod_write_reg(dev, 1, 0x9f, tmp, 2); tmp = rsamp_ratio & 0xffff; r |= rtlsdr_demod_write_reg(dev, 1, 0xa1, tmp, 2); r |= rtlsdr_set_sample_freq_correction(dev, dev->corr); /* reset demod (bit 3, soft_rst) */ r |= rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); r |= rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); /* recalculate offset frequency if offset tuning is enabled */ if (dev->offs_freq) rtlsdr_set_offset_tuning(dev, 1); return r; } uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) { if (!dev) return 0; return dev->rate; } int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) { if (!dev) return -1; return rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x03 : 0x05, 1); } int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on) { if (!dev) return -1; return rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x25 : 0x05, 1); } int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) { int r = 0; if (!dev) return -1; if (on) { if (dev->tuner && dev->tuner->exit) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->exit(dev); rtlsdr_set_i2c_repeater(dev, 0); } /* disable Zero-IF mode */ r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); /* disable spectrum inversion */ r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); /* only enable In-phase ADC input */ r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); /* swap I and Q ADC, this allows to select between two inputs */ r |= rtlsdr_demod_write_reg(dev, 0, 0x06, (on > 1) ? 0x90 : 0x80, 1); fprintf(stderr, "Enabled direct sampling mode, input %i\n", on); dev->direct_sampling = on; } else { if (dev->tuner && dev->tuner->init) { rtlsdr_set_i2c_repeater(dev, 1); r |= dev->tuner->init(dev); rtlsdr_set_i2c_repeater(dev, 0); } if (dev->tuner_type == RTLSDR_TUNER_R820T) { r |= rtlsdr_set_if_freq(dev, R820T_IF_FREQ); /* enable spectrum inversion */ r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); } else { r |= rtlsdr_set_if_freq(dev, 0); /* enable In-phase + Quadrature ADC input */ r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0xcd, 1); /* Enable Zero-IF mode */ r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1b, 1); } /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ r |= rtlsdr_demod_write_reg(dev, 0, 0x06, 0x80, 1); fprintf(stderr, "Disabled direct sampling mode\n"); dev->direct_sampling = 0; } r |= rtlsdr_set_center_freq(dev, dev->freq); return r; } int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev) { if (!dev) return -1; return dev->direct_sampling; } int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) { int r = 0; if (!dev) return -1; if (dev->tuner_type == RTLSDR_TUNER_R820T) return -2; if (dev->direct_sampling) return -3; /* based on keenerds 1/f noise measurements */ dev->offs_freq = on ? ((dev->rate / 2) * 170 / 100) : 0; r |= rtlsdr_set_if_freq(dev, dev->offs_freq); if (dev->tuner && dev->tuner->set_bw) { rtlsdr_set_i2c_repeater(dev, 1); dev->tuner->set_bw(dev, on ? (2 * dev->offs_freq) : dev->rate); rtlsdr_set_i2c_repeater(dev, 0); } if (dev->freq > dev->offs_freq) r |= rtlsdr_set_center_freq(dev, dev->freq); return r; } int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev) { if (!dev) return -1; return (dev->offs_freq) ? 1 : 0; } static 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_get_device_usb_strings(uint32_t index, char *manufact, char *product, char *serial) { int r = -2; int i; libusb_context *ctx; libusb_device **list; struct libusb_device_descriptor dd; rtlsdr_dongle_t *device = NULL; rtlsdr_dev_t devt; 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) { r = libusb_open(list[i], &devt.devh); if (!r) { r = rtlsdr_get_usb_strings(&devt, manufact, product, serial); libusb_close(devt.devh); } break; } } } libusb_free_device_list(list, 1); libusb_exit(ctx); return r; } int rtlsdr_get_index_by_serial(const char *serial) { int i, cnt, r; char str[256]; if (!serial) return -1; cnt = rtlsdr_get_device_count(); if (!cnt) return -2; for (i = 0; i < cnt; i++) { r = rtlsdr_get_device_usb_strings(i, NULL, NULL, str); if (!r && !strcmp(serial, str)) return i; } return -3; } 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_type = 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_type = RTLSDR_TUNER_FC0013; goto found; } reg = rtlsdr_i2c_read_reg(dev, R820T_I2C_ADDR, R820T_CHECK_ADDR); if (reg == R820T_CHECK_VAL) { fprintf(stderr, "Found Rafael Micro R820T tuner\n"); dev->tuner_type = RTLSDR_TUNER_R820T; /* disable Zero-IF mode */ rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); /* only enable In-phase ADC input */ rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); /* the R820T uses 3.57 MHz IF for the DVB-T 6 MHz mode, and * 4.57 MHz for the 8 MHz mode */ rtlsdr_set_if_freq(dev, R820T_IF_FREQ); /* enable spectrum inversion */ rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); 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_type = 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_type = RTLSDR_TUNER_FC0012; goto found; } found: if (dev->tuner_type == RTLSDR_TUNER_UNKNOWN) { fprintf(stderr, "No supported tuner found\n"); rtlsdr_set_direct_sampling(dev, 1); } dev->tuner = &tuners[dev->tuner_type]; dev->tun_xtal = dev->rtl_xtal; /* use the rtl clock value by default */ 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; /* block until all async operations have been completed (if any) */ while (RTLSDR_INACTIVE != dev->async_status) { #ifdef _WIN32 Sleep(1); #else usleep(1000); #endif } 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 if (LIBUSB_TRANSFER_CANCELLED == xfer->status) { /* nothing to do */ } else { /*fprintf(stderr, "transfer status: %d\n", xfer->status);*/ } } 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 = 0; struct timeval tv = { 1, 0 }; enum rtlsdr_async_status next_status = RTLSDR_INACTIVE; if (!dev) return -1; if (RTLSDR_INACTIVE != dev->async_status) return -2; dev->async_status = RTLSDR_RUNNING; 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]); } 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) { next_status = RTLSDR_INACTIVE; if (!dev->xfer) break; for(i = 0; i < dev->xfer_buf_num; ++i) { if (!dev->xfer[i]) continue; if (LIBUSB_TRANSFER_CANCELLED != dev->xfer[i]->status) { libusb_cancel_transfer(dev->xfer[i]); next_status = RTLSDR_CANCELING; } } if (RTLSDR_INACTIVE == next_status) break; } } _rtlsdr_free_async_buffers(dev); dev->async_status = next_status; return r; } int rtlsdr_cancel_async(rtlsdr_dev_t *dev) { if (!dev) return -1; /* if streaming, try to cancel gracefully */ if (RTLSDR_RUNNING == dev->async_status) { dev->async_status = RTLSDR_CANCELING; return 0; } /* if called while in pending state, change the state forcefully */ if (RTLSDR_INACTIVE != dev->async_status) { dev->async_status = RTLSDR_INACTIVE; return 0; } return -2; } uint32_t rtlsdr_get_tuner_clock(void *dev) { uint32_t tuner_freq; if (!dev) return 0; /* read corrected clock value */ if (rtlsdr_get_xtal_freq((rtlsdr_dev_t *)dev, NULL, &tuner_freq)) return 0; return tuner_freq; } 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; }