aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--binding/radio-binding.c92
-rw-r--r--binding/radio_impl_kingfisher.c134
2 files changed, 152 insertions, 74 deletions
diff --git a/binding/radio-binding.c b/binding/radio-binding.c
index 3f34150..5f67567 100644
--- a/binding/radio-binding.c
+++ b/binding/radio-binding.c
@@ -30,8 +30,6 @@
#include "radio_impl_rtlsdr.h"
#include "radio_impl_kingfisher.h"
-#define RADIO_MODE_NAME_MAXLEN 8
-
static radio_impl_ops_t *radio_impl_ops;
static struct afb_event freq_event;
@@ -103,7 +101,6 @@ static void rds(struct afb_req request)
}
ret_json = json_object_new_object();
-
rds = radio_impl_ops->get_rds_info();
json_object_object_add(ret_json, "rds", json_object_new_string(rds?rds:""));
free(rds);
@@ -391,7 +388,6 @@ static void stereo_mode(struct afb_req request)
const char *value = afb_req_value(request, "value");
int valid = 0;
radio_stereo_mode_t mode;
- char mode_name[RADIO_MODE_NAME_MAXLEN];
if(value) {
if(!strcasecmp(value, "mono")) {
@@ -423,8 +419,8 @@ static void stereo_mode(struct afb_req request)
}
ret_json = json_object_new_object();
mode = radio_impl_ops->get_stereo_mode();
- snprintf(mode_name, RADIO_MODE_NAME_MAXLEN, "%s", mode == MONO ? "mono" : "stereo");
- json_object_object_add(ret_json, "mode", json_object_new_string(mode_name));
+
+ json_object_object_add(ret_json, "mode", json_object_new_string(mode == MONO ? "mono" : "stereo"));
afb_req_success(request, ret_json, NULL);
}
@@ -495,32 +491,54 @@ static int init()
char *output = NULL;
#ifdef HAVE_4A_FRAMEWORK
- json_object *response;
-
+ json_object *response = NULL;
json_object *jsonData = json_object_new_object();
- json_object_object_add(jsonData, "audio_role", json_object_new_string("Radio"));
- json_object_object_add(jsonData, "endpoint_type", json_object_new_string("sink"));
- rc = afb_service_call_sync("ahl-4a", "stream_open", jsonData, &response);
- if(!rc) {
- json_object *valJson = NULL;
- json_object *val = NULL;
-
- rc = json_object_object_get_ex(response, "response", &valJson);
- if(rc) {
- rc = json_object_object_get_ex(valJson, "device_uri", &val);
- if(rc) {
- const char *jres_pcm = json_object_get_string(val);
- char *p;
- if((p = strchr(jres_pcm, ':'))) {
- output = strdup(p + 1);
- }
- }
- }
- } else {
- AFB_ERROR("afb_service_call_sync failed\n");
- return rc;
+
+ json_object_object_add(jsonData, "action", json_object_new_string("open"));
+ rc = afb_service_call_sync("ahl-4a", "get_roles", NULL, &response);
+ if (rc < 0) {
+ AFB_ERROR("Failed to query 4A about roles");
+ goto failed;
+ }
+ AFB_NOTICE("4A: available roles are '%s'", json_object_get_string(response));
+ json_object_put(response);
+
+ rc = afb_service_call_sync("ahl-4a", "radio", jsonData, &response);
+ if (rc < 0) {
+ AFB_ERROR("Failed to query 'radio' role to 4A");
+ goto failed;
+ }
+
+ json_object *valJson = NULL;
+ json_object *val = NULL;
+
+ rc = json_object_object_get_ex(response, "response", &valJson);
+ if (rc == 0) {
+ AFB_ERROR("Reply from 4A is missing a 'response' field");
+ goto failed_malformed;
}
-#endif
+
+ rc = json_object_object_get_ex(valJson, "device_uri", &val);
+ if (rc == 0) {
+ AFB_ERROR("Reply from 4A is missing a 'device_uri' field");
+ goto failed_malformed;
+ }
+
+ const char *jres_pcm = json_object_get_string(val);
+ char * p = strchr(jres_pcm, ':');
+
+ if (p == NULL) {
+ AFB_ERROR("Unable to parse 'device_uri' value field");
+ rc = -1;
+ goto failed_malformed;
+ }
+
+ if (asprintf(&output, "hw:%s", p + 1) < 0) {
+ AFB_ERROR("Insufficient memory");
+ rc = -1;
+ goto failed_malformed;
+ }
+#endif /* HAVE_4A_FRAMEWORK */
// Initialize event structures
freq_event = afb_daemon_make_event("frequency");
@@ -534,11 +552,23 @@ static int init()
radio_impl_ops = &kf_impl_ops;
rc = radio_impl_ops->init(output);
}
+ if (rc != 0) {
+ AFB_ERROR("No radio device found, exiting");
+ goto failed;
+ }
+
if(rc == 0) {
- printf("%s found\n", radio_impl_ops->name);
+ AFB_NOTICE("%s found\n", radio_impl_ops->name);
radio_impl_ops->set_frequency_callback(freq_callback, NULL);
}
free(output);
+
+#ifdef HAVE_4A_FRAMEWORK
+failed_malformed:
+ json_object_put(response);
+#endif /* HAVE_4A_FRAMEWORK */
+
+failed:
return rc;
}
diff --git a/binding/radio_impl_kingfisher.c b/binding/radio_impl_kingfisher.c
index e183c1b..7775d5b 100644
--- a/binding/radio_impl_kingfisher.c
+++ b/binding/radio_impl_kingfisher.c
@@ -22,7 +22,11 @@
#include <glib.h>
#include <fcntl.h>
#include <sys/stat.h>
+#include <json-c/json.h>
+#ifndef HAVE_4A_FRAMEWORK
#include <gst/gst.h>
+#endif /* !HAVE_4A_FRAMEWORK */
+
#include <afb/afb-binding.h>
#include "radio_impl.h"
@@ -30,9 +34,15 @@
#define SI_NODE "/sys/firmware/devicetree/base/si468x@0/compatible"
#define SI_INIT "/usr/bin/si_init"
#define SI_CTL "/usr/bin/si_ctl"
+#define SI_CTL_CMDLINE_MAXLEN 128
+#define SI_CTL_OUTPUT_MAXLEN 128
-#define GST_SINK_OPT_LEN 128
-#define GST_PIPELINE_LEN 256
+#ifndef HAVE_4A_FRAMEWORK
+#define GST_SINK_OPT_LEN 128
+#define GST_PIPELINE_LEN 256
+// GStreamer state
+static GstElement *pipeline;
+#endif /* !HAVE_4A_FRAMEWORK */
// Structure to describe FM band plans, all values in Hz.
typedef struct {
@@ -57,8 +67,8 @@ static int scan_valid_snr_threshold = 128;
static int scan_valid_rssi_threshold = 128;
static bool scanning = false;
-// GStreamer state
-static GstElement *pipeline;
+// stream state
+
static bool running;
static void (*freq_callback)(uint32_t, void*);
@@ -73,10 +83,12 @@ static int kf_init(const char *output)
int conf_file_present = 0;
struct stat statbuf;
char *value_str;
- char cmd[128];
+ char cmd[SI_CTL_CMDLINE_MAXLEN];
int rc;
+#ifndef HAVE_4A_FRAMEWORK
char output_sink_opt[GST_SINK_OPT_LEN];
char gst_pipeline_str[GST_PIPELINE_LEN];
+#endif /* !HAVE_4A_FRAMEWORK */
if(present)
return 0;
@@ -156,7 +168,8 @@ static int kf_init(const char *output)
AFB_INFO("Using FM Bandplan: %s", known_fm_band_plans[bandplan].name);
current_frequency = kf_get_min_frequency(BAND_FM);
- sprintf(cmd,
+ snprintf(cmd,
+ sizeof(cmd),
"%s /dev/i2c-12 0x65 -b fm -p %s -t %d -u %d -c %d",
SI_CTL,
known_fm_band_plans[bandplan].name,
@@ -169,6 +182,7 @@ static int kf_init(const char *output)
return -1;
}
+#ifndef HAVE_4A_FRAMEWORK
// Initialize GStreamer
gst_init(NULL, NULL);
@@ -185,17 +199,11 @@ static int kf_init(const char *output)
// NOTE: If muting without pausing is desired, it can likely be done
// by adding a "volume" element to the pipeline before the sink,
// and setting the volume to 0 to mute.
-#ifdef HAVE_4A_FRAMEWORK
- rc = snprintf(gst_pipeline_str,
- GST_PIPELINE_LEN,
- "alsasrc device=hw:radio ! queue ! audioconvert ! audioresample ! alsasink%s",
- output_sink_opt);
-#else
// Use PulseAudio output for compatibility with audiomanager / module_router
rc = snprintf(gst_pipeline_str,
- GST_PIPELINE_LEN,
- "alsasrc device=hw:radio ! queue ! audioconvert ! audioresample ! pulsesink stream-properties=\"props,media.role=radio\"");
-#endif
+ sizeof(gst_pipeline_str),
+ "alsasrc device=hw:radio ! queue ! audioconvert ! audioresample ! pulsesink stream-properties=\"props,media.role=radio\"");
+
if(rc >= GST_PIPELINE_LEN) {
AFB_ERROR("pipeline string too long");
return -1;
@@ -208,6 +216,7 @@ static int kf_init(const char *output)
// Start pipeline in paused state
gst_element_set_state(pipeline, GST_STATE_PAUSED);
+#endif /* !HAVE_4A_FRAMEWORK */
present = true;
return 0;
@@ -220,7 +229,7 @@ static uint32_t kf_get_frequency(void)
static void kf_set_frequency(uint32_t frequency)
{
- char cmd[128];
+ char cmd[SI_CTL_CMDLINE_MAXLEN];
int rc;
if(!present)
@@ -234,7 +243,7 @@ static void kf_set_frequency(uint32_t frequency)
return;
kf_scan_stop();
- sprintf(cmd, "%s /dev/i2c-12 0x65 -c %d", SI_CTL, frequency / 1000);
+ snprintf(cmd, sizeof(cmd), "%s /dev/i2c-12 0x65 -c %d", SI_CTL, frequency / 1000);
rc = system(cmd);
if(rc == 0)
current_frequency = frequency;
@@ -259,14 +268,14 @@ static char * kf_get_rds_info(void) {
if (scanning)
goto done;
- snprintf(cmd, SI_CTL_CMDLINE_MAXLEN, "%s /dev/i2c-12 0x65 -m", SI_CTL);
+ snprintf(cmd, sizeof(cmd), "%s /dev/i2c-12 0x65 -m", SI_CTL);
fp = popen(cmd, "r");
if(fp == NULL) {
fprintf(stderr, "Could not run: %s!\n", cmd);
goto done;
}
- // Look for "Name:" in output
- while (fgets(line, SI_CTL_OUTPUT_MAXLEN, fp) != NULL) {
+ /* Look for "Name:" in output */
+ while (fgets(line, sizeof(line), fp) != NULL) {
char* nS = strstr(line, "Name:");
char * end;
@@ -274,15 +283,16 @@ static char * kf_get_rds_info(void) {
continue;
end = nS+strlen("Name:");
- /* remove the trailing \n */
+ /* remove the trailing '\n' */
end[strlen(end)-1] = '\0';
rds = strdup(end);
break;
}
- // Make sure si_ctl has finished
+ /* Make sure si_ctl has finished */
pclose(fp);
+
done:
return rds;
}
@@ -336,29 +346,64 @@ static void kf_start(void)
if(!present)
return;
- if(!running) {
- // Start pipeline
- gst_element_set_state(pipeline, GST_STATE_PLAYING);
- running = true;
+ if (running)
+ return;
+
+#ifdef HAVE_4A_FRAMEWORK
+ int rc;
+ json_object *response;
+
+ json_object *jsonData = json_object_new_object();
+ json_object_object_add(jsonData, "action", json_object_new_string("unmute"));
+ rc = afb_service_call_sync("ahl-4a", "radio", jsonData, &response);
+ if (rc == 0) {
+ AFB_INFO("Muted\n");
+ json_object_put(response);
+ }
+ else {
+ AFB_ERROR("afb_service_call_sync failed\n");
}
+#else /* !HAVE_4A_FRAMEWORK */
+ // Start pipeline
+ gst_element_set_state(pipeline, GST_STATE_PLAYING);
+#endif /* !HAVE_4A_FRAMEWORK */
+ running = true;
}
static void kf_stop(void)
{
- GstEvent *event;
-
- if(present && running) {
- // Stop pipeline
- running = false;
- gst_element_set_state(pipeline, GST_STATE_PAUSED);
-
- // Flush pipeline
- // This seems required to avoid stutters on starts after a stop
- event = gst_event_new_flush_start();
- gst_element_send_event(GST_ELEMENT(pipeline), event);
- event = gst_event_new_flush_stop(TRUE);
- gst_element_send_event(GST_ELEMENT(pipeline), event);
+ if (!present)
+ return;
+
+ if (!running)
+ return;
+
+#ifdef HAVE_4A_FRAMEWORK
+ int rc;
+ json_object *response;
+
+ json_object *jsonData = json_object_new_object();
+ json_object_object_add(jsonData, "action", json_object_new_string("mute"));
+ rc = afb_service_call_sync("ahl-4a", "radio", jsonData, &response);
+ if (rc == 0) {
+ AFB_INFO("UnMuted\n");
+ json_object_put(response);
}
+ else {
+ AFB_ERROR("afb_service_call_sync failed\n");
+ }
+#else /* !HAVE_4A_FRAMEWORK */
+ gst_element_set_state(pipeline, GST_STATE_PAUSED);
+ GstEvent *event;
+
+ // Flush pipeline
+ // This seems required to avoid stutters on starts after a stop
+ event = gst_event_new_flush_start();
+ gst_element_send_event(GST_ELEMENT(pipeline), event);
+ event = gst_event_new_flush_stop(TRUE);
+ gst_element_send_event(GST_ELEMENT(pipeline), event);
+#endif /* !HAVE_4A_FRAMEWORK */
+ running = false;
}
static void kf_scan_start(radio_scan_direction_t direction,
@@ -366,8 +411,8 @@ static void kf_scan_start(radio_scan_direction_t direction,
void *data)
{
int rc;
- char cmd[128];
- char line[128];
+ char cmd[SI_CTL_CMDLINE_MAXLEN];
+ char line[SI_CTL_OUTPUT_MAXLEN];
uint32_t new_frequency = 0;
FILE *fp;
@@ -378,14 +423,17 @@ static void kf_scan_start(radio_scan_direction_t direction,
return;
scanning = true;
- sprintf(cmd, "%s /dev/i2c-12 0x65 -l %s", SI_CTL, direction == SCAN_FORWARD ? "up" : "down");
+ snprintf(cmd,
+ SI_CTL_CMDLINE_MAXLEN,
+ "%s /dev/i2c-12 0x65 -l %s",
+ SI_CTL, direction == SCAN_FORWARD ? "up" : "down");
fp = popen(cmd, "r");
if(fp == NULL) {
AFB_ERROR("Could not run: %s!", cmd);
return;
}
// Look for "Frequency:" in output
- while(fgets(line, 128, fp) != NULL) {
+ while(fgets(line, SI_CTL_OUTPUT_MAXLEN, fp) != NULL) {
if(strncmp("Frequency:", line, 10) == 0) {
new_frequency = atoi(line + 10);
//AFB_DEBUG("%s: got new_frequency = %d", __FUNCTION__, new_frequency);