/* (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 #define E1_TS_BITRATE 64000 #define E1_31TS_BITRATE (31*E1_TS_BITRATE) #define E1_31TS_BYTERATE (E1_31TS_BITRATE/8) #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)); static struct timespec ts_start; static uint64_t bytes_total; static uint64_t get_msecs_total(void) { struct timespec ts_now; clock_gettime(CLOCK_MONOTONIC, &ts_now); return (ts_now.tv_sec*1000 + ts_now.tv_nsec/1000000) - (ts_start.tv_sec*1000 + ts_start.tv_nsec/1000000); } /* 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; /* filter on the endpoint (direction) specified by the user */ switch (hdr->ep) { case 0x81: break; case 0x82: return; 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); 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; write(1, data+i, 31); bytes_total += 31; } /* check number of bytes written / number of seconds expired -> sleep or not? */ uint64_t msecs_total = get_msecs_total(); if (msecs_total == 0) return; uint64_t bytes_per_sec = bytes_total * 1000 / msecs_total; //fprintf(stderr, "%"PRIu64" msecs, %"PRIu64" bytes, %"PRIu64" bytes/s\n", msecs_total, bytes_total, bytes_per_sec); if (bytes_per_sec > E1_31TS_BYTERATE) { uint64_t msecs_expected = bytes_total * 1000 / E1_31TS_BYTERATE; //fprintf(stderr, "expected: %"PRIu64" msecs, actual: %"PRIu64" msecs, sleepint\n", msecs_expected, msecs_total); usleep((msecs_expected - msecs_total)*1000); } } static int process_file(void *map, off_t len) { unsigned long offset = 0; clock_gettime(CLOCK_MONOTONIC, &ts_start); while (offset < len) { struct e1_chunk_hdr *hdr = map + offset; offset += sizeof(*hdr); if (hdr->magic != E1_CHUNK_HDR_MAGIC) { fprintf(stderr, "offset %lu: Wrong magic 0x%08x\n", offset, hdr->magic); return -1; } /* then read payload */ handle_frame(hdr, (const uint8_t *)map + offset); offset += hdr->len; } return 0; } static void *open_mmap_file(const char *fname, off_t *size) { int fd, rc; struct stat st; void *map; fd = open(fname, O_RDONLY); if (fd < 0) return NULL; rc = fstat(fd, &st); if (rc < 0) { close(fd); return NULL; } map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); close(fd); if (map == MAP_FAILED) return NULL; if (size) *size = st.st_size; return map; } int main(int argc, char **argv) { char *fname; void *map; off_t map_len; if (argc < 2) { fprintf(stderr, "You must specify the file name of the ICE40-E1 capture\n"); exit(1); } fname = argv[1]; map = open_mmap_file(fname, &map_len); if (!map) { fprintf(stderr, "Error opening %s: %s\n", fname, strerror(errno)); exit(1); } process_file(map, map_len); }