diff --git a/implementations/main.cpp b/implementations/main.cpp index 55a79d5..5115340 100644 --- a/implementations/main.cpp +++ b/implementations/main.cpp @@ -54,6 +54,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -89,6 +90,7 @@ int g_rescan = -1; // -1 = leave the config's value alone // reached a verdict is also reported as no-match. Under rescan=0 this cannot // arise (every frame is terminal), so the knob only matters with a budget. bool g_undecidedIsNoMatch = false; +bool g_irqObserve = 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 @@ -703,10 +705,36 @@ public: } power_ = RequestLine(sn::PowerLine, GPIO_V2_LINE_FLAG_OUTPUT, "fpd-pwr"); reset_ = RequestLine(sn::ResetLine, GPIO_V2_LINE_FLAG_OUTPUT, "fpd-rst"); - irq_ = RequestLine(sn::IrqLine, GPIO_V2_LINE_FLAG_INPUT, "fpd-irq"); + // Both edges, not just the rising one the DT declares: the line is a + // ~1 ms pulse, so a level read catches it only by luck, and knowing + // the pulse WIDTH distinguishes a touch pulse from a heartbeat. + irq_ = RequestLine(sn::IrqLine, + GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_RISING + | GPIO_V2_LINE_FLAG_EDGE_FALLING, + "fpd-irq"); return power_ >= 0 && reset_ >= 0 && irq_ >= 0; } + // The line fd, for poll(): readable when an edge event is queued. + int IrqFd() const { return irq_; } + + // 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; }; + std::vector ReadEdges() { + std::vector out; + if (irq_ < 0) return out; + gpio_v2_line_event ev[16]; + for (;;) { + ssize_t n = ::read(irq_, ev, sizeof(ev)); + if (n <= 0) break; + for (std::size_t i = 0; i < static_cast(n) / sizeof(ev[0]); i++) + out.push_back({ ev[i].timestamp_ns, ev[i].id == GPIO_V2_LINE_EVENT_RISING_EDGE }); + if (static_cast(n) < sizeof(ev)) break; + } + return out; + } + // Rail up, settle, release reset, settle. Both lines are driven low first // so a warm restart starts where a cold one does. bool PowerOn() { @@ -766,6 +794,9 @@ private: std::println(std::cerr, "gpio{} request failed: {}", line, ::strerror(errno)); return -1; } + // Edge events are drained with read(); never block the caller on it. + int fl = ::fcntl(req.fd, F_GETFL); + if (fl >= 0) ::fcntl(req.fd, F_SETFL, fl | O_NONBLOCK); return req.fd; } @@ -1356,6 +1387,39 @@ public: int EnrolStages() const { return g_samples; } qcomtee_object* App() const { return app_; } + // IRQ edge observer: poll() the line and log each edge. This is the + // measurement the IRQ-driven design needs before it is built -- whether + // edges are observable at all, how wide the pulses are, and whether the + // line is quiet at idle. If the sensor pulses at rest, a wake-on-edge is + // useless and the design is dead before it starts. + void StartIrqObserver() { + if (sensor_.IrqFd() < 0) return; + irqThread_ = std::thread([this] { + std::uint64_t last = 0, lastRise = 0; + unsigned edges = 0; + for (;;) { + pollfd pfd{ sensor_.IrqFd(), POLLIN, 0 }; + int r = ::poll(&pfd, 1, 500); + if (irqQuit_.load()) break; + if (r <= 0) continue; + for (auto& e : sensor_.ReadEdges()) { + edges++; + double sinceLast = last ? (e.ns - last) / 1e6 : 0; + if (e.rising) lastRise = e.ns; + std::string width = (!e.rising && lastRise) + ? std::format(" pulse={:.2f}ms", (e.ns - lastRise) / 1e6) : ""; + std::println(" irq edge #{}: {} +{:.1f}ms{}", edges, + e.rising ? "RISE" : "fall", sinceLast, width); + last = e.ns; + } + } + }); + } + void StopIrqObserver() { + irqQuit_.store(true); + if (irqThread_.joinable()) irqThread_.join(); + } + private: bool SyncConfig() { namespace ta = fingerprintd::ta; @@ -1397,6 +1461,8 @@ private: fingerprintd::engine::Baseline baseline_; std::uint32_t gid_ = 0xFFFFFFFF; std::atomic fingerPresent_{false}; + std::thread irqThread_; + std::atomic irqQuit_{false}; }; // ============================================================================= @@ -1468,6 +1534,7 @@ private: return; } PostEvent(std::make_unique(Event{ .kind = Event::Kind::Ready })); + if (g_irqObserve) session_.StartIrqObserver(); for (;;) { Job j; @@ -1532,6 +1599,7 @@ private: } } } + session_.StopIrqObserver(); session_.Stop(); } @@ -2078,6 +2146,7 @@ int main(int argc, char** argv) { if (a.starts_with("--frames=")) frames = std::stoi(std::string(a.substr(9))); 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; 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)));