/* * 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 #include #include #include #include #include #include #include #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; }