blob: e70fadc31f015554321ef3f5cf42a609c96662c8 [file] [log] [blame]
/*
* Copyright (c) 2019 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "edtt_driver.h"
#include <zephyr/kernel.h>
#include "soc.h"
#include "bs_tracing.h"
#include "bs_utils.h"
#include "bs_oswrap.h"
#include "bs_pc_base_fifo_user.h"
/* Recheck if something arrived from the EDTT every 5ms */
#define EDTT_IF_RECHECK_DELTA 5 /* ms */
/* We want the runs to be deterministic => we want to resync with the Phy
* before we retry any read so the bridge device may also run
*/
#define EDTT_SIMU_RESYNC_TIME_WITH_EDTT \
(EDTT_IF_RECHECK_DELTA * MSEC_PER_SEC - 1)
int edtt_mode_enabled;
/* In this mode, when the EDTTool closes the FIFO we automatically terminate
* this simulated device. If false, we just continue running
*/
static int edtt_autoshutdown;
#define TO_DEVICE 0
#define TO_BRIDGE 1
static int fifo[2] = { -1, -1 };
static char *fifo_path[2] = {NULL, NULL};
extern unsigned int global_device_nbr;
static void edttd_clean_up(void);
static void edptd_create_fifo_if(void);
static int fifo_low_level_read(uint8_t *bufptr, int size);
bool edtt_start(void)
{
if (edtt_mode_enabled == false) {
/* otherwise we don't try to open the EDTT interface */
return true;
}
edptd_create_fifo_if();
extern void tm_set_phy_max_resync_offset(uint64_t offset_in_us);
tm_set_phy_max_resync_offset(EDTT_SIMU_RESYNC_TIME_WITH_EDTT);
return true;
}
bool edtt_stop(void)
{
if (edtt_mode_enabled == false) {
/* otherwise we don't try to open the EDTT interface */
return true;
}
bs_trace_raw(9, "EDTTT: %s called\n", __func__);
edttd_clean_up();
edtt_mode_enabled = false;
return true;
}
#if defined(NATIVE_TASK)
NATIVE_TASK(edtt_stop, ON_EXIT, 1);
#endif
/**
* Attempt to read size bytes thru the EDTT IF into the buffer <*ptr>
* <flags> can be set to EDTTT_BLOCK or EDTTT_NONBLOCK
*
* If set to EDTTT_BLOCK it will block the calling thread until <size>
* bytes have been read or the interface has been closed.
* If set to EDTTT_NONBLOCK it returns as soon as there is no more data to be
* read
*
* Returns the amount of read bytes, or -1 on error
*/
int edtt_read(uint8_t *ptr, size_t size, int flags)
{
if (edtt_mode_enabled == false) {
return -1;
}
bs_trace_raw_time(8, "EDTT: Asked to read %i bytes\n", size);
int read = 0;
while (size > 0) {
int received_bytes;
received_bytes = fifo_low_level_read(ptr, size);
if (received_bytes < 0) {
return -1;
} else if (received_bytes > 0) {
size -= received_bytes;
ptr += received_bytes;
read += received_bytes;
} else {
if (flags & EDTTT_BLOCK) {
bs_trace_raw_time(9, "EDTT: No enough data yet,"
"sleeping for %i ms\n",
EDTT_IF_RECHECK_DELTA);
k_sleep(K_MSEC(EDTT_IF_RECHECK_DELTA));
} else {
bs_trace_raw_time(9, "EDTT: No enough data yet,"
"returning\n");
break;
}
}
}
return read;
}
/**
* Write <size> bytes from <ptr> toward the EDTTool
*
* <flags> is ignored in this driver, all writes to the tool are
* instantaneous
*/
int edtt_write(uint8_t *ptr, size_t size, int flags)
{
if (edtt_mode_enabled == false) {
return -1;
}
bs_trace_raw_time(9, "EDTT: Asked to write %i bytes\n", size);
if (write(fifo[TO_BRIDGE], ptr, size) != size) {
if (errno == EPIPE) {
bs_trace_error_line("EDTT IF suddenly closed by other "
"end\n");
}
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
bs_trace_error_line("EDTT IF to bridge filled up (FIFO "
"size needs to be increased)\n");
}
bs_trace_error_line("EDTT IF: Unexpected error on write\n");
}
return size;
}
/*
* Applications may want to enable the EDTT interface only in some
* cases. By default it is not enabled in this driver. This function
* must be called once before starting it to do so
*/
void enable_edtt_mode(void)
{
edtt_mode_enabled = true;
}
/**
* Automatically terminate this device execution once the EDTTool disconnects
*/
void set_edtt_autoshutdown(bool Mode)
{
edtt_autoshutdown = Mode;
}
static void edptd_create_fifo_if(void)
{
int flags;
bs_trace_raw_time(9, "Bringing EDTT IF up (waiting for other side)\n");
if (pb_com_path == NULL) {
bs_trace_error_line("Not connected to Phy."
"EDTT IF cannot be brought up\n");
}
/* At this point we have connected to the Phy so the COM folder does
* already exist
* also SIGPIPE is already ignored
*/
fifo_path[TO_DEVICE] = (char *)bs_calloc(pb_com_path_length + 30,
sizeof(char));
fifo_path[TO_BRIDGE] = (char *)bs_calloc(pb_com_path_length + 30,
sizeof(char));
sprintf(fifo_path[TO_DEVICE], "%s/Device%i.PTTin",
pb_com_path, global_device_nbr);
sprintf(fifo_path[TO_BRIDGE], "%s/Device%i.PTTout",
pb_com_path, global_device_nbr);
if ((pb_create_fifo_if_not_there(fifo_path[TO_DEVICE]) != 0)
|| (pb_create_fifo_if_not_there(fifo_path[TO_BRIDGE]) != 0)) {
bs_trace_error_line("Couldn't create FIFOs for EDTT IF\n");
}
/* we block here until the bridge opens its end */
fifo[TO_BRIDGE] = open(fifo_path[TO_BRIDGE], O_WRONLY);
if (fifo[TO_BRIDGE] == -1) {
bs_trace_error_line("Couldn't create FIFOs for EDTT IF\n");
}
flags = fcntl(fifo[TO_BRIDGE], F_GETFL);
flags |= O_NONBLOCK;
fcntl(fifo[TO_BRIDGE], F_SETFL, flags);
/* we will block here until the bridge opens its end */
fifo[TO_DEVICE] = open(fifo_path[TO_DEVICE], O_RDONLY);
if (fifo[TO_DEVICE] == -1) {
bs_trace_error_line("Couldn't create FIFOs for EDTT IF\n");
}
flags = fcntl(fifo[TO_DEVICE], F_GETFL);
flags |= O_NONBLOCK;
fcntl(fifo[TO_DEVICE], F_SETFL, flags);
}
static void edttd_clean_up(void)
{
for (int dir = TO_DEVICE ; dir <= TO_BRIDGE ; dir++) {
if (fifo_path[dir]) {
if (fifo[dir] != -1) {
close(fifo[dir]);
remove(fifo_path[dir]);
fifo[dir] = -1;
}
free(fifo_path[dir]);
fifo_path[dir] = NULL;
}
}
if (pb_com_path != NULL) {
rmdir(pb_com_path);
}
}
static int fifo_low_level_read(uint8_t *bufptr, int size)
{
int received_bytes = read(fifo[TO_DEVICE], bufptr, size);
if ((received_bytes == -1) && (errno == EAGAIN)) {
return 0;
} else if (received_bytes == EOF || received_bytes == 0) {
/*The FIFO was closed by the bridge*/
if (edtt_autoshutdown) {
bs_trace_raw_time(3, "EDTT: FIFO closed "
"(ptt_autoshutdown==true) =>"
" Terminate\n");
edttd_clean_up();
bs_trace_exit_line("\n");
} else {
bs_trace_raw_time(3, "EDTT: FIFO closed "
"(ptt_autoshutdown==false) => We close "
"the FIFOs and move on\n");
edttd_clean_up();
edtt_mode_enabled = false;
return -1;
}
} else if (received_bytes == -1) {
bs_trace_error_line("EDTT: Unexpected error\n");
}
return received_bytes;
}