From 653ad6127a57b88af78b30e9790bb97f80f8eff7 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 5 Aug 2020 19:41:46 +0800 Subject: [PATCH] Use a bounce buffer for rtlsdr on ARM to work around zero-copy problems. On 5.x kernels with the USB mmap problems fixed, the distributed librtlsdr will use a zero-copy mapping for USB buffers. Unfortunately, there is something about the nature of the mapping on ARM (at least on Pis) that makes most access to the data extremely slow. The uc8_nodc converter is about 35x slower in this case compared to working on a heap-allocated buffer. Luckily, a plain memcpy() of the buffer is still reasonably fast, so we can use a bounce buffer and copy the data out of the slow mapping, then pass the copy to the converter. This mitigates most of the problem, at the expense of always needing that extra copy (which does somewhat defeat the purpose of zero-copy!) Unfortunately, librtlsdr provides no reliable way to control or detect the use of zero-copy mappings, so we have to assume the problem is always there (at least on ARM) and pay the cost of an unnecessary copy when zerocopy is _not_ in use, too. --- sdr_rtlsdr.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/sdr_rtlsdr.c b/sdr_rtlsdr.c index ec9a7a7..ef27f70 100644 --- a/sdr_rtlsdr.c +++ b/sdr_rtlsdr.c @@ -53,12 +53,17 @@ #include +#ifdef __arm__ +// Assume we need to use a bounce buffer to avoid performance problems on Pis running kernel 5.x and using zerocopy +# define USE_BOUNCE_BUFFER +#endif + static struct { rtlsdr_dev_t *dev; bool digital_agc; int ppm_error; int direct_sampling; - + uint8_t *bounce_buffer; iq_convert_fn converter; struct converter_state *converter_state; } RTLSDR; @@ -73,6 +78,7 @@ void rtlsdrInitConfig() RTLSDR.digital_agc = false; RTLSDR.ppm_error = 0; RTLSDR.direct_sampling = 0; + RTLSDR.bounce_buffer = NULL; RTLSDR.converter = NULL; RTLSDR.converter_state = NULL; } @@ -262,6 +268,14 @@ bool rtlsdrOpen(void) { return false; } +#ifdef USE_BOUNCE_BUFFER + if (!(RTLSDR.bounce_buffer = malloc(MODES_RTL_BUF_SIZE))) { + fprintf(stderr, "rtlsdr: can't allocate bounce buffer\n"); + rtlsdrClose(); + return false; + } +#endif + return true; } @@ -317,10 +331,13 @@ static void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) dropped = samples_read - to_convert; } - +#ifdef USE_BOUNCE_BUFFER + // Work around zero-copy slowness on Pis with 5.x kernels + memcpy(RTLSDR.bounce_buffer, buf, to_convert * 2); + buf = RTLSDR.bounce_buffer; +#endif RTLSDR.converter(buf, &outbuf->data[outbuf->overlap], to_convert, RTLSDR.converter_state, &outbuf->mean_level, &outbuf->mean_power); - outbuf->validLength = outbuf->overlap + to_convert; // Push to the demodulation thread @@ -353,4 +370,7 @@ void rtlsdrClose() RTLSDR.converter = NULL; RTLSDR.converter_state = NULL; } + + free(RTLSDR.bounce_buffer); + RTLSDR.bounce_buffer = NULL; }