diff options
Diffstat (limited to 'binding/radio_impl_rtlsdr.c')
-rw-r--r-- | binding/radio_impl_rtlsdr.c | 270 |
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; } |