blob: 9bb7dc47efc054e88f5a668a40e05297443730dc [file] [log] [blame]
Harald Welte92d1d7d2020-07-02 18:24:07 +02001/* Copyright (c) 2020 Harald Welte <laforge@osmocom.org> */
2
3#include <iterator>
4
5#include "E1TS_PT.hh"
6#include "E1TS_PortType.hh"
7
8extern "C" {
9#include <osmocom/core/application.h>
10}
11
12#include <poll.h>
13#include <unistd.h>
14
15using namespace E1TS__PortTypes;
16
17namespace E1TS__PortType {
18
19/* somehow std::map() won't work wit E1TS_identity as key */
20DerivedId::DerivedId(const E1TS__identity &id)
21{
22 interface_nr = id.interface__nr();
23 line_nr = id.line__nr();
24 ts_nr = id.ts__nr();
25}
26
27bool operator<(const DerivedId &fk, const DerivedId &lk) {
28 if (fk.interface_nr < lk.interface_nr)
29 return true;
30 else if (fk.interface_nr == lk.interface_nr) {
31 if (fk.line_nr < lk.line_nr)
32 return true;
33 else if (fk.line_nr == lk.line_nr) {
34 if (fk.ts_nr < lk.line_nr)
35 return true;
36 }
37 }
38 return false;
39}
40
41
42QueueEntry::QueueEntry(const uint8_t *pdata, unsigned int plen):
43 len(plen)
44{
45 data = (uint8_t *) malloc(len);
46 memcpy(data, pdata, len);
47}
48
49QueueEntry::~QueueEntry()
50{
51 free(data);
52}
53
54E1_Timeslot::E1_Timeslot(E1TS__PT_PROVIDER &pt, E1TS__identity id, E1TS__mode mode, int fd)
55 : m_pt(pt), m_id(id), m_mode(mode), m_fd(fd)
56{
57 m_pt.log("creating %d:%d:%d fd=%d",
58 (int)m_id.interface__nr(), (int)m_id.line__nr(), (int)m_id.ts__nr(), m_fd);
59}
60
61E1_Timeslot::~E1_Timeslot()
62{
63 m_pt.log("destroying %d:%d:%d fd=%d",
64 (int)m_id.interface__nr(), (int)m_id.line__nr(), (int)m_id.ts__nr(), m_fd);
65
66 close(m_fd);
67
68 /* iterate over tx-queue and free all elements */
69 while (!m_tx_queue.empty()) {
70 struct QueueEntry *qe = m_tx_queue.front();
71 printf("qe=%p\n", qe);
72 m_tx_queue.pop();
73 delete qe;
74 }
75}
76
77/* enqueue to-be-transmitted data */
78int E1_Timeslot::enqueue_tx(const uint8_t *data, unsigned int len)
79{
80 struct QueueEntry *qe = new QueueEntry(data, len);
81 if (!qe)
82 return 0;
83 m_tx_queue.push(qe);
84
85 return 1;
86}
87
88/* dequeue + write next-to-be-transmitted data from queue */
89int E1_Timeslot::dequeue_tx(void)
90{
91 struct QueueEntry *qe;
92 int rc;
93
94 if (m_tx_queue.empty()) {
95 /* queue is empty; unsubscribe write-events */
96 return 0;
97 }
98
99 qe = m_tx_queue.front();
100 m_tx_queue.pop();
101 rc = write(m_fd, qe->data, qe->len);
102 if (rc < 0) {
103 TTCN_error("error during write: %s\n", strerror(errno));
104 /* FIXME: close/delete fd */
105 }
106 else if (rc < qe->len)
107 TTCN_error("could only write %u of %u bytes\n", rc, qe->len);
108
109 delete qe;
110
111 return 1;
112}
113
114
115
116E1TS__PT_PROVIDER::E1TS__PT_PROVIDER(const char *par_port_name)
117 : PORT(par_port_name)
118{
119 osmo_init_logging2(NULL, NULL);
120}
121
122E1TS__PT_PROVIDER::~E1TS__PT_PROVIDER()
123{
124}
125
126void E1TS__PT_PROVIDER::log(const char *fmt, ...)
127{
128 TTCN_Logger::begin_event(TTCN_WARNING);
129 TTCN_Logger::log_event("E1TS Test port (%s): ", get_name());
130 va_list args;
131 va_start(args, fmt);
132 TTCN_Logger::log_event_va_list(fmt, args);
133 va_end(args);
134 TTCN_Logger::end_event();
135}
136
137void E1TS__PT_PROVIDER::set_parameter(const char *parameter_name, const char *parameter_value)
138{
139 if (!strcmp(parameter_name, "e1d_socket_path"))
140 m_e1d_socket_path = parameter_value;
141 else
142 TTCN_error("Unsupported E1TS test port parameter `%s'.", parameter_name);
143}
144
145void E1TS__PT_PROVIDER::Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable,
146 boolean is_error)
147{
148 uint8_t buf[65535];
149 E1_Timeslot *ts;
150 int rc;
151
152 /* find E1TS_identity by fd */
153 ts = ts_by_fd(fd);
154
155 if (!ts)
156 TTCN_error("Unknown file descriptor %d\n", fd);
157
158 if (is_readable) {
159 rc = read(fd, buf, sizeof(buf));
160 if (rc > 0)
161 incoming_message(E1TS__unitdata(ts->m_id, OCTETSTRING(rc, buf)));
162 else if (rc == 0) {
163 TTCN_error("EOF on E1TS fd, closing");
164 m_ts_by_id.erase(m_ts_by_id.find(ts->m_id));
165 m_ts_by_fd.erase(m_ts_by_fd.find(ts->m_fd));
166 Handler_Remove_Fd(ts->m_fd);
167 delete ts;
168 }
169 }
170
171 if (is_writable) {
172 /* dequeue next message; unregister for 'write' if nothing to write */
173 if (ts->dequeue_tx() == 0)
174 Handler_Remove_Fd_Write(ts->m_fd);
175 }
176}
177
178void E1TS__PT_PROVIDER::user_map(const char * /*system_port*/)
179{
180 m_e1d_clnt = osmo_e1dp_client_create(NULL, m_e1d_socket_path);
181}
182
183void E1TS__PT_PROVIDER::user_unmap(const char * /*system_port*/)
184{
185 /* close/destroy all timeslots */
186 for (auto it = m_ts_by_id.begin(); it != m_ts_by_id.end(); it++) {
187 E1_Timeslot *ts = it->second;
188 Handler_Remove_Fd(ts->m_fd);
189 delete ts;
190 }
191 m_ts_by_id.clear();
192 m_ts_by_fd.clear();
193
194 /* close client connection to daemon */
195 osmo_e1dp_client_destroy(m_e1d_clnt);
196}
197
198void E1TS__PT_PROVIDER::user_start()
199{
200}
201
202void E1TS__PT_PROVIDER::user_stop()
203{
204}
205
206static enum osmo_e1dp_ts_mode e1dp_mode(E1TS__mode in)
207{
208 switch (in) {
209 case E1TS__PortTypes::E1TS__mode::E1TS__MODE__RAW:
210 return E1DP_TSMODE_RAW;
211 case E1TS__PortTypes::E1TS__mode::E1TS__MODE__HDLCFCS:
212 return E1DP_TSMODE_HDLCFCS;
213 default:
214 TTCN_error("Unknown E1TS_mode %d\n", in);
215 }
216}
217
218E1_Timeslot *E1TS__PT_PROVIDER::ts_by_fd(int fd)
219{
220 auto it = m_ts_by_fd.find(fd);
221 if (it == m_ts_by_fd.end()) {
222 TTCN_error("couldn't find FD for identity");
223 return NULL;
224 } else
225 return it->second;
226}
227
228
229E1_Timeslot *E1TS__PT_PROVIDER::ts_by_id(const E1TS__identity& id)
230{
231 auto it = m_ts_by_id.find(id);
232 if (it == m_ts_by_id.end())
233 return NULL;
234 else
235 return it->second;
236}
237
238
239void E1TS__PT_PROVIDER::outgoing_send(const E1TS__open& send_par)
240{
241 int fd;
242 enum osmo_e1dp_ts_mode mode = e1dp_mode(send_par.mode());
243
244 fd = osmo_e1dp_client_ts_open(m_e1d_clnt, send_par.id().interface__nr(),
245 send_par.id().line__nr(), send_par.id().ts__nr(), mode);
246
247 if (fd >= 0) {
248 E1_Timeslot *ts = new E1_Timeslot(*this, send_par.id(), send_par.mode(), fd);
249 m_ts_by_id.insert(std::make_pair(send_par.id(), ts));
250 m_ts_by_fd.insert(std::make_pair(fd, ts));
251 Handler_Add_Fd_Read(fd);
252 incoming_message(E1TS__result(send_par.req__hdl(), 0));
253 } else {
254 incoming_message(E1TS__result(send_par.req__hdl(), fd));
255 }
256}
257
258void E1TS__PT_PROVIDER::outgoing_send(const E1TS__close& send_par)
259{
260 /* find fd by map */
261 E1_Timeslot *ts = ts_by_id(send_par.id());
262
263 if (!ts)
264 return;
265
266 m_ts_by_id.erase(m_ts_by_id.find(send_par.id()));
267 m_ts_by_fd.erase(m_ts_by_fd.find(ts->m_fd));
268 Handler_Remove_Fd(ts->m_fd);
269 delete ts;
270}
271
272void E1TS__PT_PROVIDER::outgoing_send(const E1TS__unitdata& send_par)
273{
274 /* find fd by map */
275 E1_Timeslot *ts = ts_by_id(send_par.id());
276
277 if (!ts)
278 return;
279
280 ts->enqueue_tx(send_par.data(), send_par.data().lengthof());
281 Handler_Add_Fd_Write(ts->m_fd);
282}
283
284
285} /* namespace */