diff --git a/README.rtlsdr_rpc b/README.rtlsdr_rpc new file mode 100644 index 00000000..646b13de --- /dev/null +++ b/README.rtlsdr_rpc @@ -0,0 +1,43 @@ +This implementation of librtlsdr makes remote dongles +appear to the local software as if they were on the +same computer. It works by forwarding librtlsdr calls +to the remote computer over TCP. + +It allows one to use existing tools without modifying +them. Also, it allows a developer to use the same API +no matter weither the dongle is local or distant. + +To use it, one must compile and install the library +with CMAKE the usual way. Note that you may need to +uninstall the existing librtlsdr, as people reported +runtime errors due to conflicting installs. + +Then, a server (called rtl_rpcd) must be run on the +remote location. + +In my case, the dongle is in a beagle bone black is +at address 192.168.0.43: +beagleboneblack #> ./rtl_rpcd + +Then, the existing tool (for instance rtlizer) can be +run on the local computer using: +RTLSDR_RPC_IS_ENABLED=1 RTLSDR_RPC_SERV_ADDR=192.168.0.43 \ +rtlizer + +This implementation still has some limitations, but +works well in most cases. Please report any bug to +texane@gmail.com + +Also, note that the latest version of libusb should be +used as librtlsdr crashed when used with older version +(esp. the rtlsdr_read_async routine): +https://github.com/libusb/libusb.git + +list of known working software: +rtl_fm +rtl_power +rtlsdr-waterfall +rtlizer +gnuradio-companion +cubicsdr +gqrx \ No newline at end of file diff --git a/include/rtlsdr_rpc.h b/include/rtlsdr_rpc.h new file mode 100644 index 00000000..eb782c68 --- /dev/null +++ b/include/rtlsdr_rpc.h @@ -0,0 +1,123 @@ +#ifndef RTLSDR_RPC_H_INCLUDED +#define RTLSDR_RPC_H_INCLUDED + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*rtlsdr_rpc_read_async_cb_t) +(unsigned char*, uint32_t, void*); + +uint32_t rtlsdr_rpc_get_device_count(void); + +const char* rtlsdr_rpc_get_device_name +(uint32_t nidex); + +int rtlsdr_rpc_get_device_usb_strings +(uint32_t index, char* manufact, char* product, char* serial); + +int rtlsdr_rpc_get_index_by_serial +(const char* serial); + +int rtlsdr_rpc_open +(void** dev, uint32_t index); + +int rtlsdr_rpc_close +(void* dev); + +int rtlsdr_rpc_set_xtal_freq +(void* dev, uint32_t rtl_freq, uint32_t tuner_freq); + +int rtlsdr_rpc_get_xtal_freq +(void* dev, uint32_t* rtl_freq, uint32_t* tuner_freq); + +int rtlsdr_rpc_get_usb_strings +(void* dev, char* manufact, char* product, char* serial); + +int rtlsdr_rpc_write_eeprom +(void* dev, uint8_t* data, uint8_t offset, uint16_t len); + +int rtlsdr_rpc_read_eeprom +(void* dev, uint8_t* data, uint8_t offset, uint16_t len); + +int rtlsdr_rpc_set_center_freq +(void* dev, uint32_t freq); + +uint32_t rtlsdr_rpc_get_center_freq +(void* dev); + +int rtlsdr_rpc_set_freq_correction +(void* dev, int ppm); + +int rtlsdr_rpc_get_freq_correction +(void *dev); + +int rtlsdr_rpc_get_tuner_type +(void* dev); + +int rtlsdr_rpc_get_tuner_gains +(void* dev, int* gainsp); + +int rtlsdr_rpc_set_tuner_gain +(void *dev, int gain); + +int rtlsdr_rpc_get_tuner_gain +(void* dev); + +int rtlsdr_rpc_set_tuner_if_gain +(void* dev, int stage, int gain); + +int rtlsdr_rpc_set_tuner_gain_mode +(void* dev, int manual); + +int rtlsdr_rpc_set_sample_rate +(void* dev, uint32_t rate); + +uint32_t rtlsdr_rpc_get_sample_rate +(void* dev); + +int rtlsdr_rpc_set_testmode +(void* dev, int on); + +int rtlsdr_rpc_set_agc_mode +(void* dev, int on); + +int rtlsdr_rpc_set_direct_sampling +(void* dev, int on); + +int rtlsdr_rpc_get_direct_sampling +(void* dev); + +int rtlsdr_rpc_set_offset_tuning +(void* dev, int on); + +int rtlsdr_rpc_get_offset_tuning +(void* dev); + +int rtlsdr_rpc_reset_buffer +(void* dev); + +int rtlsdr_rpc_read_sync +(void* dev, void* buf, int len, int* n_read); + +int rtlsdr_rpc_wait_async +(void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx); + +int rtlsdr_rpc_read_async +(void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx, uint32_t buf_num, uint32_t buf_len); + +int rtlsdr_rpc_cancel_async +(void* dev); + +unsigned int rtlsdr_rpc_is_enabled(void); + +#ifdef __cplusplus +} +#endif + + +#endif /* RTLSDR_RPC_H_INCLUDED */ diff --git a/include/rtlsdr_rpc_msg.h b/include/rtlsdr_rpc_msg.h new file mode 100644 index 00000000..2ad9bd66 --- /dev/null +++ b/include/rtlsdr_rpc_msg.h @@ -0,0 +1,94 @@ +#ifndef RTLSDR_RPC_MSG_H_INCLUDED +#define RTLSDR_RPC_MSG_H_INCLUDED + + +#include +#include + +typedef enum +{ + RTLSDR_RPC_OP_GET_DEVICE_COUNT = 0, + RTLSDR_RPC_OP_GET_DEVICE_NAME, + RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS, + RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL, + RTLSDR_RPC_OP_OPEN, + RTLSDR_RPC_OP_CLOSE, + RTLSDR_RPC_OP_SET_XTAL_FREQ, + RTLSDR_RPC_OP_GET_XTAL_FREQ, + RTLSDR_RPC_OP_GET_USB_STRINGS, + RTLSDR_RPC_OP_WRITE_EEPROM, + RTLSDR_RPC_OP_READ_EEPROM, + RTLSDR_RPC_OP_SET_CENTER_FREQ, + RTLSDR_RPC_OP_GET_CENTER_FREQ, + RTLSDR_RPC_OP_SET_FREQ_CORRECTION, + RTLSDR_RPC_OP_GET_FREQ_CORRECTION, + RTLSDR_RPC_OP_GET_TUNER_TYPE, + RTLSDR_RPC_OP_GET_TUNER_GAINS, + RTLSDR_RPC_OP_SET_TUNER_GAIN, + RTLSDR_RPC_OP_GET_TUNER_GAIN, + RTLSDR_RPC_OP_SET_TUNER_IF_GAIN, + RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE, + RTLSDR_RPC_OP_SET_SAMPLE_RATE, + RTLSDR_RPC_OP_GET_SAMPLE_RATE, + RTLSDR_RPC_OP_SET_TESTMODE, + RTLSDR_RPC_OP_SET_AGC_MODE, + RTLSDR_RPC_OP_SET_DIRECT_SAMPLING, + RTLSDR_RPC_OP_GET_DIRECT_SAMPLING, + RTLSDR_RPC_OP_SET_OFFSET_TUNING, + RTLSDR_RPC_OP_GET_OFFSET_TUNING, + RTLSDR_RPC_OP_RESET_BUFFER, + RTLSDR_RPC_OP_READ_SYNC, + RTLSDR_RPC_OP_WAIT_ASYNC, + RTLSDR_RPC_OP_READ_ASYNC, + RTLSDR_RPC_OP_CANCEL_ASYNC, + + /* non api operations */ + RTLSDR_RPC_OP_EVENT_STATE, + + RTLSDR_RPC_OP_INVALID +} rtlsdr_rpc_op_t; + +typedef struct +{ + /* raw network format */ + uint32_t size; + uint8_t op; + uint8_t id; + uint32_t err; + uint8_t data[1]; +} __attribute__((packed)) rtlsdr_rpc_fmt_t; + +typedef struct +{ + size_t off; + size_t size; + uint8_t* fmt; +} rtlsdr_rpc_msg_t; + +int rtlsdr_rpc_msg_init(rtlsdr_rpc_msg_t*, size_t); +int rtlsdr_rpc_msg_fini(rtlsdr_rpc_msg_t*); +void rtlsdr_rpc_msg_reset(rtlsdr_rpc_msg_t*); +int rtlsdr_rpc_msg_realloc(rtlsdr_rpc_msg_t*, size_t); + +void rtlsdr_rpc_msg_set_size(rtlsdr_rpc_msg_t*, size_t); +size_t rtlsdr_rpc_msg_get_size(const rtlsdr_rpc_msg_t*); +void rtlsdr_rpc_msg_set_op(rtlsdr_rpc_msg_t*, rtlsdr_rpc_op_t); +rtlsdr_rpc_op_t rtlsdr_rpc_msg_get_op(const rtlsdr_rpc_msg_t*); +void rtlsdr_rpc_msg_set_id(rtlsdr_rpc_msg_t*, uint8_t); +uint8_t rtlsdr_rpc_msg_get_id(const rtlsdr_rpc_msg_t*); +void rtlsdr_rpc_msg_set_err(rtlsdr_rpc_msg_t*, int); +int rtlsdr_rpc_msg_get_err(const rtlsdr_rpc_msg_t*); + +int rtlsdr_rpc_msg_push_int32(rtlsdr_rpc_msg_t*, int32_t); +int rtlsdr_rpc_msg_push_uint32(rtlsdr_rpc_msg_t*, uint32_t); +void rtlsdr_rpc_msg_push_uint32_safe(rtlsdr_rpc_msg_t*, uint32_t); +int rtlsdr_rpc_msg_push_str(rtlsdr_rpc_msg_t*, const char*); +int rtlsdr_rpc_msg_push_buf(rtlsdr_rpc_msg_t*, const uint8_t*, size_t); +void rtlsdr_rpc_msg_skip_safe(rtlsdr_rpc_msg_t*, size_t); +int rtlsdr_rpc_msg_pop_int32(rtlsdr_rpc_msg_t*, int32_t*); +int rtlsdr_rpc_msg_pop_uint32(rtlsdr_rpc_msg_t*, uint32_t*); +int rtlsdr_rpc_msg_pop_str(rtlsdr_rpc_msg_t*, const char**); +int rtlsdr_rpc_msg_pop_buf(rtlsdr_rpc_msg_t*, const uint8_t**, size_t*); + + +#endif /* RTLSDR_RPC_MSG_H_INCLUDED */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07d64abe..defa14d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,8 @@ RTLSDR_APPEND_SRCS( tuner_fc0013.c tuner_fc2580.c tuner_r82xx.c + rtlsdr_rpc.c + rtlsdr_rpc_msg.c ) ######################################################################## @@ -91,7 +93,8 @@ add_executable(rtl_fm rtl_fm.c) add_executable(rtl_eeprom rtl_eeprom.c) add_executable(rtl_adsb rtl_adsb.c) add_executable(rtl_power rtl_power.c) -set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb rtl_power) +add_executable(rtl_rpcd rtl_rpcd.c rtlsdr_rpc_msg.c) +set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb rtl_power rtl_rpcd) target_link_libraries(rtl_sdr rtlsdr_shared convenience_static ${LIBUSB_LIBRARIES} @@ -121,6 +124,10 @@ target_link_libraries(rtl_power rtlsdr_shared convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) +target_link_libraries(rtl_rpcd rtlsdr_shared convenience_static + ${LIBUSB_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} +) if(UNIX) target_link_libraries(rtl_fm m) target_link_libraries(rtl_adsb m) @@ -140,6 +147,7 @@ target_link_libraries(rtl_fm libgetopt_static) target_link_libraries(rtl_eeprom libgetopt_static) target_link_libraries(rtl_adsb libgetopt_static) target_link_libraries(rtl_power libgetopt_static) +target_link_libraries(rtl_rpcd ws2_32 libgetopt_static) set_property(TARGET rtl_sdr APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_tcp APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_test APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) @@ -147,6 +155,7 @@ set_property(TARGET rtl_fm APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_eeprom APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_adsb APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_power APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) +set_property(TARGET rtl_rpcd APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) endif() ######################################################################## # Install built library files & utilities diff --git a/src/Makefile.am b/src/Makefile.am index 200990a2..11855a9b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,10 +9,10 @@ AM_CFLAGS = ${CFLAGS} -fPIC ${SYMBOL_VISIBILITY} lib_LTLIBRARIES = librtlsdr.la -librtlsdr_la_SOURCES = librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c tuner_r82xx.c +librtlsdr_la_SOURCES = librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c tuner_r82xx.c rtlsdr_rpc.c rtlsdr_rpc_msg.c librtlsdr_la_LDFLAGS = -version-info $(LIBVERSION) -bin_PROGRAMS = rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb rtl_power +bin_PROGRAMS = rtl_sdr rtl_tcp rtl_test rtl_fm rtl_eeprom rtl_adsb rtl_power rtl_rpcd rtl_sdr_SOURCES = rtl_sdr.c convenience/convenience.c rtl_sdr_LDADD = librtlsdr.la @@ -34,3 +34,6 @@ rtl_adsb_LDADD = librtlsdr.la $(LIBM) rtl_power_SOURCES = rtl_power.c convenience/convenience.c rtl_power_LDADD = librtlsdr.la $(LIBM) + +rtl_rpcd_SOURCES = rtl_rpcd.c rtlsdr_rpc_msg.c convenience/convenience.c +rtl_rpcd_LDADD = librtlsdr.la diff --git a/src/librtlsdr.c b/src/librtlsdr.c index 9a3ebcd1..eb1c4eb0 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -28,6 +28,7 @@ #endif #include +#include "rtlsdr_rpc.h" /* * All libusb callback functions should be marked with the LIBUSB_CALL macro @@ -714,6 +715,11 @@ int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_fr { int r = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_xtal_freq(dev, rtl_freq, tuner_freq); + } + if (!dev) return -1; @@ -750,6 +756,11 @@ int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_fr int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_freq) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_xtal_freq(dev, rtl_freq, tuner_freq); + } + if (!dev) return -1; @@ -772,6 +783,11 @@ int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, const int buf_max = 256; int r = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_usb_strings(dev, manufact, product, serial); + } + if (!dev || !dev->devh) return -1; @@ -811,6 +827,11 @@ int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16 int i; uint8_t cmd[2]; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_write_eeprom(dev, data, offset, len); + } + if (!dev) return -1; @@ -848,6 +869,11 @@ int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_ int r = 0; int i; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_read_eeprom(dev, data, offset, len); + } + if (!dev) return -1; @@ -872,6 +898,11 @@ int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) { int r = -1; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_center_freq(dev, freq); + } + if (!dev || !dev->tuner) return -1; @@ -893,6 +924,11 @@ int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_center_freq(dev); + } + if (!dev) return 0; @@ -903,6 +939,11 @@ int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) { int r = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_freq_correction(dev, ppm); + } + if (!dev) return -1; @@ -926,6 +967,11 @@ int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_freq_correction(dev); + } + if (!dev) return 0; @@ -934,6 +980,11 @@ int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev) enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return (enum rtlsdr_tuner)rtlsdr_rpc_get_tuner_type(dev); + } + if (!dev) return RTLSDR_TUNER_UNKNOWN; @@ -959,6 +1010,11 @@ int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains) const int *ptr = NULL; int len = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_tuner_gains(dev, gains); + } + if (!dev) return -1; @@ -998,6 +1054,11 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) { int r = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_tuner_gain(dev, gain); + } + if (!dev || !dev->tuner) return -1; @@ -1017,6 +1078,11 @@ int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_tuner_gain(dev); + } + if (!dev) return 0; @@ -1027,6 +1093,11 @@ int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain) { int r = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_tuner_if_gain(dev, stage, gain); + } + if (!dev || !dev->tuner) return -1; @@ -1043,6 +1114,11 @@ int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) { int r = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_tuner_gain_mode(dev, mode); + } + if (!dev || !dev->tuner) return -1; @@ -1062,6 +1138,11 @@ int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) uint32_t rsamp_ratio, real_rsamp_ratio; double real_rate; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_sample_rate(dev, samp_rate); + } + if (!dev) return -1; @@ -1109,6 +1190,11 @@ int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_sample_rate(dev); + } + if (!dev) return 0; @@ -1117,6 +1203,11 @@ uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_testmode(dev, on); + } + if (!dev) return -1; @@ -1125,6 +1216,11 @@ int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_agc_mode(dev, on); + } + if (!dev) return -1; @@ -1135,6 +1231,11 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) { int r = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_direct_sampling(dev, on); + } + if (!dev) return -1; @@ -1196,6 +1297,11 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_direct_sampling(dev); + } + if (!dev) return -1; @@ -1206,6 +1312,11 @@ int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) { int r = 0; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_set_offset_tuning(dev, on); + } + if (!dev) return -1; @@ -1234,6 +1345,11 @@ int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_offset_tuning(dev); + } + if (!dev) return -1; @@ -1264,6 +1380,11 @@ uint32_t rtlsdr_get_device_count(void) struct libusb_device_descriptor dd; ssize_t cnt; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_device_count(); + } + libusb_init(&ctx); cnt = libusb_get_device_list(ctx, &list); @@ -1292,6 +1413,11 @@ const char *rtlsdr_get_device_name(uint32_t index) uint32_t device_count = 0; ssize_t cnt; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_device_name(index); + } + libusb_init(&ctx); cnt = libusb_get_device_list(ctx, &list); @@ -1332,6 +1458,12 @@ int rtlsdr_get_device_usb_strings(uint32_t index, char *manufact, uint32_t device_count = 0; ssize_t cnt; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_device_usb_strings + (index, manufact, product, serial); + } + libusb_init(&ctx); cnt = libusb_get_device_list(ctx, &list); @@ -1370,6 +1502,11 @@ int rtlsdr_get_index_by_serial(const char *serial) int i, cnt, r; char str[256]; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_get_index_by_serial(serial); + } + if (!serial) return -1; @@ -1399,6 +1536,11 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) uint8_t reg; ssize_t cnt; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_open((void**)out_dev, index); + } + dev = malloc(sizeof(rtlsdr_dev_t)); if (NULL == dev) return -ENOMEM; @@ -1585,6 +1727,11 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) int rtlsdr_close(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_close(dev); + } + if (!dev) return -1; @@ -1623,6 +1770,11 @@ int rtlsdr_close(rtlsdr_dev_t *dev) int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_reset_buffer(dev); + } + if (!dev) return -1; @@ -1634,6 +1786,11 @@ int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_read_sync(dev, buf, len, n_read); + } + if (!dev) return -1; @@ -1670,6 +1827,11 @@ static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_wait_async(dev, cb, ctx); + } + return rtlsdr_read_async(dev, cb, ctx, 0, 0); } @@ -1739,6 +1901,11 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, struct timeval zerotv = { 0, 0 }; enum rtlsdr_async_status next_status = RTLSDR_INACTIVE; + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_read_async(dev, cb, ctx, buf_num, buf_len); + } + if (!dev) return -1; @@ -1836,6 +2003,11 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, int rtlsdr_cancel_async(rtlsdr_dev_t *dev) { + if (rtlsdr_rpc_is_enabled()) + { + return rtlsdr_rpc_cancel_async(dev); + } + if (!dev) return -1; diff --git a/src/rtl_rpcd.c b/src/rtl_rpcd.c new file mode 100644 index 00000000..1096e5fe --- /dev/null +++ b/src/rtl_rpcd.c @@ -0,0 +1,1114 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rtlsdr_rpc_msg.h" + + +#if 1 +#include +#define PRINTF(__s, ...) \ +do { \ + fprintf(stderr, __s, ##__VA_ARGS__); \ + fflush(stderr); \ +} while (0) +#define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) +#define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) +#else +#define TRACE() +#define ERROR() +#define PRINTF(...) +#endif + + +typedef struct +{ + int listen_sock; + int cli_sock; + + rtlsdr_dev_t* dev; + uint32_t did; + + rtlsdr_rpc_msg_t query_msg; + rtlsdr_rpc_msg_t reply_msg; + rtlsdr_rpc_msg_t event_msg; + + unsigned int async_replied; + uint8_t async_id; + +} rpcd_t; + +static int resolve_ip_addr +( + struct sockaddr_storage saddr_both[2], size_t size_both[2], + const char* addr, const char* port +) +{ + struct addrinfo ai; + struct addrinfo* aip = NULL; + int err = -1; + size_t i; + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = AF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_PASSIVE; + + if (getaddrinfo(addr, port, &ai, &aip)) goto on_error; + + size_both[0] = 0; + size_both[1] = 0; + i = 0; + for (; (i != 2) && (aip != NULL); aip = aip->ai_next) + { + if ((aip->ai_family != AF_INET) && (aip->ai_family != AF_INET6)) continue ; + if (aip->ai_addrlen == 0) continue ; + memcpy(&saddr_both[i], aip->ai_addr, aip->ai_addrlen); + size_both[i] = aip->ai_addrlen; + ++i; + } + + if (i == 0) goto on_error; + + err = 0; + on_error: + if (aip != NULL) freeaddrinfo(aip); + return err; +} + +static int open_nonblock_socket +( + struct sockaddr_storage saddr_both[2], size_t size_both[2], + int type, int proto, + struct sockaddr_storage** saddr_used, size_t* size_used +) +{ + size_t i; + int fd = -1; + + for (i = 0; (i != 2) && (size_both[i]); ++i) + { + const struct sockaddr* const sa = (const struct sockaddr*)&saddr_both[i]; + fd = socket(sa->sa_family, type, proto); + if (fd != -1) break ; + } + + if ((i == 2) || (size_both[i] == 0)) return -1; + + *saddr_used = &saddr_both[i]; + *size_used = size_both[i]; + + if (fcntl(fd, F_SETFL, O_NONBLOCK)) + { + close(fd); + return -1; + } + + return fd; +} + +static int init_rpcd(rpcd_t* rpcd, const char* addr, const char* port) +{ + struct sockaddr_storage saddrs[2]; + struct sockaddr_storage* saddr; + size_t sizes[2]; + size_t size; + int err; + int enable = 1; + + /* TODO: handle errors */ + rtlsdr_rpc_msg_init(&rpcd->query_msg, 0); + rtlsdr_rpc_msg_init(&rpcd->reply_msg, 0); + rtlsdr_rpc_msg_init(&rpcd->event_msg, 0); + + if (resolve_ip_addr(saddrs, sizes, addr, port)) + { + ERROR(); + goto on_error_0; + } + + rpcd->listen_sock = open_nonblock_socket + (saddrs, sizes, SOCK_STREAM, IPPROTO_TCP, &saddr, &size); + if (rpcd->listen_sock == -1) + { + ERROR(); + goto on_error_0; + } + + err = setsockopt + (rpcd->listen_sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + if (err) + { + ERROR(); + goto on_error_1; + } + + err = bind + (rpcd->listen_sock, (const struct sockaddr*)saddr, (socklen_t)size); + if (err) + { + ERROR(); + goto on_error_1; + } + + if (listen(rpcd->listen_sock, 5)) + { + ERROR(); + goto on_error_1; + } + + rpcd->cli_sock = -1; + rpcd->dev = NULL; + + return 0; + + on_error_1: + close(rpcd->listen_sock); + on_error_0: + return -1; +} + +static int fini_rpcd(rpcd_t* rpcd) +{ + if (rpcd->cli_sock != -1) + { + shutdown(rpcd->cli_sock, SHUT_RDWR); + close(rpcd->cli_sock); + } + + shutdown(rpcd->listen_sock, SHUT_RDWR); + close(rpcd->listen_sock); + + rtlsdr_rpc_msg_fini(&rpcd->query_msg); + rtlsdr_rpc_msg_fini(&rpcd->reply_msg); + rtlsdr_rpc_msg_fini(&rpcd->event_msg); + + return 0; +} + +static int recv_all(int fd, uint8_t* buf, size_t size) +{ + ssize_t n; + fd_set rset; + + while (1) + { + errno = 0; + n = recv(fd, buf, size, 0); + if (n <= 0) + { + if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) + return -1; + } + else + { + size -= (size_t)n; + buf += (size_t)n; + if (size == 0) break ; + } + + FD_ZERO(&rset); + FD_SET(fd, &rset); + if (select(fd + 1, &rset, NULL, NULL, NULL) <= 0) + { + return -1; + } + } + + return 0; +} + +static int send_all_check_recv +(int fd, const uint8_t* buf, size_t size, unsigned int* is_recv) +{ + ssize_t n; + fd_set wset; + fd_set rset; + fd_set* rsetp; + + rsetp = NULL; + if (is_recv != NULL) *is_recv = 0; + + while (1) + { + FD_ZERO(&wset); + FD_SET(fd, &wset); + + rsetp = NULL; + if ((is_recv != NULL) && (*is_recv == 0)) + { + FD_ZERO(&rset); + FD_SET(fd, &rset); + rsetp = &rset; + } + + if (select(fd + 1, rsetp, &wset, NULL, NULL) <= 0) + { + return -1; + } + + if ((rsetp != NULL) && FD_ISSET(fd, rsetp)) + { + *is_recv = 1; + } + + if (FD_ISSET(fd, &wset)) + { + errno = 0; + n = send(fd, buf, size, 0); + if (n <= 0) + { + if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) + return -1; + } + else + { + size -= (size_t)n; + buf += (size_t)n; + if (size == 0) break ; + } + } + } + + return 0; +} + +static int send_all(int fd, const uint8_t* buf, size_t size) +{ + return send_all_check_recv(fd, buf, size, NULL); +} + +static int recv_msg(int fd, rtlsdr_rpc_msg_t* m) +{ + uint32_t size; + + if (recv_all(fd, (uint8_t*)&size, sizeof(uint32_t))) + { + ERROR(); + return -1; + } + + rtlsdr_rpc_msg_set_size(m, size); + size = rtlsdr_rpc_msg_get_size(m); + + if (rtlsdr_rpc_msg_realloc(m, size)) + { + ERROR(); + return -1; + } + + if (recv_all(fd, m->fmt + sizeof(uint32_t), size - sizeof(uint32_t))) + { + ERROR(); + return -1; + } + + return 0; +} + +static int send_msg(int fd, rtlsdr_rpc_msg_t* m) +{ + return send_all(fd, m->fmt, m->off); +} + +static int recv_query(rpcd_t* rpcd, rtlsdr_rpc_msg_t** q) +{ + *q = NULL; + rtlsdr_rpc_msg_reset(&rpcd->query_msg); + if (recv_msg(rpcd->cli_sock, &rpcd->query_msg)) return -1; + *q = &rpcd->query_msg; + return 0; +} + +static int send_reply(rpcd_t* rpcd, rtlsdr_rpc_msg_t* r) +{ + return send_msg(rpcd->cli_sock, r); +} + +static void read_async_cb +(unsigned char* buf, uint32_t len, void* ctx) +{ + const size_t off = offsetof(rtlsdr_rpc_fmt_t, data); + + rpcd_t* const rpcd = ctx; + uint8_t fmt[offsetof(rtlsdr_rpc_fmt_t, data)]; + rtlsdr_rpc_msg_t msg; + unsigned int is_recv; + + if (rpcd->async_replied == 0) + { + send_reply(rpcd, &rpcd->reply_msg); + rpcd->async_replied = 1; + } + + msg.off = off; + msg.size = off + len; + msg.fmt = fmt; + rtlsdr_rpc_msg_set_size(&msg, msg.size); + rtlsdr_rpc_msg_set_op(&msg, RTLSDR_RPC_OP_READ_ASYNC); + rtlsdr_rpc_msg_set_id(&msg, rpcd->async_id); + rtlsdr_rpc_msg_set_err(&msg, 0); + + send_all(rpcd->cli_sock, fmt, off); + send_all_check_recv(rpcd->cli_sock, buf, len, &is_recv); + + if (is_recv) rtlsdr_cancel_async(rpcd->dev); +} + +__attribute__((unused)) +static const char* op_to_string(rtlsdr_rpc_op_t op) +{ + const char* const s[] = + { + "RTLSDR_RPC_OP_GET_DEVICE_COUNT", + "RTLSDR_RPC_OP_GET_DEVICE_NAME", + "RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS", + "RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL", + "RTLSDR_RPC_OP_OPEN", + "RTLSDR_RPC_OP_CLOSE", + "RTLSDR_RPC_OP_SET_XTAL_FREQ", + "RTLSDR_RPC_OP_GET_XTAL_FREQ", + "RTLSDR_RPC_OP_GET_USB_STRINGS", + "RTLSDR_RPC_OP_WRITE_EEPROM", + "RTLSDR_RPC_OP_READ_EEPROM", + "RTLSDR_RPC_OP_SET_CENTER_FREQ", + "RTLSDR_RPC_OP_GET_CENTER_FREQ", + "RTLSDR_RPC_OP_SET_FREQ_CORRECTION", + "RTLSDR_RPC_OP_GET_FREQ_CORRECTION", + "RTLSDR_RPC_OP_GET_TUNER_TYPE", + "RTLSDR_RPC_OP_GET_TUNER_GAINS", + "RTLSDR_RPC_OP_SET_TUNER_GAIN", + "RTLSDR_RPC_OP_GET_TUNER_GAIN", + "RTLSDR_RPC_OP_SET_TUNER_IF_GAIN", + "RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE", + "RTLSDR_RPC_OP_SET_SAMPLE_RATE", + "RTLSDR_RPC_OP_GET_SAMPLE_RATE", + "RTLSDR_RPC_OP_SET_TESTMODE", + "RTLSDR_RPC_OP_SET_AGC_MODE", + "RTLSDR_RPC_OP_SET_DIRECT_SAMPLING", + "RTLSDR_RPC_OP_GET_DIRECT_SAMPLING", + "RTLSDR_RPC_OP_SET_OFFSET_TUNING", + "RTLSDR_RPC_OP_GET_OFFSET_TUNING", + "RTLSDR_RPC_OP_RESET_BUFFER", + "RTLSDR_RPC_OP_READ_SYNC", + "RTLSDR_RPC_OP_WAIT_ASYNC", + "RTLSDR_RPC_OP_READ_ASYNC", + "RTLSDR_RPC_OP_CANCEL_ASYNC", + "RTLSDR_RPC_OP_EVENT_STATE", + "RTLSDR_RPC_OP_INVALID" + }; + if (op >= RTLSDR_RPC_OP_INVALID) op = RTLSDR_RPC_OP_INVALID; + return s[op]; +} + +static int handle_query +(rpcd_t* rpcd, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t** rr) +{ + rtlsdr_rpc_msg_t* const r = &rpcd->reply_msg; + rtlsdr_rpc_op_t op; + int err = -1; + + *rr = NULL; + + rtlsdr_rpc_msg_reset(r); + + op = rtlsdr_rpc_msg_get_op(q); + switch (op) + { + case RTLSDR_RPC_OP_GET_DEVICE_COUNT: + { + uint32_t n; + + n = rtlsdr_get_device_count(); + if (rtlsdr_rpc_msg_push_uint32(r, n)) goto on_error; + err = 0; + break ; + } + + case RTLSDR_RPC_OP_GET_DEVICE_NAME: + { + const char* s; + uint32_t i; + + if (rtlsdr_rpc_msg_pop_uint32(q, &i)) goto on_error; + + s = rtlsdr_get_device_name(i); + if (s == NULL) s = ""; + + if (rtlsdr_rpc_msg_push_str(r, s)) goto on_error; + + err = 0; + + break ; + } + + case RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS: + { + char manuf[256]; + char product[256]; + char serial[256]; + uint32_t i; + + if (rtlsdr_rpc_msg_pop_uint32(q, &i)) goto on_error; + + err = rtlsdr_get_device_usb_strings(i, manuf, product, serial); + if (err) goto on_error; + + if (rtlsdr_rpc_msg_push_str(r, manuf)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_str(r, product)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_str(r, serial)) + { + err = -1; + goto on_error; + } + + break ; + } + + case RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL: + { + const char* serial; + + if (rtlsdr_rpc_msg_pop_str(q, &serial)) goto on_error; + err = rtlsdr_get_index_by_serial(serial); + if (err < 0) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_OPEN: + { + if (rpcd->dev != NULL) + { + /* already opened */ + err = 0; + goto on_error; + } + + if (rtlsdr_rpc_msg_pop_uint32(q, &rpcd->did)) goto on_error; + err = rtlsdr_open(&rpcd->dev, rpcd->did); + if (err) + { + rpcd->dev = NULL; + goto on_error; + } + + break ; + } + + case RTLSDR_RPC_OP_CLOSE: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + err = rtlsdr_close(rpcd->dev); + rpcd->dev = NULL; + + break ; + } + + case RTLSDR_RPC_OP_SET_XTAL_FREQ: + { + uint32_t did; + uint32_t rtl_freq; + uint32_t tuner_freq; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &rtl_freq)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &tuner_freq)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_xtal_freq(rpcd->dev, rtl_freq, tuner_freq); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_XTAL_FREQ: + { + uint32_t did; + uint32_t rtl_freq; + uint32_t tuner_freq; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_xtal_freq(rpcd->dev, &rtl_freq, &tuner_freq); + if (err) goto on_error; + + if (rtlsdr_rpc_msg_push_uint32(r, rtl_freq)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_uint32(r, tuner_freq)) + { + err = -1; + goto on_error; + } + + break ; + } + + case RTLSDR_RPC_OP_GET_USB_STRINGS: + { + uint32_t did; + char manuf[256]; + char product[256]; + char serial[256]; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_usb_strings(rpcd->dev, manuf, product, serial); + if (err) goto on_error; + + if (rtlsdr_rpc_msg_push_str(r, manuf)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_str(r, product)) + { + err = -1; + goto on_error; + } + + if (rtlsdr_rpc_msg_push_str(r, serial)) + { + err = -1; + goto on_error; + } + + break ; + } + + case RTLSDR_RPC_OP_SET_CENTER_FREQ: + { + uint32_t did; + uint32_t freq; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &freq)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_center_freq(rpcd->dev, freq); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_CENTER_FREQ: + { + uint32_t did; + uint32_t freq; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + freq = rtlsdr_get_center_freq(rpcd->dev); + if (freq == 0) goto on_error; + if (rtlsdr_rpc_msg_push_uint32(r, freq)) goto on_error; + err = 0; + + break ; + } + + case RTLSDR_RPC_OP_SET_FREQ_CORRECTION: + { + uint32_t did; + uint32_t ppm; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &ppm)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_freq_correction(rpcd->dev, (int)ppm); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_FREQ_CORRECTION: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_freq_correction(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_GET_TUNER_TYPE: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_tuner_type(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_GET_TUNER_GAINS: + { + uint32_t did; + uint32_t is_null; + uint32_t gain_size; + size_t new_size; + uint8_t* buf; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &is_null)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &gain_size)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + if (is_null) + { + err = rtlsdr_get_tuner_gains(rpcd->dev, NULL); + if (err <= 0) goto on_error; + } + else + { + new_size = r->off + sizeof(uint32_t) + gain_size; + if (rtlsdr_rpc_msg_realloc(r, new_size)) goto on_error; + buf = r->fmt + r->off + sizeof(uint32_t); + + err = rtlsdr_get_tuner_gains(rpcd->dev, (int*)buf); + if (err <= 0) goto on_error; + + rtlsdr_rpc_msg_push_uint32_safe(r, gain_size); + rtlsdr_rpc_msg_skip_safe(r, gain_size); + } + + break ; + } + + case RTLSDR_RPC_OP_SET_TUNER_GAIN: + { + uint32_t did; + uint32_t gain; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &gain)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_tuner_gain(rpcd->dev, (int)gain); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_TUNER_GAIN: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_tuner_gain(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_SET_TUNER_IF_GAIN: + { + uint32_t did; + uint32_t stage; + uint32_t gain; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &stage)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &gain)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_tuner_if_gain(rpcd->dev, (int)stage, (int)gain); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE: + { + uint32_t did; + uint32_t manual; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &manual)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_tuner_gain_mode(rpcd->dev, (int)manual); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_SET_SAMPLE_RATE: + { + uint32_t did; + uint32_t rate; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &rate)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_sample_rate(rpcd->dev, rate); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_SAMPLE_RATE: + { + uint32_t did; + uint32_t rate; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + rate = rtlsdr_get_sample_rate(rpcd->dev); + if (rate == 0) goto on_error; + if (rtlsdr_rpc_msg_push_uint32(r, rate)) goto on_error; + err = 0; + + break ; + } + + case RTLSDR_RPC_OP_SET_TESTMODE: + { + uint32_t did; + uint32_t on; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_testmode(rpcd->dev, (int)on); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_SET_AGC_MODE: + { + uint32_t did; + uint32_t on; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_agc_mode(rpcd->dev, (int)on); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_SET_DIRECT_SAMPLING: + { + uint32_t did; + uint32_t on; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_direct_sampling(rpcd->dev, (int)on); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_DIRECT_SAMPLING: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_direct_sampling(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_SET_OFFSET_TUNING: + { + uint32_t did; + uint32_t on; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_set_offset_tuning(rpcd->dev, (int)on); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_GET_OFFSET_TUNING: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_get_offset_tuning(rpcd->dev); + + break ; + } + + case RTLSDR_RPC_OP_RESET_BUFFER: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + err = rtlsdr_reset_buffer(rpcd->dev); + if (err) goto on_error; + + break ; + } + + case RTLSDR_RPC_OP_READ_ASYNC: + { + uint32_t did; + uint32_t buf_num; + uint32_t buf_len; + rtlsdr_rpc_op_t new_op; + rtlsdr_rpc_msg_t* new_q; + rtlsdr_rpc_msg_t* new_r; + uint8_t id; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &buf_num)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &buf_len)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + /* prepare the reply here */ + id = rtlsdr_rpc_msg_get_id(q); + rtlsdr_rpc_msg_set_size(r, r->off); + rtlsdr_rpc_msg_set_op(r, op); + rtlsdr_rpc_msg_set_id(r, id); + rtlsdr_rpc_msg_set_err(r, 0); + + rpcd->async_id = id; + rpcd->async_replied = 0; + while (1) + { + rtlsdr_read_async + (rpcd->dev, read_async_cb, rpcd, buf_num, buf_len); + + if (rpcd->async_replied == 0) goto on_error; + + if (recv_query(rpcd, &new_q)) goto on_error; + + new_op = rtlsdr_rpc_msg_get_op(new_q); + + /* do not reply is cancel_async */ + if (new_op == RTLSDR_RPC_OP_CANCEL_ASYNC) return 0; + handle_query(rpcd, new_q, &new_r); + + if (new_r != NULL) send_reply(rpcd, new_r); + } + + /* do not resend reply */ + if (rpcd->async_replied) return 0; + goto on_error; + break ; + } + + case RTLSDR_RPC_OP_CANCEL_ASYNC: + { + uint32_t did; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + /* already cancelled if here */ + err = 0; + + break ; + } + + case RTLSDR_RPC_OP_READ_SYNC: + { + uint32_t did; + uint32_t len; + int n_read; + uint8_t* buf; + size_t new_size; + + if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; + if (rtlsdr_rpc_msg_pop_uint32(q, &len)) goto on_error; + + if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; + + new_size = r->off + sizeof(uint32_t) + len; + if (rtlsdr_rpc_msg_realloc(r, new_size)) goto on_error; + buf = r->fmt + r->off + sizeof(uint32_t); + + err = rtlsdr_read_sync(rpcd->dev, buf, (int)len, &n_read); + if (err) goto on_error; + + rtlsdr_rpc_msg_push_uint32_safe(r, (uint32_t)n_read); + rtlsdr_rpc_msg_skip_safe(r, (size_t)n_read); + + break ; + } + + default: + { + PRINTF("invalid op: %u\n", op); + break ; + } + } + + on_error: + rtlsdr_rpc_msg_set_size(r, r->off); + rtlsdr_rpc_msg_set_op(r, op); + rtlsdr_rpc_msg_set_id(r, rtlsdr_rpc_msg_get_id(q)); + rtlsdr_rpc_msg_set_err(r, err); + *rr = r; + return 0; +} + +static int do_rpcd(rpcd_t* rpcd) +{ + fd_set rset; + int max_fd; + + while (1) + { + FD_ZERO(&rset); + + if (rpcd->cli_sock != -1) + { + FD_SET(rpcd->cli_sock, &rset); + max_fd = rpcd->cli_sock; + } + else + { + FD_SET(rpcd->listen_sock, &rset); + max_fd = rpcd->listen_sock; + } + + if (select(max_fd + 1, &rset, NULL, NULL, NULL) <= 0) + { + ERROR(); + return -1; + } + + if (FD_ISSET(rpcd->listen_sock, &rset)) + { + PRINTF("new client\n"); + + rpcd->cli_sock = accept(rpcd->listen_sock, NULL, NULL); + if (rpcd->cli_sock == -1) + { + if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) + continue ; + ERROR(); + break ; + } + + if (fcntl(rpcd->cli_sock, F_SETFL, O_NONBLOCK)) + { + ERROR(); + break ; + } + } + else if (FD_ISSET(rpcd->cli_sock, &rset)) + { + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + + if (recv_query(rpcd, &q)) + { + PRINTF("cli closed\n"); + shutdown(rpcd->cli_sock, SHUT_RDWR); + close(rpcd->cli_sock); + rpcd->cli_sock = -1; + } + else if (q != NULL) + { + handle_query(rpcd, q, &r); + if (r != NULL) send_reply(rpcd, r); + } + } + } + + return 0; +} + +int main(int ac, char** av) +{ + rpcd_t rpcd; + const char* addr; + const char* port; + + addr = getenv("RTLSDR_RPC_SERV_ADDR"); + if (addr == NULL) addr = "0.0.0.0"; + + port = getenv("RTLSDR_RPC_SERV_PORT"); + if (port == NULL) port = "40000"; + + if (init_rpcd(&rpcd, addr, port)) return -1; + do_rpcd(&rpcd); + fini_rpcd(&rpcd); + + return 0; +} diff --git a/src/rtlsdr_rpc.c b/src/rtlsdr_rpc.c new file mode 100644 index 00000000..b51fb48c --- /dev/null +++ b/src/rtlsdr_rpc.c @@ -0,0 +1,1342 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rtlsdr_rpc_msg.h" + + +#if 1 +#include +#define PRINTF(__s, ...) fprintf(stderr, __s, ##__VA_ARGS__) +#define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) +#define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) +#define UNIMPL() PRINTF("[u] %s,%u\n", __FILE__, __LINE__) +#else +#define PRINTF(...) +#define TRACE() +#define ERROR() +#define UNIMPL() +#endif + + +typedef struct +{ + volatile unsigned int is_locked; + volatile unsigned int is_init; + int sock; + +#define QR_COUNT 32 + pthread_mutex_t qr_lock; + volatile uint32_t qr_mask; + volatile uint32_t qr_box; + rtlsdr_rpc_msg_t query[QR_COUNT]; + rtlsdr_rpc_msg_t reply[QR_COUNT]; + + pthread_mutex_t send_lock; + pthread_mutex_t recv_lock; + + volatile unsigned int is_async_cancel; + +} rtlsdr_rpc_cli_t; + +static rtlsdr_rpc_cli_t rtlsdr_rpc_cli = { 0, }; + +typedef void (*rtlsdr_rpc_read_async_cb_t) +(unsigned char*, uint32_t, void*); + +typedef struct rtlsdr_rpc_dev +{ + uint32_t index; + size_t gain_count; + rtlsdr_rpc_cli_t* cli; +} rtlsdr_rpc_dev_t; + +static int resolve_ip_addr +( + struct sockaddr_storage saddr_both[2], size_t size_both[2], + const char* addr, const char* port +) +{ + struct addrinfo ai; + struct addrinfo* aip = NULL; + int err = -1; + size_t i; + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = AF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_PASSIVE; + + if (getaddrinfo(addr, port, &ai, &aip)) goto on_error; + + size_both[0] = 0; + size_both[1] = 0; + i = 0; + for (; (i != 2) && (aip != NULL); aip = aip->ai_next) + { + if ((aip->ai_family != AF_INET) && (aip->ai_family != AF_INET6)) continue ; + if (aip->ai_addrlen == 0) continue ; + memcpy(&saddr_both[i], aip->ai_addr, aip->ai_addrlen); + size_both[i] = aip->ai_addrlen; + ++i; + } + + if (i == 0) goto on_error; + + err = 0; + on_error: + if (aip != NULL) freeaddrinfo(aip); + return err; +} + +static int open_socket +( + struct sockaddr_storage saddr_both[2], size_t size_both[2], + int type, int proto, + struct sockaddr_storage** saddr_used, size_t* size_used +) +{ + size_t i; + int fd; + + for (i = 0; (i != 2) && (size_both[i]); ++i) + { + const struct sockaddr* const sa = (const struct sockaddr*)&saddr_both[i]; + fd = socket(sa->sa_family, type, proto); + if (fd != -1) break ; + } + + if ((i == 2) || (size_both[i] == 0)) return -1; + + *saddr_used = &saddr_both[i]; + *size_used = size_both[i]; + + return fd; +} + +static int init_cli(rtlsdr_rpc_cli_t* cli) +{ + struct sockaddr_storage saddrs[2]; + struct sockaddr_storage* saddr; + size_t sizes[2]; + size_t size; + const char* addr; + const char* port; + size_t i; + size_t j; + int err = -1; + + /* no better way in this case ... */ + while (cli->is_locked) usleep(10000); + cli->is_locked = 1; + + if (cli->is_init) goto on_success; + + addr = getenv("RTLSDR_RPC_SERV_ADDR"); + if (addr == NULL) addr = "127.0.0.1"; + + port = getenv("RTLSDR_RPC_SERV_PORT"); + if (port == NULL) port = "40000"; + + if (resolve_ip_addr(saddrs, sizes, addr, port)) + { + ERROR(); + goto on_error_0; + } + + cli->sock = open_socket + (saddrs, sizes, SOCK_STREAM, IPPROTO_TCP, &saddr, &size); + if (cli->sock == -1) + { + ERROR(); + goto on_error_0; + } + + if (connect(cli->sock, (const struct sockaddr*)saddr, (socklen_t)size)) + { + ERROR(); + goto on_error_1; + } + + if (fcntl(cli->sock, F_SETFL, O_NONBLOCK)) + { + ERROR(); + goto on_error_1; + } + + for (i = 0; i != QR_COUNT; ++i) + { + rtlsdr_rpc_msg_t* const q = &cli->query[i]; + rtlsdr_rpc_msg_t* const r = &cli->reply[i]; + + if (rtlsdr_rpc_msg_init(q, 0)) + goto on_error_2; + + if (rtlsdr_rpc_msg_init(r, 0)) + { + rtlsdr_rpc_msg_fini(q); + goto on_error_2; + } + } + pthread_mutex_init(&cli->qr_lock, NULL); + cli->qr_mask = 0; + cli->qr_box = 0; + + pthread_mutex_init(&cli->send_lock, NULL); + pthread_mutex_init(&cli->recv_lock, NULL); + + cli->is_init = 1; + + on_success: + err = 0; + goto on_error_0; + + on_error_2: + for (j = 0; j != i; ++j) + { + rtlsdr_rpc_msg_fini(&cli->query[j]); + rtlsdr_rpc_msg_fini(&cli->reply[j]); + } + + on_error_1: + shutdown(cli->sock, SHUT_RDWR); + close(cli->sock); + + on_error_0: + cli->is_locked = 0; + return err; +} + +__attribute__((unused)) +static int fini_cli(rtlsdr_rpc_cli_t* cli) +{ + size_t i; + + for (i = 0; i != QR_COUNT; ++i) + { + rtlsdr_rpc_msg_fini(&cli->query[i]); + rtlsdr_rpc_msg_fini(&cli->reply[i]); + } + pthread_mutex_destroy(&cli->qr_lock); + + pthread_mutex_destroy(&cli->send_lock); + pthread_mutex_destroy(&cli->recv_lock); + + shutdown(cli->sock, SHUT_RDWR); + close(cli->sock); + + return 0; +} + +static int alloc_qr +(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t** q, rtlsdr_rpc_msg_t** r) +{ + size_t i; + + pthread_mutex_lock(&cli->qr_lock); + for (i = 0; i != QR_COUNT; ++i) + { + const uint32_t m = 1 << i; + if ((cli->qr_mask & m) == 0) + { + cli->qr_mask |= m; + break ; + } + } + pthread_mutex_unlock(&cli->qr_lock); + + if (i == QR_COUNT) return -1; + + *q = &cli->query[i]; + *r = &cli->reply[i]; + + /* set the query id */ + rtlsdr_rpc_msg_reset(*q); + rtlsdr_rpc_msg_set_id(*q, (uint8_t)i); + + return 0; +} + +static void free_qr +(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t* r) +{ + const uint32_t m = 1 << (uint32_t)rtlsdr_rpc_msg_get_id(q); + pthread_mutex_lock(&cli->qr_lock); + cli->qr_mask &= ~m; + pthread_mutex_unlock(&cli->qr_lock); +} + +static int recv_all(int fd, uint8_t* buf, size_t size) +{ + ssize_t n; + fd_set rset; + + while (1) + { + errno = 0; + n = recv(fd, buf, size, 0); + if (n <= 0) + { + if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) + return -1; + } + else + { + size -= (size_t)n; + buf += (size_t)n; + if (size == 0) break ; + } + + FD_ZERO(&rset); + FD_SET(fd, &rset); + if (select(fd + 1, &rset, NULL, NULL, NULL) <= 0) + { + return -1; + } + } + + return 0; +} + +static int send_all(int fd, const uint8_t* buf, size_t size) +{ + ssize_t n; + fd_set wset; + + while (1) + { + errno = 0; + n = send(fd, buf, size, 0); + if (n <= 0) + { + if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) + return -1; + } + else + { + size -= (size_t)n; + buf += (size_t)n; + if (size == 0) break ; + } + + FD_ZERO(&wset); + FD_SET(fd, &wset); + if (select(fd + 1, NULL, &wset, NULL, NULL) <= 0) + { + return -1; + } + } + + return 0; +} + +static int recv_msg +(rtlsdr_rpc_cli_t* cli, uint8_t id, rtlsdr_rpc_msg_t* m) +{ + static const size_t fmt_size = offsetof(rtlsdr_rpc_fmt_t, data); + const uint32_t mask = 1 << (uint32_t)id; + const int fd = cli->sock; + uint32_t size; + uint8_t to_id; + int err = -1; + rtlsdr_rpc_msg_t* to_m; + + pthread_mutex_lock(&cli->recv_lock); + + if (cli->qr_box & mask) + { + cli->qr_box &= ~mask; + goto on_success; + } + + while (1) + { + /* receive next message */ + if (recv_all(fd, m->fmt, fmt_size)) goto on_error; + + /* get destination message by id */ + to_id = rtlsdr_rpc_msg_get_id(m); + + if (to_id >= QR_COUNT) goto on_error; + to_m = &cli->reply[to_id]; + if (to_id != id) memcpy(to_m->fmt, m->fmt, fmt_size); + + size = rtlsdr_rpc_msg_get_size(to_m); + if (size < fmt_size) goto on_error; + + if (rtlsdr_rpc_msg_realloc(to_m, size)) goto on_error; + + size -= fmt_size; + if (size) + { + if (recv_all(fd, to_m->fmt + fmt_size, size)) goto on_error; + } + + if (to_id == id) goto on_success; + + /* message not for this query, forward */ + cli->qr_box |= 1 << (uint32_t)to_id; + } + + on_success: + err = 0; + on_error: + pthread_mutex_unlock(&cli->recv_lock); + return err; +} + +static int send_msg(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* m) +{ + int err; + + rtlsdr_rpc_msg_set_size(m, (uint32_t)m->off); + + pthread_mutex_lock(&cli->send_lock); + err = send_all(cli->sock, m->fmt, m->off); + pthread_mutex_unlock(&cli->send_lock); + + return err; +} + +static int send_recv_msg +(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t* r) +{ + const uint8_t id = rtlsdr_rpc_msg_get_id(q); + + if (send_msg(cli, q)) return -1; + if (recv_msg(cli, id, r)) return -1; + rtlsdr_rpc_msg_reset(r); + + return 0; +} + +static int send_flush_msgs +(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q) +{ + struct timeval tm; + ssize_t n; + fd_set rset; + uint8_t buf[256]; + + if (send_msg(cli, q)) return -1; + + pthread_mutex_lock(&cli->recv_lock); + + while (1) + { + FD_ZERO(&rset); + FD_SET(cli->sock, &rset); + tm.tv_sec = 0; + tm.tv_usec = 200000; + if (select(cli->sock + 1, &rset, NULL, NULL, &tm) < 0) break ; + if (recv(cli->sock, buf, sizeof(buf), 0) <= 0) break ; + } + + pthread_mutex_unlock(&cli->recv_lock); + + return 0; +} + +uint32_t rtlsdr_rpc_get_device_count(void) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + uint32_t n = 0; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_COUNT); + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_uint32(r, &n)) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return n; +} + +const char* rtlsdr_rpc_get_device_name +( + uint32_t index +) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const char* s = NULL; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_NAME); + if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + /* TODO: memory leak here */ + s = strdup(s); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return s; +} + +int rtlsdr_rpc_get_device_usb_strings +(uint32_t index, char* manufact, char* product, char* serial) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const char* s; + int err = -1; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS); + if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(manufact, s); + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(product, s); + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(serial, s); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_index_by_serial(const char* serial) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL); + if (rtlsdr_rpc_msg_push_str(q, serial)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_open(void** devp, uint32_t index) +{ + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + rtlsdr_rpc_dev_t* dev; + int err = -1; + + *devp = NULL; + + if (init_cli(cli)) goto on_error_0; + + dev = malloc(sizeof(rtlsdr_rpc_dev_t)); + if (dev == NULL) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_1; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_OPEN); + if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_2; + + if (send_recv_msg(cli, q, r)) goto on_error_2; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_2; + + dev->index = index; + dev->gain_count = 32; + dev->cli = cli; + *devp = dev; + + on_error_2: + free_qr(cli, q, r); + on_error_1: + if (err) free(dev); + on_error_0: + return err; +} + +int rtlsdr_rpc_close(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_CLOSE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + free(dev); + return err; +} + +int rtlsdr_rpc_set_xtal_freq +(void* devp, uint32_t rtl_freq, uint32_t tuner_freq) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_XTAL_FREQ); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, rtl_freq)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, tuner_freq)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_xtal_freq +(void* devp, uint32_t* rtl_freq, uint32_t* tuner_freq) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_XTAL_FREQ); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_uint32(r, rtl_freq)) + { + err = -1; + goto on_error_1; + } + + if (rtlsdr_rpc_msg_pop_uint32(r, tuner_freq)) + { + err = -1; + goto on_error_1; + } + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_usb_strings +(void* devp, char* manufact, char* product, char* serial) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const char* s; + int err = -1; + + if (init_cli(cli)) goto on_error_0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_USB_STRINGS); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(manufact, s); + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(product, s); + if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; + strcpy(serial, s); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_write_eeprom +(void* dev, uint8_t* data, uint8_t offset, uint16_t len) +{ + UNIMPL(); + return -1; +} + +int rtlsdr_rpc_read_eeprom +(void* dev, uint8_t* data, uint8_t offset, uint16_t len) +{ + UNIMPL(); + return -1; +} + +int rtlsdr_rpc_set_center_freq(void* devp, uint32_t freq) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_CENTER_FREQ); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, freq)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +uint32_t rtlsdr_rpc_get_center_freq(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + uint32_t freq = 0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_CENTER_FREQ); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; + if (rtlsdr_rpc_msg_pop_uint32(r, &freq)) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return freq; +} + +int rtlsdr_rpc_set_freq_correction(void* devp, int ppm) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_FREQ_CORRECTION); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)ppm)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_freq_correction(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_FREQ_CORRECTION); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_tuner_type(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_TYPE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_tuner_gains(void* devp, int* gainsp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const uint32_t is_null = (gainsp == NULL); + const uint32_t gain_size = (uint32_t)dev->gain_count * sizeof(int); + const uint8_t* tmp; + size_t size; + int err = 0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_GAINS); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, is_null)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, gain_size)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + if (err <= 0) goto on_error_1; + + dev->gain_count = (size_t)err; + + if (is_null == 0) + { + if (rtlsdr_rpc_msg_pop_buf(r, &tmp, &size)) + { + err = 0; + goto on_error_1; + } + + /* TODO: endianess */ + memcpy(gainsp, tmp, size); + } + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_tuner_gain(void* devp, int gain) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_GAIN); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)gain)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_tuner_gain(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_GAIN); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_tuner_if_gain(void* devp, int stage, int gain) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_IF_GAIN); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)stage)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)gain)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_tuner_gain_mode(void* devp, int manual) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, manual)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_sample_rate(void* devp, uint32_t rate) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_SAMPLE_RATE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, rate)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +uint32_t rtlsdr_rpc_get_sample_rate(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + uint32_t rate = 0; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_SAMPLE_RATE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; + if (rtlsdr_rpc_msg_pop_uint32(r, &rate)) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return rate; +} + +int rtlsdr_rpc_set_testmode(void* devp, int on) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TESTMODE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_agc_mode(void* devp, int on) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_AGC_MODE); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_direct_sampling(void* devp, int on) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_DIRECT_SAMPLING); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_direct_sampling(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DIRECT_SAMPLING); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_set_offset_tuning(void* devp, int on) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_OFFSET_TUNING); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_get_offset_tuning(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_OFFSET_TUNING); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_reset_buffer(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_RESET_BUFFER); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_read_sync +(void* devp, void* buf, int len, int* n_read) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + const uint8_t* tmp; + size_t size; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_READ_SYNC); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)len)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + if (rtlsdr_rpc_msg_pop_buf(r, &tmp, &size)) + { + err = -1; + goto on_error_1; + } + + if (size > (size_t)len) size = len; + + memcpy(buf, tmp, size); + + *n_read = (int)size; + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + + +static volatile unsigned int is_cancel; +int rtlsdr_rpc_read_async +( + void* devp, + rtlsdr_rpc_read_async_cb_t cb, void* ctx, + uint32_t buf_num, uint32_t buf_len +) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + rtlsdr_rpc_msg_t* q; + rtlsdr_rpc_msg_t* r; + uint8_t id; + size_t size; + int err = -1; + + if (alloc_qr(cli, &q, &r)) goto on_error_0; + + id = rtlsdr_rpc_msg_get_id(q); + + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_READ_ASYNC); + if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, buf_num)) goto on_error_1; + if (rtlsdr_rpc_msg_push_uint32(q, buf_len)) goto on_error_1; + + if (send_recv_msg(cli, q, r)) goto on_error_1; + + err = rtlsdr_rpc_msg_get_err(r); + if (err) goto on_error_1; + + cli->is_async_cancel = 0; + while (cli->is_async_cancel == 0) + { + static const size_t off = offsetof(rtlsdr_rpc_fmt_t, data); + + if (recv_msg(cli, id, r)) + { + err = -1; + goto on_error_1; + } + + size = rtlsdr_rpc_msg_get_size(r); + cb(r->fmt + off, size - off, ctx); + } + + rtlsdr_rpc_msg_reset(q); + rtlsdr_rpc_msg_set_id(q, id); + rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_CANCEL_ASYNC); + rtlsdr_rpc_msg_push_uint32(q, dev->index); + send_flush_msgs(cli, q); + + on_error_1: + free_qr(cli, q, r); + on_error_0: + return err; +} + +int rtlsdr_rpc_wait_async +( + void* dev, + rtlsdr_rpc_read_async_cb_t cb, void* ctx +) +{ + return rtlsdr_rpc_read_async(dev, cb, ctx, 0, 0); +} + +int rtlsdr_rpc_cancel_async(void* devp) +{ + rtlsdr_rpc_dev_t* const dev = devp; + rtlsdr_rpc_cli_t* const cli = dev->cli; + cli->is_async_cancel = 1; + return 0; +} + +unsigned int rtlsdr_rpc_is_enabled(void) +{ + static unsigned int is_enabled = (unsigned int)-1; + if (is_enabled == (unsigned int)-1) + is_enabled = (getenv("RTLSDR_RPC_IS_ENABLED") != NULL); + return is_enabled; +} diff --git a/src/rtlsdr_rpc_msg.c b/src/rtlsdr_rpc_msg.c new file mode 100644 index 00000000..ea9e7870 --- /dev/null +++ b/src/rtlsdr_rpc_msg.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include "rtlsdr_rpc_msg.h" + +#if 1 +#include +#define PRINTF(__s, ...) fprintf(stderr, __s, ##__VA_ARGS__) +#define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) +#define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) +#else +#define TRACE() +#define ERROR() +#define PRINTF(...) +#endif + + +int rtlsdr_rpc_msg_init(rtlsdr_rpc_msg_t* msg, size_t data_size) +{ + size_t fmt_size; + + if (data_size == 0) data_size = 64; + + fmt_size = offsetof(rtlsdr_rpc_fmt_t, data) + data_size; + msg->fmt = malloc(fmt_size); + if (msg->fmt == NULL) return -1; + + msg->off = offsetof(rtlsdr_rpc_fmt_t, data); + msg->size = fmt_size; + + return 0; +} + +int rtlsdr_rpc_msg_fini(rtlsdr_rpc_msg_t* msg) +{ + free(msg->fmt); + return 0; +} + +void rtlsdr_rpc_msg_reset(rtlsdr_rpc_msg_t* msg) +{ + msg->off = offsetof(rtlsdr_rpc_fmt_t, data); +} + +int rtlsdr_rpc_msg_realloc(rtlsdr_rpc_msg_t* msg, size_t size) +{ + uint8_t* new_fmt; + + if (msg->size >= size) return 0; + + new_fmt = malloc(size); + if (new_fmt == NULL) return -1; + + memcpy(new_fmt, msg->fmt, msg->off); + free(msg->fmt); + msg->fmt = new_fmt; + msg->size = size; + + return 0; +} + +static int check_size(const rtlsdr_rpc_msg_t* msg, size_t size) +{ + if ((msg->off + size) > msg->size) return -1; + return 0; +} + +static int check_size_or_realloc(rtlsdr_rpc_msg_t* msg, size_t size) +{ + uint8_t* new_fmt; + size_t new_size; + + if (check_size(msg, size) == 0) return 0; + + new_size = (msg->off + size + 256) & ~(256 - 1); + new_fmt = malloc(new_size); + if (new_fmt == NULL) return -1; + + memcpy(new_fmt, msg->fmt, msg->off); + free(msg->fmt); + + msg->fmt = new_fmt; + msg->size = new_size; + + return 0; +} + +static int pop_uint32(rtlsdr_rpc_msg_t* msg, uint32_t* x) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + if (check_size(msg, sizeof(uint32_t))) return -1; + *x = *(const uint32_t*)(msg->fmt + msg->off); + msg->off += sizeof(uint32_t); + return 0; +} + +static void push_mem_safe(rtlsdr_rpc_msg_t* msg, const uint8_t* x, size_t n) +{ + memcpy(msg->fmt + msg->off, x, n); + msg->off += n; +} + +static void push_uint32_safe(rtlsdr_rpc_msg_t* msg, uint32_t x) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + push_mem_safe(msg, (const uint8_t*)&x, sizeof(uint32_t)); +} + +int rtlsdr_rpc_msg_push_int32(rtlsdr_rpc_msg_t* msg, int x) +{ + if (check_size_or_realloc(msg, sizeof(x))) return -1; + push_uint32_safe(msg, (uint32_t)x); + return 0; +} + +int rtlsdr_rpc_msg_push_uint32(rtlsdr_rpc_msg_t* msg, uint32_t x) +{ + if (check_size_or_realloc(msg, sizeof(x))) return -1; + push_uint32_safe(msg, x); + return 0; +} + +void rtlsdr_rpc_msg_push_uint32_safe(rtlsdr_rpc_msg_t* msg, uint32_t x) +{ + push_uint32_safe(msg, x); +} + +int rtlsdr_rpc_msg_push_str(rtlsdr_rpc_msg_t* msg, const char* s) +{ + if (check_size_or_realloc(msg, strlen(s) + 1)) return -1; + push_mem_safe(msg, (const uint8_t*)s, strlen(s) + 1); + return 0; +} + +int rtlsdr_rpc_msg_push_buf(rtlsdr_rpc_msg_t* msg, const uint8_t* buf, size_t size) +{ + size_t total_size = sizeof(uint32_t) + size; + if (check_size_or_realloc(msg, total_size)) return -1; + push_uint32_safe(msg, (uint32_t)size); + push_mem_safe(msg, buf, size); + return 0; +} + +void rtlsdr_rpc_msg_skip_safe(rtlsdr_rpc_msg_t* msg, size_t size) +{ + msg->off += size; +} + +int rtlsdr_rpc_msg_pop_int(rtlsdr_rpc_msg_t* msg, int* x) +{ + return pop_uint32(msg, (uint32_t*)x); +} + +int rtlsdr_rpc_msg_pop_uint32(rtlsdr_rpc_msg_t* msg, uint32_t* x) +{ + return pop_uint32(msg, x); +} + +int rtlsdr_rpc_msg_pop_str(rtlsdr_rpc_msg_t* msg, const char** s) +{ + size_t i; + + *s = (const char*)(msg->fmt + msg->off); + + for (i = msg->off; i != msg->size; ++i) + { + if (msg->fmt[i] == 0) + { + msg->off = i + 1; + return 0; + } + } + + return -1; +} + +int rtlsdr_rpc_msg_pop_buf +(rtlsdr_rpc_msg_t* msg, const uint8_t** buf, size_t* size) +{ + uint32_t x; + + if (pop_uint32(msg, &x)) return -1; + if ((msg->off + x) > msg->size) return -1; + + *buf = (const uint8_t*)(msg->fmt + msg->off); + msg->off += x; + + *size = (size_t)x; + + return 0; +} + +static void put_uint8(void* p, uint8_t x) +{ + memcpy(p, (const void*)&x, sizeof(x)); +} + +static void put_uint16(void* p, uint16_t x) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + memcpy(p, (const void*)&x, sizeof(x)); +} + +static void put_uint32(void* p, uint32_t x) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + memcpy(p, (const void*)&x, sizeof(x)); +} + +static uint8_t get_uint8(const void* p) +{ + uint8_t x; + memcpy((void*)&x, p, sizeof(x)); + return x; +} + +static uint16_t get_uint16(const void* p) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + uint16_t x; + memcpy((void*)&x, p, sizeof(x)); + return x; +} + +static uint32_t get_uint32(const void* p) +{ +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#error "unsupported endianness" +#endif + + uint32_t x; + memcpy((void*)&x, p, sizeof(x)); + return x; +} + +void rtlsdr_rpc_msg_set_size(rtlsdr_rpc_msg_t* msg, size_t size) +{ + rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; + put_uint32(&fmt->size, (uint32_t)size); +} + +size_t rtlsdr_rpc_msg_get_size(const rtlsdr_rpc_msg_t* msg) +{ + const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; + return (size_t)get_uint32(&fmt->size); +} + +void rtlsdr_rpc_msg_set_op(rtlsdr_rpc_msg_t* msg, rtlsdr_rpc_op_t op) +{ + rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; + put_uint8(&fmt->op, (uint8_t)op); +} + +rtlsdr_rpc_op_t rtlsdr_rpc_msg_get_op(const rtlsdr_rpc_msg_t* msg) +{ + const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; + return (rtlsdr_rpc_op_t)get_uint8(&fmt->op); +} + +void rtlsdr_rpc_msg_set_id(rtlsdr_rpc_msg_t* msg, uint8_t id) +{ + rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; + put_uint16(&fmt->id, id); +} + +uint8_t rtlsdr_rpc_msg_get_id(const rtlsdr_rpc_msg_t* msg) +{ + const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; + return get_uint8(&fmt->id); +} + +void rtlsdr_rpc_msg_set_err(rtlsdr_rpc_msg_t* msg, int err) +{ + rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; + put_uint32(&fmt->err, (uint32_t)err); +} + +int rtlsdr_rpc_msg_get_err(const rtlsdr_rpc_msg_t* msg) +{ + const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; + return (int)get_uint32(&fmt->err); +}