diff options
-rw-r--r-- | README.md | 78 | ||||
-rw-r--r-- | binding/CMakeLists.txt | 9 | ||||
-rw-r--r-- | binding/radio-binding.c | 49 | ||||
-rw-r--r-- | binding/radio_impl.h | 37 | ||||
-rw-r--r-- | binding/radio_impl_kingfisher.c | 396 | ||||
-rw-r--r-- | binding/radio_impl_kingfisher.h | 25 | ||||
-rw-r--r-- | binding/radio_impl_rtlsdr.c | 75 | ||||
-rw-r--r-- | binding/radio_impl_rtlsdr.h | 25 |
8 files changed, 632 insertions, 62 deletions
@@ -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 <afb/afb-service-itf.h> #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 <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <glib.h> +#include <fcntl.h> +#include <sys/stat.h> + +#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 */ + |