/* (C) 2019 by Harald Welte * All Rights Reserved * * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include "osmo_e1f.h" #define E1_CHUNK_HDR_MAGIC 0xe115600d /* E1 is good */ struct e1_chunk_hdr { uint32_t magic; struct { uint64_t sec; uint64_t usec; } time; uint16_t len; /* length of following payload */ uint8_t ep; /* USB endpoint */ } __attribute__((packed)); /* local state per E1 line */ struct line_state { unsigned int idx; struct osmo_isdnhdlc_vars hdlc; struct osmo_e1f_instance e1f; struct { FILE *outfile; uint64_t last_sec; uint64_t frame_err_cnt; uint64_t crc_err_cnt; bool ts0_crc4_error; bool ts0_remote_alarm; } errplot; }; static struct line_state g_line[2]; /* one per direction */ static int g_pcap_fd = -1; static struct msgb *g_pcap_msg; struct gsmtap_inst *g_gsmtap; /* called for each HDLC payload frame */ static void handle_payload(uint8_t idx, const uint8_t *data, int len) { #if 0 int dir; switch (idx) { case 0: dir = OSMO_LAPD_PCAP_INPUT; break; case 1: dir = OSMO_LAPD_PCAP_OUTPUT; break; default: fprintf(stderr, "Unexpected USB EP idx %d\n", idx); return; } if (g_pcap_fd >= 0) { uint8_t *cur = msgb_put(g_pcap_msg, len); memcpy(cur, data, len); osmo_pcap_lapd_write(g_pcap_fd, dir, g_pcap_msg); msgb_reset(g_pcap_msg); } else #endif printf("%u: OUT: %s\n", idx, osmo_hexdump(data, len)); if (g_gsmtap) { struct msgb *msg; msg = gsmtap_makemsg_ex(GSMTAP_TYPE_E1T1, idx ? GSMTAP_ARFCN_F_UPLINK : 0, 255, GSMTAP_E1T1_FR, 0, 0, 0, 0, data, len); OSMO_ASSERT(msg); gsmtap_sendmsg(g_gsmtap, msg); } } static void handle_frame_errplot(struct line_state *ls, const struct e1_chunk_hdr *hdr, const uint8_t *data) { if (!ls->errplot.outfile) return; if (!ls->errplot.last_sec) ls->errplot.last_sec = hdr->time.sec; if (ls->errplot.last_sec != hdr->time.sec) { /* dump the per-second total; start from 0 again */ fprintf(ls->errplot.outfile, "%"PRIu64 " %"PRIu64 " %"PRIu64" %u %u\n", hdr->time.sec, ls->errplot.frame_err_cnt, ls->errplot.crc_err_cnt, ls->errplot.ts0_crc4_error, ls->errplot.ts0_remote_alarm); ls->errplot.frame_err_cnt = 0; ls->errplot.crc_err_cnt = 0; ls->errplot.ts0_remote_alarm = false; ls->errplot.ts0_crc4_error = false; ls->errplot.last_sec = hdr->time.sec; fflush(ls->errplot.outfile); } } /* called for each USB transfer read from the file */ static void handle_frame(const struct e1_chunk_hdr *hdr, const uint8_t *data) { uint8_t nots0[1024]; unsigned int offs = 0; struct line_state *ls; /* filter on the endpoint (direction) specified by the user */ switch (hdr->ep) { case 0x81: ls = &g_line[0]; break; case 0x82: ls = &g_line[1]; break; default: fprintf(stderr, "Unexpected USB EP 0x%02x\n", hdr->ep); return; } if (hdr->len <= 4) return; //printf("%u: %"PRIu64".%"PRIu64" EP=0x%02x\n", ls->idx, hdr->time.sec, hdr->time.usec, hdr->ep); OSMO_ASSERT(((hdr->len-4)/32)*31 < ARRAY_SIZE(nots0)); /* gather the TS1..TS31 data, skipping TS0 */ for (int i = 4; i < hdr->len-4; i += 32) { //printf("%u:\t%s\n", ls->idx, osmo_hexdump(data+i, 32)); memcpy(nots0+offs, data+i+1, 32-1); offs += 31; osmo_e1f_rx_frame(&ls->e1f, data+i); } //printf("%u: IN(%u): %s\n", ls->idx, offs, osmo_hexdump(nots0, offs)); uint8_t out[2048]; int rc; int rl; int oi = 0; while (oi < offs) { rc = osmo_isdnhdlc_decode(&ls->hdlc, nots0+oi, offs-oi, &rl, out, sizeof(out)); //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); if (rc < 0) { fprintf(stdout, "%u: ERR in HDLC decode: %d\n", ls->idx, rc); if (rc == -1) ls->errplot.frame_err_cnt++; else if (rc == -2) ls->errplot.crc_err_cnt++; } else if (rc > 0) handle_payload(ls->idx, out, rc); oi += rl; } handle_frame_errplot(ls, hdr, data); } static int process_file(int fd) { struct e1_chunk_hdr hdr; unsigned long offset = 0; uint8_t buf[65535]; int rc; while (1) { memset(buf, 0, sizeof(buf)); /* first read header */ rc = read(fd, &hdr, sizeof(hdr)); if (rc < 0) return rc; if (rc != sizeof(hdr)) { fprintf(stderr, "%d is less than header size (%zd)\n", rc, sizeof(hdr)); return -1; } offset += rc; if (hdr.magic != E1_CHUNK_HDR_MAGIC) { fprintf(stderr, "offset %lu: Wrong magic 0x%08x\n", offset, hdr.magic); return -1; } /* then read payload */ rc = read(fd, buf, hdr.len); if (rc < 0) return rc; offset += rc; if (rc != hdr.len) { fprintf(stderr, "%d is less than payload size (%d)\n", rc, hdr.len); return -1; } handle_frame(&hdr, buf); } } static int open_file(const char *fname) { return open(fname, O_RDONLY); } /* E1 framer notifies us of something */ static void notify_cb(struct osmo_e1f_instance *e1i, enum osmo_e1f_notify_event evt, bool present, void *data) { struct line_state *ls = e1i->priv; printf("%u: NOTIFY: %s %s\n", ls->idx, osmo_e1f_notify_event_name(evt), present ? "PRESENT" : "ABSENT"); if (present) { switch (evt) { case E1_NTFY_EVT_CRC_ERROR: ls->errplot.ts0_crc4_error = present; break; case E1_NTFY_EVT_REMOTE_ALARM: ls->errplot.ts0_remote_alarm = present; break; } } } static const struct log_info_cat log_categories[] = { }; static const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { char *fname; int rc; int i; osmo_init_logging2(NULL, &log_info); osmo_e1f_init(); if (argc < 2) { fprintf(stderr, "You must specify the file name of the ICE40-E1 capture\n"); exit(1); } fname = argv[1]; rc = open_file(fname); if (rc < 0) { fprintf(stderr, "Error opening %s: %s\n", fname, strerror(errno)); exit(1); } g_gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 0); gsmtap_source_add_sink(g_gsmtap); if (argc >= 3) { #if 0 g_pcap_fd = osmo_pcap_lapd_open(argv[2], 0640); if (g_pcap_fd < 0) { fprintf(stderr, "Unable to open PCAP output: %s\n", strerror(errno)); exit(1); } g_pcap_msg = msgb_alloc(4096, "pcap"); #endif } for (i = 0; i < ARRAY_SIZE(g_line); i++) { struct line_state *ls = &g_line[i]; char namebuf[32]; ls->idx = i; osmo_isdnhdlc_rcv_init(&ls->hdlc, OSMO_HDLC_F_BITREVERSE); osmo_e1f_instance_init(&ls->e1f, "dump", ¬ify_cb, true, ls); snprintf(namebuf, sizeof(namebuf), "errplot-%d.dat", i); ls->errplot.outfile = fopen(namebuf, "w"); } process_file(rc); }