summaryrefslogtreecommitdiffstats
path: root/binding/radio_impl_rtlsdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'binding/radio_impl_rtlsdr.c')
-rw-r--r--binding/radio_impl_rtlsdr.c270
1 files changed, 216 insertions, 54 deletions
diff --git a/binding/radio_impl_rtlsdr.c b/binding/radio_impl_rtlsdr.c
index 22d627e..7fd4d69 100644
--- a/binding/radio_impl_rtlsdr.c
+++ b/binding/radio_impl_rtlsdr.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 Konsulko Group
+ * Copyright (C) 2017,2018 Konsulko Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,11 +19,18 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <errno.h>
#include <glib.h>
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
#include "radio_impl.h"
-#include "radio_output.h"
-#include "rtl_fm.h"
+
+#define HELPER_NAME "rtl_fm_helper"
+#define HELPER_MAX PATH_MAX + 64
// Structure to describe FM band plans, all values in Hz.
typedef struct {
@@ -42,27 +49,91 @@ static fm_band_plan_t known_fm_band_plans[5] = {
};
static unsigned int bandplan;
+static pid_t helper_pid;
+static int helper_in;
+static int helper_out;
+static pthread_mutex_t helper_mutex = PTHREAD_MUTEX_INITIALIZER;
static bool present;
static bool active;
+static bool scanning;
static uint32_t current_frequency;
+static radio_freq_callback_t freq_callback;
+static void *freq_callback_data;
static uint32_t rtlsdr_get_min_frequency(radio_band_t band);
+static void rtlsdr_set_frequency(uint32_t frequency);
static void rtlsdr_scan_stop(void);
-static void rtl_output_callback(int16_t *result, int result_len, void *ctx)
+/*
+ * Bi-directional popen implementation
+ * Based on one of the versions given in:
+ *
+ * https://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2
+ */
+static pid_t popen2(char *command, int *in_fd, int *out_fd)
{
- if(active)
- radio_output_write((char*) result, result_len * 2);
+ int pin[2], pout[2];
+ pid_t pid;
+
+ if(out_fd != NULL) {
+ if(pipe(pin) != 0)
+ return -1;
+ }
+ if(in_fd != NULL) {
+ if (pipe(pout) != 0) {
+ if(out_fd != NULL) {
+ close(pin[0]);
+ close(pin[1]);
+ }
+ return -1;
+ }
+ }
+
+ pid = fork();
+ if(pid < 0) {
+ if(out_fd != NULL) {
+ close(pin[0]);
+ close(pin[1]);
+ }
+ if(in_fd != NULL) {
+ close(pout[0]);
+ close(pout[1]);
+ }
+ return pid;
+ }
+ if(pid == 0) {
+ if(out_fd != NULL) {
+ close(pin[1]);
+ dup2(pin[0], 0);
+ }
+ if(in_fd != NULL) {
+ close(pout[0]);
+ dup2(pout[1], 1);
+ }
+ execlp(command, command, NULL);
+ perror("Error:");
+ exit(1);
+ }
+ if(in_fd != NULL) {
+ close(pout[1]);
+ *in_fd = pout[0];
+ }
+ if(out_fd != NULL) {
+ close(pin[0]);
+ *out_fd = pin[1];
+ }
+ return pid;
}
static int rtlsdr_init(void)
{
- GKeyFile* conf_file;
- int conf_file_present = 0;
+ GKeyFile *conf_file;
char *value_str;
+ char *rootdir;
+ char *helper_path;
if(present)
- return -1;
+ return 0;
// Load settings from configuration file if it exists
conf_file = g_key_file_new();
@@ -73,7 +144,6 @@ static int rtlsdr_init(void)
NULL,
G_KEY_FILE_KEEP_COMMENTS,
NULL) == TRUE) {
- conf_file_present = 1;
// Set band plan if it is specified
value_str = g_key_file_get_string(conf_file,
@@ -92,42 +162,43 @@ static int rtlsdr_init(void)
}
}
}
- fprintf(stderr, "Using FM Bandplan: %s\n", known_fm_band_plans[bandplan].name);
+ // Start off with minimum bandplan frequency
current_frequency = rtlsdr_get_min_frequency(BAND_FM);
- if(rtl_fm_init(current_frequency, 200000, 48000, rtl_output_callback, NULL) < 0) {
- return -1;
- }
- if(conf_file_present) {
- GError *error = NULL;
- int n;
-
- // Allow over-riding scanning parameters just in case a demo
- // setup needs to do so to work reliably.
- n = g_key_file_get_integer(conf_file,
- "radio",
- "scan_squelch_level",
- &error);
- if(!error) {
- fprintf(stderr, "Scanning squelch level set to %d\n", n);
- rtl_fm_scan_set_squelch_level(n);
- }
+ rootdir = getenv("AFM_APP_INSTALL_DIR");
+ if(!rootdir)
+ return -1;
- error = NULL;
- n = g_key_file_get_integer(conf_file,
- "radio",
- "scan_squelch_limit",
- &error);
- if(!error) {
- fprintf(stderr, "Scanning squelch limit set to %d\n", n);
- rtl_fm_scan_set_squelch_limit(n);
- }
+ // Run helper to detect adapter
+ helper_path = malloc(HELPER_MAX);
+ if(!helper_path)
+ return -ENOMEM;
+ if(snprintf(helper_path, HELPER_MAX, "%s/bin/%s --detect", rootdir, HELPER_NAME) == HELPER_MAX) {
+ AFB_ERROR("Could not create command for \"%s --detect\"", HELPER_NAME);
+ return -EINVAL;
+ }
+ if(system(helper_path) != 0) {
+ free(helper_path);
+ return -1;
+ }
- g_key_file_free(conf_file);
+ // Run helper
+ if(snprintf(helper_path, PATH_MAX, "%s/bin/%s", rootdir, HELPER_NAME) == PATH_MAX) {
+ AFB_ERROR("Could not create path to %s", HELPER_NAME);
+ return -EINVAL;
}
+ helper_pid = popen2(helper_path, &helper_out, &helper_in);
+ if(helper_pid < 0) {
+ AFB_ERROR("Could not run %s!", HELPER_NAME);
+ return -1;
+ }
+ AFB_DEBUG("%s started", HELPER_NAME);
+ free(helper_path);
present = true;
+ rtlsdr_set_frequency(current_frequency);
+
return 0;
}
@@ -138,6 +209,12 @@ static uint32_t rtlsdr_get_frequency(void)
static void rtlsdr_set_frequency(uint32_t frequency)
{
+ char cmd[64];
+ char output[128];
+ bool found = false;
+ int rc;
+ uint32_t n;
+
if(!present)
return;
@@ -145,19 +222,45 @@ static void rtlsdr_set_frequency(uint32_t frequency)
frequency > known_fm_band_plans[bandplan].max)
return;
- rtlsdr_scan_stop();
+ if(scanning)
+ rtlsdr_scan_stop();
+
current_frequency = frequency;
- rtl_fm_set_freq(frequency);
+ sprintf(cmd, "F=%u\n", frequency);
+ pthread_mutex_lock(&helper_mutex);
+ rc = write(helper_in, cmd, strlen(cmd));
+ if(rc < 0) {
+ pthread_mutex_unlock(&helper_mutex);
+ return;
+ }
+ while(!found) {
+ rc = read(helper_out, output, 128);
+ if(rc <= 0)
+ break;
+ if(rc == 128)
+ rc = 127;
+ output[rc] = '\0';
+ if(output[0] == 'F') {
+ if(sscanf(output, "F=%u\n", &n) == 1) {
+ if(freq_callback)
+ freq_callback(n, freq_callback_data);
+ found = true;
+ }
+ }
+ }
+ pthread_mutex_unlock(&helper_mutex);
}
static void rtlsdr_set_frequency_callback(radio_freq_callback_t callback,
void *data)
{
- rtl_fm_set_freq_callback(callback, data);
+ freq_callback = callback;
+ freq_callback_data = data;
}
static radio_band_t rtlsdr_get_band(void)
{
+ // We only support FM
return BAND_FM;
}
@@ -206,10 +309,15 @@ static void rtlsdr_start(void)
return;
if(!active) {
- if(radio_output_start() != 0)
+ int rc;
+ char cmd[64];
+
+ sprintf(cmd, "START\n");
+ pthread_mutex_lock(&helper_mutex);
+ rc = write(helper_in, cmd, strlen(cmd));
+ pthread_mutex_unlock(&helper_mutex);
+ if(rc < 0)
return;
-
- rtl_fm_start();
active = true;
}
}
@@ -220,10 +328,14 @@ static void rtlsdr_stop(void)
return;
if(active) {
- active = false;
- radio_output_stop();
- rtl_fm_stop();
+ int rc;
+ char cmd[64];
+ active = false;
+ sprintf(cmd, "STOP\n");
+ pthread_mutex_lock(&helper_mutex);
+ write(helper_in, cmd, strlen(cmd));
+ pthread_mutex_unlock(&helper_mutex);
}
}
@@ -231,21 +343,71 @@ static void rtlsdr_scan_start(radio_scan_direction_t direction,
radio_scan_callback_t callback,
void *data)
{
- rtl_fm_scan_start(direction == SCAN_FORWARD ? 0 : 1,
- callback,
- data,
- rtlsdr_get_frequency_step(BAND_FM),
- rtlsdr_get_min_frequency(BAND_FM),
- rtlsdr_get_max_frequency(BAND_FM));
+ int rc;
+ char cmd[64];
+ char output[128];
+ bool found = false;
+ uint32_t n;
+
+ if(!active || scanning)
+ return;
+
+ scanning = true;
+ sprintf(cmd, "S=%s\n", direction == SCAN_FORWARD ? "UP" : "DOWN");
+ pthread_mutex_lock(&helper_mutex);
+ if(!scanning) {
+ pthread_mutex_unlock(&helper_mutex);
+ return;
+ }
+ rc = write(helper_in, cmd, strlen(cmd));
+ pthread_mutex_unlock(&helper_mutex);
+ if(rc < 0)
+ return;
+ while(!found) {
+ pthread_mutex_lock(&helper_mutex);
+ if(!scanning) {
+ pthread_mutex_unlock(&helper_mutex);
+ break;
+ }
+ rc = read(helper_out, output, 128);
+ pthread_mutex_unlock(&helper_mutex);
+ if(rc <= 0)
+ break;
+ if(rc == 128)
+ rc = 127;
+ output[rc] = '\0';
+ if(output[0] == 'F') {
+ if(sscanf(output, "F=%u\n", &n) == 1) {
+ current_frequency = n;
+ if(freq_callback)
+ freq_callback(n, freq_callback_data);
+ }
+ }
+ if(output[0] == 'S') {
+ if(sscanf(output, "S=%u\n", &n) == 1) {
+ if(callback)
+ callback(n, data);
+ found = true;
+ scanning = false;
+ }
+ }
+ }
}
static void rtlsdr_scan_stop(void)
{
- rtl_fm_scan_stop();
+ char cmd[64];
+
+ sprintf(cmd, "S=STOP\n");
+ pthread_mutex_lock(&helper_mutex);
+ scanning = false;
+ write(helper_in, cmd, strlen(cmd));
+ pthread_mutex_unlock(&helper_mutex);
}
static radio_stereo_mode_t rtlsdr_get_stereo_mode(void)
{
+ // We only support stereo
return STEREO;
}