diff --git a/README.md b/README.md
index f1b0f9b2..a2075eb9 100644
--- a/README.md
+++ b/README.md
@@ -12,14 +12,14 @@ where (brace yourself) most things actually work.
```
- [FPVue_android](https://github.com/gehee/FPVue_android): basic and unique work to combine all components into a single application by [Gee He](https://github.com/gehee).
-- [devourer](https://github.com/openipc/devourer): userspace rtl8812au driver initially created by [buldo](https://github.com/buldo) and converted to C by [josephnef](https://github.com/josephnef).
+- [devourer](https://github.com/openipc/devourer): userspace Realtek 11ac driver (rtl8812au + rtl8812eu) initially created by [buldo](https://github.com/buldo) and converted to C by [josephnef](https://github.com/josephnef).
- [LiveVideo10ms](https://github.com/Consti10/LiveVideo10ms): excellent video decoder from [Consti10](https://github.com/Consti10) converted into a module.
- [wfb-ng](https://github.com/svpcom/wfb-ng): library allowing the broadcast of the video feed over the air.
The wfb-ng [gs.key](https://github.com/OpenIPC/PixelPilot/raw/main/app/src/main/assets/gs.key) is embedded in the app.
The settings menu allows selecting a different key from your phone.
-Supported rtl8812au wifi adapter are listed [here](https://github.com/OpenIPC/PixelPilot/blob/master/app/src/main/res/xml/usb_device_filter.xml).
+Supported rtl8812au and rtl8812eu wifi adapters are listed [here](https://github.com/OpenIPC/PixelPilot/blob/master/app/src/main/res/xml/usb_device_filter.xml).
Feel free to send pull requests to add new supported wifi adapters hardware IDs.
Now support saving a dvr of the video feed to `Files/Internal Storage/Movies/`
diff --git a/app/src/main/res/xml/usb_device_filter.xml b/app/src/main/res/xml/usb_device_filter.xml
index f230ca27..d3c40a60 100644
--- a/app/src/main/res/xml/usb_device_filter.xml
+++ b/app/src/main/res/xml/usb_device_filter.xml
@@ -33,4 +33,13 @@
+
+
+
diff --git a/app/wfbngrtl8812/src/main/cpp/CMakeLists.txt b/app/wfbngrtl8812/src/main/cpp/CMakeLists.txt
index 42811e99..a7320129 100644
--- a/app/wfbngrtl8812/src/main/cpp/CMakeLists.txt
+++ b/app/wfbngrtl8812/src/main/cpp/CMakeLists.txt
@@ -26,47 +26,73 @@ target_compile_definitions(wfb-ng PRIVATE
ZFEX_INLINE_ADDMUL_SIMD
)
+# devourer — userspace Realtek 11ac driver. Mirrors the source groups of
+# devourer's own CMakeLists.txt (which we can't add_subdirectory here because
+# it resolves libusb via pkg-config, unavailable in the NDK build).
+# Chip support compiled in:
+# Jaguar1 (RTL8812AU / 8811AU / 8821AU) — the classic PixelPilot adapters
+# Jaguar3 8822E (RTL8812EU / RTL8822EU) — e.g. LB-LINK BL-M8812EU2
add_library(devourer STATIC
- ${CMAKE_SOURCE_DIR}/devourer/hal/Hal8812PhyReg.h
- ${CMAKE_SOURCE_DIR}/devourer/hal/Hal8812PwrSeq.c
- ${CMAKE_SOURCE_DIR}/devourer/hal/Hal8812PwrSeq.h
- ${CMAKE_SOURCE_DIR}/devourer/hal/basic_types.h
+ # --- generation-agnostic core ---
+ ${CMAKE_SOURCE_DIR}/devourer/src/ParsedRadioPacket.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/Radiotap.c
+ ${CMAKE_SOURCE_DIR}/devourer/src/RadiotapBuilder.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/RtlAdapter.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/UsbTransport.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/UsbDeviceLock.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/UsbOpen.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/TxMode.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/TxPower.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/LinkHealth.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/SignalStop.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/WiFiDriver.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/PhyTableLoader.cpp
+
+ # --- Jaguar1 (RTL8812AU / 8811AU / 8821AU) ---
${CMAKE_SOURCE_DIR}/devourer/hal/hal8812a_fw.c
- ${CMAKE_SOURCE_DIR}/devourer/hal/hal8812a_fw.h
- ${CMAKE_SOURCE_DIR}/devourer/hal/hal_com_reg.h
- ${CMAKE_SOURCE_DIR}/devourer/hal/rtl8812a_hal.h
- ${CMAKE_SOURCE_DIR}/devourer/hal/rtl8812a_recv.h
- ${CMAKE_SOURCE_DIR}/devourer/hal/rtl8812a_spec.h
+ ${CMAKE_SOURCE_DIR}/devourer/hal/hal8821a_fw.c
+ ${CMAKE_SOURCE_DIR}/devourer/hal/Hal8812PwrSeq.c
+ ${CMAKE_SOURCE_DIR}/devourer/hal/Hal8821APwrSeq.c
+ ${CMAKE_SOURCE_DIR}/devourer/hal/Hal8812a_TxPwrTrack.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/RtlJaguarDevice.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/HalModule.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/RadioManagementModule.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/EepromManager.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/FirmwareManager.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/Iqk8812a.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/PhydmWatchdog.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/PowerTracking8812a.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/FrameParser.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/BbDbgportReader.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/Radiotap.c
- ${CMAKE_SOURCE_DIR}/devourer/src/registry_priv.h
- ${CMAKE_SOURCE_DIR}/devourer/src/ieee80211_radiotap.h
+ # --- Jaguar3 shared (dispatchers compiled for any Jaguar3 variant) ---
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/RtlJaguar3Device.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/HalJaguar3.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/HalmacJaguar3Fw.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/HalmacJaguar3MacInit.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/RadioManagementJaguar3.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/PhyTableLoaderJaguar3.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/Phy8822cTables.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/Halrf8822c.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/EepromManager.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/EepromManager.h
- ${CMAKE_SOURCE_DIR}/devourer/src/Firmware.h
- ${CMAKE_SOURCE_DIR}/devourer/src/FirmwareManager.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/FirmwareManager.h
- ${CMAKE_SOURCE_DIR}/devourer/src/FrameParser.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/FrameParser.h
- ${CMAKE_SOURCE_DIR}/devourer/src/HalModule.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/HalModule.h
- ${CMAKE_SOURCE_DIR}/devourer/src/ParsedRadioPacket.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/RadioManagementModule.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/RadioManagementModule.h
- ${CMAKE_SOURCE_DIR}/devourer/src/Rtl8812aDevice.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/Rtl8812aDevice.h
- ${CMAKE_SOURCE_DIR}/devourer/src/RtlUsbAdapter.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/RtlUsbAdapter.h
- ${CMAKE_SOURCE_DIR}/devourer/src/SelectedChannel.h
- ${CMAKE_SOURCE_DIR}/devourer/src/WiFiDriver.cpp
- ${CMAKE_SOURCE_DIR}/devourer/src/WiFiDriver.h
- ${CMAKE_SOURCE_DIR}/devourer/src/registry_priv.h)
+ # --- Jaguar3 8822E variant (RTL8812EU / RTL8822EU) ---
+ ${CMAKE_SOURCE_DIR}/devourer/hal/hal8822e_fw.c
+ ${CMAKE_SOURCE_DIR}/devourer/hal/phydm/rtl8822e/Hal8822e_PhyTables.c
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/Phy8822eTables.cpp
+ ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/Halrf8822e.cpp)
set_target_properties(devourer PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/devourer)
+target_compile_definitions(devourer PUBLIC
+ DEVOURER_HAVE_JAGUAR1=1
+ DEVOURER_HAVE_JAGUAR3=1
+ DEVOURER_HAVE_JAGUAR3_8822E=1)
target_include_directories(devourer PUBLIC ${CMAKE_SOURCE_DIR}/devourer)
+target_include_directories(devourer PUBLIC ${CMAKE_SOURCE_DIR}/devourer/src)
target_include_directories(devourer PUBLIC ${CMAKE_SOURCE_DIR}/devourer/hal)
+target_include_directories(devourer PUBLIC ${CMAKE_SOURCE_DIR}/devourer/hal/phydm/rtl8822c)
+target_include_directories(devourer PUBLIC ${CMAKE_SOURCE_DIR}/devourer/hal/phydm/rtl8822e)
+target_link_libraries(devourer PUBLIC log)
set_property(TARGET devourer PROPERTY CXX_STANDARD 20)
# WFB-NG RTL8812 library
diff --git a/app/wfbngrtl8812/src/main/cpp/TxFrame.cpp b/app/wfbngrtl8812/src/main/cpp/TxFrame.cpp
index 1107e7f5..ac65b2b1 100644
--- a/app/wfbngrtl8812/src/main/cpp/TxFrame.cpp
+++ b/app/wfbngrtl8812/src/main/cpp/TxFrame.cpp
@@ -3,7 +3,7 @@
#include "sodium/crypto_aead_chacha20poly1305.h"
#include "sodium/crypto_box.h"
#include "sodium/randombytes.h"
-#include "src/Rtl8812aDevice.h"
+#include "src/IRtlDevice.h"
#include "src/wifibroadcast.hpp"
#include "src/zfex.h"
@@ -422,7 +422,7 @@ UsbTransmitter::UsbTransmitter(int k,
uint8_t *radiotapHeader,
size_t radiotapHeaderLen,
uint8_t frameType,
- Rtl8812aDevice *device)
+ IRtlDevice *device)
: Transmitter(k, n, keypair, epoch, channelId), channelId_(channelId), currentOutput_(0), ieee80211Sequence_(0),
radiotapHeader_(radiotapHeader), radiotapHeaderLen_(radiotapHeaderLen), frameType_(frameType),
rtlDevice_(device) {
@@ -467,11 +467,11 @@ void UsbTransmitter::dumpStats(
}
void UsbTransmitter::injectPacket(const uint8_t *buf, size_t size) {
- if (!rtlDevice_ || rtlDevice_->should_stop) {
+ if (!rtlDevice_) {
#ifdef __ANDROID__
- __android_log_print(ANDROID_LOG_DEBUG, TAG, "Main thread exited, cannot send packets");
+ __android_log_print(ANDROID_LOG_DEBUG, TAG, "No USB device, cannot send packets");
#endif
- throw std::runtime_error("USB Transmitter: main thread exit, should stop");
+ throw std::runtime_error("USB Transmitter: no device, should stop");
}
if (size > MAX_FORWARDER_PACKET_SIZE) {
@@ -795,7 +795,7 @@ void TxFrame::dataSource(
}
}
-void TxFrame::run(Rtl8812aDevice *rtlDevice, TxArgs *arg) {
+void TxFrame::run(IRtlDevice *rtlDevice, TxArgs *arg) {
// Decide if using VHT
if (arg->bandwidth >= 80) {
arg->vht_mode = true;
diff --git a/app/wfbngrtl8812/src/main/cpp/TxFrame.h b/app/wfbngrtl8812/src/main/cpp/TxFrame.h
index 72d37375..b37e3ab4 100644
--- a/app/wfbngrtl8812/src/main/cpp/TxFrame.h
+++ b/app/wfbngrtl8812/src/main/cpp/TxFrame.h
@@ -6,8 +6,8 @@ extern "C" {
#include "wfb-ng/src/zfex.h" // FEC library
}
-#include "devourer/src/Rtl8812aDevice.h" // Rtl8812aDevice definition
-#include "wfb-ng/src/wifibroadcast.hpp" // Wifibroadcast definitions
+#include "devourer/src/IRtlDevice.h" // IRtlDevice interface (all chip generations)
+#include "wfb-ng/src/wifibroadcast.hpp" // Wifibroadcast definitions
// -- System / C++ Includes --
#include
@@ -294,7 +294,7 @@ class UsbTransmitter : public Transmitter {
uint8_t *radiotapHeader,
size_t radiotapHeaderLen,
uint8_t frameType,
- Rtl8812aDevice *device);
+ IRtlDevice *device);
~UsbTransmitter() override = default;
@@ -314,7 +314,7 @@ class UsbTransmitter : public Transmitter {
uint8_t *radiotapHeader_;
size_t radiotapHeaderLen_;
uint8_t frameType_;
- Rtl8812aDevice *rtlDevice_;
+ IRtlDevice *rtlDevice_;
};
//-------------------------------------------------------------
@@ -379,10 +379,10 @@ class TxFrame {
/**
* @brief Configures and runs the transmitter with the given arguments.
- * @param rtlDevice The Rtl8812aDevice pointer (if using USB).
+ * @param rtlDevice The IRtlDevice pointer (if using USB).
* @param arg TxArgs structure with user parameters.
*/
- void run(Rtl8812aDevice *rtlDevice, TxArgs *arg);
+ void run(IRtlDevice *rtlDevice, TxArgs *arg);
/**
* @brief Signals that the main loop should stop.
diff --git a/app/wfbngrtl8812/src/main/cpp/WfbngLink.cpp b/app/wfbngrtl8812/src/main/cpp/WfbngLink.cpp
index c7a364c7..217a9214 100644
--- a/app/wfbngrtl8812/src/main/cpp/WfbngLink.cpp
+++ b/app/wfbngrtl8812/src/main/cpp/WfbngLink.cpp
@@ -8,6 +8,7 @@
#include "RxFrame.h"
#include "SignalQualityCalculator.h"
#include "TxFrame.h"
+#include "devourer/src/RxPacket.h"
#include "libusb.h"
#include "wfb-ng/src/wifibroadcast.hpp"
@@ -56,7 +57,7 @@ std::string generate_random_string(size_t length) {
WfbngLink::WfbngLink(JNIEnv *env, jobject context)
: current_fd(-1), adaptive_link_enabled(true), adaptive_tx_power(30) {
initAgg();
- Logger_t log;
+ log = std::make_shared(); // routes to logcat under the "devourer" tag
wifi_driver = std::make_unique(log);
}
@@ -115,7 +116,16 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint
r = libusb_claim_interface(dev_handle, 0);
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Creating driver and device for fd=%d", fd);
- rtl_devices.emplace(fd, wifi_driver->CreateRtlDevice(dev_handle));
+ devourer::DeviceConfig cfg;
+ // TX+RX on the one claimed handle. Jaguar3 (RTL8812EU/8822EU) must know
+ // this before InitWrite so the bring-up keeps the RX filters open;
+ // Jaguar1 (RTL8812AU) ignores it.
+ cfg.rx.enable_with_tx = true;
+ // The per-adapter advisory lock defaults to /tmp, which doesn't exist on
+ // Android — use the app's files dir (same location as gs.key).
+ cfg.usb.lock_dir = "/data/user/0/com.openipc.pixelpilot/files";
+
+ rtl_devices[fd] = wifi_driver->CreateRtlDevice(dev_handle, ctx, nullptr, cfg);
if (!rtl_devices.at(fd)) {
libusb_exit(ctx);
__android_log_print(ANDROID_LOG_ERROR, TAG, "CreateRtlDevice error");
@@ -185,22 +195,19 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint
// Store the current fd for later TX power updates.
current_fd = fd;
- if (!usb_event_thread) {
- auto usb_event_thread_func = [ctx, this, fd] {
- while (true) {
- auto dev = this->rtl_devices.at(fd).get();
- if (dev == nullptr || dev->should_stop) break;
- struct timeval timeout = {0, 500000}; // 500ms timeout
- int r = libusb_handle_events_timeout(ctx, &timeout);
- if (r < 0) {
- this->log->error("Error handling events: {}", r);
- // break;
- }
- }
- };
+ IRtlDevice *current_device = rtl_devices.at(fd).get();
- init_thread(usb_event_thread, [=]() { return std::make_unique(usb_event_thread_func); });
+ // TX-capable bring-up with RX enabled (cfg.rx.enable_with_tx). On
+ // Jaguar3 (RTL8812EU/8822EU) this also starts the coex runtime thread
+ // that sustained TX needs.
+ auto bandWidth = (bw == 20 ? CHANNEL_WIDTH_20 : CHANNEL_WIDTH_40);
+ current_device->InitWrite(SelectedChannel{
+ .Channel = static_cast(wifiChannel),
+ .ChannelOffset = 0,
+ .ChannelWidth = bandWidth,
+ });
+ if (!usb_tx_thread) {
std::shared_ptr args = std::make_shared();
args->udp_port = 8001;
args->link_id = link_id;
@@ -218,15 +225,12 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint
__android_log_print(
ANDROID_LOG_ERROR, TAG, "radio link ID %d, radio PORT %d", args->link_id, args->radio_port);
- Rtl8812aDevice *current_device = rtl_devices.at(fd).get();
- if (!usb_tx_thread) {
- init_thread(usb_tx_thread, [&]() {
- return std::make_unique([this, current_device, args] {
- txFrame->run(current_device, args.get());
- __android_log_print(ANDROID_LOG_DEBUG, TAG, "usb_transfer thread should terminate");
- });
+ init_thread(usb_tx_thread, [&]() {
+ return std::make_unique([this, current_device, args] {
+ txFrame->run(current_device, args.get());
+ __android_log_print(ANDROID_LOG_DEBUG, TAG, "usb_transfer thread should terminate");
});
- }
+ });
if (adaptive_link_enabled) {
stop_adaptive_link();
@@ -234,38 +238,37 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint
}
}
- auto bandWidth = (bw == 20 ? CHANNEL_WIDTH_20 : CHANNEL_WIDTH_40);
- rtl_devices.at(fd)->Init(packetProcessor,
- SelectedChannel{
- .Channel = static_cast(wifiChannel),
- .ChannelOffset = 0,
- .ChannelWidth = bandWidth,
- });
+ // Blocking RX loop on this thread; devourer pumps the libusb events
+ // itself. Returns once StopRxLoop() is called.
+ current_device->StartRxLoop(packetProcessor);
} catch (const std::runtime_error &error) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "runtime_error: %s", error.what());
- auto dev = rtl_devices.at(fd).get();
- if (dev) {
- dev->should_stop = true;
- }
txFrame->stop();
destroy_thread(usb_tx_thread);
- destroy_thread(usb_event_thread);
stop_adaptive_link();
+ auto dev = rtl_devices.at(fd).get();
+ if (dev) {
+ dev->Stop();
+ }
+ libusb_release_interface(dev_handle, 0);
+ libusb_exit(ctx);
return -1;
}
- __android_log_print(ANDROID_LOG_DEBUG, TAG, "Init done, releasing...");
- auto dev = rtl_devices.at(fd).get();
- if (dev) {
- dev->should_stop = true;
- }
+ __android_log_print(ANDROID_LOG_DEBUG, TAG, "RX loop exited, releasing...");
txFrame->stop();
destroy_thread(usb_tx_thread);
- destroy_thread(usb_event_thread);
stop_adaptive_link();
+ // Clean shutdown: halt TRX DMA and power the chip down before releasing
+ // the USB interface.
+ auto dev = rtl_devices.at(fd).get();
+ if (dev) {
+ dev->Stop();
+ }
+
r = libusb_release_interface(dev_handle, 0);
__android_log_print(ANDROID_LOG_DEBUG, TAG, "libusb_release_interface: %d", r);
libusb_exit(ctx);
@@ -280,7 +283,7 @@ void WfbngLink::stop(JNIEnv *env, jobject context, jint fd) {
}
auto dev = rtl_devices.at(fd).get();
if (dev) {
- dev->should_stop = true;
+ dev->StopRxLoop();
} else {
__android_log_print(ANDROID_LOG_ERROR, TAG, "rtl_devices.at(%d) is nullptr", fd);
}
diff --git a/app/wfbngrtl8812/src/main/cpp/WfbngLink.hpp b/app/wfbngrtl8812/src/main/cpp/WfbngLink.hpp
index 195cce6e..24f74c5b 100644
--- a/app/wfbngrtl8812/src/main/cpp/WfbngLink.hpp
+++ b/app/wfbngrtl8812/src/main/cpp/WfbngLink.hpp
@@ -9,16 +9,20 @@ extern "C" {
#include "wfb-ng/src/zfex.h"
}
+#include "devourer/src/IRtlDevice.h"
#include "devourer/src/WiFiDriver.h"
#include "wfb-ng/src/rx.hpp"
+#include
+#include
#include
#include
#include