From 5262f5ee41e91d564e4b3447da0a1cd625d351eb Mon Sep 17 00:00:00 2001 From: Harunobu Kurokawa Date: Thu, 21 Dec 2017 20:13:52 -0500 Subject: Add Kingfisher Si4689 support Add conditionally compilable support for the Si4689 radio on the M3ULCB Kingfisher infotainment board. The codebase has been refactored to allow multiple radio implementations, and when Kingfisher support is enabled, the binding will first look for a USB RTL-SDR adapter, then fallback to the Kingfisher Si4689 if one is not found. This allows easily switching to a RTL-SDR adapter if this initial Kingfisher support encounters issues. The back end implementation relies on a patched version of the "si_ctl" utility from Cogent Embedded's Kingfisher BSP changes. The modifications to it add FM band plan selection and scanning threshold tweaking for poor radio environments. Audio output is achieved by looping the radio's PulseAudio source to the appropriate sink depending on 4A or non-4A operation. For 4A compatibility, the PulseAudio source is created if it does not exist, which currently is the case due to PulseAudio's udev module being disabled when 4A is enabled. Additionally, the FM band plan for Japan has been corrected to go to 95 MHz, and a README.md file has been added documenting the optional configuration that can be done via /etc/xdg/AGL.conf for band plan selection and scanning sensitivity. Change-Id: I204906fed741d917fc3b8be962deadb4e59989db Signed-off-by: Scott Murray Signed-off-by: Harunobu Kurokawa --- README.md | 78 ++++++++ binding/CMakeLists.txt | 9 +- binding/radio-binding.c | 49 +++-- binding/radio_impl.h | 37 ++-- binding/radio_impl_kingfisher.c | 396 ++++++++++++++++++++++++++++++++++++++++ binding/radio_impl_kingfisher.h | 25 +++ binding/radio_impl_rtlsdr.c | 75 +++++--- binding/radio_impl_rtlsdr.h | 25 +++ 8 files changed, 632 insertions(+), 62 deletions(-) create mode 100644 binding/radio_impl_kingfisher.c create mode 100644 binding/radio_impl_kingfisher.h create mode 100644 binding/radio_impl_rtlsdr.h diff --git a/README.md b/README.md index 1bfe68d..ab76ec2 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,81 @@ JSON response has a single field **frequency** which is the currently tuned freq ### station_found Event JSON Response JSON response has a single field **value** of the frequency of the discovered radio station. + +# AGL Radio Tuner Binding + +## FM Band Plan Selection + +The FM band plan may be selected by adding: +``` +fmbandplan=X +``` +to the [radio] section in /etc/xdg/AGL.conf, where X is one of the +following strings: + +US = United States / Canada +JP = Japan +EU = European Union +ITU-1 +ITU-2 + +Example: +``` +[radio] +fmbandplan=JP +``` + +## Implementation Specific Confguration + +### USB RTL-SDR adapter + +The scanning sensitivity can be tweaked by adding: +``` +scan_squelch_level=X +``` +to the [radio] section in /etc/xdg/AGL.conf, where X is an integer. Lower +values make the scanning more sensitive. Default value is 140. + +Example: +``` +[radio] +scan_squelch_level=70 +``` + +### M3ULCB Kingfisher Si4689 + +The scanning sensitivity can be tweaked by adding: +``` +scan_valid_snr_threshold=X +scan_valid_rssi_threshold=Y +``` +to the [radio] section in /etc/xdg/AGL.conf, where X and Y are integers +between -127 and 127. The SNR value is in units of dB, and the RSSI is in +units of dBuV. Lower values make the scanning more sensitive. Default +values in the Si4689 are 10 and 17, respectively. You may determine the +values that the Si4689 is seeing when tuning by examining the results of +tuning in the systemd journal, looking for lines like: + +Example: +``` +[radio] +scan_valid_snr_threshold=7 +scan_valid_rssi_threshold=10 +``` + +## Known Issues + +### M3ULCB Kingfisher + +Initial setup for a new Kingfisher board requires booting an image with +Kingfisher support and running the commands: +``` +si_init +si_firmware_update +``` +This installs the provided firmware into the flash attached to the Si4689. + +Since all operations are currently done by calling a patched version of +Cogent Embedded's si_ctl utility, scanning currently cannot be interrupted. +Additionally, sometimes a failure in scanning seems to result in muted +state that currently has not been debugged. diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt index 8f339e4..d0d23ed 100644 --- a/binding/CMakeLists.txt +++ b/binding/CMakeLists.txt @@ -21,12 +21,18 @@ PROJECT_TARGET_ADD(radio-binding) # Define project Targets - add_library(${TARGET_NAME} MODULE + set(radio_SOURCES radio-binding.c radio_output.c radio_impl_rtlsdr.c rtl_fm.c convenience/convenience.c) + if(HAVE_KINGFISHER) + set(radio_SOURCES ${radio_SOURCES} radio_impl_kingfisher.c) + add_definitions(-DHAVE_KINGFISHER) + endif() + + add_library(${TARGET_NAME} MODULE ${radio_SOURCES}) # Binder exposes a unique public entry point SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES @@ -42,4 +48,3 @@ PROJECT_TARGET_ADD(radio-binding) # installation directory INSTALL(TARGETS ${TARGET_NAME} LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}) - diff --git a/binding/radio-binding.c b/binding/radio-binding.c index f9b7e34..63c2491 100644 --- a/binding/radio-binding.c +++ b/binding/radio-binding.c @@ -29,6 +29,12 @@ #include #include "radio_impl.h" +#include "radio_impl_rtlsdr.h" +#ifdef HAVE_KINGFISHER +#include "radio_impl_kingfisher.h" +#endif + +static radio_impl_ops_t *radio_impl_ops; static struct afb_event freq_event; static struct afb_event scan_event; @@ -71,14 +77,14 @@ static void frequency(struct afb_req request) char *p; frequency = strtoul(value, &p, 10); if(frequency && *p == '\0') { - radio_impl_set_frequency(frequency); + radio_impl_ops->set_frequency(frequency); } else { afb_req_fail(request, "failed", "Invalid scan direction"); return; } } ret_json = json_object_new_object(); - frequency = radio_impl_get_frequency(); + frequency = radio_impl_ops->get_frequency(); json_object_object_add(ret_json, "frequency", json_object_new_int((int32_t) frequency)); afb_req_success(request, ret_json, NULL); } @@ -119,14 +125,14 @@ static void band(struct afb_req request) } } if(valid) { - radio_impl_set_band(band); + radio_impl_ops->set_band(band); } else { afb_req_fail(request, "failed", "Invalid band"); return; } } ret_json = json_object_new_object(); - band = radio_impl_get_band(); + band = radio_impl_ops->get_band(); sprintf(band_name, "%s", band == BAND_AM ? "AM" : "FM"); json_object_object_add(ret_json, "band", json_object_new_string(band_name)); afb_req_success(request, ret_json, NULL); @@ -174,7 +180,7 @@ static void band_supported(struct afb_req request) ret_json = json_object_new_object(); json_object_object_add(ret_json, "supported", - json_object_new_int(radio_impl_band_supported(band))); + json_object_new_int(radio_impl_ops->band_supported(band))); afb_req_success(request, ret_json, NULL); } @@ -220,8 +226,8 @@ static void frequency_range(struct afb_req request) return; } ret_json = json_object_new_object(); - min_frequency = radio_impl_get_min_frequency(band); - max_frequency = radio_impl_get_max_frequency(band); + min_frequency = radio_impl_ops->get_min_frequency(band); + max_frequency = radio_impl_ops->get_max_frequency(band); json_object_object_add(ret_json, "min", json_object_new_int((int32_t) min_frequency)); json_object_object_add(ret_json, "max", json_object_new_int((int32_t) max_frequency)); afb_req_success(request, ret_json, NULL); @@ -268,7 +274,7 @@ static void frequency_step(struct afb_req request) return; } ret_json = json_object_new_object(); - step = radio_impl_get_frequency_step(band); + step = radio_impl_ops->get_frequency_step(band); json_object_object_add(ret_json, "step", json_object_new_int((int32_t) step)); afb_req_success(request, ret_json, NULL); } @@ -281,7 +287,7 @@ static void frequency_step(struct afb_req request) */ static void start(struct afb_req request) { - radio_impl_start(); + radio_impl_ops->start(); afb_req_success(request, NULL, NULL); } @@ -293,7 +299,7 @@ static void start(struct afb_req request) */ static void stop(struct afb_req request) { - radio_impl_stop(); + radio_impl_ops->stop(); afb_req_success(request, NULL, NULL); } @@ -335,7 +341,7 @@ static void scan_start(struct afb_req request) afb_req_fail(request, "failed", "Invalid direction"); return; } - radio_impl_scan_start(direction, scan_callback, NULL); + radio_impl_ops->scan_start(direction, scan_callback, NULL); afb_req_success(request, NULL, NULL); } @@ -347,7 +353,7 @@ static void scan_start(struct afb_req request) */ static void scan_stop(struct afb_req request) { - radio_impl_scan_stop(); + radio_impl_ops->scan_stop(); afb_req_success(request, NULL, NULL); } @@ -387,14 +393,14 @@ static void stereo_mode(struct afb_req request) } } if(valid) { - radio_impl_set_stereo_mode(mode); + radio_impl_ops->set_stereo_mode(mode); } else { afb_req_fail(request, "failed", "Invalid mode"); return; } } ret_json = json_object_new_object(); - mode = radio_impl_get_stereo_mode(); + mode = radio_impl_ops->get_stereo_mode(); sprintf(mode_name, "%s", mode == MONO ? "mono" : "stereo"); json_object_object_add(ret_json, "mode", json_object_new_string(mode_name)); afb_req_success(request, ret_json, NULL); @@ -467,11 +473,20 @@ static int init() freq_event = afb_daemon_make_event("frequency"); scan_event = afb_daemon_make_event("station_found"); - rc = radio_impl_init(); + // Look for RTL-SDR USB adapter + radio_impl_ops = &rtlsdr_impl_ops; + rc = radio_impl_ops->init(); +#ifdef HAVE_KINGFISHER + if(rc != 0) { + // Look for Kingfisher Si4689 + radio_impl_ops = &kf_impl_ops; + rc = radio_impl_ops->init(); + } +#endif if(rc == 0) { - radio_impl_set_frequency_callback(freq_callback, NULL); + printf("%s found\n", radio_impl_ops->name); + radio_impl_ops->set_frequency_callback(freq_callback, NULL); } - return rc; } diff --git a/binding/radio_impl.h b/binding/radio_impl.h index 79e91a4..4b4a2f7 100644 --- a/binding/radio_impl.h +++ b/binding/radio_impl.h @@ -38,39 +38,44 @@ typedef enum { STEREO } radio_stereo_mode_t; -int radio_impl_init(void); +typedef struct { + char *name; -uint32_t radio_impl_get_frequency(void); + int (*init)(void); -void radio_impl_set_frequency(uint32_t frequency); + uint32_t (*get_frequency)(void); -void radio_impl_set_frequency_callback(radio_freq_callback_t callback, + void (*set_frequency)(uint32_t frequency); + + void (*set_frequency_callback)(radio_freq_callback_t callback, void *data); -radio_band_t radio_impl_get_band(void); + radio_band_t (*get_band)(void); -void radio_impl_set_band(radio_band_t band); + void (*set_band)(radio_band_t band); -int radio_impl_band_supported(radio_band_t band); + int (*band_supported)(radio_band_t band); -uint32_t radio_impl_get_min_frequency(radio_band_t band); + uint32_t (*get_min_frequency)(radio_band_t band); -uint32_t radio_impl_get_max_frequency(radio_band_t band); + uint32_t (*get_max_frequency)(radio_band_t band); -uint32_t radio_impl_get_frequency_step(radio_band_t band); + uint32_t (*get_frequency_step)(radio_band_t band); -void radio_impl_start(void); + void (*start)(void); -void radio_impl_stop(void); + void (*stop)(void); -void radio_impl_scan_start(radio_scan_direction_t direction, + void (*scan_start)(radio_scan_direction_t direction, radio_scan_callback_t callback, void *data); -void radio_impl_scan_stop(void); + void (*scan_stop)(void); + + radio_stereo_mode_t (*get_stereo_mode)(void); -radio_stereo_mode_t radio_impl_get_stereo_mode(void); + void (*set_stereo_mode)(radio_stereo_mode_t mode); -void radio_impl_set_stereo_mode(radio_stereo_mode_t mode); +} radio_impl_ops_t; #endif /* _RADIO_IMPL_H */ diff --git a/binding/radio_impl_kingfisher.c b/binding/radio_impl_kingfisher.c new file mode 100644 index 0000000..069ca10 --- /dev/null +++ b/binding/radio_impl_kingfisher.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2017 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "radio_impl.h" + +#define SI_INIT "/usr/bin/si_init" +#define SI_CTL "/usr/bin/si_ctl" + +// Structure to describe FM band plans, all values in Hz. +typedef struct { + char *name; + uint32_t min; + uint32_t max; + uint32_t step; +} fm_band_plan_t; + +static fm_band_plan_t known_fm_band_plans[5] = { + { .name = "US", .min = 87900000, .max = 107900000, .step = 200000 }, + { .name = "JP", .min = 76000000, .max = 95000000, .step = 100000 }, + { .name = "EU", .min = 87500000, .max = 108000000, .step = 50000 }, + { .name = "ITU-1", .min = 87500000, .max = 108000000, .step = 50000 }, + { .name = "ITU-2", .min = 87900000, .max = 107900000, .step = 50000 } +}; + +static unsigned int bandplan = 0; +static bool present; +static bool active; +static uint32_t current_frequency; +static int scan_valid_snr_threshold = 128; +static int scan_valid_rssi_threshold = 128; +static bool scanning = false; + +static void (*freq_callback)(uint32_t, void*); +static void *freq_callback_data; + +static uint32_t kf_get_min_frequency(radio_band_t band); +static void kf_scan_stop(void); + +static int kf_init(void) +{ + GKeyFile* conf_file; + int conf_file_present = 0; + char *value_str; + char cmd[128]; + int rc; + char *output_sink; + struct stat statbuf; + + if(present) + return -1; + + // Check for Cogent's si_init script and si_ctl utility + if(stat(SI_INIT, &statbuf) != 0) + return -1; + if(stat(SI_CTL, &statbuf) != 0) + return -1; + + // Load settings from configuration file if it exists + conf_file = g_key_file_new(); + if(conf_file && + g_key_file_load_from_dirs(conf_file, + "AGL.conf", + (const gchar**) g_get_system_config_dirs(), + 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, + "radio", + "fmbandplan", + NULL); + if(value_str) { + unsigned int i; + for(i = 0; + i < sizeof(known_fm_band_plans) / sizeof(fm_band_plan_t); + i++) { + if(!strcasecmp(value_str, known_fm_band_plans[i].name)) { + bandplan = i; + break; + } + } + } + } + + 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_valid_snr_threshold", + &error); + if(!error) { + fprintf(stderr, "Scan valid SNR level set to %d\n", n); + scan_valid_snr_threshold = n; + } + + error = NULL; + n = g_key_file_get_integer(conf_file, + "radio", + "scan_valid_rssi_threshold", + &error); + if(!error) { + fprintf(stderr, "Scan valid SNR level set to %d\n", n); + scan_valid_rssi_threshold = n; + } + + g_key_file_free(conf_file); + } + + rc = system(SI_INIT); + if(rc != 0) { + fprintf(stderr, "si_init failed, rc = %d", rc); + return -1; + } + + fprintf(stderr, "Using FM Bandplan: %s\n", known_fm_band_plans[bandplan].name); + current_frequency = kf_get_min_frequency(BAND_FM); + sprintf(cmd, + "%s /dev/i2c-11 0x65 -b fm -p %s -t %d -u %d -c %d", + SI_CTL, + known_fm_band_plans[bandplan].name, + scan_valid_snr_threshold, + scan_valid_rssi_threshold, + current_frequency / 1000); + rc = system(cmd); + if(rc != 0) { + fprintf(stderr, "%s failed, rc = %d", SI_CTL, rc); + return -1; + } + + // Handle 4A disabling PA udev module + if(system("pactl list sources short |grep -q alsa_input.radio") != 0) { + // Set up radio source + if(system("pactl load-module module-alsa-source device=hw:radio,0 name=radio") != 0) { + fprintf(stderr, "radio PA source creation failed!\n"); + return -1; + } + } + + // Set initial state to muted + rc = system("pactl set-source-mute alsa_input.radio 1"); + if(rc != 0) { + fprintf(stderr, "pactl failed, rc = %d", rc); + return -1; + } + + // Set up loopback to output sink + output_sink = getenv("PULSE_SINK"); + if(!output_sink) { + // On non-4A, loopback to the default output sink + output_sink = "0"; + } + sprintf(cmd, "pactl load-module module-loopback source=alsa_input.radio sink=%s", output_sink); + rc = system(cmd); + if(rc != 0) { + fprintf(stderr, "pactl failed, rc = %d", rc); + return -1; + } + + present = true; + return 0; +} + +static uint32_t kf_get_frequency(void) +{ + return current_frequency; +} + +static void kf_set_frequency(uint32_t frequency) +{ + char cmd[128]; + int rc; + + if(!present) + return; + + if(scanning) + return; + + if(frequency < known_fm_band_plans[bandplan].min || + frequency > known_fm_band_plans[bandplan].max) + return; + + kf_scan_stop(); + sprintf(cmd, "%s /dev/i2c-11 0x65 -c %d", SI_CTL, frequency / 1000); + rc = system(cmd); + if(rc == 0) + current_frequency = frequency; + + if(freq_callback) + freq_callback(current_frequency, freq_callback_data); +} + +static void kf_set_frequency_callback(radio_freq_callback_t callback, + void *data) +{ + freq_callback = callback; + freq_callback_data = data; +} + +static radio_band_t kf_get_band(void) +{ + return BAND_FM; +} + +static void kf_set_band(radio_band_t band) +{ + // We only support FM, so do nothing +} + +static int kf_band_supported(radio_band_t band) +{ + if(band == BAND_FM) + return 1; + return 0; +} + +static uint32_t kf_get_min_frequency(radio_band_t band) +{ + return known_fm_band_plans[bandplan].min; +} + +static uint32_t kf_get_max_frequency(radio_band_t band) +{ + return known_fm_band_plans[bandplan].max; +} + +static uint32_t kf_get_frequency_step(radio_band_t band) +{ + uint32_t ret = 0; + + switch (band) { + case BAND_AM: + ret = 1000; // 1 kHz + break; + case BAND_FM: + ret = known_fm_band_plans[bandplan].step; + break; + default: + break; + } + return ret; +} + +static void kf_start(void) +{ + int rc; + + if(!present) + return; + + if(!active) { + rc = system("pactl set-source-mute alsa_input.radio 0"); + if(rc != 0) + fprintf(stderr, "pactl set-source-mute failed!\n"); + active = true; + } +} + +static void kf_stop(void) +{ + int rc; + + if(!present) + return; + + if(active) { + active = false; + rc = system("pactl set-source-mute alsa_input.radio 1"); + if(rc != 0) + fprintf(stderr, "pactl set-source-mute failed!\n"); + } +} + +static void kf_scan_start(radio_scan_direction_t direction, + radio_scan_callback_t callback, + void *data) +{ + int rc; + char cmd[128]; + char line[128]; + uint32_t new_frequency = 0; + FILE *fp; + + if(!present) + return; + + if(scanning) + return; + + scanning = true; + sprintf(cmd, "%s /dev/i2c-11 0x65 -l %s", SI_CTL, direction == SCAN_FORWARD ? "up" : "down"); + fp = popen(cmd, "r"); + if(fp == NULL) { + fprintf(stderr, "Could not run: %s!\n", cmd); + return; + } + // Look for "Frequency:" in output + while(fgets(line, 128, fp) != NULL) { + if(strncmp("Frequency:", line, 10) == 0) { + new_frequency = atoi(line + 10); + //fprintf(stderr, "%s: got new_frequency = %d\n", __FUNCTION__, new_frequency); + break; + } + } + + // Make sure si_ctl has finished + rc = pclose(fp); + if(rc != 0) { + // Make sure we reset to original frequency, the Si4689 seems + // to auto-mute sometimes on failed scans, this hopefully works + // around that. + new_frequency = 0; + } + + if(new_frequency) { + current_frequency = new_frequency * 1000; + + // Push up the new frequency + // This is more efficient than calling kf_set_frequency and calling + // out to si_ctl again. + if(freq_callback) + freq_callback(current_frequency, freq_callback_data); + } else { + // Assume no station found, go back to starting frequency + kf_set_frequency(current_frequency); + } + + // Push up scan state + if(callback) + callback(current_frequency, data); + + scanning = false; +} + +static void kf_scan_stop(void) +{ + // ATM, it's not straightforward to stop a scan since we're using the si_ctl utility... +} + +static radio_stereo_mode_t kf_get_stereo_mode(void) +{ + return STEREO; +} + +static void kf_set_stereo_mode(radio_stereo_mode_t mode) +{ + // We only support stereo, so do nothing +} + +radio_impl_ops_t kf_impl_ops = { + .name = "Kingfisher Si4689", + .init = kf_init, + .get_frequency = kf_get_frequency, + .set_frequency = kf_set_frequency, + .set_frequency_callback = kf_set_frequency_callback, + .get_band = kf_get_band, + .set_band = kf_set_band, + .band_supported = kf_band_supported, + .get_min_frequency = kf_get_min_frequency, + .get_max_frequency = kf_get_max_frequency, + .get_frequency_step = kf_get_frequency_step, + .start = kf_start, + .stop = kf_stop, + .scan_start = kf_scan_start, + .scan_stop = kf_scan_stop, + .get_stereo_mode = kf_get_stereo_mode, + .set_stereo_mode = kf_set_stereo_mode +}; diff --git a/binding/radio_impl_kingfisher.h b/binding/radio_impl_kingfisher.h new file mode 100644 index 0000000..5d4f064 --- /dev/null +++ b/binding/radio_impl_kingfisher.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _RADIO_IMPL_KINGFISHER_H +#define _RADIO_IMPL_KINGFISHER_H + +#include "radio_impl.h" + +extern radio_impl_ops_t kf_impl_ops; + +#endif /* _RADIO_IMPL_KINGFISHER_H */ + diff --git a/binding/radio_impl_rtlsdr.c b/binding/radio_impl_rtlsdr.c index 4364fd5..22d627e 100644 --- a/binding/radio_impl_rtlsdr.c +++ b/binding/radio_impl_rtlsdr.c @@ -35,7 +35,7 @@ typedef struct { static fm_band_plan_t known_fm_band_plans[5] = { { .name = "US", .min = 87900000, .max = 107900000, .step = 200000 }, - { .name = "JP", .min = 76100000, .max = 89900000, .step = 100000 }, + { .name = "JP", .min = 76000000, .max = 95000000, .step = 100000 }, { .name = "EU", .min = 87500000, .max = 108000000, .step = 50000 }, { .name = "ITU-1", .min = 87500000, .max = 108000000, .step = 50000 }, { .name = "ITU-2", .min = 87900000, .max = 107900000, .step = 50000 } @@ -46,13 +46,16 @@ static bool present; static bool active; static uint32_t current_frequency; +static uint32_t rtlsdr_get_min_frequency(radio_band_t band); +static void rtlsdr_scan_stop(void); + static void rtl_output_callback(int16_t *result, int result_len, void *ctx) { if(active) radio_output_write((char*) result, result_len * 2); } -int radio_impl_init(void) +static int rtlsdr_init(void) { GKeyFile* conf_file; int conf_file_present = 0; @@ -91,7 +94,7 @@ int radio_impl_init(void) } fprintf(stderr, "Using FM Bandplan: %s\n", known_fm_band_plans[bandplan].name); - current_frequency = radio_impl_get_min_frequency(BAND_FM); + current_frequency = rtlsdr_get_min_frequency(BAND_FM); if(rtl_fm_init(current_frequency, 200000, 48000, rtl_output_callback, NULL) < 0) { return -1; } @@ -106,8 +109,6 @@ int radio_impl_init(void) "radio", "scan_squelch_level", &error); - //error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && - //error->code != G_KEY_FILE_ERROR_INVALID_VALUE) { if(!error) { fprintf(stderr, "Scanning squelch level set to %d\n", n); rtl_fm_scan_set_squelch_level(n); @@ -130,12 +131,12 @@ int radio_impl_init(void) return 0; } -uint32_t radio_impl_get_frequency(void) +static uint32_t rtlsdr_get_frequency(void) { return current_frequency; } -void radio_impl_set_frequency(uint32_t frequency) +static void rtlsdr_set_frequency(uint32_t frequency) { if(!present) return; @@ -144,45 +145,45 @@ void radio_impl_set_frequency(uint32_t frequency) frequency > known_fm_band_plans[bandplan].max) return; - radio_impl_scan_stop(); + rtlsdr_scan_stop(); current_frequency = frequency; rtl_fm_set_freq(frequency); } -void radio_impl_set_frequency_callback(radio_freq_callback_t callback, - void *data) +static void rtlsdr_set_frequency_callback(radio_freq_callback_t callback, + void *data) { rtl_fm_set_freq_callback(callback, data); } -radio_band_t radio_impl_get_band(void) +static radio_band_t rtlsdr_get_band(void) { return BAND_FM; } -void radio_impl_set_band(radio_band_t band) +static void rtlsdr_set_band(radio_band_t band) { // We only support FM, so do nothing } -int radio_impl_band_supported(radio_band_t band) +static int rtlsdr_band_supported(radio_band_t band) { if(band == BAND_FM) return 1; return 0; } -uint32_t radio_impl_get_min_frequency(radio_band_t band) +static uint32_t rtlsdr_get_min_frequency(radio_band_t band) { return known_fm_band_plans[bandplan].min; } -uint32_t radio_impl_get_max_frequency(radio_band_t band) +static uint32_t rtlsdr_get_max_frequency(radio_band_t band) { return known_fm_band_plans[bandplan].max; } -uint32_t radio_impl_get_frequency_step(radio_band_t band) +static uint32_t rtlsdr_get_frequency_step(radio_band_t band) { uint32_t ret = 0; @@ -199,7 +200,7 @@ uint32_t radio_impl_get_frequency_step(radio_band_t band) return ret; } -void radio_impl_start(void) +static void rtlsdr_start(void) { if(!present) return; @@ -213,7 +214,7 @@ void radio_impl_start(void) } } -void radio_impl_stop(void) +static void rtlsdr_stop(void) { if(!present) return; @@ -226,29 +227,49 @@ void radio_impl_stop(void) } } -void radio_impl_scan_start(radio_scan_direction_t direction, - radio_scan_callback_t callback, - void *data) +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, - radio_impl_get_frequency_step(BAND_FM), - radio_impl_get_min_frequency(BAND_FM), - radio_impl_get_max_frequency(BAND_FM)); + rtlsdr_get_frequency_step(BAND_FM), + rtlsdr_get_min_frequency(BAND_FM), + rtlsdr_get_max_frequency(BAND_FM)); } -void radio_impl_scan_stop(void) +static void rtlsdr_scan_stop(void) { rtl_fm_scan_stop(); } -radio_stereo_mode_t radio_impl_get_stereo_mode(void) +static radio_stereo_mode_t rtlsdr_get_stereo_mode(void) { return STEREO; } -void radio_impl_set_stereo_mode(radio_stereo_mode_t mode) +static void rtlsdr_set_stereo_mode(radio_stereo_mode_t mode) { // We only support stereo, so do nothing } + +radio_impl_ops_t rtlsdr_impl_ops = { + .name = "RTL-SDR USB adapter", + .init = rtlsdr_init, + .get_frequency = rtlsdr_get_frequency, + .set_frequency = rtlsdr_set_frequency, + .set_frequency_callback = rtlsdr_set_frequency_callback, + .get_band = rtlsdr_get_band, + .set_band = rtlsdr_set_band, + .band_supported = rtlsdr_band_supported, + .get_min_frequency = rtlsdr_get_min_frequency, + .get_max_frequency = rtlsdr_get_max_frequency, + .get_frequency_step = rtlsdr_get_frequency_step, + .start = rtlsdr_start, + .stop = rtlsdr_stop, + .scan_start = rtlsdr_scan_start, + .scan_stop = rtlsdr_scan_stop, + .get_stereo_mode = rtlsdr_get_stereo_mode, + .set_stereo_mode = rtlsdr_set_stereo_mode +}; diff --git a/binding/radio_impl_rtlsdr.h b/binding/radio_impl_rtlsdr.h new file mode 100644 index 0000000..6c83338 --- /dev/null +++ b/binding/radio_impl_rtlsdr.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _RADIO_IMPL_RTLSDR_H +#define _RADIO_IMPL_RTLSDR_H + +#include "radio_impl.h" + +extern radio_impl_ops_t rtlsdr_impl_ops; + +#endif /* _RADIO_IMPL_RTLSDR_H */ + -- cgit 1.2.3-korg