rl8822bu support - #105
Conversation
"No compatible wifi adapter found." is a common report, and the one thing needed
to act on it - the adapter's vendor and product id - could not be obtained.
sysfs is not readable by the shell on some devices (Horizon OS for one) and
dumpsys usb does not list host devices there either, so the app is the only
thing in a position to report it.
getAttachedAdapters() now logs every attached device with its ids, manufacturer
and product name, and whether usb_device_filter.xml matched:
usb devices attached: 1
/dev/bus/usb/001/002 0BDA:8812 Realtek 802.11n NIC -> supported
which turns "it does not work" into a line that can be pasted into a filter
entry. Relevant to the standing requests for new adapters (#82, #91, #93, #105).
Also fixes a landmine in the same code path: wirelessInfo() is static and reads
a static WifiManager that only VideoActivity.initializeUI() ever assigns, yet it
is called from WfbLinkManager.refreshAdapters(). Any other caller, or this one
before onCreate has got that far, gets a NullPointerException. It now takes a
Context, fetches the service itself and null checks both the manager and the
WifiInfo; the static field is gone, so it cannot come back through a different
entry point.
|
@RomanLut sorry for the long silence on this one, it's my fault, not yours. I've gone through it now, notes below. Could you rebase onto current master? I have no 8822BU here, so I'd rather the branch stays yours and gets re-tested on your dongle after the rebase; I'll merge right after. The submodule points at The
The |
The bounded join fixed the ANR but not the reason the join was timing out in the first place, as pointed out in review. StopRxLoop() only sets a flag, and RtlJaguarDevice::StartRxLoop() clears it on entry. So a stop is thrown away anywhere between the fd being handed to run() and the loop actually starting - which includes the whole chip bring-up in InitWrite(), the longest part of run(). Until CreateRtlDevice() there is not even an entry in rtl_devices for stop() to find, so it returns "already gone" and does nothing at all. run() then blocks in a loop nobody asked for. stop() now records the fd in stop_requested_fds before anything else, and run() checks it at the two points where the flag itself cannot be trusted: after CreateRtlDevice(), and again immediately before entering the loop. Skipping the loop falls through to the same teardown a StopRxLoop() would have taken. run() clears the entry on the way in, because fd numbers are reused and a stale request must not abort a new session. This narrows the window to a few instructions rather than closing it - closing it needs devourer to stop clearing the flag. The second half was the timeout path itself. libusb_wrap_sys_device() keeps the fd it is given rather than duplicating it - the comment claiming otherwise was wrong - so closing the UsbDeviceConnection after a timed-out join pulled the fd out from under a libusb that was still polling it. The kernel cancels the URBs on close, but libusb never reaps them, because op_handle_events() checks POLLERR and not POLLNVAL: poll() then returns immediately forever and the loop spins on one core waiting for a transfer count that never drops. Dropping the map entries at the same time hid it from the duplicate check in start(), so the next openDevice() would most likely be handed the same fd number back and overwrite rtl_devices[fd] underneath the spinning thread. So a timed-out join now leaves both the thread and its connection in place. start() refuses a second RX loop on that device, and releases the connection once the old thread has actually finished. Still worth a follow-up: 3s of join on the main thread from onPause is under the ANR limit but visible. Moving the stop off the main thread would remove it. OpenIPC#105 touches WfbngLink::stop too, so whichever lands second will need a rebase.
* Harden the USB adapter lifecycle Four separate ways the adapter path can take the app down or wedge it. All of them are easy to hit on a powered hub that re-enumerates the dongle, which is how a lot of ground stations are wired. 1. Deliberate null deref. WfbngLink::stop() ran a CRASH() macro (`int *i = 0; *i = 42;`) when the fd was no longer in rtl_devices. That is a recoverable state - the adapter was already gone - and it killed the process. Removed, now a warning and return. 2. NPE on openDevice(). UsbManager.openDevice() returns null when the permission was revoked or the device disappeared between the permission check and the open; getFileDescriptor() was called on it unconditionally. start() now returns false instead, WfbLinkManager reports it and leaves the adapter out of activeWifiAdapters so the next refresh retries it. Before, a failed adapter was recorded as active and never retried. 3. Leaked usbfs descriptors. UsbDeviceConnection was never closed and linkConns was never cleared, so every attach/detach cycle leaked one fd plus the map entry. 4. USB permission dialog on Android 14. requestPermission() got a PendingIntent built from an implicit Intent. Android 14 refuses to deliver those to a runtime registered receiver, so the result never arrived and the app sat on "No permission for wifi adapter(s)". setPackage() added. Also: refreshAdapters() dereferenced getAttachedAdapters() without checking for the null it returns when the device filter fails to parse, and the wfb thread name indexed split()[1] without checking the device name matched /dev/bus/usb/. * Bound the join on the driver thread, and refuse a duplicate RX loop Found on a Quest 3 while the app was unresponsive: the main thread was asleep inside stopAll()'s t.join() and Android killed the window with Input dispatching timed out ... Waited 5000ms for MotionEvent ANR in com.openipc.pixelpilot (com.openipc.pixelpilot/.VideoActivity) stopAdapters() is called from onPause(), onStop() and the channel/bandwidth menus, so this join runs on the main thread. StopRxLoop() only breaks the receive loop; the thread then still has to stop the TX frame and the adaptive link, power the chip down, release the USB interface and exit libusb. If any of that does not come back, the UI is frozen until the watchdog fires. The join is now bounded at 3000 ms - about what a healthy unwind needs - and logs when a thread outstays it instead of hanging the UI. Also: start() refuses a device that already has a live thread. linkThreads.put() overwrites the entry, so an older thread would be orphaned, never joined, and its interface never released. * Do not blame the device filter when the adapter merely failed to start Recording an adapter as active only when it actually came up means an empty activeWifiAdapters now covers two different problems: nothing compatible is attached, or something compatible is attached and could not be opened. Showing "No compatible wifi adapter found." for both sends people looking for a usb_device_filter.xml entry that is already there. * Do not lose a stop that arrives before the rx loop is running The bounded join fixed the ANR but not the reason the join was timing out in the first place, as pointed out in review. StopRxLoop() only sets a flag, and RtlJaguarDevice::StartRxLoop() clears it on entry. So a stop is thrown away anywhere between the fd being handed to run() and the loop actually starting - which includes the whole chip bring-up in InitWrite(), the longest part of run(). Until CreateRtlDevice() there is not even an entry in rtl_devices for stop() to find, so it returns "already gone" and does nothing at all. run() then blocks in a loop nobody asked for. stop() now records the fd in stop_requested_fds before anything else, and run() checks it at the two points where the flag itself cannot be trusted: after CreateRtlDevice(), and again immediately before entering the loop. Skipping the loop falls through to the same teardown a StopRxLoop() would have taken. run() clears the entry on the way in, because fd numbers are reused and a stale request must not abort a new session. This narrows the window to a few instructions rather than closing it - closing it needs devourer to stop clearing the flag. The second half was the timeout path itself. libusb_wrap_sys_device() keeps the fd it is given rather than duplicating it - the comment claiming otherwise was wrong - so closing the UsbDeviceConnection after a timed-out join pulled the fd out from under a libusb that was still polling it. The kernel cancels the URBs on close, but libusb never reaps them, because op_handle_events() checks POLLERR and not POLLNVAL: poll() then returns immediately forever and the loop spins on one core waiting for a transfer count that never drops. Dropping the map entries at the same time hid it from the duplicate check in start(), so the next openDevice() would most likely be handed the same fd number back and overwrite rtl_devices[fd] underneath the spinning thread. So a timed-out join now leaves both the thread and its connection in place. start() refuses a second RX loop on that device, and releases the connection once the old thread has actually finished. Still worth a follow-up: 3s of join on the main thread from onPause is under the ANR limit but visible. Moving the stop off the main thread would remove it. #105 touches WfbngLink::stop too, so whichever lands second will need a rebase.
f81226e to
e4ca43a
Compare
|
Rebased, rtl8822bu works. Crashes on unplug - due to bug in devourer. Devourer has many updates recently. It makes sense to wait for it to stabilize then retest and fix rtl_devices race. |
Added rtl8822bu support
Devourer PR should be accepted first OpenIPC/devourer#290
Should fast-forward devourer reference then.