You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

260 lines
5.3 KiB

/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2017
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 <http://www.gnu.org/licenses/>.
*/
#include "freedv_eth.h"
#include <stdlib.h>
#include <stdatomic.h>
#include <string.h>
#include <math.h>
static _Atomic(struct tx_packet *) tx_packet_pool = NULL;
static uint8_t voice_transmission = 0;
static double voice_level = -INFINITY;
struct tx_packet *tx_packet_alloc(void)
{
struct tx_packet *packet;
do {
if (tx_packet_pool) {
packet = tx_packet_pool;
} else {
return malloc(sizeof(struct tx_packet));
}
} while (!atomic_compare_exchange_weak(&tx_packet_pool, &packet, packet->next));
packet->len = 0;
packet->off = 0;
packet->local_rx = false;
return packet;
}
void tx_packet_free(struct tx_packet *packet)
{
struct tx_packet *next;
do {
next = tx_packet_pool;
packet->next = next;
} while (!atomic_compare_exchange_weak(&tx_packet_pool, &next, packet));
}
static struct tx_packet *queue_voice = NULL;
static struct tx_packet **queue_voice_tail = &queue_voice;
struct tx_packet *dequeue_voice(void)
{
struct tx_packet *packet;
packet = queue_voice;
queue_voice = packet->next;
if (&packet->next == queue_voice_tail) {
queue_voice_tail = &queue_voice;
}
return packet;
}
struct tx_packet *peek_voice(void)
{
return queue_voice;
}
int enqueue_voice(struct tx_packet *packet, uint8_t transmission, double level_dbm)
{
if (queue_voice && transmission != voice_transmission) {
if (level_dbm < voice_level) {
tx_packet_free(packet);
return 0;
}
}
voice_transmission = transmission;
voice_level = level_dbm;
packet->next = NULL;
*queue_voice_tail = packet;
queue_voice_tail = &packet->next;
return 1;
}
bool queue_voice_filled(size_t min_len)
{
size_t len = 0;
struct tx_packet *entry;
for (entry = queue_voice; entry; entry = entry->next) {
len += entry->len;
if (len >= min_len) {
return true;
}
}
return false;
}
void queue_voice_end(uint8_t transmission)
{
if (transmission == voice_transmission)
voice_level = -INFINITY;
}
static struct tx_packet *queue_baseband = NULL;
static struct tx_packet **queue_baseband_tail = &queue_baseband;
struct tx_packet *dequeue_baseband(void)
{
struct tx_packet *packet;
packet = queue_baseband;
queue_baseband = packet->next;
if (&packet->next == queue_baseband_tail) {
queue_baseband_tail = &queue_baseband;
}
return packet;
}
struct tx_packet *peek_baseband(void)
{
return queue_baseband;
}
void enqueue_baseband(struct tx_packet *packet)
{
packet->next = NULL;
*queue_baseband_tail = packet;
queue_baseband_tail = &packet->next;
}
bool queue_baseband_filled(void)
{
return queue_baseband;
}
void ensure_baseband(size_t nr)
{
struct tx_packet *packet = queue_baseband;
if (packet->len == nr)
return;
if (packet->len > nr) {
struct tx_packet *p2 = tx_packet_alloc();
size_t new_nr = packet->len - nr;
p2->local_rx = packet->local_rx;
memcpy(p2->data, packet->data + nr, new_nr);
p2->len = new_nr;
packet->len = nr;
p2->next = packet->next;
packet->next = p2->next;
} else {
while (packet->next) {
struct tx_packet *p2 = packet->next;
size_t nr_extra = nr - packet->len;
if (nr_extra > p2->len)
nr_extra = p2->len;
memcpy(packet->data + packet->len, p2->data, nr_extra);
packet->len += nr_extra;
size_t nr_off = p2->len - nr_extra;
if (!nr_off) {
packet->next = p2->next;
tx_packet_free(p2);
} else {
memmove(p2->data, p2->data + nr_extra, nr_off);
p2->len = nr_off;
}
if (packet->len == nr)
return;
}
size_t nr_zero = nr - packet->len;
memset(packet->data + packet->len, 0, nr_zero);
packet->len = nr;
}
}
static struct tx_packet *queue_data = NULL;
static struct tx_packet **queue_data_tail = &queue_data;
struct tx_packet *dequeue_data(void)
{
struct tx_packet *packet;
packet = queue_data;
queue_data = packet->next;
if (&packet->next == queue_data_tail) {
queue_data_tail = &queue_data;
}
return packet;
}
struct tx_packet *peek_data(void)
{
return queue_data;
}
void enqueue_data(struct tx_packet *packet)
{
packet->next = NULL;
*queue_data_tail = packet;
queue_data_tail = &packet->next;
}
bool queue_data_filled(void)
{
return queue_data;
}
static struct tx_packet *queue_control = NULL;
static struct tx_packet **queue_control_tail = &queue_control;
struct tx_packet *dequeue_control(void)
{
struct tx_packet *packet;
packet = queue_control;
queue_control = packet->next;
if (&packet->next == queue_control_tail) {
queue_control_tail = &queue_control;
}
return packet;
}
struct tx_packet *peek_control(void)
{
return queue_control;
}
void enqueue_control(struct tx_packet *packet)
{
packet->next = NULL;
*queue_control_tail = packet;
queue_control_tail = &packet->next;
}
bool queue_control_filled(void)
{
return queue_control;
}