blob: 9be8130ed5e76cd708346e55cfa107f15ec15696 [file] [log] [blame]
Harald Weltef5e72642022-10-30 22:20:55 +01001/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
2 * All Rights Reserved
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <string.h>
18#include <sys/types.h>
19#include <fcntl.h>
20#include <stdint.h>
21#include <stdlib.h>
22#include <getopt.h>
23#include <unistd.h>
24#include <stdio.h>
25#include <errno.h>
26#include <inttypes.h>
27
28#include <osmocom/core/utils.h>
29#include <osmocom/core/logging.h>
30#include <osmocom/core/application.h>
31#include <osmocom/core/isdnhdlc.h>
32#include <osmocom/core/gsmtap.h>
33#include <osmocom/core/gsmtap_util.h>
34//#include <osmocom/abis/lapd_pcap.h>
35
36#include "osmo_e1f.h"
37
38#define E1_CHUNK_HDR_MAGIC 0xe115600d /* E1 is good */
39struct e1_chunk_hdr {
40 uint32_t magic;
41 struct {
42 uint64_t sec;
43 uint64_t usec;
44 } time;
45 uint16_t len; /* length of following payload */
46 uint8_t ep; /* USB endpoint */
47} __attribute__((packed));
48
49
50/* local state per E1 line */
51struct line_state {
52 unsigned int idx;
53 struct osmo_isdnhdlc_vars hdlc;
54 struct osmo_e1f_instance e1f;
55 struct {
56 FILE *outfile;
57 uint64_t last_sec;
58 uint64_t frame_err_cnt;
59 uint64_t crc_err_cnt;
60 bool ts0_crc4_error;
61 bool ts0_remote_alarm;
62 } errplot;
63};
64
65static struct line_state g_line[2]; /* one per direction */
66static int g_pcap_fd = -1;
67static struct msgb *g_pcap_msg;
68struct gsmtap_inst *g_gsmtap;
69
70/* called for each HDLC payload frame */
71static void handle_payload(uint8_t idx, const uint8_t *data, int len)
72{
73#if 0
74 int dir;
75
76 switch (idx) {
77 case 0:
78 dir = OSMO_LAPD_PCAP_INPUT;
79 break;
80 case 1:
81 dir = OSMO_LAPD_PCAP_OUTPUT;
82 break;
83 default:
84 fprintf(stderr, "Unexpected USB EP idx %d\n", idx);
85 return;
86 }
87
88 if (g_pcap_fd >= 0) {
89 uint8_t *cur = msgb_put(g_pcap_msg, len);
90 memcpy(cur, data, len);
91 osmo_pcap_lapd_write(g_pcap_fd, dir, g_pcap_msg);
92 msgb_reset(g_pcap_msg);
93 } else
94#endif
95 printf("%u: OUT: %s\n", idx, osmo_hexdump(data, len));
96
97 if (g_gsmtap) {
98 struct msgb *msg;
99 msg = gsmtap_makemsg_ex(GSMTAP_TYPE_E1T1, idx ? GSMTAP_ARFCN_F_UPLINK : 0, 255,
100 GSMTAP_E1T1_FR, 0, 0, 0, 0, data, len);
101 OSMO_ASSERT(msg);
102 gsmtap_sendmsg(g_gsmtap, msg);
103 }
104}
105
106
107
108static void handle_frame_errplot(struct line_state *ls, const struct e1_chunk_hdr *hdr, const uint8_t *data)
109{
110 if (!ls->errplot.outfile)
111 return;
112
113 if (!ls->errplot.last_sec)
114 ls->errplot.last_sec = hdr->time.sec;
115
116 if (ls->errplot.last_sec != hdr->time.sec) {
117 /* dump the per-second total; start from 0 again */
118 fprintf(ls->errplot.outfile, "%"PRIu64 " %"PRIu64 " %"PRIu64" %u %u\n",
119 hdr->time.sec, ls->errplot.frame_err_cnt, ls->errplot.crc_err_cnt,
120 ls->errplot.ts0_crc4_error, ls->errplot.ts0_remote_alarm);
121 ls->errplot.frame_err_cnt = 0;
122 ls->errplot.crc_err_cnt = 0;
123 ls->errplot.ts0_remote_alarm = false;
124 ls->errplot.ts0_crc4_error = false;
125 ls->errplot.last_sec = hdr->time.sec;
126 fflush(ls->errplot.outfile);
127 }
128}
129
130/* called for each USB transfer read from the file */
131static void handle_frame(const struct e1_chunk_hdr *hdr, const uint8_t *data)
132{
133 uint8_t nots0[1024];
134 unsigned int offs = 0;
135 struct line_state *ls;
136
137 /* filter on the endpoint (direction) specified by the user */
138 switch (hdr->ep) {
139 case 0x81:
140 ls = &g_line[0];
141 break;
142 case 0x82:
143 ls = &g_line[1];
144 break;
145 default:
146 fprintf(stderr, "Unexpected USB EP 0x%02x\n", hdr->ep);
147 return;
148 }
149
150 if (hdr->len <= 4)
151 return;
152
153 //printf("%u: %"PRIu64".%"PRIu64" EP=0x%02x\n", ls->idx, hdr->time.sec, hdr->time.usec, hdr->ep);
154
155 OSMO_ASSERT(((hdr->len-4)/32)*31 < ARRAY_SIZE(nots0));
156 /* gather the TS1..TS31 data, skipping TS0 */
157 for (int i = 4; i < hdr->len-4; i += 32) {
158 //printf("%u:\t%s\n", ls->idx, osmo_hexdump(data+i, 32));
159 memcpy(nots0+offs, data+i+1, 32-1);
160 offs += 31;
161 osmo_e1f_rx_frame(&ls->e1f, data+i);
162 }
163
164 //printf("%u: IN(%u): %s\n", ls->idx, offs, osmo_hexdump(nots0, offs));
165 uint8_t out[2048];
166 int rc;
167 int rl;
168
169 int oi = 0;
170
171 while (oi < offs) {
172 rc = osmo_isdnhdlc_decode(&ls->hdlc, nots0+oi, offs-oi, &rl, out, sizeof(out));
173 //printf("%u: osmo_isdnhdlc_decode(hdlc, nots0+%d, inlen=%d, &rl=%d, out, %zu)=%d\n", ls->idx, oi, offs-oi, rl, sizeof(out), rc);
174 if (rc < 0) {
175 fprintf(stdout, "%u: ERR in HDLC decode: %d\n", ls->idx, rc);
176 if (rc == -1)
177 ls->errplot.frame_err_cnt++;
178 else if (rc == -2)
179 ls->errplot.crc_err_cnt++;
180 } else if (rc > 0)
181 handle_payload(ls->idx, out, rc);
182 oi += rl;
183 }
184 handle_frame_errplot(ls, hdr, data);
185}
186
187static int process_file(int fd)
188{
189 struct e1_chunk_hdr hdr;
190 unsigned long offset = 0;
191 uint8_t buf[65535];
192 int rc;
193
194 while (1) {
195 memset(buf, 0, sizeof(buf));
196 /* first read header */
197 rc = read(fd, &hdr, sizeof(hdr));
198 if (rc < 0)
199 return rc;
200 if (rc != sizeof(hdr)) {
201 fprintf(stderr, "%d is less than header size (%zd)\n", rc, sizeof(hdr));
202 return -1;
203 }
204 offset += rc;
205 if (hdr.magic != E1_CHUNK_HDR_MAGIC) {
206 fprintf(stderr, "offset %lu: Wrong magic 0x%08x\n", offset, hdr.magic);
207 return -1;
208 }
209
210 /* then read payload */
211 rc = read(fd, buf, hdr.len);
212 if (rc < 0)
213 return rc;
214 offset += rc;
215 if (rc != hdr.len) {
216 fprintf(stderr, "%d is less than payload size (%d)\n", rc, hdr.len);
217 return -1;
218 }
219 handle_frame(&hdr, buf);
220 }
221}
222
223static int open_file(const char *fname)
224{
225 return open(fname, O_RDONLY);
226}
227
228/* E1 framer notifies us of something */
229static void notify_cb(struct osmo_e1f_instance *e1i, enum osmo_e1f_notify_event evt, bool present, void *data)
230{
231 struct line_state *ls = e1i->priv;
232
233 printf("%u: NOTIFY: %s %s\n", ls->idx, osmo_e1f_notify_event_name(evt),
234 present ? "PRESENT" : "ABSENT");
235
236 if (present) {
237 switch (evt) {
238 case E1_NTFY_EVT_CRC_ERROR:
239 ls->errplot.ts0_crc4_error = present;
240 break;
241 case E1_NTFY_EVT_REMOTE_ALARM:
242 ls->errplot.ts0_remote_alarm = present;
243 break;
244 }
245 }
246}
247
248static const struct log_info_cat log_categories[] = {
249};
250
251static const struct log_info log_info = {
252 .cat = log_categories,
253 .num_cat = ARRAY_SIZE(log_categories),
254};
255
256int main(int argc, char **argv)
257{
258 char *fname;
259 int rc;
260 int i;
261
262 osmo_init_logging2(NULL, &log_info);
263 osmo_e1f_init();
264
265 if (argc < 2) {
266 fprintf(stderr, "You must specify the file name of the ICE40-E1 capture\n");
267 exit(1);
268 }
269 fname = argv[1];
270
271 rc = open_file(fname);
272 if (rc < 0) {
273 fprintf(stderr, "Error opening %s: %s\n", fname, strerror(errno));
274 exit(1);
275 }
276
277 g_gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 0);
278 gsmtap_source_add_sink(g_gsmtap);
279
280 if (argc >= 3) {
281#if 0
282 g_pcap_fd = osmo_pcap_lapd_open(argv[2], 0640);
283 if (g_pcap_fd < 0) {
284 fprintf(stderr, "Unable to open PCAP output: %s\n", strerror(errno));
285 exit(1);
286 }
287 g_pcap_msg = msgb_alloc(4096, "pcap");
288#endif
289 }
290
291 for (i = 0; i < ARRAY_SIZE(g_line); i++) {
292 struct line_state *ls = &g_line[i];
293 char namebuf[32];
294 ls->idx = i;
295 osmo_isdnhdlc_rcv_init(&ls->hdlc, OSMO_HDLC_F_BITREVERSE);
296 osmo_e1f_instance_init(&ls->e1f, "dump", &notify_cb, true, ls);
297
298 snprintf(namebuf, sizeof(namebuf), "errplot-%d.dat", i);
299 ls->errplot.outfile = fopen(namebuf, "w");
300 }
301
302 process_file(rc);
303}