change async cancellation mechnism to make it more reliable

- fixes crashes on windows platform while calling rtlsdr_close()
- makes it possible to restart async reads after cancellation
master
Dimitri Stolnikov 13 years ago
parent 24a12a44d1
commit 7651ff1940
  1. 165
      src/rtl-sdr.c

@ -139,6 +139,12 @@ static rtlsdr_device_t devices[] = {
#define DEFAULT_BUF_NUMBER 32 #define DEFAULT_BUF_NUMBER 32
#define DEFAULT_BUF_LENGTH (16 * 16384) #define DEFAULT_BUF_LENGTH (16 * 16384)
enum rtlsdr_async_status {
RTLSDR_INACTIVE = 0,
RTLSDR_CANCELING,
RTLSDR_RUNNING
};
struct rtlsdr_dev { struct rtlsdr_dev {
libusb_context *ctx; libusb_context *ctx;
struct libusb_device_handle *devh; struct libusb_device_handle *devh;
@ -148,7 +154,7 @@ struct rtlsdr_dev {
unsigned char **xfer_buf; unsigned char **xfer_buf;
rtlsdr_read_async_cb_t cb; rtlsdr_read_async_cb_t cb;
void *cb_ctx; void *cb_ctx;
int run_async; enum rtlsdr_async_status async_status;
rtlsdr_tuner_t *tuner; rtlsdr_tuner_t *tuner;
int rate; /* Hz */ int rate; /* Hz */
}; };
@ -269,7 +275,7 @@ uint16_t rtlsdr_read_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_
r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT);
if (r < 0) if (r < 0)
fprintf(stderr, "%s failed\n", __FUNCTION__); fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r);
reg = (data[1] << 8) | data[0]; reg = (data[1] << 8) | data[0];
@ -293,7 +299,7 @@ void rtlsdr_write_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint16_t
r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT);
if (r < 0) if (r < 0)
fprintf(stderr, "%s failed\n", __FUNCTION__); fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r);
} }
uint16_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint8_t len) uint16_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint8_t len)
@ -308,7 +314,7 @@ uint16_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, u
r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT);
if (r < 0) if (r < 0)
fprintf(stderr, "%s failed\n", __FUNCTION__); fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r);
reg = (data[1] << 8) | data[0]; reg = (data[1] << 8) | data[0];
@ -332,7 +338,7 @@ void rtlsdr_demod_write_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint
r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT);
if (r < 0) if (r < 0)
fprintf(stderr, "%s failed\n", __FUNCTION__); fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r);
rtlsdr_demod_read_reg(dev, 0x0a, 0x01, 1); rtlsdr_demod_read_reg(dev, 0x0a, 0x01, 1);
} }
@ -567,61 +573,6 @@ rtlsdr_device_t *find_known_device(uint16_t vid, uint16_t pid)
return device; return device;
} }
static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev)
{
int i;
if (!dev)
return -1;
if (!dev->xfer) {
dev->xfer = malloc(dev->xfer_buf_num *
sizeof(struct libusb_transfer *));
for(i = 0; i < dev->xfer_buf_num; ++i)
dev->xfer[i] = libusb_alloc_transfer(0);
}
if (!dev->xfer_buf) {
dev->xfer_buf = malloc(dev->xfer_buf_num *
sizeof(unsigned char *));
for(i = 0; i < dev->xfer_buf_num; ++i)
dev->xfer_buf[i] = malloc(dev->xfer_buf_len);
}
return 0;
}
static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev)
{
int i;
if (!dev)
return -1;
for(i = 0; i < dev->xfer_buf_num; ++i) {
if (dev->xfer[i]) {
libusb_cancel_transfer(dev->xfer[i]);
libusb_free_transfer(dev->xfer[i]);
}
if (dev->xfer_buf[i])
free(dev->xfer_buf[i]);
}
if (dev->xfer) {
free(dev->xfer);
dev->xfer = NULL;
}
if (dev->xfer_buf) {
free(dev->xfer_buf);
dev->xfer_buf = NULL;
}
return 0;
}
uint32_t rtlsdr_get_device_count(void) uint32_t rtlsdr_get_device_count(void)
{ {
int i; int i;
@ -813,8 +764,6 @@ int rtlsdr_close(rtlsdr_dev_t *dev)
rtlsdr_deinit_baseband(dev); rtlsdr_deinit_baseband(dev);
_rtlsdr_free_async_buffers(dev);
libusb_release_interface(dev->devh, 0); libusb_release_interface(dev->devh, 0);
libusb_close(dev->devh); libusb_close(dev->devh);
@ -855,8 +804,7 @@ static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer)
libusb_submit_transfer(xfer); /* resubmit transfer */ libusb_submit_transfer(xfer); /* resubmit transfer */
} else { } else {
/*fprintf(stderr, "transfer status: %d\n", xfer->status);*/ /*fprintf(stderr, "transfer status: %d\n", xfer->status);*/
if (dev->run_async) rtlsdr_cancel_async(dev); /* abort async loop */
dev->run_async = 0; /* abort async loop */
} }
} }
@ -865,6 +813,63 @@ int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx)
return rtlsdr_read_async(dev, cb, ctx, 0, 0); return rtlsdr_read_async(dev, cb, ctx, 0, 0);
} }
static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev)
{
int i;
if (!dev)
return -1;
if (!dev->xfer) {
dev->xfer = malloc(dev->xfer_buf_num *
sizeof(struct libusb_transfer *));
for(i = 0; i < dev->xfer_buf_num; ++i)
dev->xfer[i] = libusb_alloc_transfer(0);
}
if (!dev->xfer_buf) {
dev->xfer_buf = malloc(dev->xfer_buf_num *
sizeof(unsigned char *));
for(i = 0; i < dev->xfer_buf_num; ++i)
dev->xfer_buf[i] = malloc(dev->xfer_buf_len);
}
return 0;
}
static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev)
{
int i;
if (!dev)
return -1;
if (dev->xfer) {
for(i = 0; i < dev->xfer_buf_num; ++i) {
if (dev->xfer[i]) {
libusb_free_transfer(dev->xfer[i]);
}
}
free(dev->xfer);
dev->xfer = NULL;
}
if (dev->xfer_buf) {
for(i = 0; i < dev->xfer_buf_num; ++i) {
if (dev->xfer_buf[i])
free(dev->xfer_buf[i]);
}
free(dev->xfer_buf);
dev->xfer_buf = NULL;
}
return 0;
}
int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx,
uint32_t buf_num, uint32_t buf_len) uint32_t buf_num, uint32_t buf_len)
{ {
@ -877,8 +882,6 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx,
dev->cb = cb; dev->cb = cb;
dev->cb_ctx = ctx; dev->cb_ctx = ctx;
_rtlsdr_free_async_buffers(dev);
if (buf_num > 0) if (buf_num > 0)
dev->xfer_buf_num = buf_num; dev->xfer_buf_num = buf_num;
else else
@ -904,22 +907,40 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx,
libusb_submit_transfer(dev->xfer[i]); libusb_submit_transfer(dev->xfer[i]);
} }
dev->run_async = 1; dev->async_status = RTLSDR_RUNNING;
while (dev->run_async) { while (RTLSDR_INACTIVE != dev->async_status) {
r = libusb_handle_events_timeout(dev->ctx, &tv); r = libusb_handle_events_timeout(dev->ctx, &tv);
if (r < 0) { if (r < 0) {
/*fprintf(stderr, "handle_events returned: %d\n", r);*/ /*fprintf(stderr, "handle_events returned: %d\n", r);*/
if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */
continue;
break; break;
} }
}
if (RTLSDR_CANCELING == dev->async_status) {
dev->async_status = RTLSDR_INACTIVE;
if (!dev->xfer)
break;
for(i = 0; i < dev->xfer_buf_num; ++i) { for(i = 0; i < dev->xfer_buf_num; ++i) {
if (dev->xfer[i]) { if (!dev->xfer[i])
continue;
if (dev->xfer[i]->status == LIBUSB_TRANSFER_COMPLETED) {
libusb_cancel_transfer(dev->xfer[i]); libusb_cancel_transfer(dev->xfer[i]);
dev->async_status = RTLSDR_CANCELING;
}
}
if (RTLSDR_INACTIVE == dev->async_status)
break;
} }
} }
_rtlsdr_free_async_buffers(dev);
return r; return r;
} }
@ -928,8 +949,8 @@ int rtlsdr_cancel_async(rtlsdr_dev_t *dev)
if (!dev) if (!dev)
return -1; return -1;
if (dev->run_async) { if (RTLSDR_RUNNING == dev->async_status) {
dev->run_async = 0; dev->async_status = RTLSDR_CANCELING;
return 0; return 0;
} }

Loading…
Cancel
Save