Wake the capture loop on the touch edge
The measurement that made this worth building: gpio75 is silent at idle under WAIT_TOUCH -- zero edges in sixty seconds -- and bursts within milliseconds of a finger landing. In the observed taps the burst appeared in the transcript hundreds of milliseconds before the polled capture noticed the finger, which is why a quick tap only ever produced one frame however cheap the frame became. So the inter-frame wait is now a wait on the line rather than a sleep. At idle it times out at the fallback cadence and costs nothing. At contact it returns at once, so the first capture of a press happens when the finger lands. While the finger stays down the sensor keeps pulsing, so the wait keeps returning immediately and the loop runs as fast as QTEE allows -- which is what a press wants and what stock's architecture does. Queued edges are dropped when a session arms, or the previous press's burst would wake the first wait instantly. --edge-wake gates it as a single variable against the labelled 2-of-10 baseline; it and --irq-observe are mutually exclusive because both would drain the same fd.
This commit is contained in:
parent
479f653e65
commit
578e2321fa
1 changed files with 44 additions and 5 deletions
|
|
@ -91,6 +91,7 @@ int g_rescan = -1; // -1 = leave the config's value alone
|
|||
// arise (every frame is terminal), so the knob only matters with a budget.
|
||||
bool g_undecidedIsNoMatch = false;
|
||||
bool g_irqObserve = false;
|
||||
bool g_edgeWake = false;
|
||||
|
||||
// The namespace key the trustlet hashes into the SFS group's directory name.
|
||||
// It defaults to Android's because that is what this device's existing store
|
||||
|
|
@ -718,6 +719,10 @@ public:
|
|||
// The line fd, for poll(): readable when an edge event is queued.
|
||||
int IrqFd() const { return irq_; }
|
||||
|
||||
// Discard anything already queued, so a wait sees only NEW edges. Without
|
||||
// this the burst from the previous press wakes the next wait instantly.
|
||||
void DrainEdges() { ReadEdges(); }
|
||||
|
||||
// Drain queued edge events. Each is a gpio_v2_line_event with a kernel
|
||||
// timestamp in ns and RISING/FALLING.
|
||||
struct Edge { std::uint64_t ns; bool rising; };
|
||||
|
|
@ -735,6 +740,13 @@ public:
|
|||
return out;
|
||||
}
|
||||
|
||||
// Block up to timeoutMs for an edge, then drain. Empty on timeout. This is
|
||||
// what turns the capture loop from a fixed-cadence poll into a
|
||||
// wake-on-contact: the line is silent at idle and pulses within
|
||||
// milliseconds of a finger landing, hundreds of milliseconds before a
|
||||
// polled capture notices.
|
||||
std::vector<Edge> WaitEdges(int timeoutMs);
|
||||
|
||||
// Rail up, settle, release reset, settle. Both lines are driven low first
|
||||
// so a warm restart starts where a cold one does.
|
||||
bool PowerOn() {
|
||||
|
|
@ -820,6 +832,17 @@ private:
|
|||
bool on_ = false;
|
||||
};
|
||||
|
||||
std::vector<Sensor::Edge> Sensor::WaitEdges(int timeoutMs) {
|
||||
if (irq_ < 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(timeoutMs));
|
||||
return {};
|
||||
}
|
||||
pollfd pfd{ irq_, POLLIN, 0 };
|
||||
int r = ::poll(&pfd, 1, timeoutMs);
|
||||
if (r <= 0) return {};
|
||||
return ReadEdges();
|
||||
}
|
||||
|
||||
// ---- The trustlet
|
||||
//
|
||||
// The loader is IQSEEComCompatAppLoader (UID 122): op 1 loadFromBuffer, op 2
|
||||
|
|
@ -1284,6 +1307,9 @@ public:
|
|||
auto a = SendCommand(app_, ta::Cmd::Authenticate, au);
|
||||
Report(ta::Cmd::Authenticate, a);
|
||||
if (!a.Ok()) return out;
|
||||
// The previous press's burst is still queued; drop it so the first
|
||||
// wait of this session cannot be woken by an old finger.
|
||||
if (g_edgeWake) sensor_.DrainEdges();
|
||||
|
||||
en::TouchTracker tracker;
|
||||
bool inPress = false, pressMatched = false, pressRejected = false;
|
||||
|
|
@ -1362,15 +1388,24 @@ public:
|
|||
}
|
||||
SendCommand(app_, ta::Cmd::QueryEventStatus, q);
|
||||
if (g_verbose) {
|
||||
// The IRQ line alongside the metric: if it tracks the finger
|
||||
// under an armed session, lift detection can become an edge
|
||||
// wait instead of a poll.
|
||||
auto irq = sensor_.ReadIrq();
|
||||
std::println(" frame {:3} @{:5}ms: metric={:<4}{} irq={}{}", i + 1,
|
||||
msSince(t0), c.metric, finger ? " FINGER" : " ",
|
||||
irq ? std::to_string(*irq) : "?", note);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(g_frameGapMs));
|
||||
// WAKE ON CONTACT. Measured: gpio75 is silent at idle under
|
||||
// WAIT_TOUCH (0 edges in 60 s) and bursts within milliseconds of a
|
||||
// finger landing -- and the burst arrives HUNDREDS of ms before a
|
||||
// fixed-cadence capture notices, which is why a quick tap only
|
||||
// ever yielded one frame. Waiting on the edge instead of sleeping
|
||||
// means the first capture of a press happens at contact.
|
||||
//
|
||||
// While a finger is DOWN the sensor keeps pulsing, so the wait
|
||||
// returns immediately and the loop runs as fast as QTEE allows --
|
||||
// exactly what a press wants. The timeout is the idle fallback, so
|
||||
// a release is still noticed promptly.
|
||||
if (g_edgeWake) sensor_.WaitEdges(g_frameGapMs);
|
||||
else std::this_thread::sleep_for(std::chrono::milliseconds(g_frameGapMs));
|
||||
}
|
||||
fingerPresent_.store(false);
|
||||
std::println(" verify loop: {} frames in {} ms ({} ms/frame incl. {} ms gap)",
|
||||
|
|
@ -1534,7 +1569,8 @@ private:
|
|||
return;
|
||||
}
|
||||
PostEvent(std::make_unique<Event>(Event{ .kind = Event::Kind::Ready }));
|
||||
if (g_irqObserve) session_.StartIrqObserver();
|
||||
if (g_irqObserve && !g_edgeWake) session_.StartIrqObserver();
|
||||
else if (g_irqObserve) std::println("--irq-observe ignored: --edge-wake owns the line fd");
|
||||
|
||||
for (;;) {
|
||||
Job j;
|
||||
|
|
@ -2147,6 +2183,9 @@ int main(int argc, char** argv) {
|
|||
if (a.starts_with("--frame-gap=")) g_frameGapMs = std::stoi(std::string(a.substr(12)));
|
||||
if (a == "--undecided=nomatch") g_undecidedIsNoMatch = true;
|
||||
if (a == "--irq-observe") g_irqObserve = true;
|
||||
// The observer thread and the loop would both drain the same fd, so
|
||||
// the diagnostic and the wake are mutually exclusive.
|
||||
if (a == "--edge-wake") g_edgeWake = true;
|
||||
if (a.starts_with("--log-dir=")) g_logDir = a.substr(10);
|
||||
if (a.starts_with("--state-dir=")) g_stateDir = a.substr(12);
|
||||
if (a.starts_with("--rescan=")) g_rescan = std::stoi(std::string(a.substr(9)));
|
||||
|
|
|
|||
Loading…
Reference in a new issue