import latest e1-tracer host software
This was originally in https://github.com/smunaut/ice40-playground
but has meanwhile been removed, only a fork at
https://github.com/laf0rge/ice40-playground remained.
Let's import the code here. The history is not all that exciting
so I'm saving myself the effort of git filter-branch or the like
to import the actual old commits.
The commit log is here for reference:
commit bd36d40cc30acbae68978df79eff03f0539a9a21
Author: Harald Welte <laforge@osmocom.org>
Date: Fri Sep 4 10:21:51 2020 +0200
e1-recorder main.c: Don't exist just because one ISO transfer failed
I get LIBUSB_TRANSFER_ERROR every so often (once every few hours) on my
laptop. Sure, it likely means there was some data lost, but the trace
can continue juts fine after this error. So make it non-fatal and
continue running.
commit 9b02b3e87400ba3d27bff05e2632fbd5a0698e8c
Author: Harald Welte <laforge@osmocom.org>
Date: Mon Aug 31 10:01:20 2020 +0200
Add 'replay' to re-play 2Mbps stream with realistic speed (to stdout)
commit 23103d3a8f9cc8b3f0ba602dbb4240040b2d008a
Author: Harald Welte <laforge@osmocom.org>
Date: Sun Aug 30 15:41:12 2020 +0200
dump: Use osmo_e1f to decode; send HLDC over GSMTAP
commit 7acd063a1245a34835d4488be3caad592f3c19d5
Author: Harald Welte <laforge@osmocom.org>
Date: Thu Aug 20 16:48:15 2020 +0200
dump.c: Add error plotting support
commit 90b90d04fff494b70ad220bd82ac48d43f3096c9
Author: Harald Welte <laforge@osmocom.org>
Date: Mon Jan 13 18:20:39 2020 +0100
dump: Add support for writing LAPD PCAP of E1 superchannel
commit f3a0cc34f0901c2cf257367351dddfddc6f7301e
Author: Harald Welte <laforge@osmocom.org>
Date: Mon Jan 13 16:37:23 2020 +0100
dump.c: Move handling of frames to separate function
commit 8deffb5907979e43e3a4236ea7bb8d54513513ac
Author: Harald Welte <laforge@osmocom.org>
Date: Mon Jan 13 16:34:39 2020 +0100
dump.c: remove local hexdump.[ch] and use libosmocore proper
commit 80d81e78737237ba3b6bae1deb7d5107d7f613b6
Author: Harald Welte <laforge@osmocom.org>
Date: Tue Sep 1 13:52:52 2020 +0200
tracer: use 0x6151 as PID
See https://github.com/openmoko/openmoko-usb-oui/commit/5e68831b26dbf912d831a0c00ca3045e6285b458
commit b219af27b34bbd6deb2419aa38852d8d227b9fc0
Author: Harald Welte <laforge@osmocom.org>
Date: Mon Jan 6 13:47:28 2020 +0100
e1-recorder: Don't silently ignore write failures
The disk may be full, for example. We should report that to the user
and terminate the program.
commit ead99fdfae6f1db54c79a91ef407a86c67352d87
Author: Harald Welte <laforge@osmocom.org>
Date: Mon Jan 6 12:41:23 2020 +0100
e1-recorder: Exit process on transfer + resubmit failure
This is particularly importnat in case the USB device for some reason
gets disconnected and re-connects. We want the process to fail fast
and have systemd respawn us.
commit a521e026c76056a60531c490fa2ef84e8e633c9a
Author: Harald Welte <laforge@osmocom.org>
Date: Mon Jan 6 12:35:20 2020 +0100
e1-recorder: use libusb_strerror() to decode error messages
commit 43a8fbdfdc35f5ba7564cfe5c92f43b124abf710
Author: Harald Welte <laforge@osmocom.org>
Date: Sun Jan 5 19:49:58 2020 +0100
e1-recorder: Acytually make '-r' (SCHED_RR) work
commit 84e28225d9ba1de9fb09660a96bd3b0b798c88d9
Author: Harald Welte <laforge@osmocom.org>
Date: Sun Jan 5 17:53:11 2020 +0100
e1-recorder: Add small 'dump' program
This program will print one line for each E1 frame, where each line
consists of 32 hex bytes: one for each timeslot
commit 0c697b40620b5d18480e626991dc639daadf58a6
Author: Sylvain Munaut <tnt@246tNt.com>
Date: Sun Dec 29 13:13:17 2019 +0100
projects/riscv_usb: Host software for the dual channel sniffer
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Closes: OS#5673
Change-Id: I9319c1eb8f822830307c1a181d357c58ce43efba
diff --git a/software/e1-tracer/main.c b/software/e1-tracer/main.c
new file mode 100644
index 0000000..9d004a3
--- /dev/null
+++ b/software/e1-tracer/main.c
@@ -0,0 +1,451 @@
+/* (C) 2019 by Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2019-2020 by Harald Welte <laforge@gnumonks.org>
+ * 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 <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sched.h>
+#include <errno.h>
+
+#include <sys/time.h>
+
+#include <libusb.h>
+
+#include "idt82v2081.h"
+#include "idt82v2081_usb.h"
+#include "idt82v2081_regs.h"
+
+
+static int g_do_exit = 0;
+
+#define USB_VID 0x1d50
+#define USB_PID 0x6151
+#define EP_DATA_IN0 0x81
+#define EP_DATA_IN1 0x82
+
+struct e1_streamer;
+struct flow;
+
+typedef int (*xfer_cb_t)(struct e1_streamer *e1s, struct flow *flow, uint8_t *buf, int size);
+
+struct flow_entry {
+ uint8_t *buf;
+ struct libusb_transfer *xfr;
+};
+
+struct flow {
+ struct e1_streamer *parent;
+ xfer_cb_t cb;
+
+ int ep;
+ int count;
+ int size;
+ int ppx;
+ struct flow_entry *entries;
+};
+
+struct e1_streamer {
+ struct libusb_device_handle *devh;
+ struct flow data_in[2];
+ struct idt82 liu[2];
+ FILE *fh;
+};
+
+struct e1_chunk_hdr {
+ uint32_t magic;
+ struct {
+ uint64_t sec;
+ uint64_t usec;
+ } time;
+ int16_t len;
+ uint8_t ep;
+} __attribute__((packed));
+
+
+static int
+cb_xfr_data_in(struct e1_streamer *e1s, struct flow *flow, uint8_t *buf, int size)
+{
+ struct e1_chunk_hdr hdr;
+ struct timeval tv;
+ int rc;
+
+ hdr.magic = 0xe115600d; /* E1 is good */
+
+ gettimeofday(&tv, NULL);
+ hdr.time.sec = tv.tv_sec;
+ hdr.time.usec = tv.tv_usec;
+
+ hdr.ep = flow->ep;
+ hdr.len = size;
+
+ if (size < 0) {
+ printf("EP %02x - Err %d: %s\n", flow->ep, size, libusb_strerror(size));
+ return 0;
+ }
+
+ if (!e1s->fh)
+ return 0;
+
+ rc = fwrite(&hdr, sizeof(struct e1_chunk_hdr), 1, e1s->fh);
+ if (rc != 1) {
+ fprintf(stderr, "[!] Short write: %d != %zd", rc, sizeof(struct e1_chunk_hdr));
+ if (rc == -1)
+ fprintf(stderr, ", %s\n", strerror(errno));
+ else
+ fprintf(stderr, "\n");
+ g_do_exit = 1;
+ }
+
+ if (size > 0) {
+ rc = fwrite(buf, size, 1, e1s->fh);
+ if (rc != 1) {
+ fprintf(stderr, "[!] Short write: %d != %zd", rc, sizeof(struct e1_chunk_hdr));
+ if (rc == -1)
+ fprintf(stderr, ", %s\n", strerror(errno));
+ else
+ fprintf(stderr, "\n");
+ g_do_exit = 1;
+ }
+ }
+
+ return 0;
+}
+
+static void LIBUSB_CALL cb_xfr(struct libusb_transfer *xfr)
+{
+ struct flow *flow = (struct flow *) xfr->user_data;
+ struct e1_streamer *e1s = flow->parent;
+ int j, rv, len;
+
+#if 0
+ fprintf(stderr, "transfer status (%02x) %d [%d %d] [%d %d]\n", flow->ep, xfr->status,
+ xfr->iso_packet_desc[0].status,
+ xfr->iso_packet_desc[0].actual_length,
+ xfr->iso_packet_desc[1].status,
+ xfr->iso_packet_desc[1].actual_length
+ );
+#endif
+
+ if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
+ fprintf(stderr, "[!] XFR status != completed (%d)\n", xfr->status);
+ g_do_exit = 1;
+ }
+
+ len = 0;
+
+ if (flow->ep & 0x80) {
+ for (j=0; j<flow->ppx; j++) {
+ flow->cb(e1s, flow,
+ libusb_get_iso_packet_buffer_simple(xfr, j),
+ (xfr->iso_packet_desc[j].status == LIBUSB_TRANSFER_COMPLETED) ?
+ xfr->iso_packet_desc[j].actual_length : -1
+ );
+ if (!(xfr->iso_packet_desc[j].status == LIBUSB_TRANSFER_COMPLETED)) {
+ fprintf(stderr, "[!] ISO packet status != completed (%d)\n",
+ xfr->iso_packet_desc[j].status);
+ }
+
+ len += (xfr->iso_packet_desc[j].length = flow->size);
+ }
+ } else {
+ for (j=0; j<flow->ppx; j++)
+ len += (xfr->iso_packet_desc[j].length = flow->cb(e1s, flow, &xfr->buffer[len], flow->size));
+ }
+
+ libusb_fill_iso_transfer(xfr, e1s->devh, flow->ep,
+ xfr->buffer, len, flow->ppx,
+ cb_xfr, flow, 0
+ );
+
+ rv = libusb_submit_transfer(xfr);
+ if (rv) {
+ fprintf(stderr, "[!] Error re-submitting buffer (%d): %s\n", rv, libusb_strerror(rv));
+ g_do_exit = 1;
+ }
+}
+
+
+static void
+_e1s_flow_fini(struct flow *flow)
+{
+ int i;
+
+ for (i=0; i<flow->count; i++)
+ free(flow->entries[i].buf);
+
+ free(flow->entries);
+}
+
+static void
+_e1s_flow_init(struct e1_streamer *e1s, struct flow *flow, xfer_cb_t cb, int ep, int count, int size, int ppx)
+{
+ int i;
+
+ flow->parent = e1s;
+ flow->cb = cb;
+ flow->ep = ep;
+ flow->count = count;
+ flow->size = size;
+ flow->ppx = ppx;
+ flow->entries = calloc(count, sizeof(struct flow_entry));
+
+ for (i=0; i<count; i++)
+ flow->entries[i].buf = malloc(size * ppx);
+}
+
+static int
+_e1s_flow_start(struct e1_streamer *e1s, struct flow *flow)
+{
+ struct libusb_transfer *xfr;
+ int i, j, rv, len;
+
+ for (i=0; i<flow->count; i++)
+ {
+ xfr = libusb_alloc_transfer(flow->ppx);
+ if (!xfr)
+ return -ENOMEM;
+
+ len = 0;
+
+ if (flow->ep & 0x80) {
+ for (j=0; j<flow->ppx; j++)
+ len += (xfr->iso_packet_desc[j].length = flow->size);
+ } else {
+ for (j=0; j<flow->ppx; j++)
+ len += (xfr->iso_packet_desc[j].length = flow->cb(e1s, flow, &flow->entries[i].buf[len], flow->size));
+ }
+
+ libusb_fill_iso_transfer(xfr, e1s->devh, flow->ep,
+ flow->entries[i].buf, len, flow->ppx,
+ cb_xfr, flow, 0
+ );
+
+ rv = libusb_submit_transfer(xfr);
+ if (rv) {
+ return rv;
+ }
+
+ flow->entries[i].xfr = xfr;
+ }
+
+ return 0;
+}
+
+
+static void
+e1s_release(struct e1_streamer *e1s)
+{
+ if (!e1s)
+ return;
+
+ _e1s_flow_fini(&e1s->data_in[0]);
+ _e1s_flow_fini(&e1s->data_in[1]);
+
+ if (e1s->devh) {
+ libusb_release_interface(e1s->devh, 0);
+ libusb_close(e1s->devh);
+ }
+
+ free(e1s);
+}
+
+static struct e1_streamer *
+e1s_new(bool monitor, const char *out_file, bool append, int nx, int ppx)
+{
+ struct e1_streamer *e1s = NULL;
+ int rv;
+
+ e1s = calloc(1, sizeof(struct e1_streamer));
+ if (!e1s)
+ return NULL;
+
+ e1s->devh = libusb_open_device_with_vid_pid(NULL, USB_VID, USB_PID);
+ if (!e1s->devh) {
+ fprintf(stderr, "Error finding USB device\n");
+ goto err;
+ }
+
+ rv = libusb_claim_interface(e1s->devh, 0);
+ if (rv < 0) {
+ fprintf(stderr, "Error claiming interface: %s\n", libusb_error_name(rv));
+ goto err;
+ }
+
+ rv = libusb_set_interface_alt_setting(e1s->devh, 0, 1);
+ if (rv < 0) {
+ fprintf(stderr, "Error enabling interface: %s\n", libusb_error_name(rv));
+ goto err;
+ }
+
+ _e1s_flow_init(e1s, &e1s->data_in[0], cb_xfr_data_in, EP_DATA_IN0, nx, 388, ppx);
+ _e1s_flow_init(e1s, &e1s->data_in[1], cb_xfr_data_in, EP_DATA_IN1, nx, 388, ppx);
+
+ idt82_usb_init(&e1s->liu[0], e1s->devh, EP_DATA_IN0);
+ idt82_usb_init(&e1s->liu[1], e1s->devh, EP_DATA_IN1);
+ idt82_init(&e1s->liu[0], monitor);
+ idt82_init(&e1s->liu[1], monitor);
+
+ if (out_file) {
+ e1s->fh = fopen(out_file, append ? "ab" : "wb");
+ if (!e1s->fh)
+ fprintf(stderr, "[1] Failed to open recording file\n");
+ }
+
+ return e1s;
+
+err:
+ e1s_release(e1s);
+ return NULL;
+}
+
+struct options {
+ /* Transfer config */
+ int nx;
+ int ppx;
+
+ /* Output */
+ const char *out_filename;
+ bool out_append;
+
+ /* PHY */
+ bool monitor;
+
+ /* OS */
+ bool realtime;
+};
+
+static void
+opts_defaults(struct options *opts)
+{
+ memset(opts, 0x00, sizeof(struct options));
+
+ opts->nx = 2;
+ opts->ppx = 4;
+}
+
+static void
+opts_help(void)
+{
+ fprintf(stderr, " -a Output : append mode\n");
+ fprintf(stderr, " -o FILE Output : filename\n");
+ fprintf(stderr, " -n NX Xfer : Number of queued transfers (default: 2)\n");
+ fprintf(stderr, " -p PPX Xfer : Number of packets per transfer (default: 4)\n");
+ fprintf(stderr, " -m PHY : Monitor mode (i.e. high gain)\n");
+ fprintf(stderr, " -r OS : Set real-time priority on process\n");
+ fprintf(stderr, " -h help\n");
+}
+
+static int
+opts_parse(struct options *opts, int argc, char *argv[])
+{
+ const char *opts_short = "ao:n:p:mrh";
+ int opt;
+
+ while ((opt = getopt(argc, argv, opts_short)) != -1)
+ {
+ switch(opt) {
+ case 'a':
+ opts->out_append = true;
+ break;
+
+ case 'o':
+ opts->out_filename = optarg;
+ break;
+
+ case 'n':
+ opts->nx = atoi(optarg);
+ if (opts->nx <= 0) {
+ fprintf(stderr, "[!] Invalid nx value ignored\n");
+ opts->nx = 2;
+ }
+ break;
+
+ case 'p':
+ opts->ppx = atoi(optarg);
+ if (opts->ppx <= 0) {
+ fprintf(stderr, "[!] Invalid ppx value ignored\n");
+ opts->ppx = 4;
+ }
+ break;
+
+ case 'm':
+ opts->monitor = true;
+ break;
+
+ case 'r':
+ opts->realtime = true;
+ break;
+
+ case 'h':
+ default:
+ opts_help();
+ exit(1);
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct e1_streamer *e1s;
+ struct sched_param sp;
+ struct options opts;
+ int rv;
+
+ opts_defaults(&opts);
+ opts_parse(&opts, argc, argv);
+
+ if (opts.realtime) {
+ memset(&sp, 0x00, sizeof(sp));
+ sp.sched_priority = 50;
+ rv = sched_setscheduler(0, SCHED_RR, &sp);
+ printf("%d %d\n", rv, errno);
+ perror("sched_setscheduler");
+ }
+
+ rv = libusb_init(NULL);
+ if (rv < 0) {
+ fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rv));
+ return rv;
+ }
+
+ e1s = e1s_new(opts.monitor, opts.out_filename, opts.out_append, opts.nx, opts.ppx);
+ if (!e1s)
+ goto out;
+
+ _e1s_flow_start(e1s, &e1s->data_in[0]);
+ _e1s_flow_start(e1s, &e1s->data_in[1]);
+
+ while (!g_do_exit) {
+ rv = libusb_handle_events(NULL);
+ if (rv != LIBUSB_SUCCESS)
+ break;
+ }
+
+out:
+ e1s_release(e1s);
+
+ libusb_exit(NULL);
+
+ return 0;
+}