/*
Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/)
Copyright (C) 2014-2020 Matthias P. Braendli (http://opendigitalradio.org)
Copyright (C) 2015-2019 Stefan Pöschel (http://opendigitalradio.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "config.h"
#include "pad_interface.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MESSAGE_REQUEST 1
#define MESSAGE_PAD_DATA 2
using namespace std;
void PadInterface::open(const std::string &pad_ident)
{
m_pad_ident = pad_ident;
m_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
if (m_sock == -1) {
throw runtime_error("PAD socket creation failed: " + string(strerror(errno)));
}
struct sockaddr_un claddr;
memset(&claddr, 0, sizeof(struct sockaddr_un));
claddr.sun_family = AF_UNIX;
snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/%s.padenc", m_pad_ident.c_str());
if (unlink(claddr.sun_path) == -1 and errno != ENOENT) {
fprintf(stderr, "Unlinking of socket %s failed: %s\n", claddr.sun_path, strerror(errno));
}
int ret = bind(m_sock, (const struct sockaddr *) &claddr, sizeof(struct sockaddr_un));
if (ret == -1) {
throw runtime_error("PAD socket bind failed " + string(strerror(errno)));
}
}
uint8_t PadInterface::receive_request()
{
if (m_pad_ident.empty()) {
throw logic_error("Uninitialised PadInterface::request() called");
}
vector buffer(4);
while (true) {
struct pollfd fds[1];
fds[0].fd = m_sock;
fds[0].events = POLLIN;
int timeout_ms = 240;
int retval = poll(fds, 1, timeout_ms);
if (retval == -1) {
std::string errstr(strerror(errno));
throw std::runtime_error("PAD socket poll error: " + errstr);
}
else if (retval > 0) {
ssize_t ret = recvfrom(m_sock, buffer.data(), buffer.size(), 0, nullptr, nullptr);
if (ret == -1) {
throw runtime_error(string("Can't receive data: ") + strerror(errno));
}
else {
buffer.resize(ret);
// We could check where the data comes from, but since we're using UNIX sockets
// the source is anyway local to the machine.
if (buffer[0] == MESSAGE_REQUEST) {
uint8_t padlen = buffer[1];
return padlen;
}
else {
continue;
}
}
}
else {
return 0;
}
}
}
void PadInterface::send_pad_data(const uint8_t *data, size_t len)
{
struct sockaddr_un claddr;
memset(&claddr, 0, sizeof(struct sockaddr_un));
claddr.sun_family = AF_UNIX;
snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/%s.audioenc", m_pad_ident.c_str());
vector message(len + 1);
message[0] = MESSAGE_PAD_DATA;
copy(data, data + len, message.begin() + 1);
ssize_t ret = sendto(m_sock, message.data(), message.size(), 0, (struct sockaddr*)&claddr, sizeof(struct sockaddr_un));
if (ret == -1) {
// This suppresses the -Wlogical-op warning
if (errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
or errno == EWOULDBLOCK
#endif
or errno == ECONNREFUSED
or errno == ENOENT) {
if (m_audioenc_reachable) {
fprintf(stderr, "ODR-PadEnc at %s not reachable\n", claddr.sun_path);
m_audioenc_reachable = false;
}
}
else {
fprintf(stderr, "PAD send failed: %s\n", strerror(errno));
}
}
else if ((size_t)ret != message.size()) {
fprintf(stderr, "PAD incorrect length sent: %zu bytes of %zu transmitted\n", ret, len);
}
else if (not m_audioenc_reachable) {
fprintf(stderr, "Audio encoder is now reachable at %s\n", claddr.sun_path);
m_audioenc_reachable = true;
}
}