diff --git a/app/src/main/java/com/openipc/pixelpilot/UsbDeviceFilter.java b/app/src/main/java/com/openipc/pixelpilot/UsbDeviceFilter.java index f59ecfd9..ebcd1b6f 100644 --- a/app/src/main/java/com/openipc/pixelpilot/UsbDeviceFilter.java +++ b/app/src/main/java/com/openipc/pixelpilot/UsbDeviceFilter.java @@ -31,8 +31,8 @@ public static List parseXml(Context context, int resourceId) String vendorIdString = parser.getAttributeValue(null, "vendor-id"); String productIdString = parser.getAttributeValue(null, "product-id"); - int vendorId = Integer.parseInt(vendorIdString, 16); - int productId = Integer.parseInt(productIdString, 16); + int vendorId = parseHexId(vendorIdString); + int productId = parseHexId(productIdString); UsbDeviceFilter device = new UsbDeviceFilter(vendorId, productId); devices.add(device); @@ -42,4 +42,12 @@ public static List parseXml(Context context, int resourceId) } return devices; } + + private static int parseHexId(String value) { + if (value == null) { + throw new IllegalArgumentException("USB ID is missing"); + } + String normalized = value.trim().replaceFirst("^0[xX]", ""); + return Integer.parseInt(normalized, 16); + } } diff --git a/app/src/main/java/com/openipc/pixelpilot/WfbLinkManager.java b/app/src/main/java/com/openipc/pixelpilot/WfbLinkManager.java index 8580be69..f8175fd8 100644 --- a/app/src/main/java/com/openipc/pixelpilot/WfbLinkManager.java +++ b/app/src/main/java/com/openipc/pixelpilot/WfbLinkManager.java @@ -97,8 +97,8 @@ public Map getAttachedAdapters() { try { filters = UsbDeviceFilter.parseXml(context, R.xml.usb_device_filter); } catch (Exception e) { - e.printStackTrace(); - return null; + Log.e(TAG, "Unable to parse USB device filter", e); + return new HashMap<>(); } Map res = new HashMap<>(); diff --git a/app/src/main/res/xml/usb_device_filter.xml b/app/src/main/res/xml/usb_device_filter.xml index d3c40a60..f28c0ecc 100644 --- a/app/src/main/res/xml/usb_device_filter.xml +++ b/app/src/main/res/xml/usb_device_filter.xml @@ -1,45 +1,76 @@ + product-id="0x8812" + vendor-id="0x0BDA" /> + product-id="0x881A" + vendor-id="0x0BDA" /> + product-id="0x881B" + vendor-id="0x0BDA" /> + product-id="0x0101" + vendor-id="0x2357" /> + product-id="0x0103" + vendor-id="0x2357" /> + product-id="0x010D" + vendor-id="0x2357" /> + product-id="0x010E" + vendor-id="0x2357" /> + product-id="0x17D2" + vendor-id="0x0B05" /> + product-id="0x0012" + vendor-id="0x2604" /> + product-id="0x0408" + vendor-id="0x0409" /> + product-id="0x3426" + vendor-id="0x0586" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + product-id="0xA81A" + vendor-id="0x0BDA" /> + product-id="0xA82A" + vendor-id="0x0BDA" /> + product-id="0xE822" + vendor-id="0x0BDA" /> + diff --git a/app/wfbngrtl8812/src/main/cpp/CMakeLists.txt b/app/wfbngrtl8812/src/main/cpp/CMakeLists.txt index a7320129..87c1dc0b 100644 --- a/app/wfbngrtl8812/src/main/cpp/CMakeLists.txt +++ b/app/wfbngrtl8812/src/main/cpp/CMakeLists.txt @@ -44,6 +44,7 @@ add_library(devourer STATIC ${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/LaCapture.cpp ${CMAKE_SOURCE_DIR}/devourer/src/SignalStop.cpp ${CMAKE_SOURCE_DIR}/devourer/src/WiFiDriver.cpp ${CMAKE_SOURCE_DIR}/devourer/src/PhyTableLoader.cpp @@ -65,6 +66,20 @@ add_library(devourer STATIC ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/FrameParser.cpp ${CMAKE_SOURCE_DIR}/devourer/src/jaguar1/BbDbgportReader.cpp + # --- Jaguar2 (RTL8822BU / RTL8812BU) --- + ${CMAKE_SOURCE_DIR}/devourer/src/jaguar2/RtlJaguar2Device.cpp + ${CMAKE_SOURCE_DIR}/devourer/src/jaguar2/HalJaguar2.cpp + ${CMAKE_SOURCE_DIR}/devourer/src/jaguar2/HalmacJaguar2Fw.cpp + ${CMAKE_SOURCE_DIR}/devourer/src/jaguar2/HalmacJaguar2MacInit.cpp + ${CMAKE_SOURCE_DIR}/devourer/src/jaguar2/Phy8822bTables.cpp + ${CMAKE_SOURCE_DIR}/devourer/src/jaguar2/Halrf8822b.cpp + ${CMAKE_SOURCE_DIR}/devourer/hal/hal8822b_fw.c + ${CMAKE_SOURCE_DIR}/devourer/hal/phydm/rtl8822b/Hal8822b_PhyTables.c + ${CMAKE_SOURCE_DIR}/devourer/src/jaguar2/Phy8821cTables.cpp + ${CMAKE_SOURCE_DIR}/devourer/src/jaguar2/Halrf8821c.cpp + ${CMAKE_SOURCE_DIR}/devourer/hal/hal8821c_fw.c + ${CMAKE_SOURCE_DIR}/devourer/hal/phydm/rtl8821c/Hal8821c_PhyTables.c + # --- Jaguar3 shared (dispatchers compiled for any Jaguar3 variant) --- ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/RtlJaguar3Device.cpp ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/HalJaguar3.cpp @@ -72,6 +87,7 @@ add_library(devourer STATIC ${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/PhydmRuntimeJaguar3.cpp ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/Phy8822cTables.cpp ${CMAKE_SOURCE_DIR}/devourer/src/jaguar3/Halrf8822c.cpp @@ -84,12 +100,17 @@ set_target_properties(devourer PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_ target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR1=1 + DEVOURER_HAVE_JAGUAR2=1 + DEVOURER_HAVE_JAGUAR2_8822B=1 + DEVOURER_HAVE_JAGUAR2_8821C=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/rtl8822b) +target_include_directories(devourer PUBLIC ${CMAKE_SOURCE_DIR}/devourer/hal/phydm/rtl8821c) 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) diff --git a/app/wfbngrtl8812/src/main/cpp/WfbngLink.cpp b/app/wfbngrtl8812/src/main/cpp/WfbngLink.cpp index 217a9214..4cfb39c3 100644 --- a/app/wfbngrtl8812/src/main/cpp/WfbngLink.cpp +++ b/app/wfbngrtl8812/src/main/cpp/WfbngLink.cpp @@ -40,6 +40,54 @@ *i = 42; \ } while (0) +namespace { + +// Finds the vendor-specific Wi-Fi interface before libusb claims it. +// RTL8812BU / RTL8812BU is a composite USB device: interfaces 0 and 1 belong to +// Bluetooth, while its Wi-Fi bulk endpoints are on interface 2. Assuming +// interface 0 works for RTL8812AU but claims the wrong function on that BU +// layout, so select the interface that actually provides bulk IN and OUT. +int findRtlWifiInterface(libusb_device_handle *dev_handle) { + if (dev_handle == nullptr) { + return 0; + } + + libusb_config_descriptor *config = nullptr; + if (libusb_get_active_config_descriptor(libusb_get_device(dev_handle), &config) != LIBUSB_SUCCESS) { + return 0; + } + + for (uint8_t interface_index = 0; interface_index < config->bNumInterfaces; interface_index++) { + const libusb_interface &interface = config->interface[interface_index]; + for (int alt_index = 0; alt_index < interface.num_altsetting; alt_index++) { + const libusb_interface_descriptor &descriptor = interface.altsetting[alt_index]; + bool has_bulk_in = false; + bool has_bulk_out = false; + for (uint8_t endpoint_index = 0; endpoint_index < descriptor.bNumEndpoints; endpoint_index++) { + const libusb_endpoint_descriptor &endpoint = descriptor.endpoint[endpoint_index]; + if ((endpoint.bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) != LIBUSB_TRANSFER_TYPE_BULK) { + continue; + } + if (endpoint.bEndpointAddress & LIBUSB_ENDPOINT_IN) { + has_bulk_in = true; + } else { + has_bulk_out = true; + } + } + if (descriptor.bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && has_bulk_in && has_bulk_out) { + const int interface_number = descriptor.bInterfaceNumber; + libusb_free_config_descriptor(config); + return interface_number; + } + } + } + + libusb_free_config_descriptor(config); + return 0; +} + +} + std::string generate_random_string(size_t length) { const std::string characters = "abcdefghijklmnopqrstuvwxyz"; std::random_device rd; @@ -94,6 +142,11 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint libusb_context *ctx = NULL; txFrame = std::make_shared(); + { + std::lock_guard lock(stop_request_mutex); + stop_requested_fds.erase(fd); + } + r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY); r = libusb_init(&ctx); if (r < 0) { @@ -109,12 +162,18 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint return r; } - if (libusb_kernel_driver_active(dev_handle, 0)) { - r = libusb_detach_kernel_driver(dev_handle, 0); + const int wifi_interface = findRtlWifiInterface(dev_handle); + if (libusb_kernel_driver_active(dev_handle, wifi_interface)) { + r = libusb_detach_kernel_driver(dev_handle, wifi_interface); __android_log_print(ANDROID_LOG_DEBUG, TAG, "libusb_detach_kernel_driver: %d", r); } - r = libusb_claim_interface(dev_handle, 0); - __android_log_print(ANDROID_LOG_DEBUG, TAG, "Creating driver and device for fd=%d", fd); + r = libusb_claim_interface(dev_handle, wifi_interface); + if (r != LIBUSB_SUCCESS) { + __android_log_print(ANDROID_LOG_ERROR, TAG, "libusb_claim_interface(%d) failed: %d", wifi_interface, r); + libusb_exit(ctx); + return r; + } + __android_log_print(ANDROID_LOG_DEBUG, TAG, "Creating driver and device for fd=%d interface=%d", fd, wifi_interface); devourer::DeviceConfig cfg; // TX+RX on the one claimed handle. Jaguar3 (RTL8812EU/8822EU) must know @@ -127,11 +186,24 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint rtl_devices[fd] = wifi_driver->CreateRtlDevice(dev_handle, ctx, nullptr, cfg); if (!rtl_devices.at(fd)) { + rtl_devices.erase(fd); libusb_exit(ctx); __android_log_print(ANDROID_LOG_ERROR, TAG, "CreateRtlDevice error"); return -1; } + { + std::lock_guard lock(stop_request_mutex); + if (stop_requested_fds.erase(fd) != 0) { + __android_log_print(ANDROID_LOG_INFO, TAG, "RTL device fd %d was stopped during startup", fd); + rtl_devices.at(fd)->Stop(); + libusb_release_interface(dev_handle, wifi_interface); + rtl_devices.erase(fd); + libusb_exit(ctx); + return 0; + } + } + uint8_t *video_channel_id_be8 = reinterpret_cast(&video_channel_id_be); uint8_t *udp_channel_id_be8 = reinterpret_cast(&udp_channel_id_be); uint8_t *mavlink_channel_id_be8 = reinterpret_cast(&mavlink_channel_id_be); @@ -149,12 +221,23 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint uint8_t antenna[4] = {1, 1, 1, 1}; std::lock_guard lock(agg_mutex); + // Every device parser returns the complete 802.11 frame, + // including its four-byte FCS. wfb-ng authenticates only the + // payload, after both the MAC header and FCS are removed. + constexpr size_t fcs_length = 4; + if (packet.Data.size() < sizeof(ieee80211_header) + fcs_length) { + return; + } + + const size_t wfb_packet_length = + packet.Data.size() - sizeof(ieee80211_header) - fcs_length; + if (frame.MatchesChannelID(video_channel_id_be8)) { SignalQualityCalculator::get_instance().add_rssi(packet.RxAtrib.rssi[0], packet.RxAtrib.rssi[1]); SignalQualityCalculator::get_instance().add_snr(packet.RxAtrib.snr[0], packet.RxAtrib.snr[1]); video_aggregator->process_packet(packet.Data.data() + sizeof(ieee80211_header), - packet.Data.size() - sizeof(ieee80211_header) - 4, + wfb_packet_length, 0, antenna, rssi, @@ -169,7 +252,7 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint } } else if (frame.MatchesChannelID(mavlink_channel_id_be8)) { mavlink_aggregator->process_packet(packet.Data.data() + sizeof(ieee80211_header), - packet.Data.size() - sizeof(ieee80211_header) - 4, + wfb_packet_length, 0, antenna, rssi, @@ -180,7 +263,7 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint NULL); } else if (frame.MatchesChannelID(udp_channel_id_be8)) { udp_aggregator->process_packet(packet.Data.data() + sizeof(ieee80211_header), - packet.Data.size() - sizeof(ieee80211_header) - 4, + wfb_packet_length, 0, antenna, rssi, @@ -240,6 +323,20 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint // Blocking RX loop on this thread; devourer pumps the libusb events // itself. Returns once StopRxLoop() is called. + { + std::lock_guard lock(stop_request_mutex); + if (stop_requested_fds.erase(fd) != 0) { + __android_log_print(ANDROID_LOG_INFO, TAG, "RTL device fd %d stopped before RX loop", fd); + current_device->Stop(); + libusb_release_interface(dev_handle, wifi_interface); + rtl_devices.erase(fd); + if (current_fd == fd) { + current_fd = -1; + } + libusb_exit(ctx); + return 0; + } + } current_device->StartRxLoop(packetProcessor); } catch (const std::runtime_error &error) { __android_log_print(ANDROID_LOG_ERROR, TAG, "runtime_error: %s", error.what()); @@ -251,7 +348,11 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint if (dev) { dev->Stop(); } - libusb_release_interface(dev_handle, 0); + libusb_release_interface(dev_handle, wifi_interface); + rtl_devices.erase(fd); + if (current_fd == fd) { + current_fd = -1; + } libusb_exit(ctx); return -1; } @@ -269,16 +370,25 @@ int WfbngLink::run(JNIEnv *env, jobject context, jint wifiChannel, jint bw, jint dev->Stop(); } - r = libusb_release_interface(dev_handle, 0); + r = libusb_release_interface(dev_handle, wifi_interface); __android_log_print(ANDROID_LOG_DEBUG, TAG, "libusb_release_interface: %d", r); + rtl_devices.erase(fd); + if (current_fd == fd) { + current_fd = -1; + } libusb_exit(ctx); return 0; } void WfbngLink::stop(JNIEnv *env, jobject context, jint fd) { + { + std::lock_guard lock(stop_request_mutex); + stop_requested_fds.insert(fd); + } if (rtl_devices.find(fd) == rtl_devices.end()) { - __android_log_print(ANDROID_LOG_ERROR, TAG, "rtl_devices.find(%d) == rtl_devices.end()", fd); - CRASH(); + // run() may already have removed a driver that failed to initialise. + // Stop is also called during the Java-side teardown in that case. + __android_log_print(ANDROID_LOG_WARN, TAG, "No active RTL device for fd %d", fd); return; } auto dev = rtl_devices.at(fd).get(); @@ -591,4 +701,4 @@ extern "C" JNIEXPORT void JNICALL Java_com_openipc_wfbngrtl8812_WfbNgLink_native link->fec_recovered_to_3 = recTo3; link->fec_recovered_to_2 = recTo2; link->fec_recovered_to_1 = recTo1; -} \ No newline at end of file +} diff --git a/app/wfbngrtl8812/src/main/cpp/WfbngLink.hpp b/app/wfbngrtl8812/src/main/cpp/WfbngLink.hpp index 24f74c5b..a38a586e 100644 --- a/app/wfbngrtl8812/src/main/cpp/WfbngLink.hpp +++ b/app/wfbngrtl8812/src/main/cpp/WfbngLink.hpp @@ -18,6 +18,7 @@ extern "C" { #include #include #include +#include #include #include // Added for std::vector @@ -97,6 +98,8 @@ class WfbngLink { const char *keyPath = "/data/user/0/com.openipc.pixelpilot/files/gs.key"; std::recursive_mutex thread_mutex; + std::mutex stop_request_mutex; + std::set stop_requested_fds; std::unique_ptr wifi_driver; std::shared_ptr txFrame; uint32_t video_channel_id_be; diff --git a/app/wfbngrtl8812/src/main/cpp/devourer b/app/wfbngrtl8812/src/main/cpp/devourer index 73f1cb4b..1511c8ef 160000 --- a/app/wfbngrtl8812/src/main/cpp/devourer +++ b/app/wfbngrtl8812/src/main/cpp/devourer @@ -1 +1 @@ -Subproject commit 73f1cb4bcc6d1cfd6c5560f6637ce34aeaa94803 +Subproject commit 1511c8ef78430bc171c8dab83dd154cc2daf2caa