aboutsummaryrefslogtreecommitdiffstats
path: root/src/RadioImpl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/RadioImpl.cc')
-rw-r--r--src/RadioImpl.cc326
1 files changed, 326 insertions, 0 deletions
diff --git a/src/RadioImpl.cc b/src/RadioImpl.cc
new file mode 100644
index 0000000..634f066
--- /dev/null
+++ b/src/RadioImpl.cc
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright (C) 2022 Konsulko Group
+ */
+
+#include "RadioImpl.h"
+
+// C backend implementations
+extern "C" {
+#include "radio_impl_null.h"
+#include "radio_impl_rtlsdr.h"
+#include "radio_impl_kingfisher.h"
+#include "radio_impl_tef665x.h"
+}
+
+using grpc::StatusCode;
+using automotivegradelinux::Band;
+using automotivegradelinux::BAND_UNSPECIFIED;
+using automotivegradelinux::BAND_AM;
+using automotivegradelinux::BAND_FM;
+using automotivegradelinux::StereoMode;
+using automotivegradelinux::STEREO_MODE_UNSPECIFIED;
+using automotivegradelinux::STEREO_MODE_MONO;
+using automotivegradelinux::STEREO_MODE_STEREO;
+using automotivegradelinux::ScanDirection;
+using automotivegradelinux::SCAN_DIRECTION_FORWARD;
+using automotivegradelinux::SCAN_DIRECTION_BACKWARD;
+
+RadioImpl::RadioImpl()
+{
+}
+
+bool RadioImpl::Detect()
+{
+ // Probe for radio backends
+ m_radio_impl_ops = &rtlsdr_impl_ops;
+ int rc = m_radio_impl_ops->probe();
+ if(rc != 0) {
+ // Look for Kingfisher Si4689
+ m_radio_impl_ops = &kf_impl_ops;
+ rc = m_radio_impl_ops->probe();
+ }
+#if 0
+ if(rc != 0) {
+ m_radio_impl_ops = &tef665x_impl_ops;
+ rc = m_radio_impl_ops->probe();
+ }
+#endif
+ if (rc != 0) {
+ m_radio_impl_ops = &null_impl_ops;
+ rc = m_radio_impl_ops->probe();
+ }
+ if (rc != 0) {
+ // We don't expect the null implementation to fail probe, but just in case...
+ std::cerr << "No radio device found, exiting" << std::endl;
+ return false;
+ }
+ // Try to initialize detected backend
+ rc = m_radio_impl_ops->init();
+ if(rc < 0) {
+ std::cerr << m_radio_impl_ops->name << " initialization failed" << std::endl;
+ return false;
+ }
+ std::cout << m_radio_impl_ops->name << "found" << std::endl;
+ m_radio_impl_ops->set_frequency_callback(FrequencyCallback, this);
+ m_radio_impl_ops->set_frequency_callback(FrequencyCallback, this);
+#if 0
+ if(m_radio_impl_ops->set_rds_callback) {
+ m_radio_impl_ops->set_rds_callback(RDSCallback);
+ }
+#endif
+ return true;
+}
+
+Status RadioImpl::GetFrequency(ServerContext* context, const GetFrequencyRequest* request, GetFrequencyResponse* response)
+{
+ response->set_frequency(m_radio_impl_ops->get_frequency());
+ return Status::OK;
+}
+
+Status RadioImpl::SetFrequency(ServerContext* context, const SetFrequencyRequest* request, SetFrequencyResponse* response)
+{
+ radio_band_t band = m_radio_impl_ops->get_band();
+ uint32_t min_frequency = m_radio_impl_ops->get_min_frequency(band);
+ uint32_t max_frequency = m_radio_impl_ops->get_max_frequency(band);
+ uint32_t step = m_radio_impl_ops->get_frequency_step(band);
+
+ uint32_t frequency = request->frequency();
+ if(frequency < min_frequency ||
+ frequency > max_frequency ||
+ (frequency - min_frequency) % step) {
+ //afb_req_reply(request, NULL, "failed", "Invalid frequency");
+ return Status::OK;
+ }
+ m_radio_impl_ops->set_frequency(frequency);
+ return Status::OK;
+}
+
+Status RadioImpl::GetBand(ServerContext* context, const GetBandRequest* request, GetBandResponse* response)
+{
+ Band band = BAND_UNSPECIFIED;
+ radio_band_t impl_band = m_radio_impl_ops->get_band();
+
+ if (impl_band == RADIO_BAND_AM)
+ band = BAND_AM;
+ else if (impl_band == RADIO_BAND_FM)
+ band = BAND_FM;
+ response->set_band(band);
+ return Status::OK;
+}
+
+Status RadioImpl::SetBand(ServerContext* context, const SetBandRequest* request, SetBandResponse* response)
+{
+ radio_band_t impl_band = RADIO_BAND_UNSPECIFIED;
+ if (request->band() == BAND_AM)
+ impl_band = RADIO_BAND_AM;
+ else if (request->band() == BAND_FM)
+ impl_band = RADIO_BAND_FM;
+
+ if (impl_band != RADIO_BAND_UNSPECIFIED) {
+ m_radio_impl_ops->set_band(impl_band);
+ } else {
+ // FIXME: Indicate error
+ return Status::OK;
+ }
+
+ return Status::OK;
+}
+
+Status RadioImpl::GetBandSupported(ServerContext* context, const GetBandSupportedRequest* request, GetBandSupportedResponse* response)
+{
+ radio_band_t impl_band = RADIO_BAND_UNSPECIFIED;
+ if (request->band() == BAND_AM)
+ impl_band = RADIO_BAND_AM;
+ else if (request->band() == BAND_FM)
+ impl_band = RADIO_BAND_FM;
+
+ if (impl_band != RADIO_BAND_UNSPECIFIED)
+ response->set_supported(m_radio_impl_ops->band_supported(impl_band));
+ else
+ response->set_supported(false);
+
+ return Status::OK;
+}
+
+Status RadioImpl::GetBandParameters(ServerContext* context, const GetBandParametersRequest* request, GetBandParametersResponse* response)
+{
+ radio_band_t impl_band = RADIO_BAND_UNSPECIFIED;
+ if (request->band() == BAND_AM)
+ impl_band = RADIO_BAND_AM;
+ else if (request->band() == BAND_FM)
+ impl_band = RADIO_BAND_FM;
+
+ if (impl_band != RADIO_BAND_UNSPECIFIED) {
+ response->set_min(m_radio_impl_ops->get_min_frequency(impl_band));
+ response->set_max(m_radio_impl_ops->get_max_frequency(impl_band));
+ response->set_step(m_radio_impl_ops->get_frequency_step(impl_band));
+ } else {
+ // FIXME: Indicate error
+ return Status::OK;
+ }
+ return Status::OK;
+}
+
+Status RadioImpl::GetStereoMode(ServerContext* context, const GetStereoModeRequest* request, GetStereoModeResponse* response)
+{
+ StereoMode mode = STEREO_MODE_UNSPECIFIED;
+ radio_stereo_mode_t impl_mode = m_radio_impl_ops->get_stereo_mode();
+
+ if (impl_mode == RADIO_MODE_MONO)
+ mode = STEREO_MODE_MONO;
+ else if (impl_mode == RADIO_MODE_STEREO)
+ mode = STEREO_MODE_STEREO;
+ response->set_mode(mode);
+ return Status::OK;
+}
+
+Status RadioImpl::SetStereoMode(ServerContext* context, const SetStereoModeRequest* request, SetStereoModeResponse* response)
+{
+ radio_stereo_mode_t impl_mode = RADIO_MODE_UNSPECIFIED;
+ if (request->mode() == STEREO_MODE_MONO)
+ impl_mode = RADIO_MODE_MONO;
+ else if (request->mode() == STEREO_MODE_STEREO)
+ impl_mode = RADIO_MODE_STEREO;
+
+ if (impl_mode != RADIO_MODE_UNSPECIFIED) {
+ m_radio_impl_ops->set_stereo_mode(impl_mode);
+ } else {
+ // FIXME: Indicate error
+ return Status::OK;
+ }
+ return Status::OK;
+}
+
+Status RadioImpl::Start(ServerContext* context, const StartRequest* request, StartResponse* response)
+{
+ m_radio_impl_ops->set_output(NULL);
+ m_radio_impl_ops->start();
+ m_playing = true;
+
+ StatusResponse status_response;
+ auto play_status = status_response.mutable_play();
+ play_status->set_playing(true);
+ SendStatusResponse(status_response);
+
+ return Status::OK;
+}
+
+Status RadioImpl::Stop(ServerContext* context, const StopRequest* request, StopResponse* response)
+{
+ m_radio_impl_ops->stop();
+ m_playing = false;
+
+ StatusResponse status_response;
+ auto play_status = status_response.mutable_play();
+ play_status->set_playing(false);
+ SendStatusResponse(status_response);
+
+ return Status::OK;
+}
+
+Status RadioImpl::ScanStart(ServerContext* context, const ScanStartRequest* request, ScanStartResponse* response)
+{
+ radio_scan_direction_t impl_direction = RADIO_SCAN_UNSPECIFIED;
+ if (request->direction() == SCAN_DIRECTION_FORWARD)
+ impl_direction = RADIO_SCAN_FORWARD;
+ else if (request->direction() == SCAN_DIRECTION_BACKWARD)
+ impl_direction = RADIO_SCAN_BACKWARD;
+
+ if (impl_direction != RADIO_SCAN_UNSPECIFIED) {
+ m_radio_impl_ops->scan_start(impl_direction, ScanCallback, this);
+ } else {
+ // FIXME: Indicate error
+ return Status::OK;
+ }
+
+ return Status::OK;
+}
+
+Status RadioImpl::ScanStop(ServerContext* context, const ScanStopRequest* request, ScanStopResponse* response)
+{
+ m_radio_impl_ops->scan_stop();
+ return Status::OK;
+}
+
+Status RadioImpl::GetRDS(ServerContext* context, const GetRDSRequest* request, GetRDSResponse* response)
+{
+ return Status::OK;
+}
+
+Status RadioImpl::GetQuality(ServerContext* context, const GetQualityRequest* request, GetQualityResponse* response)
+{
+ return Status::OK;
+}
+
+Status RadioImpl::SetAlternativeFrequency(ServerContext* context, const SetAlternativeFrequencyRequest* request, SetAlternativeFrequencyResponse* response)
+{
+ return Status::OK;
+}
+
+Status RadioImpl::GetStatusEvents(ServerContext* context, const StatusRequest* request, ServerWriter<StatusResponse>* writer)
+{
+ // Save client information
+ m_clients_mutex.lock();
+ m_clients.push_back(std::pair(context, writer));
+ m_clients_mutex.unlock();
+
+ // For now block until client disconnect / server shutdown
+ // A switch to the async or callback server APIs might be more elegant than
+ // holding the thread like this, and may be worth investigating at some point.
+ std::unique_lock lock(m_done_mutex);
+ m_done_cv.wait(lock, [context, this]{ return (context->IsCancelled() || m_done); });
+
+ return Status::OK;
+}
+
+void RadioImpl::SendStatusResponse(StatusResponse &response)
+{
+ const std::lock_guard<std::mutex> lock(m_clients_mutex);
+
+ if (m_clients.empty())
+ return;
+
+ auto it = m_clients.begin();
+ while (it != m_clients.end()) {
+ if (it->first->IsCancelled()) {
+ // Client has gone away, remove from list
+ std::cout << "Removing cancelled RPC client!" << std::endl;
+ it = m_clients.erase(it);
+
+ // We're not exiting, but wake up blocked client RPC handlers so
+ // the canceled one will clean exit.
+ // Note that in practice this means the client RPC handler thread
+ // sticks around until the next status event is sent.
+ m_done_cv.notify_all();
+
+ continue;
+ } else {
+ it->second->Write(response);
+ ++it;
+ }
+ }
+}
+
+
+void RadioImpl::HandleFrequencyEvent(uint32_t frequency)
+{
+ StatusResponse response;
+ auto frequency_status = response.mutable_frequency();
+ frequency_status->set_frequency(frequency);
+ SendStatusResponse(response);
+}
+
+void RadioImpl::HandleScanEvent(uint32_t frequency)
+{
+ StatusResponse response;
+ auto scan_status = response.mutable_scan();
+ scan_status->set_station_found(frequency);
+ SendStatusResponse(response);
+}
+
+void RadioImpl::HandleRDSEvent(void *rds_data)
+{
+ // TODO
+}
+