/* * 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 #include "rtl-sdr.h" #include "tuner_e4k.h" #include "tuner_fc0012.h" #include "tuner_fc0013.h" #include "tuner_fc2580.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 /* 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 */ /* tuner context */ enum rtlsdr_tuner tuner_type; rtlsdr_tuner_iface_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) { 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_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) { /* select V-band/U-band filter */ rtlsdr_set_gpio_bit(dev, 6, (freq > 300000000) ? 1 : 0); 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_gain(dev, gain); } 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; } /* definition order must match enum rtlsdr_tuner */ static rtlsdr_tuner_iface_t tuners[] = { { 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_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, 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)" }, { 0x185b, 0x0620, "Compro Videomate U620F"}, { 0x185b, 0x0650, "Compro Videomate U650F"}, { 0x185b, 0x0680, "Compro Videomate U680F"}, { 0x1f4d, 0xb803, "GTek T803" }, { 0x1f4d, 0xc803, "Lifeview LV5TDeluxe" }, { 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" }, { 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); /* 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); 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_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_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; } enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev) { if (!dev) return -1; return dev->tuner_type; } int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains) { const int e4k_gains[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, 240, 290, 340, 420, 430, 450, 470, 490 }; const int fc0012_gains[] = { 0 /* no gain values */ }; const int fc0013_gains[] = { -63, 71, 191, 197 }; const int fc2580_gains[] = { 0 /* no gain values */ }; int *ptr = NULL; int len = 0; if (!dev) return -1; switch (dev->tuner_type) { case RTLSDR_TUNER_E4000: ptr = (int *)e4k_gains; len = sizeof(e4k_gains); break; case RTLSDR_TUNER_FC0012: ptr = (int *)fc0012_gains; len = sizeof(fc0012_gains); break; case RTLSDR_TUNER_FC0013: ptr = (int *)fc0013_gains; len = sizeof(fc0013_gains); break; case RTLSDR_TUNER_FC2580: ptr = (int *)fc2580_gains; len = sizeof(fc2580_gains); break; default: fprintf(stderr, "Invalid tuner type %d\n", dev->tuner_type); 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 || !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; } /* two raised to the power of n */ #define TWO_POW(n) ((double)(1ULL<<(n))) 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 * TWO_POW(22)) / samp_rate; rsamp_ratio &= ~3; real_rate = (dev->rtl_xtal * 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); 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; } int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) { if (!dev) return -1; rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x23 : 0x25 , 1); return 0; } 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_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"); rtlsdr_set_gpio_output(dev, 6); dev->tuner_type = 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_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) { r = -1; goto err; } dev->tuner = &tuners[dev->tuner_type]; 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; }