From f8b84cf48099fb020b1174c8cc6195dde42308c3 Mon Sep 17 00:00:00 2001 From: FreeArtMan <=> Date: Sat, 19 Sep 2015 14:36:47 +0100 Subject: Initial --- .gitignore | 4 + Makefile | 41 + build/src/.empty | 0 build/utils/convenience/.empty | 0 include/reg_field.h | 60 ++ include/rtl-sdr.h | 387 ++++++++ include/rtl-sdr_export.h | 47 + include/rtlsdr_i2c.h | 8 + include/tuner_e4k.h | 222 +++++ include/tuner_fc0012.h | 36 + include/tuner_fc0013.h | 37 + include/tuner_fc2580.h | 127 +++ include/tuner_r82xx.h | 120 +++ src/librtlsdr.c | 1938 +++++++++++++++++++++++++++++++++++++++ src/make.mk | 13 + src/tuner_e4k.c | 1000 ++++++++++++++++++++ src/tuner_fc0012.c | 345 +++++++ src/tuner_fc0013.c | 500 ++++++++++ src/tuner_fc2580.c | 494 ++++++++++ src/tuner_r82xx.c | 1328 +++++++++++++++++++++++++++ utils/convenience/convenience.c | 304 ++++++ utils/convenience/convenience.h | 142 +++ utils/getopt/getopt.c | 1059 +++++++++++++++++++++ utils/getopt/getopt.h | 180 ++++ utils/make.mk | 15 + utils/rtl_adsb.c | 496 ++++++++++ utils/rtl_eeprom.c | 425 +++++++++ utils/rtl_fm.c | 1264 +++++++++++++++++++++++++ utils/rtl_power.c | 998 ++++++++++++++++++++ utils/rtl_sdr.c | 278 ++++++ utils/rtl_tcp.c | 603 ++++++++++++ utils/rtl_test.c | 423 +++++++++ 32 files changed, 12894 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 build/src/.empty create mode 100644 build/utils/convenience/.empty create mode 100644 include/reg_field.h create mode 100644 include/rtl-sdr.h create mode 100644 include/rtl-sdr_export.h create mode 100644 include/rtlsdr_i2c.h create mode 100644 include/tuner_e4k.h create mode 100644 include/tuner_fc0012.h create mode 100644 include/tuner_fc0013.h create mode 100644 include/tuner_fc2580.h create mode 100644 include/tuner_r82xx.h create mode 100644 src/librtlsdr.c create mode 100644 src/make.mk create mode 100644 src/tuner_e4k.c create mode 100644 src/tuner_fc0012.c create mode 100644 src/tuner_fc0013.c create mode 100644 src/tuner_fc2580.c create mode 100644 src/tuner_r82xx.c create mode 100644 utils/convenience/convenience.c create mode 100644 utils/convenience/convenience.h create mode 100644 utils/getopt/getopt.c create mode 100644 utils/getopt/getopt.h create mode 100644 utils/make.mk create mode 100644 utils/rtl_adsb.c create mode 100644 utils/rtl_eeprom.c create mode 100644 utils/rtl_fm.c create mode 100644 utils/rtl_power.c create mode 100644 utils/rtl_sdr.c create mode 100644 utils/rtl_tcp.c create mode 100644 utils/rtl_test.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d9484d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/* +*.a +*.so +*.so.1 \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2388da1 --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +PROJECT=r820t +CC=gcc +CFLAGS= +INCLUDE=-I./include +INCLUDE+=`pkg-config --cflags libusb` +LDFLAGS= +LDFLAGS+=`pkg-config --libs libusb` + +SOURCES= +SRC_LIB= +OBJ_LIB= +SRC_UTILS= +OBJ_UTILS= + +BUILD_DIR=build/ + +include src/make.mk +include utils/make.mk + +OBJECTS=$(SOURCES:.c=.o) + +lib: src-lib + +utils: $(DIR)-pre $(OBJ_UTILS) + +all: $(OBJECTS) + +make: $(OBJECTS) + echo $(OBJECTS) + + +%.o: %.c + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $(BUILD_DIR)$@ + +clean: + rm -f *.a + rm -f *.so + rm -f *.so.1 + rm -f build/src/*.o + rm -f build/utils/rtl_* + rm -f build/utils/convenience/*.o diff --git a/build/src/.empty b/build/src/.empty new file mode 100644 index 0000000..e69de29 diff --git a/build/utils/convenience/.empty b/build/utils/convenience/.empty new file mode 100644 index 0000000..e69de29 diff --git a/include/reg_field.h b/include/reg_field.h new file mode 100644 index 0000000..18a6922 --- /dev/null +++ b/include/reg_field.h @@ -0,0 +1,60 @@ +#ifndef _REG_FIELD_H +#define _REG_FIELD_H + +#include +#include + +enum cmd_op { + CMD_OP_GET = (1 << 0), + CMD_OP_SET = (1 << 1), + CMD_OP_EXEC = (1 << 2), +}; + +enum pstate { + ST_IN_CMD, + ST_IN_ARG, +}; + +struct strbuf { + uint8_t idx; + char buf[32]; +}; + +struct cmd_state { + struct strbuf cmd; + struct strbuf arg; + enum pstate state; + void (*out)(const char *format, va_list ap); +}; + +struct cmd { + const char *cmd; + uint32_t ops; + int (*cb)(struct cmd_state *cs, enum cmd_op op, const char *cmd, + int argc, char **argv); + const char *help; +}; + +/* structure describing a field in a register */ +struct reg_field { + uint8_t reg; + uint8_t shift; + uint8_t width; +}; + +struct reg_field_ops { + const struct reg_field *fields; + const char **field_names; + uint32_t num_fields; + void *data; + int (*write_cb)(void *data, uint32_t reg, uint32_t val); + uint32_t (*read_cb)(void *data, uint32_t reg); +}; + +uint32_t reg_field_read(struct reg_field_ops *ops, struct reg_field *field); +int reg_field_write(struct reg_field_ops *ops, struct reg_field *field, uint32_t val); +int reg_field_cmd(struct cmd_state *cs, enum cmd_op op, + const char *cmd, int argc, char **argv, + struct reg_field_ops *ops); + +#endif diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h new file mode 100644 index 0000000..fe64bea --- /dev/null +++ b/include/rtl-sdr.h @@ -0,0 +1,387 @@ +/* + * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver + * Copyright (C) 2012-2013 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 . + */ + +#ifndef __RTL_SDR_H +#define __RTL_SDR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct rtlsdr_dev rtlsdr_dev_t; + +RTLSDR_API uint32_t rtlsdr_get_device_count(void); + +RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index); + +/*! + * Get USB device strings. + * + * NOTE: The string arguments must provide space for up to 256 bytes. + * + * \param index the device index + * \param manufact manufacturer name, may be NULL + * \param product product name, may be NULL + * \param serial serial number, may be NULL + * \return 0 on success + */ +RTLSDR_API int rtlsdr_get_device_usb_strings(uint32_t index, + char *manufact, + char *product, + char *serial); + +/*! + * Get device index by USB serial string descriptor. + * + * \param serial serial string of the device + * \return device index of first device where the name matched + * \return -1 if name is NULL + * \return -2 if no devices were found at all + * \return -3 if devices were found, but none with matching name + */ +RTLSDR_API int rtlsdr_get_index_by_serial(const char *serial); + +RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index); + +RTLSDR_API int rtlsdr_close(rtlsdr_dev_t *dev); + +/* configuration functions */ + +/*! + * Set crystal oscillator frequencies used for the RTL2832 and the tuner IC. + * + * Usually both ICs use the same clock. Changing the clock may make sense if + * you are applying an external clock to the tuner or to compensate the + * frequency (and samplerate) error caused by the original (cheap) crystal. + * + * NOTE: Call this function only if you fully understand the implications. + * + * \param dev the device handle given by rtlsdr_open() + * \param rtl_freq frequency value used to clock the RTL2832 in Hz + * \param tuner_freq frequency value used to clock the tuner IC in Hz + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, + uint32_t tuner_freq); + +/*! + * Get crystal oscillator frequencies used for the RTL2832 and the tuner IC. + * + * Usually both ICs use the same clock. + * + * \param dev the device handle given by rtlsdr_open() + * \param rtl_freq frequency value used to clock the RTL2832 in Hz + * \param tuner_freq frequency value used to clock the tuner IC in Hz + * \return 0 on success + */ +RTLSDR_API int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, + uint32_t *tuner_freq); + +/*! + * Get USB device strings. + * + * NOTE: The string arguments must provide space for up to 256 bytes. + * + * \param dev the device handle given by rtlsdr_open() + * \param manufact manufacturer name, may be NULL + * \param product product name, may be NULL + * \param serial serial number, may be NULL + * \return 0 on success + */ +RTLSDR_API int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, + char *product, char *serial); + +/*! + * Write the device EEPROM + * + * \param dev the device handle given by rtlsdr_open() + * \param data buffer of data to be written + * \param offset address where the data should be written + * \param len length of the data + * \return 0 on success + * \return -1 if device handle is invalid + * \return -2 if EEPROM size is exceeded + * \return -3 if no EEPROM was found + */ + +RTLSDR_API int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, + uint8_t offset, uint16_t len); + +/*! + * Read the device EEPROM + * + * \param dev the device handle given by rtlsdr_open() + * \param data buffer where the data should be written + * \param offset address where the data should be read from + * \param len length of the data + * \return 0 on success + * \return -1 if device handle is invalid + * \return -2 if EEPROM size is exceeded + * \return -3 if no EEPROM was found + */ + +RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, + uint8_t offset, uint16_t len); + +RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq); + +/*! + * Get actual frequency the device is tuned to. + * + * \param dev the device handle given by rtlsdr_open() + * \return 0 on error, frequency in Hz otherwise + */ +RTLSDR_API uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev); + +/*! + * Set the frequency correction value for the device. + * + * \param dev the device handle given by rtlsdr_open() + * \param ppm correction value in parts per million (ppm) + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm); + +/*! + * Get actual frequency correction value of the device. + * + * \param dev the device handle given by rtlsdr_open() + * \return correction value in parts per million (ppm) + */ +RTLSDR_API int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev); + +enum rtlsdr_tuner { + RTLSDR_TUNER_UNKNOWN = 0, + RTLSDR_TUNER_E4000, + RTLSDR_TUNER_FC0012, + RTLSDR_TUNER_FC0013, + RTLSDR_TUNER_FC2580, + RTLSDR_TUNER_R820T, + RTLSDR_TUNER_R828D +}; + +/*! + * Get the tuner type. + * + * \param dev the device handle given by rtlsdr_open() + * \return RTLSDR_TUNER_UNKNOWN on error, tuner type otherwise + */ +RTLSDR_API enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev); + +/*! + * Get a list of gains supported by the tuner. + * + * NOTE: The gains argument must be preallocated by the caller. If NULL is + * being given instead, the number of available gain values will be returned. + * + * \param dev the device handle given by rtlsdr_open() + * \param gains array of gain values. In tenths of a dB, 115 means 11.5 dB. + * \return <= 0 on error, number of available (returned) gain values otherwise + */ +RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains); + +/*! + * Set the gain for the device. + * Manual gain mode must be enabled for this to work. + * + * Valid gain values (in tenths of a dB) for the E4000 tuner: + * -10, 15, 40, 65, 90, 115, 140, 165, 190, + * 215, 240, 290, 340, 420, 430, 450, 470, 490 + * + * Valid gain values may be queried with \ref rtlsdr_get_tuner_gains function. + * + * \param dev the device handle given by rtlsdr_open() + * \param gain in tenths of a dB, 115 means 11.5 dB. + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain); + +/*! + * Set the bandwidth for the device. + * + * \param dev the device handle given by rtlsdr_open() + * \param bw bandwidth in Hz. Zero means automatic BW selection. + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw); + +/*! + * Get actual gain the device is configured to. + * + * \param dev the device handle given by rtlsdr_open() + * \return 0 on error, gain in tenths of a dB, 115 means 11.5 dB. + */ +RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev); + +/*! + * Set the intermediate frequency gain for the device. + * + * \param dev the device handle given by rtlsdr_open() + * \param stage intermediate frequency gain stage number (1 to 6 for E4000) + * \param gain in tenths of a dB, -30 means -3.0 dB. + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain); + +/*! + * Set the gain mode (automatic/manual) for the device. + * Manual gain mode must be enabled for the gain setter function to work. + * + * \param dev the device handle given by rtlsdr_open() + * \param manual gain mode, 1 means manual gain mode shall be enabled. + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual); + +/*! + * Set the sample rate for the device, also selects the baseband filters + * according to the requested sample rate for tuners where this is possible. + * + * \param dev the device handle given by rtlsdr_open() + * \param samp_rate the sample rate to be set, possible values are: + * 225001 - 300000 Hz + * 900001 - 3200000 Hz + * sample loss is to be expected for rates > 2400000 + * \return 0 on success, -EINVAL on invalid rate + */ +RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate); + +/*! + * Get actual sample rate the device is configured to. + * + * \param dev the device handle given by rtlsdr_open() + * \return 0 on error, sample rate in Hz otherwise + */ +RTLSDR_API uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev); + +/*! + * Enable test mode that returns an 8 bit counter instead of the samples. + * The counter is generated inside the RTL2832. + * + * \param dev the device handle given by rtlsdr_open() + * \param test mode, 1 means enabled, 0 disabled + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on); + +/*! + * Enable or disable the internal digital AGC of the RTL2832. + * + * \param dev the device handle given by rtlsdr_open() + * \param digital AGC mode, 1 means enabled, 0 disabled + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on); + +/*! + * Enable or disable the direct sampling mode. When enabled, the IF mode + * of the RTL2832 is activated, and rtlsdr_set_center_freq() will control + * the IF-frequency of the DDC, which can be used to tune from 0 to 28.8 MHz + * (xtal frequency of the RTL2832). + * + * \param dev the device handle given by rtlsdr_open() + * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on); + +/*! + * Get state of the direct sampling mode + * + * \param dev the device handle given by rtlsdr_open() + * \return -1 on error, 0 means disabled, 1 I-ADC input enabled + * 2 Q-ADC input enabled + */ +RTLSDR_API int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev); + +/*! + * Enable or disable offset tuning for zero-IF tuners, which allows to avoid + * problems caused by the DC offset of the ADCs and 1/f noise. + * + * \param dev the device handle given by rtlsdr_open() + * \param on 0 means disabled, 1 enabled + * \return 0 on success + */ +RTLSDR_API int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on); + +/*! + * Get state of the offset tuning mode + * + * \param dev the device handle given by rtlsdr_open() + * \return -1 on error, 0 means disabled, 1 enabled + */ +RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev); + +/* streaming functions */ + +RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev); + +RTLSDR_API int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read); + +typedef void(*rtlsdr_read_async_cb_t)(unsigned char *buf, uint32_t len, void *ctx); + +/*! + * Read samples from the device asynchronously. This function will block until + * it is being canceled using rtlsdr_cancel_async() + * + * NOTE: This function is deprecated and is subject for removal. + * + * \param dev the device handle given by rtlsdr_open() + * \param cb callback function to return received samples + * \param ctx user specific context to pass via the callback function + * \return 0 on success + */ +RTLSDR_API int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx); + +/*! + * Read samples from the device asynchronously. This function will block until + * it is being canceled using rtlsdr_cancel_async() + * + * \param dev the device handle given by rtlsdr_open() + * \param cb callback function to return received samples + * \param ctx user specific context to pass via the callback function + * \param buf_num optional buffer count, buf_num * buf_len = overall buffer size + * set to 0 for default buffer count (15) + * \param buf_len optional buffer length, must be multiple of 512, + * should be a multiple of 16384 (URB size), set to 0 + * for default buffer length (16 * 32 * 512) + * \return 0 on success + */ +RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev, + rtlsdr_read_async_cb_t cb, + void *ctx, + uint32_t buf_num, + uint32_t buf_len); + +/*! + * Cancel all pending asynchronous operations on the device. + * + * \param dev the device handle given by rtlsdr_open() + * \return 0 on success + */ +RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* __RTL_SDR_H */ diff --git a/include/rtl-sdr_export.h b/include/rtl-sdr_export.h new file mode 100644 index 0000000..69e178d --- /dev/null +++ b/include/rtl-sdr_export.h @@ -0,0 +1,47 @@ +/* + * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver + * Copyright (C) 2012 by Hoernchen + * + * 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 . + */ + +#ifndef RTLSDR_EXPORT_H +#define RTLSDR_EXPORT_H + +#if defined __GNUC__ +# if __GNUC__ >= 4 +# define __SDR_EXPORT __attribute__((visibility("default"))) +# define __SDR_IMPORT __attribute__((visibility("default"))) +# else +# define __SDR_EXPORT +# define __SDR_IMPORT +# endif +#elif _MSC_VER +# define __SDR_EXPORT __declspec(dllexport) +# define __SDR_IMPORT __declspec(dllimport) +#else +# define __SDR_EXPORT +# define __SDR_IMPORT +#endif + +#ifndef rtlsdr_STATIC +# ifdef rtlsdr_EXPORTS +# define RTLSDR_API __SDR_EXPORT +# else +# define RTLSDR_API __SDR_IMPORT +# endif +#else +#define RTLSDR_API +#endif +#endif /* RTLSDR_EXPORT_H */ diff --git a/include/rtlsdr_i2c.h b/include/rtlsdr_i2c.h new file mode 100644 index 0000000..7676689 --- /dev/null +++ b/include/rtlsdr_i2c.h @@ -0,0 +1,8 @@ +#ifndef __I2C_H +#define __I2C_H + +uint32_t rtlsdr_get_tuner_clock(void *dev); +int rtlsdr_i2c_write_fn(void *dev, uint8_t addr, uint8_t *buf, int len); +int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len); + +#endif diff --git a/include/tuner_e4k.h b/include/tuner_e4k.h new file mode 100644 index 0000000..79591ce --- /dev/null +++ b/include/tuner_e4k.h @@ -0,0 +1,222 @@ +#ifndef _E4K_TUNER_H +#define _E4K_TUNER_H + +/* + * Elonics E4000 tuner driver + * + * (C) 2011-2012 by Harald Welte + * (C) 2012 by Sylvain Munaut + * (C) 2012 by Hoernchen + * + * All Rights Reserved + * + * 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 . + */ + +#define E4K_I2C_ADDR 0xc8 +#define E4K_CHECK_ADDR 0x02 +#define E4K_CHECK_VAL 0x40 + +enum e4k_reg { + E4K_REG_MASTER1 = 0x00, + E4K_REG_MASTER2 = 0x01, + E4K_REG_MASTER3 = 0x02, + E4K_REG_MASTER4 = 0x03, + E4K_REG_MASTER5 = 0x04, + E4K_REG_CLK_INP = 0x05, + E4K_REG_REF_CLK = 0x06, + E4K_REG_SYNTH1 = 0x07, + E4K_REG_SYNTH2 = 0x08, + E4K_REG_SYNTH3 = 0x09, + E4K_REG_SYNTH4 = 0x0a, + E4K_REG_SYNTH5 = 0x0b, + E4K_REG_SYNTH6 = 0x0c, + E4K_REG_SYNTH7 = 0x0d, + E4K_REG_SYNTH8 = 0x0e, + E4K_REG_SYNTH9 = 0x0f, + E4K_REG_FILT1 = 0x10, + E4K_REG_FILT2 = 0x11, + E4K_REG_FILT3 = 0x12, + // gap + E4K_REG_GAIN1 = 0x14, + E4K_REG_GAIN2 = 0x15, + E4K_REG_GAIN3 = 0x16, + E4K_REG_GAIN4 = 0x17, + // gap + E4K_REG_AGC1 = 0x1a, + E4K_REG_AGC2 = 0x1b, + E4K_REG_AGC3 = 0x1c, + E4K_REG_AGC4 = 0x1d, + E4K_REG_AGC5 = 0x1e, + E4K_REG_AGC6 = 0x1f, + E4K_REG_AGC7 = 0x20, + E4K_REG_AGC8 = 0x21, + // gap + E4K_REG_AGC11 = 0x24, + E4K_REG_AGC12 = 0x25, + // gap + E4K_REG_DC1 = 0x29, + E4K_REG_DC2 = 0x2a, + E4K_REG_DC3 = 0x2b, + E4K_REG_DC4 = 0x2c, + E4K_REG_DC5 = 0x2d, + E4K_REG_DC6 = 0x2e, + E4K_REG_DC7 = 0x2f, + E4K_REG_DC8 = 0x30, + // gap + E4K_REG_QLUT0 = 0x50, + E4K_REG_QLUT1 = 0x51, + E4K_REG_QLUT2 = 0x52, + E4K_REG_QLUT3 = 0x53, + // gap + E4K_REG_ILUT0 = 0x60, + E4K_REG_ILUT1 = 0x61, + E4K_REG_ILUT2 = 0x62, + E4K_REG_ILUT3 = 0x63, + // gap + E4K_REG_DCTIME1 = 0x70, + E4K_REG_DCTIME2 = 0x71, + E4K_REG_DCTIME3 = 0x72, + E4K_REG_DCTIME4 = 0x73, + E4K_REG_PWM1 = 0x74, + E4K_REG_PWM2 = 0x75, + E4K_REG_PWM3 = 0x76, + E4K_REG_PWM4 = 0x77, + E4K_REG_BIAS = 0x78, + E4K_REG_CLKOUT_PWDN = 0x7a, + E4K_REG_CHFILT_CALIB = 0x7b, + E4K_REG_I2C_REG_ADDR = 0x7d, + // FIXME +}; + +#define E4K_MASTER1_RESET (1 << 0) +#define E4K_MASTER1_NORM_STBY (1 << 1) +#define E4K_MASTER1_POR_DET (1 << 2) + +#define E4K_SYNTH1_PLL_LOCK (1 << 0) +#define E4K_SYNTH1_BAND_SHIF 1 + +#define E4K_SYNTH7_3PHASE_EN (1 << 3) + +#define E4K_SYNTH8_VCOCAL_UPD (1 << 2) + +#define E4K_FILT3_DISABLE (1 << 5) + +#define E4K_AGC1_LIN_MODE (1 << 4) +#define E4K_AGC1_LNA_UPDATE (1 << 5) +#define E4K_AGC1_LNA_G_LOW (1 << 6) +#define E4K_AGC1_LNA_G_HIGH (1 << 7) + +#define E4K_AGC6_LNA_CAL_REQ (1 << 4) + +#define E4K_AGC7_MIX_GAIN_AUTO (1 << 0) +#define E4K_AGC7_GAIN_STEP_5dB (1 << 5) + +#define E4K_AGC8_SENS_LIN_AUTO (1 << 0) + +#define E4K_AGC11_LNA_GAIN_ENH (1 << 0) + +#define E4K_DC1_CAL_REQ (1 << 0) + +#define E4K_DC5_I_LUT_EN (1 << 0) +#define E4K_DC5_Q_LUT_EN (1 << 1) +#define E4K_DC5_RANGE_DET_EN (1 << 2) +#define E4K_DC5_RANGE_EN (1 << 3) +#define E4K_DC5_TIMEVAR_EN (1 << 4) + +#define E4K_CLKOUT_DISABLE 0x96 + +#define E4K_CHFCALIB_CMD (1 << 0) + +#define E4K_AGC1_MOD_MASK 0xF + +enum e4k_agc_mode { + E4K_AGC_MOD_SERIAL = 0x0, + E4K_AGC_MOD_IF_PWM_LNA_SERIAL = 0x1, + E4K_AGC_MOD_IF_PWM_LNA_AUTONL = 0x2, + E4K_AGC_MOD_IF_PWM_LNA_SUPERV = 0x3, + E4K_AGC_MOD_IF_SERIAL_LNA_PWM = 0x4, + E4K_AGC_MOD_IF_PWM_LNA_PWM = 0x5, + E4K_AGC_MOD_IF_DIG_LNA_SERIAL = 0x6, + E4K_AGC_MOD_IF_DIG_LNA_AUTON = 0x7, + E4K_AGC_MOD_IF_DIG_LNA_SUPERV = 0x8, + E4K_AGC_MOD_IF_SERIAL_LNA_AUTON = 0x9, + E4K_AGC_MOD_IF_SERIAL_LNA_SUPERV = 0xa, +}; + +enum e4k_band { + E4K_BAND_VHF2 = 0, + E4K_BAND_VHF3 = 1, + E4K_BAND_UHF = 2, + E4K_BAND_L = 3, +}; + +enum e4k_mixer_filter_bw { + E4K_F_MIX_BW_27M = 0, + E4K_F_MIX_BW_4M6 = 8, + E4K_F_MIX_BW_4M2 = 9, + E4K_F_MIX_BW_3M8 = 10, + E4K_F_MIX_BW_3M4 = 11, + E4K_F_MIX_BW_3M = 12, + E4K_F_MIX_BW_2M7 = 13, + E4K_F_MIX_BW_2M3 = 14, + E4K_F_MIX_BW_1M9 = 15, +}; + +enum e4k_if_filter { + E4K_IF_FILTER_MIX, + E4K_IF_FILTER_CHAN, + E4K_IF_FILTER_RC +}; +struct e4k_pll_params { + uint32_t fosc; + uint32_t intended_flo; + uint32_t flo; + uint16_t x; + uint8_t z; + uint8_t r; + uint8_t r_idx; + uint8_t threephase; +}; + +struct e4k_state { + void *i2c_dev; + uint8_t i2c_addr; + enum e4k_band band; + struct e4k_pll_params vco; + void *rtl_dev; +}; + +int e4k_init(struct e4k_state *e4k); +int e4k_standby(struct e4k_state *e4k, int enable); +int e4k_if_gain_set(struct e4k_state *e4k, uint8_t stage, int8_t value); +int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value); +int e4k_commonmode_set(struct e4k_state *e4k, int8_t value); +int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq); +int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p); +uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint32_t intended_flo); +int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter); +int e4k_if_filter_bw_set(struct e4k_state *e4k, enum e4k_if_filter filter, + uint32_t bandwidth); +int e4k_if_filter_chan_enable(struct e4k_state *e4k, int on); +int e4k_rf_filter_set(struct e4k_state *e4k); + +int e4k_manual_dc_offset(struct e4k_state *e4k, int8_t iofs, int8_t irange, int8_t qofs, int8_t qrange); +int e4k_dc_offset_calibrate(struct e4k_state *e4k); +int e4k_dc_offset_gen_table(struct e4k_state *e4k); + +int e4k_set_lna_gain(struct e4k_state *e4k, int32_t gain); +int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual); +int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain); +#endif /* _E4K_TUNER_H */ diff --git a/include/tuner_fc0012.h b/include/tuner_fc0012.h new file mode 100644 index 0000000..9dd5356 --- /dev/null +++ b/include/tuner_fc0012.h @@ -0,0 +1,36 @@ +/* + * Fitipower FC0012 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * modified for use in librtlsdr + * Copyright (C) 2012 Steve Markgraf + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _FC0012_H_ +#define _FC0012_H_ + +#define FC0012_I2C_ADDR 0xc6 +#define FC0012_CHECK_ADDR 0x00 +#define FC0012_CHECK_VAL 0xa1 + +int fc0012_init(void *dev); +int fc0012_set_params(void *dev, uint32_t freq, uint32_t bandwidth); +int fc0012_set_gain(void *dev, int gain); + +#endif diff --git a/include/tuner_fc0013.h b/include/tuner_fc0013.h new file mode 100644 index 0000000..68a26ee --- /dev/null +++ b/include/tuner_fc0013.h @@ -0,0 +1,37 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * modified for use in librtlsdr + * Copyright (C) 2012 Steve Markgraf + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _FC0013_H_ +#define _FC0013_H_ + +#define FC0013_I2C_ADDR 0xc6 +#define FC0013_CHECK_ADDR 0x00 +#define FC0013_CHECK_VAL 0xa3 + +int fc0013_init(void *dev); +int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth); +int fc0013_set_gain_mode(void *dev, int manual); +int fc0013_set_lna_gain(void *dev, int gain); + +#endif diff --git a/include/tuner_fc2580.h b/include/tuner_fc2580.h new file mode 100644 index 0000000..9ebd935 --- /dev/null +++ b/include/tuner_fc2580.h @@ -0,0 +1,127 @@ +#ifndef __TUNER_FC2580_H +#define __TUNER_FC2580_H + +#define BORDER_FREQ 2600000 //2.6GHz : The border frequency which determines whether Low VCO or High VCO is used +#define USE_EXT_CLK 0 //0 : Use internal XTAL Oscillator / 1 : Use External Clock input +#define OFS_RSSI 57 + +#define FC2580_I2C_ADDR 0xac +#define FC2580_CHECK_ADDR 0x01 +#define FC2580_CHECK_VAL 0x56 + +typedef enum { + FC2580_UHF_BAND, + FC2580_L_BAND, + FC2580_VHF_BAND, + FC2580_NO_BAND +} fc2580_band_type; + +typedef enum { + FC2580_FCI_FAIL, + FC2580_FCI_SUCCESS +} fc2580_fci_result_type; + +enum FUNCTION_STATUS +{ + FUNCTION_SUCCESS, + FUNCTION_ERROR, +}; + +extern void fc2580_wait_msec(void *pTuner, int a); + +fc2580_fci_result_type fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val); +fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data); + +/*============================================================================== + fc2580 initial setting + + This function is a generic function which gets called to initialize + + fc2580 in DVB-H mode or L-Band TDMB mode + + + + ifagc_mode + type : integer + 1 : Internal AGC + 2 : Voltage Control Mode + +==============================================================================*/ +fc2580_fci_result_type fc2580_set_init(void *pTuner, int ifagc_mode, unsigned int freq_xtal ); + +/*============================================================================== + fc2580 frequency setting + + This function is a generic function which gets called to change LO Frequency + + of fc2580 in DVB-H mode or L-Band TDMB mode + + + + f_lo + Value of target LO Frequency in 'kHz' unit + ex) 2.6GHz = 2600000 + +==============================================================================*/ +fc2580_fci_result_type fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned int freq_xtal ); + + +/*============================================================================== + fc2580 filter BW setting + + This function is a generic function which gets called to change Bandwidth + + frequency of fc2580's channel selection filter + + + + filter_bw + 1 : 1.53MHz(TDMB) + 6 : 6MHz + 7 : 7MHz + 8 : 7.8MHz + + +==============================================================================*/ +fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, unsigned int freq_xtal ); + +// The following context is FC2580 tuner API source code +// Definitions + +// AGC mode +enum FC2580_AGC_MODE +{ + FC2580_AGC_INTERNAL = 1, + FC2580_AGC_EXTERNAL = 2, +}; + + +// Bandwidth mode +enum FC2580_BANDWIDTH_MODE +{ + FC2580_BANDWIDTH_1530000HZ = 1, + FC2580_BANDWIDTH_6000000HZ = 6, + FC2580_BANDWIDTH_7000000HZ = 7, + FC2580_BANDWIDTH_8000000HZ = 8, +}; + +// Manipulaing functions +int +fc2580_Initialize( + void *pTuner + ); + +int +fc2580_SetRfFreqHz( + void *pTuner, + unsigned long RfFreqHz + ); + +// Extra manipulaing functions +int +fc2580_SetBandwidthMode( + void *pTuner, + int BandwidthMode + ); + +#endif diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h new file mode 100644 index 0000000..f6c206a --- /dev/null +++ b/include/tuner_r82xx.h @@ -0,0 +1,120 @@ +/* + * Rafael Micro R820T/R828D driver + * + * Copyright (C) 2013 Mauro Carvalho Chehab + * Copyright (C) 2013 Steve Markgraf + * + * This driver is a heavily modified version of the driver found in the + * Linux kernel: + * http://git.linuxtv.org/linux-2.6.git/history/HEAD:/drivers/media/tuners/r820t.c + * + * 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 . + */ + +#ifndef R82XX_H +#define R82XX_H + +#define R820T_I2C_ADDR 0x34 +#define R828D_I2C_ADDR 0x74 +#define R828D_XTAL_FREQ 16000000 + +#define R82XX_CHECK_ADDR 0x00 +#define R82XX_CHECK_VAL 0x69 + +#define R82XX_IF_FREQ 3570000 + +#define REG_SHADOW_START 5 +#define NUM_REGS 30 +#define NUM_IMR 5 +#define IMR_TRIAL 9 + +#define VER_NUM 49 + +enum r82xx_chip { + CHIP_R820T, + CHIP_R620D, + CHIP_R828D, + CHIP_R828, + CHIP_R828S, + CHIP_R820C, +}; + +enum r82xx_tuner_type { + TUNER_RADIO = 1, + TUNER_ANALOG_TV, + TUNER_DIGITAL_TV +}; + +enum r82xx_xtal_cap_value { + XTAL_LOW_CAP_30P = 0, + XTAL_LOW_CAP_20P, + XTAL_LOW_CAP_10P, + XTAL_LOW_CAP_0P, + XTAL_HIGH_CAP_0P +}; + +struct r82xx_config { + uint8_t i2c_addr; + uint32_t xtal; + enum r82xx_chip rafael_chip; + unsigned int max_i2c_msg_len; + int use_predetect; +}; + +struct r82xx_priv { + struct r82xx_config *cfg; + + uint8_t regs[NUM_REGS]; + uint8_t buf[NUM_REGS + 1]; + enum r82xx_xtal_cap_value xtal_cap_sel; + uint16_t pll; /* kHz */ + uint32_t int_freq; + uint8_t fil_cal_code; + uint8_t input; + int has_lock; + int init_done; + + /* Store current mode */ + uint32_t delsys; + enum r82xx_tuner_type type; + + uint32_t bw; /* in MHz */ + + void *rtl_dev; +}; + +struct r82xx_freq_range { + uint32_t freq; + uint8_t open_d; + uint8_t rf_mux_ploy; + uint8_t tf_c; + uint8_t xtal_cap20p; + uint8_t xtal_cap10p; + uint8_t xtal_cap0p; +}; + +enum r82xx_delivery_system { + SYS_UNDEFINED, + SYS_DVBT, + SYS_DVBT2, + SYS_ISDBT, +}; + +int r82xx_standby(struct r82xx_priv *priv); +int r82xx_init(struct r82xx_priv *priv); +int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq); +int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain); +int r82xx_set_bandwidth(struct r82xx_priv *priv, int bandwidth, uint32_t rate); + +#endif diff --git a/src/librtlsdr.c b/src/librtlsdr.c new file mode 100644 index 0000000..cdf1ca9 --- /dev/null +++ b/src/librtlsdr.c @@ -0,0 +1,1938 @@ +/* + * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver + * Copyright (C) 2012-2014 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 + +/* libusb < 1.0.9 doesn't have libusb_handle_events_timeout_completed */ +#ifndef HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED +#define libusb_handle_events_timeout_completed(ctx, tv, c) \ + libusb_handle_events_timeout(ctx, tv) +#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_r82xx.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 +}; + +#define FIR_LEN 16 + +/* + * FIR coefficients. + * + * The filter is running at XTal frequency. It is symmetric filter with 32 + * coefficients. Only first 16 coefficients are specified, the other 16 + * use the same values but in reversed order. The first coefficient in + * the array is the outer one, the last, the last is the inner one. + * First 8 coefficients are 8 bit signed integers, the next 8 coefficients + * are 12 bit signed integers. All coefficients have the same weight. + * + * Default FIR coefficients used for DAB/FM by the Windows driver, + * the DVB driver uses different ones + */ +static const int fir_default[FIR_LEN] = { + -54, -36, -41, -40, -32, -14, 14, 53, /* 8 bit signed */ + 101, 156, 215, 273, 327, 372, 404, 421 /* 12 bit signed */ +}; + +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; + int async_cancel; + /* rtl demod context */ + uint32_t rate; /* Hz */ + uint32_t rtl_xtal; /* Hz */ + int fir[FIR_LEN]; + 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 bw; + uint32_t offs_freq; /* Hz */ + int corr; /* ppm */ + int gain; /* tenth dB */ + struct e4k_state e4k_s; + struct r82xx_config r82xx_c; + struct r82xx_priv r82xx_p; + /* status */ + int dev_lost; + int driver_active; + unsigned int xfer_errors; +}; + +void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); +static int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq); + +/* 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) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return e4k_standby(&devt->e4k_s, 1); +} +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) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + devt->r82xx_p.rtl_dev = dev; + + if (devt->tuner_type == RTLSDR_TUNER_R828D) { + devt->r82xx_c.i2c_addr = R828D_I2C_ADDR; + devt->r82xx_c.rafael_chip = CHIP_R828D; + } else { + devt->r82xx_c.i2c_addr = R820T_I2C_ADDR; + devt->r82xx_c.rafael_chip = CHIP_R820T; + } + + rtlsdr_get_xtal_freq(devt, NULL, &devt->r82xx_c.xtal); + + devt->r82xx_c.max_i2c_msg_len = 8; + devt->r82xx_c.use_predetect = 0; + devt->r82xx_p.cfg = &devt->r82xx_c; + + return r82xx_init(&devt->r82xx_p); +} +int r820t_exit(void *dev) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_standby(&devt->r82xx_p); +} + +int r820t_set_freq(void *dev, uint32_t freq) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_freq(&devt->r82xx_p, freq); +} + +int r820t_set_bw(void *dev, int bw) { + int r; + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + + r = r82xx_set_bandwidth(&devt->r82xx_p, bw, devt->rate); + if(r < 0) + return r; + r = rtlsdr_set_if_freq(devt, r); + if (r) + return r; + return rtlsdr_set_center_freq(devt, devt->freq); +} + +int r820t_set_gain(void *dev, int gain) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_gain(&devt->r82xx_p, 1, gain); +} +int r820t_set_gain_mode(void *dev, int manual) { + rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; + return r82xx_set_gain(&devt->r82xx_p, manual, 0); +} + +/* 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 + }, + { + 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" }, + { 0x0bda, 0x2838, "Generic RTL2832U OEM" }, + { 0x0413, 0x6680, "DigitalNow Quad DVB-T PCI-E card" }, + { 0x0413, 0x6f0f, "Leadtek WinFast DTV Dongle mini D" }, + { 0x0458, 0x707f, "Genius TVGo DVB-T03 USB dongle (Ver. B)" }, + { 0x0ccd, 0x00a9, "Terratec Cinergy T Stick Black (rev 1)" }, + { 0x0ccd, 0x00b3, "Terratec NOXON DAB/DAB+ USB dongle (rev 1)" }, + { 0x0ccd, 0x00b4, "Terratec Deutschlandradio DAB Stick" }, + { 0x0ccd, 0x00b5, "Terratec NOXON DAB Stick - Radio Energy" }, + { 0x0ccd, 0x00b7, "Terratec Media Broadcast DAB Stick" }, + { 0x0ccd, 0x00b8, "Terratec BR DAB Stick" }, + { 0x0ccd, 0x00b9, "Terratec WDR DAB Stick" }, + { 0x0ccd, 0x00c0, "Terratec MuellerVerlag DAB Stick" }, + { 0x0ccd, 0x00c6, "Terratec Fraunhofer DAB Stick" }, + { 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)" }, + { 0x15f4, 0x0131, "Astrometa DVB-T/DVB-T2" }, + { 0x185b, 0x0620, "Compro Videomate U620F"}, + { 0x185b, 0x0650, "Compro Videomate U650F"}, + { 0x185b, 0x0680, "Compro Videomate U680F"}, + { 0x1b80, 0xd393, "GIGABYTE GT-U7300" }, + { 0x1b80, 0xd394, "DIKOM USB-DVBT HD" }, + { 0x1b80, 0xd395, "Peak 102569AGPK" }, + { 0x1b80, 0xd397, "KWorld KW-UB450-T USB DVB-T Pico TV" }, + { 0x1b80, 0xd398, "Zaapa ZT-MINDVBZP" }, + { 0x1b80, 0xd39d, "SVEON STV20 DVB-T USB & FM" }, + { 0x1b80, 0xd3a4, "Twintech UT-40" }, + { 0x1b80, 0xd3a8, "ASUS U3100MINI_PLUS_V2" }, + { 0x1b80, 0xd3af, "SVEON STV27 DVB-T USB & FM" }, + { 0x1b80, 0xd3b0, "SVEON STV21 DVB-T USB & FM" }, + { 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" }, + { 0x1f4d, 0xa803, "Sweex DVB-T USB" }, + { 0x1f4d, 0xb803, "GTek T803" }, + { 0x1f4d, 0xc803, "Lifeview LV5TDeluxe" }, + { 0x1f4d, 0xd286, "MyGica TD312" }, + { 0x1f4d, 0xd803, "PROlectrix DV107669" }, +}; + +#define DEFAULT_BUF_NUMBER 15 +#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 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; +} + +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) +{ + uint16_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); +} + +int rtlsdr_set_fir(rtlsdr_dev_t *dev) +{ + uint8_t fir[20]; + + int i; + /* format: int8_t[8] */ + for (i = 0; i < 8; ++i) { + const int val = dev->fir[i]; + if (val < -128 || val > 127) { + return -1; + } + fir[i] = val; + } + /* format: int12_t[8] */ + for (i = 0; i < 8; i += 2) { + const int val0 = dev->fir[8+i]; + const int val1 = dev->fir[8+i+1]; + if (val0 < -2048 || val0 > 2047 || val1 < -2048 || val1 > 2047) { + return -1; + } + fir[8+i*3/2] = val0 >> 4; + fir[8+i*3/2+1] = (val0 << 4) | ((val1 >> 8) & 0x0f); + fir[8+i*3/2+2] = val1; + } + + for (i = 0; i < (int)sizeof(fir); i++) { + if (rtlsdr_demod_write_reg(dev, 1, 0x1c + i, fir[i], 1)) + return -1; + } + + return 0; +} + +void rtlsdr_init_baseband(rtlsdr_dev_t *dev) +{ + unsigned int i; + + /* 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); + + rtlsdr_set_fir(dev); + + /* 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; +} + +static 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 and r82xx structure */ + if (rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc) || + rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal)) + 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 and r82xx structure */ + if (rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc) || + rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal)) + 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 r82xx_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: + case RTLSDR_TUNER_R828D: + ptr = r82xx_gains; len = sizeof(r82xx_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_bandwidth(rtlsdr_dev_t *dev, uint32_t bw) +{ + int r = 0; + + if (!dev || !dev->tuner) + return -1; + + if (dev->tuner->set_bw) { + rtlsdr_set_i2c_repeater(dev, 1); + r = dev->tuner->set_bw(dev, bw > 0 ? bw : dev->rate); + rtlsdr_set_i2c_repeater(dev, 0); + if (r) + return r; + dev->bw = bw; + } + return r; +} + +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, real_rsamp_ratio; + double real_rate; + + if (!dev) + return -1; + + /* check if the rate is supported by the resampler */ + if ((samp_rate <= 225000) || (samp_rate > 3200000) || + ((samp_rate > 300000) && (samp_rate <= 900000))) { + fp