I've just released a new R828D based dongle and it needs some code changes to work. It would be great if the osmocom drivers could support it. 

The dongle is based on the R828D chip, but unlike prior R828D dongles which use 16 MHz, the V4 uses a 28.8 MHz LO. Also the three RF inputs are used, and connected via a triplexer to separate them into HF, VHF and UHF bands. Finally, the open_d pin is also used to activate some notch filters for common interference bands.

I've written the code so it detects an EEPROM string in the V4, and only applies the new code if that exists. This makes sure that the older R828D based dongles still work.

I'm not too familiar with the patch submission process, so please let me know if something needs to be changed.  And if anyone on the team needs/wants some new V4 dongles for testing, please let me know too.

Thanks, Carl

diff --git a/src/librtlsdr.c b/src/librtlsdr.c
index 096abae..d624732 100644
--- a/src/librtlsdr.c
+++ b/src/librtlsdr.c
@@ -119,6 +119,8 @@ struct rtlsdr_dev {
  int dev_lost;
  int driver_active;
  unsigned int xfer_errors;
+ char manufact[256];
+ char product[256];
 };
 
 void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val);
@@ -1430,6 +1432,16 @@ int rtlsdr_get_index_by_serial(const char *serial)
  return -3;
 }
 
+/* Returns true if the manufact_check and product_check strings match what is in the dongles EEPROM */
+int rtlsdr_check_dongle_model(rtlsdr_dev_t *dev, char* manufact_check, char* product_check)
+{
+ if ((strcmp(dev->manufact, manufact_check) == 0 && strcmp(dev->product, product_check) == 0))
+ return 1;
+
+ return 0;
+}
+
+
 int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index)
 {
  int r;
@@ -1528,6 +1540,9 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index)
  rtlsdr_init_baseband(dev);
  dev->dev_lost = 0;
 
+ /* Get device manufacturer and product id */
+ r = rtlsdr_get_usb_strings(dev, dev->manufact, dev->product, NULL);
+
  /* Probe tuners */
  rtlsdr_set_i2c_repeater(dev, 1);
 
@@ -1555,6 +1570,10 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index)
  reg = rtlsdr_i2c_read_reg(dev, R828D_I2C_ADDR, R82XX_CHECK_ADDR);
  if (reg == R82XX_CHECK_VAL) {
  fprintf(stderr, "Found Rafael Micro R828D tuner\n");
+
+ if (rtlsdr_check_dongle_model(dev, "RTLSDRBlog", "Blog V4"))
+ fprintf(stderr, "RTL-SDR Blog V4 Detected\n");
+
  dev->tuner_type = RTLSDR_TUNER_R828D;
  goto found;
  }
@@ -1588,7 +1607,11 @@ found:
 
  switch (dev->tuner_type) {
  case RTLSDR_TUNER_R828D:
- dev->tun_xtal = R828D_XTAL_FREQ;
+ /* If NOT an RTL-SDR Blog V4, set typical R828D 16 MHz freq. Otherwise, keep at 28.8 MHz. */
+ if (!(rtlsdr_check_dongle_model(dev, "RTLSDRBlog", "Blog V4"))) {
+ fprintf(stdout, "setting 16mhz");
+ dev->tun_xtal = R828D_XTAL_FREQ;
+ }
  /* fall-through */
  case RTLSDR_TUNER_R820T:
  /* disable Zero-IF mode */
diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c
index 997abd7..9b831f4 100644
--- a/src/tuner_r82xx.c
+++ b/src/tuner_r82xx.c
@@ -33,6 +33,10 @@
 #define MHZ(x) ((x)*1000*1000)
 #define KHZ(x) ((x)*1000)
 
+#define HF 1
+#define VHF 2
+#define UHF 3
+
 /*
  * Static constants
  */
@@ -1098,7 +1102,14 @@ int r82xx_set_bandwidth(struct r82xx_priv *priv, int bw, uint32_t rate)
 int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq)
 {
  int rc = -1;
- uint32_t lo_freq = freq + priv->int_freq;
+
+ int is_rtlsdr_blog_v4 = rtlsdr_check_dongle_model(priv->rtl_dev, "RTLSDRBlog", "Blog V4");
+
+ /* if it's an RTL-SDR Blog V4, automatically upconvert by 28.8 MHz if we tune to HF
+ * so that we don't need to manually set any upconvert offset in the SDR software */
+ uint32_t upconvert_freq = is_rtlsdr_blog_v4 ? ((freq < MHZ(28.8)) ? (freq + MHZ(28.8)) : freq) : freq;
+
+ uint32_t lo_freq = upconvert_freq + priv->int_freq;
  uint8_t air_cable1_in;
 
  rc = r82xx_set_mux(priv, lo_freq);
@@ -1109,16 +1120,60 @@ int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq)
  if (rc < 0 || !priv->has_lock)
  goto err;
 
- /* switch between 'Cable1' and 'Air-In' inputs on sticks with
- * R828D tuner. We switch at 345 MHz, because that's where the
- * noise-floor has about the same level with identical LNA
- * settings. The original driver used 320 MHz. */
- air_cable1_in = (freq > MHZ(345)) ? 0x00 : 0x60;
+ if (is_rtlsdr_blog_v4) {
+ /* determine if notch filters should be on or off notches are turned OFF
+ * when tuned within the notch band and ON when tuned outside the notch band.
+ */
+ uint8_t open_d = (freq <= MHZ(2.2) || (freq >= MHZ(85) && freq <= MHZ(112)) || (freq >= MHZ(172) && freq <= MHZ(242))) ? 0x00 : 0x08;
+ rc = r82xx_write_reg_mask(priv, 0x17, open_d, 0x08);
+
+ if (rc < 0)
+ return rc;
+
+ /* select tuner band based on frequency and only switch if there is a band change
+ *(to avoid excessive register writes when tuning rapidly)
+ */
+ uint8_t band = (freq <= MHZ(28.8)) ? HF : ((freq > MHZ(28.8) && freq < MHZ(250)) ? VHF : UHF);
+
+ /* switch between tuner inputs on the RTL-SDR Blog V4 */
+ if (band != priv->input) {
+ priv->input = band;
+
+ /* activate cable 2 (HF input) */
+ uint8_t cable_2_in = (band == HF) ? 0x08 : 0x00;
+ rc = r82xx_write_reg_mask(priv, 0x06, cable_2_in, 0x08);
 
- if ((priv->cfg->rafael_chip == CHIP_R828D) &&
-    (air_cable1_in != priv->input)) {
- priv->input = air_cable1_in;
- rc = r82xx_write_reg_mask(priv, 0x05, air_cable1_in, 0x60);
+ if (rc < 0)
+ goto err;
+
+ /* activate cable 1 (VHF input) */
+ uint8_t cable_1_in = (band == VHF) ? 0x40 : 0x00;
+ rc = r82xx_write_reg_mask(priv, 0x05, cable_1_in, 0x40);
+
+ if (rc < 0)
+ goto err;
+
+ /* activate air_in (UHF input) */
+ uint8_t air_in = (band == UHF) ? 0x00 : 0x20;
+ rc = r82xx_write_reg_mask(priv, 0x05, air_in, 0x20);
+
+ if (rc < 0)
+ goto err;
+ }
+ }
+ else /* Standard R828D dongle*/
+ {
+ /* switch between 'Cable1' and 'Air-In' inputs on sticks with
+ * R828D tuner. We switch at 345 MHz, because that's where the
+ * noise-floor has about the same level with identical LNA
+ * settings. The original driver used 320 MHz. */
+ air_cable1_in = (freq > MHZ(345)) ? 0x00 : 0x60;
+
+ if ((priv->cfg->rafael_chip == CHIP_R828D) &&
+ (air_cable1_in != priv->input)) {
+ priv->input = air_cable1_in;
+ rc = r82xx_write_reg_mask(priv, 0x05, air_cable1_in, 0x60);
+ }
  }
 
 err: