diff options
Diffstat (limited to 'src/RadioImpl.cc')
-rw-r--r-- | src/RadioImpl.cc | 326 |
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 +} + |