titan.TestPorts.E1/src/E1TS_PT.cc

286 lines
6.2 KiB
C++

/* Copyright (c) 2020 Harald Welte <laforge@osmocom.org> */
#include <iterator>
#include "E1TS_PT.hh"
#include "E1TS_PortType.hh"
extern "C" {
#include <osmocom/core/application.h>
}
#include <poll.h>
#include <unistd.h>
using namespace E1TS__PortTypes;
namespace E1TS__PortType {
/* somehow std::map() won't work wit E1TS_identity as key */
DerivedId::DerivedId(const E1TS__identity &id)
{
interface_nr = id.interface__nr();
line_nr = id.line__nr();
ts_nr = id.ts__nr();
}
bool operator<(const DerivedId &fk, const DerivedId &lk) {
if (fk.interface_nr < lk.interface_nr)
return true;
else if (fk.interface_nr == lk.interface_nr) {
if (fk.line_nr < lk.line_nr)
return true;
else if (fk.line_nr == lk.line_nr) {
if (fk.ts_nr < lk.line_nr)
return true;
}
}
return false;
}
QueueEntry::QueueEntry(const uint8_t *pdata, unsigned int plen):
len(plen)
{
data = (uint8_t *) malloc(len);
memcpy(data, pdata, len);
}
QueueEntry::~QueueEntry()
{
free(data);
}
E1_Timeslot::E1_Timeslot(E1TS__PT_PROVIDER &pt, E1TS__identity id, E1TS__mode mode, int fd)
: m_pt(pt), m_id(id), m_mode(mode), m_fd(fd)
{
m_pt.log("creating %d:%d:%d fd=%d",
(int)m_id.interface__nr(), (int)m_id.line__nr(), (int)m_id.ts__nr(), m_fd);
}
E1_Timeslot::~E1_Timeslot()
{
m_pt.log("destroying %d:%d:%d fd=%d",
(int)m_id.interface__nr(), (int)m_id.line__nr(), (int)m_id.ts__nr(), m_fd);
close(m_fd);
/* iterate over tx-queue and free all elements */
while (!m_tx_queue.empty()) {
struct QueueEntry *qe = m_tx_queue.front();
printf("qe=%p\n", qe);
m_tx_queue.pop();
delete qe;
}
}
/* enqueue to-be-transmitted data */
int E1_Timeslot::enqueue_tx(const uint8_t *data, unsigned int len)
{
struct QueueEntry *qe = new QueueEntry(data, len);
if (!qe)
return 0;
m_tx_queue.push(qe);
return 1;
}
/* dequeue + write next-to-be-transmitted data from queue */
int E1_Timeslot::dequeue_tx(void)
{
struct QueueEntry *qe;
int rc;
if (m_tx_queue.empty()) {
/* queue is empty; unsubscribe write-events */
return 0;
}
qe = m_tx_queue.front();
m_tx_queue.pop();
rc = write(m_fd, qe->data, qe->len);
if (rc < 0) {
TTCN_error("error during write: %s\n", strerror(errno));
/* FIXME: close/delete fd */
}
else if (rc < qe->len)
TTCN_error("could only write %u of %u bytes\n", rc, qe->len);
delete qe;
return 1;
}
E1TS__PT_PROVIDER::E1TS__PT_PROVIDER(const char *par_port_name)
: PORT(par_port_name)
{
osmo_init_logging2(NULL, NULL);
}
E1TS__PT_PROVIDER::~E1TS__PT_PROVIDER()
{
}
void E1TS__PT_PROVIDER::log(const char *fmt, ...)
{
TTCN_Logger::begin_event(TTCN_WARNING);
TTCN_Logger::log_event("E1TS Test port (%s): ", get_name());
va_list args;
va_start(args, fmt);
TTCN_Logger::log_event_va_list(fmt, args);
va_end(args);
TTCN_Logger::end_event();
}
void E1TS__PT_PROVIDER::set_parameter(const char *parameter_name, const char *parameter_value)
{
if (!strcmp(parameter_name, "e1d_socket_path"))
m_e1d_socket_path = parameter_value;
else
TTCN_error("Unsupported E1TS test port parameter `%s'.", parameter_name);
}
void E1TS__PT_PROVIDER::Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable,
boolean is_error)
{
uint8_t buf[65535];
E1_Timeslot *ts;
int rc;
/* find E1TS_identity by fd */
ts = ts_by_fd(fd);
if (!ts)
TTCN_error("Unknown file descriptor %d\n", fd);
if (is_readable) {
rc = read(fd, buf, sizeof(buf));
if (rc > 0)
incoming_message(E1TS__unitdata(ts->m_id, OCTETSTRING(rc, buf)));
else if (rc == 0) {
TTCN_error("EOF on E1TS fd, closing");
m_ts_by_id.erase(m_ts_by_id.find(ts->m_id));
m_ts_by_fd.erase(m_ts_by_fd.find(ts->m_fd));
Handler_Remove_Fd(ts->m_fd);
delete ts;
}
}
if (is_writable) {
/* dequeue next message; unregister for 'write' if nothing to write */
if (ts->dequeue_tx() == 0)
Handler_Remove_Fd_Write(ts->m_fd);
}
}
void E1TS__PT_PROVIDER::user_map(const char * /*system_port*/)
{
m_e1d_clnt = osmo_e1dp_client_create(NULL, m_e1d_socket_path);
}
void E1TS__PT_PROVIDER::user_unmap(const char * /*system_port*/)
{
/* close/destroy all timeslots */
for (auto it = m_ts_by_id.begin(); it != m_ts_by_id.end(); it++) {
E1_Timeslot *ts = it->second;
Handler_Remove_Fd(ts->m_fd);
delete ts;
}
m_ts_by_id.clear();
m_ts_by_fd.clear();
/* close client connection to daemon */
osmo_e1dp_client_destroy(m_e1d_clnt);
}
void E1TS__PT_PROVIDER::user_start()
{
}
void E1TS__PT_PROVIDER::user_stop()
{
}
static enum osmo_e1dp_ts_mode e1dp_mode(E1TS__mode in)
{
switch (in) {
case E1TS__PortTypes::E1TS__mode::E1TS__MODE__RAW:
return E1DP_TSMODE_RAW;
case E1TS__PortTypes::E1TS__mode::E1TS__MODE__HDLCFCS:
return E1DP_TSMODE_HDLCFCS;
default:
TTCN_error("Unknown E1TS_mode %d\n", in);
}
}
E1_Timeslot *E1TS__PT_PROVIDER::ts_by_fd(int fd)
{
auto it = m_ts_by_fd.find(fd);
if (it == m_ts_by_fd.end()) {
TTCN_error("couldn't find FD for identity");
return NULL;
} else
return it->second;
}
E1_Timeslot *E1TS__PT_PROVIDER::ts_by_id(const E1TS__identity& id)
{
auto it = m_ts_by_id.find(id);
if (it == m_ts_by_id.end())
return NULL;
else
return it->second;
}
void E1TS__PT_PROVIDER::outgoing_send(const E1TS__open& send_par)
{
int fd;
enum osmo_e1dp_ts_mode mode = e1dp_mode(send_par.mode());
fd = osmo_e1dp_client_ts_open(m_e1d_clnt, send_par.id().interface__nr(),
send_par.id().line__nr(), send_par.id().ts__nr(), mode);
if (fd >= 0) {
E1_Timeslot *ts = new E1_Timeslot(*this, send_par.id(), send_par.mode(), fd);
m_ts_by_id.insert(std::make_pair(send_par.id(), ts));
m_ts_by_fd.insert(std::make_pair(fd, ts));
Handler_Add_Fd_Read(fd);
incoming_message(E1TS__result(send_par.req__hdl(), 0));
} else {
incoming_message(E1TS__result(send_par.req__hdl(), fd));
}
}
void E1TS__PT_PROVIDER::outgoing_send(const E1TS__close& send_par)
{
/* find fd by map */
E1_Timeslot *ts = ts_by_id(send_par.id());
if (!ts)
return;
m_ts_by_id.erase(m_ts_by_id.find(send_par.id()));
m_ts_by_fd.erase(m_ts_by_fd.find(ts->m_fd));
Handler_Remove_Fd(ts->m_fd);
delete ts;
}
void E1TS__PT_PROVIDER::outgoing_send(const E1TS__unitdata& send_par)
{
/* find fd by map */
E1_Timeslot *ts = ts_by_id(send_par.id());
if (!ts)
return;
ts->enqueue_tx(send_par.data(), send_par.data().lengthof());
Handler_Add_Fd_Write(ts->m_fd);
}
} /* namespace */