diff options
Diffstat (limited to 'binding/radio-binding.c')
-rw-r--r-- | binding/radio-binding.c | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/binding/radio-binding.c b/binding/radio-binding.c new file mode 100644 index 0000000..12ed966 --- /dev/null +++ b/binding/radio-binding.c @@ -0,0 +1,493 @@ +/* + * 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. + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <sys/types.h> + +#include <json-c/json.h> +#include <afb/afb-binding.h> +#include <afb/afb-service-itf.h> + +#include "radio_impl.h" + +static const struct afb_binding_interface *interface; + +static struct afb_event freq_event; +static struct afb_event scan_event; + +static void freq_callback(uint32_t frequency, void *data) +{ + json_object *jresp = json_object_new_object(); + json_object *value = json_object_new_int((int) frequency); + + json_object_object_add(jresp, "value", value); + afb_event_push(freq_event, json_object_get(jresp)); +} + +static void scan_callback(uint32_t frequency, void *data) +{ + json_object *jresp = json_object_new_object(); + json_object *value = json_object_new_int((int) frequency); + + json_object_object_add(jresp, "value", value); + afb_event_push(scan_event, json_object_get(jresp)); +} + +/* + * Binding verb handlers + */ + +/* + * @brief Get (and optionally set) frequency + * + * @param struct afb_req : an afb request structure + * + */ +static void frequency(struct afb_req request) +{ + json_object *ret_json; + const char *value = afb_req_value(request, "value"); + uint32_t frequency; + + if(value) { + char *p; + frequency = strtoul(value, &p, 10); + if(frequency && *p == '\0') { + radio_impl_set_frequency(frequency); + } else { + afb_req_fail(request, "failed", "Invalid scan direction"); + return; + } + } + ret_json = json_object_new_object(); + frequency = radio_impl_get_frequency(); + json_object_object_add(ret_json, "frequency", json_object_new_int((int32_t) frequency)); + afb_req_success(request, ret_json, NULL); +} + +/* + * @brief Get (and optionally set) frequency band + * + * @param struct afb_req : an afb request structure + * + */ +static void band(struct afb_req request) +{ + json_object *ret_json; + const char *value = afb_req_value(request, "value"); + int valid = 0; + radio_band_t band; + char band_name[4]; + + if(value) { + if(!strcasecmp(value, "AM")) { + band = BAND_AM; + valid = 1; + } else if(!strcasecmp(value, "FM")) { + band = BAND_FM; + valid = 1; + } else { + char *p; + band = strtoul(value, &p, 10); + if(p != value && *p == '\0') { + switch(band) { + case BAND_AM: + case BAND_FM: + valid = 1; + break; + default: + break; + } + } + } + if(valid) { + radio_impl_set_band(band); + } else { + afb_req_fail(request, "failed", "Invalid band"); + return; + } + } + ret_json = json_object_new_object(); + band = radio_impl_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); +} + +/* + * @brief Check if band is supported + * + * @param struct afb_req : an afb request structure + * + */ +static void band_supported(struct afb_req request) +{ + json_object *ret_json; + const char *value = afb_req_value(request, "band"); + int valid = 0; + radio_band_t band; + + if(value) { + if(!strcasecmp(value, "AM")) { + band = BAND_AM; + valid = 1; + } else if(!strcasecmp(value, "FM")) { + band = BAND_FM; + valid = 1; + } else { + char *p; + band = strtoul(value, &p, 10); + if(p != value && *p == '\0') { + switch(band) { + case BAND_AM: + case BAND_FM: + valid = 1; + break; + default: + break; + } + } + } + } + if(!valid) { + afb_req_fail(request, "failed", "Invalid band"); + return; + } + ret_json = json_object_new_object(); + json_object_object_add(ret_json, + "supported", + json_object_new_int(radio_impl_band_supported(band))); + afb_req_success(request, ret_json, NULL); +} + +/* + * @brief Get frequency range for a band + * + * @param struct afb_req : an afb request structure + * + */ +static void frequency_range(struct afb_req request) +{ + json_object *ret_json; + const char *value = afb_req_value(request, "band"); + int valid = 0; + radio_band_t band; + uint32_t min_frequency; + uint32_t max_frequency; + + if(value) { + if(!strcasecmp(value, "AM")) { + band = BAND_AM; + valid = 1; + } else if(!strcasecmp(value, "FM")) { + band = BAND_FM; + valid = 1; + } else { + char *p; + band = strtoul(value, &p, 10); + if(p != value && *p == '\0') { + switch(band) { + case BAND_AM: + case BAND_FM: + valid = 1; + break; + default: + break; + } + } + } + } + if(!valid) { + afb_req_fail(request, "failed", "Invalid band"); + return; + } + ret_json = json_object_new_object(); + min_frequency = radio_impl_get_min_frequency(band); + max_frequency = radio_impl_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); +} + +/* + * @brief Get frequency step size (Hz) for a band + * + * @param struct afb_req : an afb request structure + * + */ +static void frequency_step(struct afb_req request) +{ + json_object *ret_json; + const char *value = afb_req_value(request, "band"); + int valid = 0; + radio_band_t band; + uint32_t step; + + if(value) { + if(!strcasecmp(value, "AM")) { + band = BAND_AM; + valid = 1; + } else if(!strcasecmp(value, "FM")) { + band = BAND_FM; + valid = 1; + } else { + char *p; + band = strtoul(value, &p, 10); + if(p != value && *p == '\0') { + switch(band) { + case BAND_AM: + case BAND_FM: + valid = 1; + break; + default: + break; + } + } + } + } + if(!valid) { + afb_req_fail(request, "failed", "Invalid band"); + return; + } + ret_json = json_object_new_object(); + step = radio_impl_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); +} + +/* + * @brief Start radio playback + * + * @param struct afb_req : an afb request structure + * + */ +static void start(struct afb_req request) +{ + radio_impl_start(); + afb_req_success(request, NULL, NULL); +} + +/* + * @brief Stop radio playback + * + * @param struct afb_req : an afb request structure + * + */ +static void stop(struct afb_req request) +{ + radio_impl_stop(); + afb_req_success(request, NULL, NULL); +} + +/* + * @brief Scan for a station in the specified direction + * + * @param struct afb_req : an afb request structure + * + */ +static void scan_start(struct afb_req request) +{ + json_object *ret_json; + const char *value = afb_req_value(request, "direction"); + int valid = 0; + radio_scan_direction_t direction; + + if(value) { + if(!strcasecmp(value, "forward")) { + direction = SCAN_FORWARD; + valid = 1; + } else if(!strcasecmp(value, "backward")) { + direction = SCAN_BACKWARD; + valid = 1; + } else { + char *p; + direction = strtoul(value, &p, 10); + if(p != value && *p == '\0') { + switch(direction) { + case SCAN_FORWARD: + case SCAN_BACKWARD: + valid = 1; + break; + default: + break; + } + } + } + } + if(!valid) { + afb_req_fail(request, "failed", "Invalid direction"); + return; + } + radio_impl_scan_start(direction, scan_callback, NULL); + afb_req_success(request, ret_json, NULL); +} + +/* + * @brief Stop station scan + * + * @param struct afb_req : an afb request structure + * + */ +static void scan_stop(struct afb_req request) +{ + radio_impl_scan_stop(); + afb_req_success(request, NULL, NULL); +} + +/* + * @brief Get (and optionally set) stereo mode + * + * @param struct afb_req : an afb request structure + * + */ +static void stereo_mode(struct afb_req request) +{ + json_object *ret_json; + const char *value = afb_req_value(request, "value"); + int valid = 0; + radio_stereo_mode_t mode; + char mode_name[4]; + + if(value) { + if(!strcasecmp(value, "mono")) { + mode = MONO; + valid = 1; + } else if(!strcasecmp(value, "stereo")) { + mode = STEREO; + valid = 1; + } else { + char *p; + mode = strtoul(value, &p, 10); + if(p != value && *p == '\0') { + switch(mode) { + case MONO: + case STEREO: + valid = 1; + break; + default: + break; + } + } + } + if(valid) { + radio_impl_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(); + 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); +} + +/* + * @brief Subscribe for an event + * + * @param struct afb_req : an afb request structure + * + */ +static void subscribe(struct afb_req request) +{ + const char *value = afb_req_value(request, "value"); + if(value) { + if(!strcasecmp(value, "frequency")) { + afb_req_subscribe(request, freq_event); + } else if(!strcasecmp(value, "station_found")) { + afb_req_subscribe(request, scan_event); + } else { + afb_req_fail(request, "failed", "Invalid event"); + return; + } + } + afb_req_success(request, NULL, NULL); +} + +/* + * @brief Unsubscribe for an event + * + * @param struct afb_req : an afb request structure + * + */ +static void unsubscribe(struct afb_req request) +{ + const char *value = afb_req_value(request, "value"); + if(value) { + if(!strcasecmp(value, "frequency")) { + afb_req_unsubscribe(request, freq_event); + } else if(!strcasecmp(value, "station_found")) { + afb_req_unsubscribe(request, scan_event); + } else { + afb_req_fail(request, "failed", "Invalid event"); + return; + } + } + afb_req_success(request, NULL, NULL); +} + +static const struct afb_verb_desc_v1 verbs[]= { + { "frequency", AFB_SESSION_CHECK, frequency, "Get/Set frequency" }, + { "band", AFB_SESSION_CHECK, band, "Get/Set band" }, + { "band_supported", AFB_SESSION_CHECK, band_supported, "Check band support" }, + { "frequency_range", AFB_SESSION_CHECK, frequency_range, "Get frequency range" }, + { "frequency_step", AFB_SESSION_CHECK, frequency_step, "Get frequency step" }, + { "start", AFB_SESSION_CHECK, start, "Start radio playback" }, + { "stop", AFB_SESSION_CHECK, stop, "Stop radio playback" }, + { "scan_start", AFB_SESSION_CHECK, scan_start, "Start station scan" }, + { "scan_stop", AFB_SESSION_CHECK, scan_stop, "Stop station scan" }, + { "stereo_mode", AFB_SESSION_CHECK, stereo_mode, "Get/Set stereo_mode" }, + { "subscribe", AFB_SESSION_CHECK, subscribe, "Subscribe for an event" }, + { "unsubscribe", AFB_SESSION_CHECK, unsubscribe, "Unsubscribe for an event" }, + { NULL } +}; + +static const struct afb_binding binding_desc = { + .type = AFB_BINDING_VERSION_1, + .v1 = { + .info = "radio service", + .prefix = "radio", + .verbs = verbs + } +}; + +const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) +{ + interface = itf; + + return &binding_desc; +} + +int afbBindingV1ServiceInit(struct afb_service service) +{ + int rc; + + freq_event = afb_daemon_make_event(interface->daemon, "frequency"); + scan_event = afb_daemon_make_event(interface->daemon, "station_found"); + + rc = radio_impl_init(); + if(rc == 0) { + radio_impl_set_frequency_callback(freq_callback, NULL); + } + + return rc; +} |