blob: 9d004a3dd04ae0b8dea85ce8ea76b0df69b973c6 [file] [log] [blame]
Harald Weltef5e72642022-10-30 22:20:55 +01001/* (C) 2019 by Sylvain Munaut <tnt@246tNt.com>
2 * (C) 2019-2020 by Harald Welte <laforge@gnumonks.org>
3 * All Rights Reserved
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published
9 * by the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#include <errno.h>
19#include <getopt.h>
20#include <stdio.h>
21#include <stdbool.h>
22#include <stdint.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <sched.h>
27#include <errno.h>
28
29#include <sys/time.h>
30
31#include <libusb.h>
32
33#include "idt82v2081.h"
34#include "idt82v2081_usb.h"
35#include "idt82v2081_regs.h"
36
37
38static int g_do_exit = 0;
39
40#define USB_VID 0x1d50
41#define USB_PID 0x6151
42#define EP_DATA_IN0 0x81
43#define EP_DATA_IN1 0x82
44
45struct e1_streamer;
46struct flow;
47
48typedef int (*xfer_cb_t)(struct e1_streamer *e1s, struct flow *flow, uint8_t *buf, int size);
49
50struct flow_entry {
51 uint8_t *buf;
52 struct libusb_transfer *xfr;
53};
54
55struct flow {
56 struct e1_streamer *parent;
57 xfer_cb_t cb;
58
59 int ep;
60 int count;
61 int size;
62 int ppx;
63 struct flow_entry *entries;
64};
65
66struct e1_streamer {
67 struct libusb_device_handle *devh;
68 struct flow data_in[2];
69 struct idt82 liu[2];
70 FILE *fh;
71};
72
73struct e1_chunk_hdr {
74 uint32_t magic;
75 struct {
76 uint64_t sec;
77 uint64_t usec;
78 } time;
79 int16_t len;
80 uint8_t ep;
81} __attribute__((packed));
82
83
84static int
85cb_xfr_data_in(struct e1_streamer *e1s, struct flow *flow, uint8_t *buf, int size)
86{
87 struct e1_chunk_hdr hdr;
88 struct timeval tv;
89 int rc;
90
91 hdr.magic = 0xe115600d; /* E1 is good */
92
93 gettimeofday(&tv, NULL);
94 hdr.time.sec = tv.tv_sec;
95 hdr.time.usec = tv.tv_usec;
96
97 hdr.ep = flow->ep;
98 hdr.len = size;
99
100 if (size < 0) {
101 printf("EP %02x - Err %d: %s\n", flow->ep, size, libusb_strerror(size));
102 return 0;
103 }
104
105 if (!e1s->fh)
106 return 0;
107
108 rc = fwrite(&hdr, sizeof(struct e1_chunk_hdr), 1, e1s->fh);
109 if (rc != 1) {
110 fprintf(stderr, "[!] Short write: %d != %zd", rc, sizeof(struct e1_chunk_hdr));
111 if (rc == -1)
112 fprintf(stderr, ", %s\n", strerror(errno));
113 else
114 fprintf(stderr, "\n");
115 g_do_exit = 1;
116 }
117
118 if (size > 0) {
119 rc = fwrite(buf, size, 1, e1s->fh);
120 if (rc != 1) {
121 fprintf(stderr, "[!] Short write: %d != %zd", rc, sizeof(struct e1_chunk_hdr));
122 if (rc == -1)
123 fprintf(stderr, ", %s\n", strerror(errno));
124 else
125 fprintf(stderr, "\n");
126 g_do_exit = 1;
127 }
128 }
129
130 return 0;
131}
132
133static void LIBUSB_CALL cb_xfr(struct libusb_transfer *xfr)
134{
135 struct flow *flow = (struct flow *) xfr->user_data;
136 struct e1_streamer *e1s = flow->parent;
137 int j, rv, len;
138
139#if 0
140 fprintf(stderr, "transfer status (%02x) %d [%d %d] [%d %d]\n", flow->ep, xfr->status,
141 xfr->iso_packet_desc[0].status,
142 xfr->iso_packet_desc[0].actual_length,
143 xfr->iso_packet_desc[1].status,
144 xfr->iso_packet_desc[1].actual_length
145 );
146#endif
147
148 if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
149 fprintf(stderr, "[!] XFR status != completed (%d)\n", xfr->status);
150 g_do_exit = 1;
151 }
152
153 len = 0;
154
155 if (flow->ep & 0x80) {
156 for (j=0; j<flow->ppx; j++) {
157 flow->cb(e1s, flow,
158 libusb_get_iso_packet_buffer_simple(xfr, j),
159 (xfr->iso_packet_desc[j].status == LIBUSB_TRANSFER_COMPLETED) ?
160 xfr->iso_packet_desc[j].actual_length : -1
161 );
162 if (!(xfr->iso_packet_desc[j].status == LIBUSB_TRANSFER_COMPLETED)) {
163 fprintf(stderr, "[!] ISO packet status != completed (%d)\n",
164 xfr->iso_packet_desc[j].status);
165 }
166
167 len += (xfr->iso_packet_desc[j].length = flow->size);
168 }
169 } else {
170 for (j=0; j<flow->ppx; j++)
171 len += (xfr->iso_packet_desc[j].length = flow->cb(e1s, flow, &xfr->buffer[len], flow->size));
172 }
173
174 libusb_fill_iso_transfer(xfr, e1s->devh, flow->ep,
175 xfr->buffer, len, flow->ppx,
176 cb_xfr, flow, 0
177 );
178
179 rv = libusb_submit_transfer(xfr);
180 if (rv) {
181 fprintf(stderr, "[!] Error re-submitting buffer (%d): %s\n", rv, libusb_strerror(rv));
182 g_do_exit = 1;
183 }
184}
185
186
187static void
188_e1s_flow_fini(struct flow *flow)
189{
190 int i;
191
192 for (i=0; i<flow->count; i++)
193 free(flow->entries[i].buf);
194
195 free(flow->entries);
196}
197
198static void
199_e1s_flow_init(struct e1_streamer *e1s, struct flow *flow, xfer_cb_t cb, int ep, int count, int size, int ppx)
200{
201 int i;
202
203 flow->parent = e1s;
204 flow->cb = cb;
205 flow->ep = ep;
206 flow->count = count;
207 flow->size = size;
208 flow->ppx = ppx;
209 flow->entries = calloc(count, sizeof(struct flow_entry));
210
211 for (i=0; i<count; i++)
212 flow->entries[i].buf = malloc(size * ppx);
213}
214
215static int
216_e1s_flow_start(struct e1_streamer *e1s, struct flow *flow)
217{
218 struct libusb_transfer *xfr;
219 int i, j, rv, len;
220
221 for (i=0; i<flow->count; i++)
222 {
223 xfr = libusb_alloc_transfer(flow->ppx);
224 if (!xfr)
225 return -ENOMEM;
226
227 len = 0;
228
229 if (flow->ep & 0x80) {
230 for (j=0; j<flow->ppx; j++)
231 len += (xfr->iso_packet_desc[j].length = flow->size);
232 } else {
233 for (j=0; j<flow->ppx; j++)
234 len += (xfr->iso_packet_desc[j].length = flow->cb(e1s, flow, &flow->entries[i].buf[len], flow->size));
235 }
236
237 libusb_fill_iso_transfer(xfr, e1s->devh, flow->ep,
238 flow->entries[i].buf, len, flow->ppx,
239 cb_xfr, flow, 0
240 );
241
242 rv = libusb_submit_transfer(xfr);
243 if (rv) {
244 return rv;
245 }
246
247 flow->entries[i].xfr = xfr;
248 }
249
250 return 0;
251}
252
253
254static void
255e1s_release(struct e1_streamer *e1s)
256{
257 if (!e1s)
258 return;
259
260 _e1s_flow_fini(&e1s->data_in[0]);
261 _e1s_flow_fini(&e1s->data_in[1]);
262
263 if (e1s->devh) {
264 libusb_release_interface(e1s->devh, 0);
265 libusb_close(e1s->devh);
266 }
267
268 free(e1s);
269}
270
271static struct e1_streamer *
272e1s_new(bool monitor, const char *out_file, bool append, int nx, int ppx)
273{
274 struct e1_streamer *e1s = NULL;
275 int rv;
276
277 e1s = calloc(1, sizeof(struct e1_streamer));
278 if (!e1s)
279 return NULL;
280
281 e1s->devh = libusb_open_device_with_vid_pid(NULL, USB_VID, USB_PID);
282 if (!e1s->devh) {
283 fprintf(stderr, "Error finding USB device\n");
284 goto err;
285 }
286
287 rv = libusb_claim_interface(e1s->devh, 0);
288 if (rv < 0) {
289 fprintf(stderr, "Error claiming interface: %s\n", libusb_error_name(rv));
290 goto err;
291 }
292
293 rv = libusb_set_interface_alt_setting(e1s->devh, 0, 1);
294 if (rv < 0) {
295 fprintf(stderr, "Error enabling interface: %s\n", libusb_error_name(rv));
296 goto err;
297 }
298
299 _e1s_flow_init(e1s, &e1s->data_in[0], cb_xfr_data_in, EP_DATA_IN0, nx, 388, ppx);
300 _e1s_flow_init(e1s, &e1s->data_in[1], cb_xfr_data_in, EP_DATA_IN1, nx, 388, ppx);
301
302 idt82_usb_init(&e1s->liu[0], e1s->devh, EP_DATA_IN0);
303 idt82_usb_init(&e1s->liu[1], e1s->devh, EP_DATA_IN1);
304 idt82_init(&e1s->liu[0], monitor);
305 idt82_init(&e1s->liu[1], monitor);
306
307 if (out_file) {
308 e1s->fh = fopen(out_file, append ? "ab" : "wb");
309 if (!e1s->fh)
310 fprintf(stderr, "[1] Failed to open recording file\n");
311 }
312
313 return e1s;
314
315err:
316 e1s_release(e1s);
317 return NULL;
318}
319
320struct options {
321 /* Transfer config */
322 int nx;
323 int ppx;
324
325 /* Output */
326 const char *out_filename;
327 bool out_append;
328
329 /* PHY */
330 bool monitor;
331
332 /* OS */
333 bool realtime;
334};
335
336static void
337opts_defaults(struct options *opts)
338{
339 memset(opts, 0x00, sizeof(struct options));
340
341 opts->nx = 2;
342 opts->ppx = 4;
343}
344
345static void
346opts_help(void)
347{
348 fprintf(stderr, " -a Output : append mode\n");
349 fprintf(stderr, " -o FILE Output : filename\n");
350 fprintf(stderr, " -n NX Xfer : Number of queued transfers (default: 2)\n");
351 fprintf(stderr, " -p PPX Xfer : Number of packets per transfer (default: 4)\n");
352 fprintf(stderr, " -m PHY : Monitor mode (i.e. high gain)\n");
353 fprintf(stderr, " -r OS : Set real-time priority on process\n");
354 fprintf(stderr, " -h help\n");
355}
356
357static int
358opts_parse(struct options *opts, int argc, char *argv[])
359{
360 const char *opts_short = "ao:n:p:mrh";
361 int opt;
362
363 while ((opt = getopt(argc, argv, opts_short)) != -1)
364 {
365 switch(opt) {
366 case 'a':
367 opts->out_append = true;
368 break;
369
370 case 'o':
371 opts->out_filename = optarg;
372 break;
373
374 case 'n':
375 opts->nx = atoi(optarg);
376 if (opts->nx <= 0) {
377 fprintf(stderr, "[!] Invalid nx value ignored\n");
378 opts->nx = 2;
379 }
380 break;
381
382 case 'p':
383 opts->ppx = atoi(optarg);
384 if (opts->ppx <= 0) {
385 fprintf(stderr, "[!] Invalid ppx value ignored\n");
386 opts->ppx = 4;
387 }
388 break;
389
390 case 'm':
391 opts->monitor = true;
392 break;
393
394 case 'r':
395 opts->realtime = true;
396 break;
397
398 case 'h':
399 default:
400 opts_help();
401 exit(1);
402 }
403 }
404
405 return 0;
406}
407
408int main(int argc, char *argv[])
409{
410 struct e1_streamer *e1s;
411 struct sched_param sp;
412 struct options opts;
413 int rv;
414
415 opts_defaults(&opts);
416 opts_parse(&opts, argc, argv);
417
418 if (opts.realtime) {
419 memset(&sp, 0x00, sizeof(sp));
420 sp.sched_priority = 50;
421 rv = sched_setscheduler(0, SCHED_RR, &sp);
422 printf("%d %d\n", rv, errno);
423 perror("sched_setscheduler");
424 }
425
426 rv = libusb_init(NULL);
427 if (rv < 0) {
428 fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rv));
429 return rv;
430 }
431
432 e1s = e1s_new(opts.monitor, opts.out_filename, opts.out_append, opts.nx, opts.ppx);
433 if (!e1s)
434 goto out;
435
436 _e1s_flow_start(e1s, &e1s->data_in[0]);
437 _e1s_flow_start(e1s, &e1s->data_in[1]);
438
439 while (!g_do_exit) {
440 rv = libusb_handle_events(NULL);
441 if (rv != LIBUSB_SUCCESS)
442 break;
443 }
444
445out:
446 e1s_release(e1s);
447
448 libusb_exit(NULL);
449
450 return 0;
451}