lib: use USB zero-copy transfers if possible

Decreases CPU load especially for embedded machines.
Requires Linux >= 4.6 and libusb >= 1.0.21. If this
is not the case or the allocation fails, we will
fall back to buffers allocated in userspace.

Signed-off-by: Steve Markgraf <steve@steve-m.de>
master
Steve Markgraf 7 years ago
parent 326b0e3acc
commit a854ae8b48
  1. 66
      src/librtlsdr.c

@ -103,6 +103,7 @@ struct rtlsdr_dev {
void *cb_ctx; void *cb_ctx;
enum rtlsdr_async_status async_status; enum rtlsdr_async_status async_status;
int async_cancel; int async_cancel;
int use_zerocopy;
/* rtl demod context */ /* rtl demod context */
uint32_t rate; /* Hz */ uint32_t rate; /* Hz */
uint32_t rtl_xtal; /* Hz */ uint32_t rtl_xtal; /* Hz */
@ -1741,12 +1742,49 @@ static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev)
dev->xfer[i] = libusb_alloc_transfer(0); dev->xfer[i] = libusb_alloc_transfer(0);
} }
if (!dev->xfer_buf) { if (dev->xfer_buf)
dev->xfer_buf = malloc(dev->xfer_buf_num * return -2;
sizeof(unsigned char *));
for(i = 0; i < dev->xfer_buf_num; ++i) dev->xfer_buf = malloc(dev->xfer_buf_num * sizeof(unsigned char *));
memset(dev->xfer_buf, 0, dev->xfer_buf_num * sizeof(unsigned char *));
#if defined (__linux__) && LIBUSB_API_VERSION >= 0x01000105
fprintf(stderr, "Allocating %d zero-copy buffers\n", dev->xfer_buf_num);
dev->use_zerocopy = 1;
for (i = 0; i < dev->xfer_buf_num; ++i) {
dev->xfer_buf[i] = libusb_dev_mem_alloc(dev->devh, dev->xfer_buf_len);
if (!dev->xfer_buf[i]) {
fprintf(stderr, "Failed to allocate zero-copy "
"buffer for transfer %d\nFalling "
"back to buffers in userspace\n", i);
dev->use_zerocopy = 0;
break;
}
}
/* zero-copy buffer allocation failed (partially or completely)
* we need to free the buffers again if already allocated */
if (!dev->use_zerocopy) {
for (i = 0; i < dev->xfer_buf_num; ++i) {
if (dev->xfer_buf[i])
libusb_dev_mem_free(dev->devh,
dev->xfer_buf[i],
dev->xfer_buf_len);
}
}
#endif
/* no zero-copy available, allocate buffers in userspace */
if (!dev->use_zerocopy) {
for (i = 0; i < dev->xfer_buf_num; ++i) {
dev->xfer_buf[i] = malloc(dev->xfer_buf_len); dev->xfer_buf[i] = malloc(dev->xfer_buf_len);
if (!dev->xfer_buf[i])
return -ENOMEM;
}
} }
return 0; return 0;
@ -1771,10 +1809,19 @@ static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev)
} }
if (dev->xfer_buf) { if (dev->xfer_buf) {
for(i = 0; i < dev->xfer_buf_num; ++i) { for (i = 0; i < dev->xfer_buf_num; ++i) {
if (dev->xfer_buf[i]) if (dev->xfer_buf[i]) {
if (dev->use_zerocopy) {
#if defined (__linux__) && LIBUSB_API_VERSION >= 0x01000105
libusb_dev_mem_free(dev->devh,
dev->xfer_buf[i],
dev->xfer_buf_len);
#endif
} else {
free(dev->xfer_buf[i]); free(dev->xfer_buf[i]);
} }
}
}
free(dev->xfer_buf); free(dev->xfer_buf);
dev->xfer_buf = NULL; dev->xfer_buf = NULL;
@ -1828,7 +1875,12 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx,
r = libusb_submit_transfer(dev->xfer[i]); r = libusb_submit_transfer(dev->xfer[i]);
if (r < 0) { if (r < 0) {
fprintf(stderr, "Failed to submit transfer %i!\n", i); fprintf(stderr, "Failed to submit transfer %i\n"
"Please increase your allowed "
"usbfs buffer size with the "
"following command:\n"
"echo 0 > /sys/module/usbcore"
"/parameters/usbfs_memory_mb\n", i);
dev->async_status = RTLSDR_CANCELING; dev->async_status = RTLSDR_CANCELING;
break; break;
} }

Loading…
Cancel
Save