/*
    * RtlSdr.cpp
    * Realtek RTL2832U + R820T2 SDR receiver driver.
    * Copyright (c) 2026 Daniel Hammer
*/

#include "RtlSdr.hpp"
#include "R820t.hpp"
#include <Drivers/Radio/Sdr.hpp>
#include <Drivers/USB/Xhci.hpp>
#include <Drivers/USB/UsbDevice.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include <Memory/HHDM.hpp>
#include <Libraries/Memory.hpp>
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
#include <CppLib/Spinlock.hpp>
#include <Api/Syscall.hpp>
#include <atomic>

using namespace Kt;

namespace Drivers::USB::Radio {

    // =========================================================================
    // Constants
    // =========================================================================

    // Vendor control-transfer request types (vendor, host<->device).
    static constexpr uint8_t CTRL_OUT = 0x40;   // host-to-device, vendor
    static constexpr uint8_t CTRL_IN  = 0xC0;   // device-to-host, vendor

    // RTL2832U register blocks (high byte of wIndex; OR 0x10 to write).
    static constexpr uint8_t BLOCK_USB = 1;
    static constexpr uint8_t BLOCK_SYS = 2;
    static constexpr uint8_t BLOCK_IIC = 6;

    // USB / system register addresses.
    static constexpr uint16_t USB_EPA_CTL    = 0x2148;
    static constexpr uint16_t USB_EPA_MAXPKT = 0x2158;
    static constexpr uint16_t USB_SYSCTL     = 0x2000;
    static constexpr uint16_t SYS_DEMOD_CTL  = 0x3000;
    static constexpr uint16_t SYS_DEMOD_CTL1 = 0x300b;

    static constexpr uint32_t RTL_XTAL  = 28800000;  // 28.8 MHz reference
    static constexpr uint32_t R82XX_IF  = 3570000;   // IF the demod expects
    static constexpr uint32_t TWO_POW22 = 1u << 22;

    // =========================================================================
    // Driver state (single instance -- the common RTL-SDR case)
    // =========================================================================

    static bool        g_present   = false;
    static bool        g_hwInited  = false;
    static bool        g_streaming = false;
    static uint8_t     g_slotId    = 0;
    static int         g_rxIndex   = -1;

    static uint8_t*    g_ctlBuf    = nullptr;   // HHDM page for control transfers
    static kcp::Mutex  g_ctlLock;               // serialises register access

    static R820tDev    g_tuner{};
    static uint32_t    g_rtlXtal   = RTL_XTAL;  // adjusted by ppm correction
    static int         g_ppm       = 0;
    static int         g_manual    = 0;         // tuner gain mode (0=auto)
    static int         g_gain      = 0;         // tuner gain, tenths of dB
    static int         g_directSamp = 0;        // 0=tuner path, 1=I ADC, 2=Q ADC
    static uint64_t    g_lastFreq  = 0;         // last successfully tuned freq (Hz)

    // Bulk-IN streaming geometry.  We keep BULK_POOL_BUFS transfers of
    // BULK_XFER_LEN bytes outstanding at once (multi-URB), so the RTL2832U FIFO
    // always has a TRB to DMA into and never overflows in the window between a
    // completion and its re-arm -- the single-outstanding scheme dropped ~88% of
    // samples at 2.048 Msps for exactly that reason.  4 KiB == one DMA page;
    // 16 x 4 KiB == 64 KiB in flight, ~16 ms of slack at 4 MB/s.
    static constexpr uint32_t BULK_XFER_LEN  = 4096;
    static constexpr uint32_t BULK_POOL_BUFS = 16;

    // Set by the bulk-IN completion callback when the endpoint halts (cc=6
    // STALL etc.).  Recovery (Reset Endpoint) needs a command wait and so must
    // run in process context -- serviced from Read() via OpService().
    static std::atomic<bool> g_bulkStalled{false};

    // =========================================================================
    // Low-level register access (control transfers via EP0)
    // =========================================================================

    static bool RegWrite(uint8_t block, uint16_t addr, uint16_t val, uint8_t len) {
        if (!g_ctlBuf) return false;
        g_ctlBuf[0] = (len == 1) ? (uint8_t)(val & 0xff) : (uint8_t)(val >> 8);
        g_ctlBuf[1] = (uint8_t)(val & 0xff);
        uint16_t index = (uint16_t)((block << 8) | 0x10);
        return Xhci::ControlTransfer(g_slotId, CTRL_OUT, 0, addr, index, len,
                                     g_ctlBuf, false) == Xhci::CC_SUCCESS;
    }

    static uint8_t DemodRead(uint8_t page, uint16_t addr) {
        if (!g_ctlBuf) return 0;
        uint16_t raddr = (uint16_t)((addr << 8) | 0x20);
        g_ctlBuf[0] = 0;
        Xhci::ControlTransfer(g_slotId, CTRL_IN, 0, raddr, page, 1, g_ctlBuf, true);
        return g_ctlBuf[0];
    }

    static bool DemodWrite(uint8_t page, uint16_t addr, uint16_t val, uint8_t len) {
        if (!g_ctlBuf) return false;
        uint16_t waddr = (uint16_t)((addr << 8) | 0x20);
        uint16_t index = (uint16_t)(0x10 | page);
        g_ctlBuf[0] = (len == 1) ? (uint8_t)(val & 0xff) : (uint8_t)(val >> 8);
        g_ctlBuf[1] = (uint8_t)(val & 0xff);
        bool ok = Xhci::ControlTransfer(g_slotId, CTRL_OUT, 0, waddr, index, len,
                                        g_ctlBuf, false) == Xhci::CC_SUCCESS;
        // Dummy status read after every demod write (reference behaviour);
        // acts as a write barrier so the register latches before the next op.
        DemodRead(0x0a, 0x01);
        return ok;
    }

    static void SetI2cRepeater(bool on) {
        DemodWrite(1, 0x01, on ? 0x18 : 0x10, 1);
    }

    // =========================================================================
    // I2C facade for the tuner module
    // =========================================================================

    bool RtlI2cWrite(uint8_t slotId, uint8_t i2cAddr, const uint8_t* buf, uint8_t len) {
        if (!g_ctlBuf || len == 0 || len > 64) return false;
        memcpy(g_ctlBuf, buf, len);
        uint16_t index = (uint16_t)((BLOCK_IIC << 8) | 0x10);
        uint32_t cc = Xhci::ControlTransfer(slotId, CTRL_OUT, 0, i2cAddr, index, len,
                                            g_ctlBuf, false);
        if (cc != Xhci::CC_SUCCESS)
            KernelLogStream(WARNING, "RTL-SDR") << "I2C write cc=" << (uint64_t)cc
                << " reg=0x" << base::hex << (uint64_t)buf[0]
                << " len=" << base::dec << (uint64_t)len;
        return cc == Xhci::CC_SUCCESS;
    }

    bool RtlI2cRead(uint8_t slotId, uint8_t i2cAddr, uint8_t* buf, uint8_t len) {
        if (!g_ctlBuf || len == 0 || len > 64) return false;
        uint16_t index = (uint16_t)(BLOCK_IIC << 8);
        uint32_t cc = Xhci::ControlTransfer(slotId, CTRL_IN, 0, i2cAddr, index, len,
                                            g_ctlBuf, true);
        if (cc != Xhci::CC_SUCCESS) {
            KernelLogStream(WARNING, "RTL-SDR") << "I2C read cc=" << (uint64_t)cc
                << " len=" << (uint64_t)len;
            return false;
        }
        memcpy(buf, g_ctlBuf, len);
        return true;
    }

    // =========================================================================
    // Demodulator bring-up
    // =========================================================================

    // The 16-tap default FIR (8x int8 then 8x int12) used for the SDR/FM path.
    static void SetFir() {
        static const int fir[16] = {
            -54, -36, -41, -40, -32, -14, 14, 53,
            101, 156, 215, 273, 327, 372, 404, 421,
        };
        uint8_t buf[20];
        for (int i = 0; i < 8; i++) buf[i] = (uint8_t)(fir[i] & 0xff);
        for (int i = 0; i < 8; i += 2) {
            int v0 = fir[8 + i];
            int v1 = fir[8 + i + 1];
            buf[8 + i * 3 / 2]     = (uint8_t)((v0 >> 4) & 0xff);
            buf[8 + i * 3 / 2 + 1] = (uint8_t)(((v0 << 4) | ((v1 >> 8) & 0x0f)) & 0xff);
            buf[8 + i * 3 / 2 + 2] = (uint8_t)(v1 & 0xff);
        }
        for (int i = 0; i < 20; i++) DemodWrite(1, (uint16_t)(0x1c + i), buf[i], 1);
    }

    static bool BasebandInit() {
        // USB FIFO / endpoint A setup.
        RegWrite(BLOCK_USB, USB_SYSCTL,     0x09,   1);
        RegWrite(BLOCK_USB, USB_EPA_MAXPKT, 0x0002, 2);
        RegWrite(BLOCK_USB, USB_EPA_CTL,    0x1002, 2);

        // Power on the demod.
        RegWrite(BLOCK_SYS, SYS_DEMOD_CTL1, 0x22, 1);
        RegWrite(BLOCK_SYS, SYS_DEMOD_CTL,  0xe8, 1);

        // Soft-reset the demod state machine.
        DemodWrite(1, 0x01, 0x14, 1);
        DemodWrite(1, 0x01, 0x10, 1);

        // Disable spectrum inversion + clear DDC shift / IF registers.
        DemodWrite(1, 0x15, 0x00, 1);
        DemodWrite(1, 0x16, 0x0000, 2);
        for (int i = 0; i < 6; i++) DemodWrite(1, (uint16_t)(0x16 + i), 0x00, 1);

        SetFir();

        DemodWrite(0, 0x19, 0x05, 1);   // enable SDR mode, disable DAGC
        DemodWrite(1, 0x93, 0xf0, 1);
        DemodWrite(1, 0x94, 0x0f, 1);
        DemodWrite(1, 0x11, 0x00, 1);   // disable AGC loop
        DemodWrite(1, 0x04, 0x00, 1);
        DemodWrite(0, 0x61, 0x60, 1);   // disable PID filter
        DemodWrite(0, 0x06, 0x80, 1);   // default ADC I/Q datapath
        DemodWrite(1, 0xb1, 0x1b, 1);   // zero-IF + DC cancel + IQ comp/est
        DemodWrite(0, 0x0d, 0x83, 1);   // disable clock output on TP_CK0
        return true;
    }

    // Set the digital downconversion IF frequency the demod searches at.
    static void SetIfFreq(uint32_t freq) {
        int32_t ifv = (int32_t)(-(int64_t)((uint64_t)freq * TWO_POW22 / g_rtlXtal));
        DemodWrite(1, 0x19, (uint16_t)((ifv >> 16) & 0x3f), 1);
        DemodWrite(1, 0x1a, (uint16_t)((ifv >> 8) & 0xff), 1);
        DemodWrite(1, 0x1b, (uint16_t)(ifv & 0xff), 1);
    }

    static void ApplySampleFreqCorrection() {
        int32_t offs = (int32_t)(-(int64_t)g_ppm * (1 << 24) / 1000000);
        DemodWrite(1, 0x3f, (uint16_t)(offs & 0xff), 1);
        DemodWrite(1, 0x3e, (uint16_t)((offs >> 8) & 0x3f), 1);
    }

    static bool TunerInit() {
        SetI2cRepeater(true);
        // Retry detection a few times: an I2C read can transiently come back
        // wrong if it raced another core's USB activity around bring-up.
        bool detected = false;
        for (int attempt = 0; attempt < 4 && !detected; attempt++)
            detected = R820tDetect(g_slotId);
        bool ok = detected && R820tInit(g_tuner, g_slotId, g_rtlXtal, R82XX_IF);
        SetI2cRepeater(false);
        if (!detected) {
            KernelLogStream(WARNING, "RTL-SDR") << "no R820T2 tuner found on I2C";
            return false;
        }
        if (!ok) return false;

        // Demod path for the R820T2 low-IF tuner.
        DemodWrite(1, 0xb1, 0x1a, 1);   // disable zero-IF mode
        DemodWrite(0, 0x08, 0x4d, 1);   // enable In-phase ADC input only
        SetIfFreq(R82XX_IF);
        DemodWrite(1, 0x15, 0x01, 1);   // enable spectrum inversion
        return true;
    }

    static bool EnsureInit() {
        if (g_hwInited) return true;
        if (!g_present || !g_ctlBuf) return false;
        if (!BasebandInit()) return false;
        if (!TunerInit())   return false;
        g_hwInited = true;
        KernelLogStream(OK, "RTL-SDR") << "Demod + tuner brought up on slot "
            << (uint64_t)g_slotId;
        return true;
    }

    // =========================================================================
    // Tuning / configuration (each holds g_ctlLock via the op wrappers)
    // =========================================================================

    static int DoSetFreq(uint64_t hz) {
        if (!EnsureInit()) return -1;
        if (g_directSamp) {
            // Tuner is bypassed: tuning is the demod's digital downconverter.
            SetIfFreq((uint32_t)hz);
            g_lastFreq = hz;
            return 0;
        }
        SetI2cRepeater(true);
        bool ok = R820tSetFreq(g_tuner, hz);
        SetI2cRepeater(false);
        if (ok) g_lastFreq = hz;
        return ok ? 0 : -1;
    }

    static int DoSetSampleRate(uint32_t rate) {
        if (!EnsureInit()) return -1;
        // The RTL2832 resampler does not cover 300k..900k.
        if (rate <= 225000 || rate > 3200000 ||
            (rate > 300000 && rate <= 900000)) return -1;

        // The ratio uses the NOMINAL crystal frequency: ppm correction is
        // applied by the demod's sample-frequency-offset registers below, so
        // baking it into the ratio too would correct the rate twice.
        uint32_t ratio = (uint32_t)(((uint64_t)RTL_XTAL * TWO_POW22) / rate);
        ratio &= 0x0ffffffc;
        DemodWrite(1, 0x9f, (uint16_t)((ratio >> 16) & 0xffff), 2);
        DemodWrite(1, 0xa1, (uint16_t)(ratio & 0xffff), 2);

        ApplySampleFreqCorrection();
        DemodWrite(1, 0x01, 0x14, 1);   // soft reset
        DemodWrite(1, 0x01, 0x10, 1);
        SetIfFreq(g_directSamp ? (uint32_t)g_lastFreq : R82XX_IF);
        return 0;
    }

    static int DoSetGainMode(int manual) {
        if (!EnsureInit()) return -1;
        g_manual = manual ? 1 : 0;
        SetI2cRepeater(true);
        bool ok = R820tSetGain(g_tuner, g_manual, g_gain);
        SetI2cRepeater(false);
        return ok ? 0 : -1;
    }

    static int DoSetGain(int tenths) {
        if (!EnsureInit()) return -1;
        g_gain = tenths;
        g_manual = 1;   // selecting an explicit gain implies manual mode
        SetI2cRepeater(true);
        bool ok = R820tSetGain(g_tuner, 1, g_gain);
        SetI2cRepeater(false);
        return ok ? 0 : -1;
    }

    static int DoSetFreqCorrection(int ppm) {
        if (!EnsureInit()) return -1;
        g_ppm = ppm;
        g_rtlXtal = (uint32_t)((int64_t)RTL_XTAL + (int64_t)RTL_XTAL * ppm / 1000000);
        g_tuner.xtal = g_rtlXtal;
        ApplySampleFreqCorrection();
        // The tuner PLL (and, in direct mode, the DDC) derive from the xtal;
        // retune so the new correction actually takes effect.
        if (g_lastFreq) return DoSetFreq(g_lastFreq);
        return 0;
    }

    static int DoSetAgc(int on) {
        if (!EnsureInit()) return -1;
        return DemodWrite(0, 0x19, on ? 0x25 : 0x05, 1) ? 0 : -1;
    }

    static int DoSetDirectSampling(int mode) {
        if (!EnsureInit()) return -1;
        if (mode) {
            // Bypass the tuner and digitise the ADC input directly.
            SetI2cRepeater(true);
            R820tStandby(g_tuner);
            SetI2cRepeater(false);
            DemodWrite(1, 0xb1, 0x1a, 1);                       // disable zero-IF
            DemodWrite(1, 0x15, 0x00, 1);                       // no spectrum inversion
            DemodWrite(0, 0x08, 0x4d, 1);                       // In-phase ADC input
            DemodWrite(0, 0x06, (mode == 2) ? 0x90 : 0x80, 1);  // Q vs I ADC
            g_directSamp = mode;
            // Tuning now happens in the DDC; carry the current frequency over.
            SetIfFreq((uint32_t)g_lastFreq);
        } else {
            // Restore the R820T2 low-IF receive path.  Standby powered the
            // tuner down, so it needs a full re-initialisation.
            SetI2cRepeater(true);
            bool ok = R820tInit(g_tuner, g_slotId, g_rtlXtal, R82XX_IF);
            SetI2cRepeater(false);
            if (!ok) return -1;
            SetIfFreq(R82XX_IF);
            DemodWrite(1, 0x15, 0x01, 1);   // enable spectrum inversion
            DemodWrite(0, 0x06, 0x80, 1);   // default ADC I/Q datapath
            g_directSamp = 0;
            if (g_lastFreq) return DoSetFreq(g_lastFreq);
        }
        return 0;
    }

    // =========================================================================
    // Streaming
    // =========================================================================

    static void TransferCallback(uint8_t slotId, uint8_t epDci,
                                 const uint8_t* data, uint32_t length,
                                 uint32_t /*completionCode*/) {
        if (slotId != g_slotId) return;
        auto* dev = Xhci::GetDevice(slotId);
        if (!dev) return;

        uint8_t bulkInDci = dev->BulkInEpNum ? (uint8_t)(dev->BulkInEpNum * 2 + 1) : 0;
        if (epDci != bulkInDci || !g_streaming) return;

        if (data) {
            // Deliver samples only.  The xHCI layer owns the multi-buffer pool
            // (StartBulkInStream) and re-arms this very buffer automatically once
            // we return; re-queuing here would double-arm the pool and lap the
            // ring.  PushSamples copies out synchronously, so the buffer is free
            // to be re-armed the instant this returns.
            if (length > 0)
                Drivers::Radio::Sdr::PushSamples(g_rxIndex, data, length);
        } else {
            // Error (data==nullptr), e.g. cc=6 STALL: the endpoint is halted.
            // Clearing it requires a Reset Endpoint command that waits on the
            // event ring, which is unsafe here (we are inside PollEvents).
            // Flag it for process-context recovery in OpService(), which resets
            // the endpoint and re-primes the whole pool.
            g_bulkStalled.store(true, std::memory_order_relaxed);
        }
    }

    static int DoStart() {
        if (!EnsureInit()) return -1;

        // Reset endpoint-A FIFO so streaming starts on a clean boundary.
        RegWrite(BLOCK_USB, USB_EPA_CTL, 0x1002, 2);   // hold + reset
        RegWrite(BLOCK_USB, USB_EPA_CTL, 0x0000, 2);   // release

        g_bulkStalled.store(false, std::memory_order_relaxed);
        g_streaming = true;
        Xhci::RegisterTransferCallback(g_slotId, TransferCallback);

        auto* dev = Xhci::GetDevice(g_slotId);
        if (dev && dev->BulkInEpNum)
            Xhci::StartBulkInStream(g_slotId, BULK_XFER_LEN, BULK_POOL_BUFS);
        return 0;
    }

    static int DoStop() {
        g_streaming = false;
        g_bulkStalled.store(false, std::memory_order_relaxed);
        // Disarm the multi-buffer rotation so no further transfers re-arm, then
        // hold/reset the FIFO so the device stops producing samples.
        Xhci::StopBulkInStream(g_slotId);
        if (g_ctlBuf) RegWrite(BLOCK_USB, USB_EPA_CTL, 0x1002, 2);
        return 0;
    }

    // Process-context housekeeping called from Read(): recover a halted bulk-IN
    // endpoint (Reset Endpoint + Set TR Dequeue) and re-arm reception.
    static void DoService() {
        if (!g_bulkStalled.load(std::memory_order_relaxed)) return;
        g_ctlLock.Acquire();
        if (g_streaming) {
            Xhci::ResetBulkInEndpoint(g_slotId);
            Xhci::PrimeBulkInStream(g_slotId);
        }
        g_bulkStalled.store(false, std::memory_order_relaxed);
        g_ctlLock.Release();

        static uint32_t recoveries = 0;
        if (recoveries < 5) {
            recoveries++;
            KernelLogStream(INFO, "RTL-SDR") << "bulk IN stall recovered ("
                << (uint64_t)recoveries << ")";
        }
    }

    // =========================================================================
    // Ops table wrappers (lock the control path)
    // =========================================================================

    static int OpSetFreq(void*, uint64_t hz) {
        g_ctlLock.Acquire(); int r = DoSetFreq(hz); g_ctlLock.Release(); return r;
    }
    static int OpSetSampleRate(void*, uint32_t hz) {
        g_ctlLock.Acquire(); int r = DoSetSampleRate(hz); g_ctlLock.Release(); return r;
    }
    static int OpSetGainMode(void*, int manual) {
        g_ctlLock.Acquire(); int r = DoSetGainMode(manual); g_ctlLock.Release(); return r;
    }
    static int OpSetGain(void*, int tenths) {
        g_ctlLock.Acquire(); int r = DoSetGain(tenths); g_ctlLock.Release(); return r;
    }
    static int OpSetFreqCorrection(void*, int ppm) {
        g_ctlLock.Acquire(); int r = DoSetFreqCorrection(ppm); g_ctlLock.Release(); return r;
    }
    static int OpSetAgc(void*, int on) {
        g_ctlLock.Acquire(); int r = DoSetAgc(on); g_ctlLock.Release(); return r;
    }
    static int OpSetDirectSampling(void*, int mode) {
        g_ctlLock.Acquire(); int r = DoSetDirectSampling(mode); g_ctlLock.Release(); return r;
    }
    static int OpStart(void*) {
        g_ctlLock.Acquire(); int r = DoStart(); g_ctlLock.Release(); return r;
    }
    static int OpStop(void*) {
        g_ctlLock.Acquire(); int r = DoStop(); g_ctlLock.Release(); return r;
    }
    // DoService does its own locking (around the reset), so OpService must not
    // take g_ctlLock here.
    static void OpService(void*) { DoService(); }

    // =========================================================================
    // USB enumeration hooks
    // =========================================================================

    bool IsRtlSdr(uint16_t vid, uint16_t pid) {
        if (vid != 0x0bda) return false;   // Realtek Semiconductor
        switch (pid) {
            // Only the two known RTL2832U ids.  In particular 0x2831 is the
            // RTL2831U, a DIFFERENT demod this driver cannot program.
            case 0x2832:   // RTL2832U (generic)
            case 0x2838:   // RTL2838 (most RTL-SDR.com dongles)
                return true;
            default:
                return false;
        }
    }

    void RegisterDevice(uint8_t slotId) {
        if (g_present) {
            KernelLogStream(WARNING, "RTL-SDR")
                << "second RTL-SDR ignored (single instance), slot " << (uint64_t)slotId;
            return;
        }

        g_slotId    = slotId;
        g_present   = true;
        g_hwInited  = false;
        g_streaming = false;
        g_ppm       = 0;
        g_rtlXtal   = RTL_XTAL;
        g_manual    = 0;
        g_gain      = 0;
        g_directSamp = 0;
        g_lastFreq  = 0;
        g_tuner     = R820tDev{};

        g_ctlBuf = (uint8_t*)Memory::g_pfa->AllocateZeroed();
        if (!g_ctlBuf) {
            KernelLogStream(ERROR, "RTL-SDR") << "control buffer alloc failed";
            g_present = false;
            return;
        }

        int gainCount = 0;
        const int* gains = R820tGainTable(&gainCount);

        Drivers::Radio::Sdr::ReceiverDesc desc{};
        desc.name          = "Realtek RTL2832U";
        desc.tuner         = "Rafael Micro R820T2";
        desc.serial        = "USB RTL-SDR";
        desc.freqMin       = 24000000ull;
        desc.freqMax       = 1766000000ull;
        desc.sampleRateMin = 225001;
        desc.sampleRateMax = 3200000;
        desc.gains         = gains;
        desc.numGains      = (uint32_t)gainCount;
        desc.format        = montauk::abi::SDR_FORMAT_CU8;
        desc.ops.SetFreq           = OpSetFreq;
        desc.ops.SetSampleRate     = OpSetSampleRate;
        desc.ops.SetGainMode       = OpSetGainMode;
        desc.ops.SetGain           = OpSetGain;
        desc.ops.SetFreqCorrection = OpSetFreqCorrection;
        desc.ops.SetAgc            = OpSetAgc;
        desc.ops.SetDirectSampling = OpSetDirectSampling;
        desc.ops.Start             = OpStart;
        desc.ops.Stop              = OpStop;
        desc.ops.Service           = OpService;
        desc.ctx                   = nullptr;

        g_rxIndex = Drivers::Radio::Sdr::Register(desc);
        if (g_rxIndex < 0) {
            KernelLogStream(ERROR, "RTL-SDR") << "SDR registration failed";
            Memory::g_pfa->Free(g_ctlBuf);
            g_ctlBuf = nullptr;
            g_present = false;
            return;
        }

        KernelLogStream(OK, "RTL-SDR") << "RTL-SDR on slot " << (uint64_t)slotId
            << " registered as receiver " << (uint64_t)g_rxIndex;
    }

    void UnregisterDevice(uint8_t slotId) {
        if (!g_present || slotId != g_slotId) return;

        g_streaming = false;
        if (g_rxIndex >= 0) Drivers::Radio::Sdr::Unregister(g_rxIndex);
        g_rxIndex  = -1;
        g_present  = false;
        g_hwInited = false;

        if (g_ctlBuf) {
            Memory::g_pfa->Free(g_ctlBuf);
            g_ctlBuf = nullptr;
        }
        KernelLogStream(INFO, "RTL-SDR") << "RTL-SDR removed from slot " << (uint64_t)slotId;
    }

}
