286 lines
6.2 KiB
C++
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 */
|