#include "QualcommWifi.hpp" #include #include #include #include // Stubs locaux pour les réservations IONetworkController 2..7 non exportées par BootKernelExtensions.kc extern "C" { void _ZN19IONetworkController29_RESERVEDIONetworkController2Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController3Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController4Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController5Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController6Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController7Ev(void) {} } #include #include static inline void logNvramStage(const char *tag) { IOLog("QualcommWifi: >>> [STAGE] %s <<<\n", tag); } // Classe simulant un FaultReporter IOKit valide (attendu par IO80211Controller::findAndAttachToFaultReporter) class FakeFaultReporter : public OSObject { OSDeclareDefaultStructors(FakeFaultReporter) public: virtual bool init() override { return OSObject::init(); } static FakeFaultReporter* create() { FakeFaultReporter *inst = new FakeFaultReporter; if (inst && !inst->init()) { inst->release(); return nullptr; } return inst; } }; OSDefineMetaClassAndStructors(FakeFaultReporter, OSObject) // Classe de simulation binaire conforme pour CCLogStream (CoreCapture) sous macOS Tahoe // Évite tout déréférencement invalide dans CCLogStream::shouldLog (offset +0x90 -> +0x58 -> level/flags) class FakeLogStream : public OSObject { OSDeclareDefaultStructors(FakeLogStream) private: char _pad[0x90 - sizeof(OSObject)]; public: void *p1; void *fP1Mem; void *fP2Mem; virtual bool init() override { if (!OSObject::init()) return false; bzero(_pad, sizeof(_pad)); fP1Mem = IOMallocZero(0x1000); fP2Mem = IOMallocZero(0x100); if (fP1Mem && fP2Mem) { *(void **)((uint8_t *)fP1Mem + 0x58) = fP2Mem; p1 = fP1Mem; } else { p1 = nullptr; } return true; } virtual void free() override { if (fP2Mem) { IOFree(fP2Mem, 0x100); fP2Mem = nullptr; } if (fP1Mem) { IOFree(fP1Mem, 0x1000); fP1Mem = nullptr; } p1 = nullptr; OSObject::free(); } static FakeLogStream* create() { FakeLogStream *inst = new FakeLogStream; if (inst && !inst->init()) { inst->release(); return nullptr; } return inst; } }; OSDefineMetaClassAndStructors(FakeLogStream, OSObject) static_assert(__builtin_offsetof(FakeLogStream, p1) == 0x90, "p1 must be at offset 0x90 for CCLogStream ABI"); #define super IO80211Controller OSDefineMetaClassAndStructors(QualcommWifi, IO80211Controller) bool QualcommWifi::init(OSDictionary *dictionary) { logNvramStage("init_enter"); if (!super::init(dictionary)) { logNvramStage("init_super_failed"); return false; } logNvramStage("init_super_ok"); fPciDevice = nullptr; fBar0Map = nullptr; fBar0Virt = nullptr; fCurrWindow = 0xFFFFFFFF; fInterruptSource = nullptr; fWorkQueue = nullptr; fNetif = nullptr; // Adresse MAC par défaut (OUI Qualcomm 00:03:7F) en attendant la lecture BHI fMacAddress.bytes[0] = 0x00; fMacAddress.bytes[1] = 0x03; fMacAddress.bytes[2] = 0x7F; fMacAddress.bytes[3] = 0x11; fMacAddress.bytes[4] = 0x07; fMacAddress.bytes[5] = 0x01; fLinkActive = false; fCurrentChannel = 1; fLastRssi = -50; fSsidLen = 0; bzero(fSsid, sizeof(fSsid)); bzero(fBssid, sizeof(fBssid)); fScanInProgress = false; fDmaBuf = nullptr; fDmaPhysAddr = 0; fDmaVirtAddr = nullptr; fDmaPrepared = false; fFirmwareLoaded = false; fFaultReporter = FakeFaultReporter::create(); fLogger = FakeLogStream::create(); return true; } void QualcommWifi::free(void) { OSSafeReleaseNULL(fFaultReporter); OSSafeReleaseNULL(fLogger); OSSafeReleaseNULL(fWorkQueue); super::free(); } IOService *QualcommWifi::probe(IOService *provider, SInt32 *score) { logNvramStage("probe_enter"); if (!super::probe(provider, score)) { logNvramStage("probe_super_failed"); return nullptr; } IOPCIDevice *pciDev = OSDynamicCast(IOPCIDevice, provider); if (!pciDev) { logNvramStage("probe_not_pci"); return nullptr; } UInt16 vendorId = pciDev->configRead16(kIOPCIConfigVendorID); UInt16 deviceId = pciDev->configRead16(kIOPCIConfigDeviceID); if (vendorId == 0x17cb && deviceId == 0x1107) { IOLog("QualcommWifi: Controleur Wi-Fi 7 Qualcomm WCN7850 detecte (0x%04x:0x%04x) !\n", vendorId, deviceId); *score += 20000; logNvramStage("probe_match_ok"); return this; } logNvramStage("probe_no_match"); return nullptr; } UInt32 QualcommWifi::read32(UInt32 offset) { if (!fBar0Virt || offset >= 0x200000) { return 0xFFFFFFFF; } return OSReadLittleInt32(fBar0Virt, offset); } void QualcommWifi::write32(UInt32 offset, UInt32 value) { if (!fBar0Virt || offset >= 0x200000) { return; } OSWriteLittleInt32(fBar0Virt, offset, value); } void QualcommWifi::selectWindow(UInt32 offset) { UInt32 window = (offset >> ATH12K_PCI_WINDOW_SHIFT) & ATH12K_PCI_WINDOW_VALUE_MASK; if (window != fCurrWindow) { write32(ATH12K_PCI_WINDOW_REG_ADDRESS, ATH12K_PCI_WINDOW_ENABLE_BIT | window); (void)read32(ATH12K_PCI_WINDOW_REG_ADDRESS); fCurrWindow = window; } } UInt32 QualcommWifi::socRead32(UInt32 offset) { if (offset < ATH12K_PCI_WINDOW_START) { return read32(offset); } selectWindow(offset); return read32(ATH12K_PCI_WINDOW_START + (offset & ATH12K_PCI_WINDOW_RANGE_MASK)); } void QualcommWifi::socWrite32(UInt32 offset, UInt32 value) { if (offset < ATH12K_PCI_WINDOW_START) { write32(offset, value); return; } selectWindow(offset); write32(ATH12K_PCI_WINDOW_START + (offset & ATH12K_PCI_WINDOW_RANGE_MASK), value); } // Allocation du contexte DMA contigu (36-bit) bool QualcommWifi::allocDmaContext(IOByteCount size) { fDmaBuf = IOBufferMemoryDescriptor::inTaskWithPhysicalMask( kernel_task, static_cast(kIODirectionInOut) | static_cast(kIOMemoryPhysicallyContiguous), size, ATH12K_DMA_MASK_36BIT_PAGE ); if (!fDmaBuf) { IOLog("QualcommWifi: Erreur - Impossible d'allouer le descripteur DMA 36-bit.\n"); return false; } if (fDmaBuf->prepare(kIODirectionInOut) != kIOReturnSuccess) { IOLog("QualcommWifi: Erreur - Impossible de verrouiller (prepare) les pages DMA.\n"); OSSafeReleaseNULL(fDmaBuf); return false; } fDmaPrepared = true; fDmaVirtAddr = fDmaBuf->getBytesNoCopy(); IOByteCount segLen = 0; fDmaPhysAddr = fDmaBuf->getPhysicalSegment(0, &segLen); if (!fDmaVirtAddr || !fDmaPhysAddr || segLen < size) { IOLog("QualcommWifi: Erreur - Segment physique DMA invalide (segLen=%llu).\n", (uint64_t)segLen); freeDmaContext(); return false; } bzero(fDmaVirtAddr, size); IOLog("QualcommWifi: Tampon DMA 36-bit pret ! Phys: 0x%llx, Taille: %llu octets (Contigu: %llu)\n", (uint64_t)fDmaPhysAddr, (uint64_t)size, (uint64_t)segLen); return true; } void QualcommWifi::freeDmaContext(void) { if (fDmaBuf) { if (fDmaPrepared) { fDmaBuf->complete(kIODirectionInOut); fDmaPrepared = false; } OSSafeReleaseNULL(fDmaBuf); } fDmaVirtAddr = nullptr; fDmaPhysAddr = 0; } void QualcommWifi::interruptOccurred(OSObject *owner, IOInterruptEventSource *src, int count) { IOLog("QualcommWifi: Interruption materielle MSI recue (count=%d) !\n", count); } bool QualcommWifi::loadBhiFirmware(void) { if (!fBar0Virt || !fPciDevice) { return false; } UInt32 bhiOff = read32(ATH12K_MHI_BHIOFF); UInt32 bhieOff = read32(ATH12K_MHI_BHIEOFF); if (bhiOff == 0xFFFFFFFF || bhiOff >= 0x200000 || bhieOff == 0xFFFFFFFF || bhieOff >= 0x200000) { IOLog("QualcommWifi: Erreur - BHI/BHIe Offsets invalides (BHI: 0x%08x, BHIe: 0x%08x)\n", bhiOff, bhieOff); setProperty("BHI_Load_Error", "Invalid_Offsets"); return false; } // Désactivation d'ASPM PCIe pour stabiliser le transfert firmware (recommandation ath12k) UInt8 pciCapOffset = 0; if (fPciDevice->findPCICapability(kIOPCIPCIExpressCapability, &pciCapOffset) && pciCapOffset != 0) { UInt16 linkCtrl = fPciDevice->configRead16(pciCapOffset + 0x10); linkCtrl &= ~0x3; // Désactive ASPM L0s / L1 fPciDevice->configWrite16(pciCapOffset + 0x10, linkCtrl); IOLog("QualcommWifi: ASPM PCIe désactivé pour stabilité du transfert firmware (LinkCtrl: 0x%04x)\n", linkCtrl); } // Étape 1 : Réinitialisation MHI et effacement des vecteurs BHIe write32(bhieOff + ATH12K_BHIE_TXVECDB, 0); write32(bhieOff + ATH12K_BHIE_TXVECSTATUS, 0); write32(bhieOff + ATH12K_BHIE_RXVECDB, 0); write32(bhieOff + ATH12K_BHIE_RXVECSTATUS, 0); write32(ATH12K_MHI_MHICTRL, ATH12K_MHICTRL_RESET_MASK); IODelay(10000); // 10 ms size_t fwSize = get_wcn7850_amss_size(); size_t sblSize = 0x80000; // 512 Ko pour le Secondary Boot Loader (SBL) if (fwSize < sblSize) { IOLog("QualcommWifi: Erreur - Taille AMSS trop petite (%zu < 512 Ko)\n", fwSize); return false; } IOLog("QualcommWifi: Initialisation du chargeur FBC 2-étapes (SBL: 512 Ko, Image totale: %zu octets)...\n", fwSize); // Étape 2 : Allocation DMA contigu pour l'image AMSS complète IOBufferMemoryDescriptor *fwDma = IOBufferMemoryDescriptor::inTaskWithPhysicalMask( kernel_task, static_cast(kIODirectionInOut) | static_cast(kIOMemoryPhysicallyContiguous), fwSize, ATH12K_DMA_MASK_36BIT_PAGE ); if (!fwDma || fwDma->prepare(kIODirectionInOut) != kIOReturnSuccess) { IOLog("QualcommWifi: Erreur - Echec allocation/prepare DMA AMSS\n"); setProperty("BHI_Load_Error", "DMA_Alloc_Failed"); if (fwDma) OSSafeReleaseNULL(fwDma); return false; } void *virt = fwDma->getBytesNoCopy(); IOByteCount segLen = 0; IOPhysicalAddress phys = fwDma->getPhysicalSegment(0, &segLen); if (!virt || !phys || segLen < fwSize) { IOLog("QualcommWifi: Erreur - Segment DMA AMSS incomplet\n"); fwDma->complete(kIODirectionInOut); OSSafeReleaseNULL(fwDma); return false; } memcpy(virt, wcn7850_amss_bin, fwSize); setProperty("AMSS_DMA_Phys", (uint64_t)phys, 64); // Étape 3 : Chargement du SBL (512 Ko) via BHI en mode PBL write32(bhiOff + ATH12K_BHI_STATUS, 0); write32(bhiOff + ATH12K_BHI_IMGADDR_LOW, (UInt32)(phys & 0xFFFFFFFF)); write32(bhiOff + ATH12K_BHI_IMGADDR_HIGH, (UInt32)((phys >> 32) & 0xFFFFFFFF)); write32(bhiOff + ATH12K_BHI_IMGSIZE, (UInt32)sblSize); write32(bhiOff + ATH12K_BHI_IMGTXDB, 0x1); IOLog("QualcommWifi: Sonnette BHI_IMGTXDB déclenchée (Session ID = 1, SBL 512 Ko)...\n"); UInt32 bhiStatus = 0; UInt32 timeoutMs = 50; bool sblSuccess = false; while (timeoutMs > 0) { bhiStatus = read32(bhiOff + ATH12K_BHI_STATUS); UInt32 state = (bhiStatus & ATH12K_BHI_STATUS_MASK) >> ATH12K_BHI_STATUS_SHIFT; if (state == ATH12K_BHI_STATUS_SUCCESS) { sblSuccess = true; break; } else if (state == ATH12K_BHI_STATUS_ERROR) { UInt32 err = read32(bhiOff + ATH12K_BHI_ERRCODE); IOLog("QualcommWifi: Erreur BHI SBL (code=0x%08x)\n", err); setProperty("BHI_ErrorCode", err, 32); break; } IODelay(100); timeoutMs--; } setProperty("BHI_FinalStatus", bhiStatus, 32); setProperty("BHI_Status_Raw", bhiStatus, 32); setProperty("BHI_Status_State", (bhiStatus & ATH12K_BHI_STATUS_MASK) >> ATH12K_BHI_STATUS_SHIFT, 32); timeoutMs = 50; UInt32 ee = 0; while (timeoutMs > 0) { ee = read32(bhiOff + ATH12K_BHI_EXECENV); UInt32 mhiStat = read32(ATH12K_MHI_MHISTATUS); if (ee == ATH12K_MHI_EE_SBL || ee == ATH12K_MHI_EE_AMSS || (mhiStat & ATH12K_MHISTATUS_READY_MASK)) { break; } IODelay(100); timeoutMs--; } setProperty("BHI_ExecEnv_AfterLoad", ee, 32); setProperty("MHI_Status_After_SBL", read32(ATH12K_MHI_MHISTATUS), 32); setProperty("MHI_Ctrl_After_SBL", read32(ATH12K_MHI_MHICTRL), 32); IOLog("QualcommWifi: SBL BHI terminé (Status=0x%08x, State=%u, ExecEnv=0x%x, SBL_Succes=%d)\n", bhiStatus, (bhiStatus & ATH12K_BHI_STATUS_MASK) >> ATH12K_BHI_STATUS_SHIFT, ee, sblSuccess); // Étape 4 : Configuration des contextes MHI MMIO et transition vers M0 bool inM0 = false; bool amssSuccess = false; uint32_t processedEvents = 0; if (sblSuccess && fDmaPhysAddr != 0) { timeoutMs = 50; while (timeoutMs > 0) { UInt32 mstat = read32(ATH12K_MHI_MHISTATUS); if (mstat & ATH12K_MHISTATUS_READY_MASK) { break; } IODelay(100); timeoutMs--; } uint64_t chan_ctxt_phys = fDmaPhysAddr + 0x0000; uint64_t er_ctxt_phys = fDmaPhysAddr + 0x1000; uint64_t cmd_ctxt_phys = fDmaPhysAddr + 0x2000; uint64_t cmd_ring_phys = fDmaPhysAddr + 0x3000; uint64_t er0_ring_phys = fDmaPhysAddr + 0x4000; uint64_t er1_ring_phys = fDmaPhysAddr + 0x5000; uint32_t cmd_ring_len = 64 * sizeof(struct mhi_ring_element); uint32_t er0_ring_len = 32 * sizeof(struct mhi_ring_element); uint32_t er1_ring_len = 256 * sizeof(struct mhi_ring_element); struct mhi_chan_ctxt *chan_ctxt = (struct mhi_chan_ctxt *)((uint8_t *)fDmaVirtAddr + 0x0000); bzero(chan_ctxt, 64 * sizeof(struct mhi_chan_ctxt)); struct mhi_cmd_ctxt *cmd_ctxt = (struct mhi_cmd_ctxt *)((uint8_t *)fDmaVirtAddr + 0x2000); bzero(cmd_ctxt, sizeof(struct mhi_cmd_ctxt)); cmd_ctxt[0].rbase = OSSwapHostToLittleInt64(cmd_ring_phys); cmd_ctxt[0].rlen = OSSwapHostToLittleInt64(cmd_ring_len); cmd_ctxt[0].rp = OSSwapHostToLittleInt64(cmd_ring_phys); cmd_ctxt[0].wp = OSSwapHostToLittleInt64(cmd_ring_phys); struct mhi_event_ctxt *evCtxt = (struct mhi_event_ctxt *)((uint8_t *)fDmaVirtAddr + 0x1000); bzero(evCtxt, 2 * sizeof(struct mhi_event_ctxt)); evCtxt[0].intmod = 0; evCtxt[0].ertype = OSSwapHostToLittleInt32(1); evCtxt[0].msivec = 0; evCtxt[0].rbase = OSSwapHostToLittleInt64(er0_ring_phys); evCtxt[0].rlen = OSSwapHostToLittleInt64(er0_ring_len); evCtxt[0].rp = OSSwapHostToLittleInt64(er0_ring_phys); evCtxt[0].wp = OSSwapHostToLittleInt64(er0_ring_phys); evCtxt[1].intmod = OSSwapHostToLittleInt32(1); evCtxt[1].ertype = OSSwapHostToLittleInt32(1); evCtxt[1].msivec = 0; evCtxt[1].rbase = OSSwapHostToLittleInt64(er1_ring_phys); evCtxt[1].rlen = OSSwapHostToLittleInt64(er1_ring_len); evCtxt[1].rp = OSSwapHostToLittleInt64(er1_ring_phys); evCtxt[1].wp = OSSwapHostToLittleInt64(er1_ring_phys); bzero((uint8_t *)fDmaVirtAddr + 0x3000, cmd_ring_len); bzero((uint8_t *)fDmaVirtAddr + 0x4000, er0_ring_len); bzero((uint8_t *)fDmaVirtAddr + 0x5000, er1_ring_len); write32(ATH12K_MHI_CCABAP_HIGHER, (UInt32)((chan_ctxt_phys >> 32) & 0xFFFFFFFF)); write32(ATH12K_MHI_CCABAP_LOWER, (UInt32)(chan_ctxt_phys & 0xFFFFFFFF)); write32(ATH12K_MHI_ECABAP_HIGHER, (UInt32)((er_ctxt_phys >> 32) & 0xFFFFFFFF)); write32(ATH12K_MHI_ECABAP_LOWER, (UInt32)(er_ctxt_phys & 0xFFFFFFFF)); write32(ATH12K_MHI_CRCBAP_HIGHER, (UInt32)((cmd_ctxt_phys >> 32) & 0xFFFFFFFF)); write32(ATH12K_MHI_CRCBAP_LOWER, (UInt32)(cmd_ctxt_phys & 0xFFFFFFFF)); write32(ATH12K_MHI_MHICFG, (2 << 16) | (0 << 24)); write32(ATH12K_MHI_MHICTRLBASE_HIGHER, 0); write32(ATH12K_MHI_MHICTRLBASE_LOWER, 0); write32(ATH12K_MHI_MHIDATABASE_HIGHER, 0); write32(ATH12K_MHI_MHIDATABASE_LOWER, 0); write32(ATH12K_MHI_MHICTRLLIMIT_HIGHER, 0x0F); write32(ATH12K_MHI_MHICTRLLIMIT_LOWER, 0xFFFFFFFF); write32(ATH12K_MHI_MHIDATALIMIT_HIGHER, 0x0F); write32(ATH12K_MHI_MHIDATALIMIT_LOWER, 0xFFFFFFFF); uint64_t er0_armed_wp = er0_ring_phys + er0_ring_len - sizeof(struct mhi_ring_element); evCtxt[0].wp = OSSwapHostToLittleInt64(er0_armed_wp); uint64_t er1_armed_wp = er1_ring_phys + er1_ring_len - sizeof(struct mhi_ring_element); evCtxt[1].wp = OSSwapHostToLittleInt64(er1_armed_wp); UInt32 erdbOff = read32(ATH12K_MHI_ERDBOFF); if (erdbOff != 0 && erdbOff < 0x200000) { write32(erdbOff + 4, (UInt32)((er0_armed_wp >> 32) & 0xFFFFFFFF)); write32(erdbOff + 0, (UInt32)(er0_armed_wp & 0xFFFFFFFF)); write32(erdbOff + 8 + 4, (UInt32)((er1_armed_wp >> 32) & 0xFFFFFFFF)); write32(erdbOff + 8 + 0, (UInt32)(er1_armed_wp & 0xFFFFFFFF)); IOLog("QualcommWifi: Event Ring Doorbells armées (ER0 @ 0x%08x WP=0x%llx, ER1 @ 0x%08x WP=0x%llx) !\n", erdbOff, er0_armed_wp, erdbOff + 8, er1_armed_wp); setProperty("ERDB_Offset", erdbOff, 32); } UInt32 mhiCtrlVal = read32(ATH12K_MHI_MHICTRL); mhiCtrlVal &= ~(UInt32)ATH12K_MHICTRL_MHISTATE_MASK; mhiCtrlVal |= (ATH12K_MHI_STATE_M0 << ATH12K_MHICTRL_MHISTATE_SHIFT); write32(ATH12K_MHI_MHICTRL, mhiCtrlVal); IOLog("QualcommWifi: Transition MHI vers M0 demandée (MHICTRL: 0x%08x)...\n", mhiCtrlVal); } // Étape 5 : Téléversement FBC complet du firmware via BHIe const size_t segSize = 0x80000; size_t numVecEntries = (fwSize + segSize - 1) / segSize; IOBufferMemoryDescriptor *vecDma = IOBufferMemoryDescriptor::inTaskWithPhysicalMask( kernel_task, static_cast(kIODirectionInOut) | static_cast(kIOMemoryPhysicallyContiguous), numVecEntries * sizeof(struct bhi_vec_entry), ATH12K_DMA_MASK_36BIT_PAGE ); if (vecDma) { if (vecDma->prepare(kIODirectionInOut) == kIOReturnSuccess) { struct bhi_vec_entry *vecTable = (struct bhi_vec_entry *)vecDma->getBytesNoCopy(); IOByteCount vecLen = 0; IOPhysicalAddress vecPhys = vecDma->getPhysicalSegment(0, &vecLen); if (vecTable && vecPhys) { size_t curRem = fwSize; for (size_t i = 0; i < numVecEntries; i++) { size_t chunk = (curRem > segSize) ? segSize : curRem; vecTable[i].dma_addr = OSSwapHostToLittleInt64((uint64_t)phys + (uint64_t)i * segSize); vecTable[i].size = OSSwapHostToLittleInt64(chunk); curRem -= chunk; } IOLog("QualcommWifi: Table vectorielle BHIe FBC prête (%zu segments, Phys: 0x%llx). Programmation BHIe...\n", numVecEntries, (uint64_t)vecPhys); write32(bhieOff + ATH12K_BHIE_TXVECADDR_LOW, (UInt32)(vecPhys & 0xFFFFFFFF)); write32(bhieOff + ATH12K_BHIE_TXVECADDR_HIGH, (UInt32)((vecPhys >> 32) & 0xFFFFFFFF)); write32(bhieOff + ATH12K_BHIE_TXVECSIZE, (UInt32)(numVecEntries * sizeof(struct bhi_vec_entry))); write32(bhieOff + ATH12K_BHIE_TXVECSTATUS, 0); write32(bhieOff + ATH12K_BHIE_TXVECDB, 0x1); UInt32 bhieStatus = 0; timeoutMs = 50; while (timeoutMs > 0) { bhieStatus = read32(bhieOff + ATH12K_BHIE_TXVECSTATUS); UInt32 bhieState = (bhieStatus & ATH12K_BHIE_TXVECSTATUS_STATUS_MASK) >> ATH12K_BHIE_TXVECSTATUS_STATUS_SHIFT; if (bhieState == ATH12K_BHIE_TXVECSTATUS_XFER_COMPL) { amssSuccess = true; break; } IODelay(100); timeoutMs--; } setProperty("BHIE_FinalStatus", bhieStatus, 32); setProperty("BHIE_Status_Raw", bhieStatus, 32); setProperty("BHIE_Status_State", (bhieStatus & ATH12K_BHIE_TXVECSTATUS_STATUS_MASK) >> ATH12K_BHIE_TXVECSTATUS_STATUS_SHIFT, 32); IOLog("QualcommWifi: BHIe DMA terminé (Status=0x%08x) ! Dépouillement des événements MHI...\n", bhieStatus); // Étape 6 : Dépouillement des événements MHI struct mhi_ring_element *erElements = (struct mhi_ring_element *)((uint8_t *)fDmaVirtAddr + 0x4000); timeoutMs = 50; while (timeoutMs > 0) { UInt32 mstat = read32(ATH12K_MHI_MHISTATUS); UInt32 mstate = (mstat & ATH12K_MHISTATUS_MHISTATE_MASK) >> ATH12K_MHISTATUS_MHISTATE_SHIFT; while (processedEvents < 32) { struct mhi_ring_element *el = &erElements[processedEvents]; UInt32 dw1 = OSSwapLittleToHostInt32(el->dword[1]); UInt32 dw0 = OSSwapLittleToHostInt32(el->dword[0]); if (dw1 == 0 && dw0 == 0) { break; } UInt8 evType = (dw1 >> 16) & 0xFF; UInt8 evCode = (dw0 >> 24) & 0xFF; IOLog("QualcommWifi: Événement MHI reçu #%u: Type=0x%02x, Code=0x%02x, PTR=0x%llx\n", processedEvents, evType, evCode, OSSwapLittleToHostInt64(el->ptr)); if (evType == ATH12K_MHI_PKT_TYPE_STATE_CHANGE_EVENT && evCode == ATH12K_MHI_STATE_M0) { inM0 = true; } else if (evType == ATH12K_MHI_PKT_TYPE_EE_EVENT && (evCode == ATH12K_MHI_EE_AMSS || evCode == ATH12K_MHI_EE_WFW)) { ee = evCode; amssSuccess = true; } processedEvents++; } if (mstate == ATH12K_MHI_STATE_M0 || inM0 || ee == ATH12K_MHI_EE_AMSS || ee == ATH12K_MHI_EE_WFW) { inM0 = true; IOLog("QualcommWifi: MHI en mode Mission M0 (MHISTATUS: 0x%08x, EE: 0x%x) !\n", mstat, ee); break; } IODelay(100); timeoutMs--; } setProperty("MHI_Events_Processed", processedEvents, 32); setProperty("MHI_Status_In_M0", read32(ATH12K_MHI_MHISTATUS), 32); setProperty("MHI_In_M0", inM0); setProperty("BHI_ExecEnv_Final", ee, 32); } vecDma->complete(kIODirectionInOut); } OSSafeReleaseNULL(vecDma); } if (inM0 || ee == ATH12K_MHI_EE_AMSS || ee == ATH12K_MHI_EE_WFW) { IOLog("QualcommWifi: SUCCÈS MAJEUR ! Firmware WCN7850 actif en mode Mission (ExecEnv=0x%x, M0=%d) !\n", ee, inM0); setProperty("Driver_Status", "AMSS_Loaded_Success"); } else if (sblSuccess) { IOLog("QualcommWifi: SBL actif (ExecEnv=0x%x, BHIe=0x%x)\n", ee, read32(bhieOff + ATH12K_BHIE_TXVECSTATUS)); setProperty("Driver_Status", "SBL_Loaded_Success"); } else { IOLog("QualcommWifi: Échec chargement complet (BHI=0x%x, ExecEnv=0x%x)\n", bhiStatus, ee); setProperty("Driver_Status", "AMSS_Load_Timeout_Or_Error"); } fwDma->complete(kIODirectionInOut); OSSafeReleaseNULL(fwDma); return (sblSuccess || amssSuccess); } bool QualcommWifi::start(IOService *provider) { logNvramStage("start_enter"); IOLog("QualcommWifi: Demarrage du pilote Wi-Fi natif (IO80211Controller)...\n"); if (!createWorkQueue()) { logNvramStage("workQueue_failed"); IOLog("QualcommWifi: Erreur - Impossible de creer IO80211WorkQueue !\n"); return false; } if (!super::start(provider)) { logNvramStage("super_start_failed"); IOLog("QualcommWifi: Erreur - IO80211Controller::start(provider) a echoue !\n"); return false; } logNvramStage("super_start_ok"); fPciDevice = OSDynamicCast(IOPCIDevice, provider); if (!fPciDevice) { logNvramStage("start_no_pci"); return false; } fPciDevice->retain(); logNvramStage("pci_open"); if (!fPciDevice->open(this)) { logNvramStage("pci_open_failed"); OSSafeReleaseNULL(fPciDevice); return false; } logNvramStage("pci_open_ok"); // La gestion de l'espace mémoire PCIe et du Bus Master est gérée automatiquement par IOKit // (Éviter les écritures de configuration brutes risquant de déstabiliser le root port AMD) logNvramStage("pci_ready"); int allow_mmio = 0; PE_parse_boot_argn("qcom_mmio", &allow_mmio, sizeof(allow_mmio)); UInt32 bhiSerial = 0x123456; if (allow_mmio) { logNvramStage("bar0_map"); fBar0Map = fPciDevice->mapDeviceMemoryWithRegister(kIOPCIConfigBaseAddress0); if (fBar0Map) { fBar0Virt = (volatile void *)fBar0Map->getVirtualAddress(); logNvramStage("read_mhi"); UInt32 mhiStatus = read32(ATH12K_MHI_MHISTATUS); UInt32 mhiCtrl = read32(ATH12K_MHI_MHICTRL); UInt32 bhiOff = read32(ATH12K_MHI_BHIOFF); UInt32 bhieOff = read32(ATH12K_MHI_BHIEOFF); setProperty("MHI_Status", mhiStatus, 32); setProperty("MHI_Ctrl", mhiCtrl, 32); setProperty("BHI_Offset", bhiOff, 32); setProperty("BHIE_Offset", bhieOff, 32); if (bhiOff != 0xFFFFFFFF && bhiOff > 0 && bhiOff < 0x200000) { logNvramStage("read_bhi"); UInt32 bhiMajor = read32(bhiOff + ATH12K_BHI_BHIVERSION_MAJOR); UInt32 bhiMinor = read32(bhiOff + ATH12K_BHI_BHIVERSION_MINOR); UInt32 bhiEe = read32(bhiOff + ATH12K_BHI_EXECENV); UInt32 bhiStat = read32(bhiOff + ATH12K_BHI_STATUS); bhiSerial = read32(bhiOff + ATH12K_BHI_SERIALNU); setProperty("BHI_Major", bhiMajor, 32); setProperty("BHI_Minor", bhiMinor, 32); setProperty("BHI_ExecEnv", bhiEe, 32); setProperty("BHI_Status", bhiStat, 32); setProperty("BHI_Serial", bhiSerial, 32); } } } else { logNvramStage("safe_mode_active"); setProperty("QualcommWifi_SafeMode", "Active"); } // Configuration de l'adresse MAC (OUI Qualcomm 00:03:7F + BHI serial) fMacAddress.bytes[0] = 0x00; fMacAddress.bytes[1] = 0x03; fMacAddress.bytes[2] = 0x7F; fMacAddress.bytes[3] = (bhiSerial >> 16) & 0xFF; fMacAddress.bytes[4] = (bhiSerial >> 8) & 0xFF; fMacAddress.bytes[5] = (bhiSerial) & 0xFF; IOLog("QualcommWifi: Adresse MAC physique : %02x:%02x:%02x:%02x:%02x:%02x\n", fMacAddress.bytes[0], fMacAddress.bytes[1], fMacAddress.bytes[2], fMacAddress.bytes[3], fMacAddress.bytes[4], fMacAddress.bytes[5]); // Propriétés Wi-Fi AirPort pour macOS et airportd logNvramStage("set_airport_props"); setProperty("IO80211AirPort", true); setProperty("AirPort", true); setProperty("IO80211Locale", "ETSI"); setProperty("IO80211CountryCode", "FR"); setProperty("IO80211ScanCache", true); setProperty("IO80211RadioState", (UInt64)1, 32); // Configuration et publication des médiums réseau (IEEE 802.11) logNvramStage("pub_mediums"); OSDictionary *mediumDict = OSDictionary::withCapacity(2); if (mediumDict) { IONetworkMedium *medium = IONetworkMedium::medium(kIOMediumIEEE80211, 54000000); if (medium) { IONetworkMedium::addMedium(mediumDict, medium); publishMediumDictionary(mediumDict); setCurrentMedium(medium); setSelectedMedium(medium); medium->release(); } mediumDict->release(); } // Enregistrement IOKit du contrôleur Wi-Fi natif (déclenche le matching airportd) logNvramStage("register_service"); registerService(); logNvramStage("start_done"); IOLog("QualcommWifi: Pilote Wi-Fi natif (IO80211Controller) demarre avec succes !\n"); return true; } void QualcommWifi::stop(IOService *provider) { IOLog("QualcommWifi: Arret du pilote Wi-Fi natif, liberation MSI, DMA et BAR0.\n"); IOWorkLoop *workLoop = getWorkLoop(); if (fInterruptSource) { fInterruptSource->disable(); if (workLoop) { workLoop->removeEventSource(fInterruptSource); } OSSafeReleaseNULL(fInterruptSource); } freeDmaContext(); fBar0Virt = nullptr; if (fBar0Map) { OSSafeReleaseNULL(fBar0Map); } if (fPciDevice) { fPciDevice->close(this); OSSafeReleaseNULL(fPciDevice); } super::stop(provider); } bool QualcommWifi::createWorkQueue(void) { logNvramStage("createWorkQueue"); if (fWorkQueue) { return true; } fWorkQueue = IO80211WorkQueue::workQueue(); if (!fWorkQueue) { logNvramStage("workQueue_failed"); IOLog("QualcommWifi: Erreur - Impossible d'allouer IO80211WorkQueue !\n"); return false; } logNvramStage("workQueue_ok"); IOLog("QualcommWifi: IO80211WorkQueue allouee avec succes (0x%p) !\n", fWorkQueue); return true; } IO80211WorkQueue* QualcommWifi::getWorkQueue(void) const { return fWorkQueue; } bool QualcommWifi::createWorkLoop(void) { return createWorkQueue(); } IOWorkLoop* QualcommWifi::getWorkLoop(void) const { return getWorkQueue(); } IOReturn QualcommWifi::getHardwareAddress(IOEthernetAddress *addrP) { logNvramStage("getHardwareAddress"); if (!addrP) return kIOReturnBadArgument; bcopy(&fMacAddress, addrP, sizeof(IOEthernetAddress)); return kIOReturnSuccess; } IOReturn QualcommWifi::enable(IONetworkInterface *netif) { logNvramStage("netif_enable"); IOLog("QualcommWifi: Interface reseau activee par macOS.\n"); const IONetworkMedium *medium = getSelectedMedium(); setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, medium); return kIOReturnSuccess; } IOReturn QualcommWifi::disable(IONetworkInterface *netif) { logNvramStage("netif_disable"); IOLog("QualcommWifi: Interface reseau desactivee par macOS.\n"); setLinkStatus(kIONetworkLinkValid, nullptr); return kIOReturnSuccess; } UInt32 QualcommWifi::outputPacket(mbuf_t m, void *param) { if (m) { freePacket(m); } return kIOReturnOutputDropped; } void QualcommWifi::dataLinkLayerAttachComplete(void) { logNvramStage("dataLinkAttachDone"); IOLog("QualcommWifi: dataLinkLayerAttachComplete (Skywalk Tahoe).\n"); } IOReturn QualcommWifi::handleCardSpecific(IO80211SkywalkInterface *iface, unsigned long req_type, void *req_data, bool isSet) { return (IOReturn)handleApple80211Request((unsigned int)req_type, isSet ? 1 : 0, req_data); } void* QualcommWifi::getLogger(void) const { return (void*)fLogger; } void* QualcommWifi::getFaultReporterFromDriver(void) { return (void*)fFaultReporter; } UInt32 QualcommWifi::getActionFramePoolCapacity(void) { return 0x100; } IOReturn QualcommWifi::getDRIVER_VERSION(IO80211SkywalkInterface *iface, apple80211_version_data *vd) { return (IOReturn)handleApple80211Request(APPLE80211_IOC_DRIVER_VERSION, 0, vd); } IOReturn QualcommWifi::getHARDWARE_VERSION(IO80211SkywalkInterface *iface, apple80211_version_data *vd) { return (IOReturn)handleApple80211Request(APPLE80211_IOC_HARDWARE_VERSION, 0, vd); } IOReturn QualcommWifi::getCARD_CAPABILITIES(IO80211SkywalkInterface *iface, apple80211_capability_data *cap) { return (IOReturn)handleApple80211Request(APPLE80211_IOC_CARD_CAPABILITIES, 0, cap); } IOReturn QualcommWifi::getPOWER(IO80211SkywalkInterface *iface, apple80211_power_data *pwr) { return (IOReturn)handleApple80211Request(APPLE80211_IOC_POWER, -1, pwr); } IOReturn QualcommWifi::setPOWER(IO80211SkywalkInterface *iface, apple80211_power_data *pwr) { logNvramStage("setPOWER"); int val = (pwr && pwr->power_state[0]) ? 1 : 0; fLinkActive = (val == 1); IOLog("QualcommWifi: setPOWER invoqué par airportd (val=%d, fLinkActive=%d)\n", val, fLinkActive); return (IOReturn)handleApple80211Request(APPLE80211_IOC_POWER, val, pwr); } IOReturn QualcommWifi::getCOUNTRY_CODE(IO80211SkywalkInterface *iface, apple80211_country_code_data *cc) { return (IOReturn)handleApple80211Request(APPLE80211_IOC_COUNTRY_CODE, 0, cc); } SInt32 QualcommWifi::handleApple80211Request(unsigned int req_type, int req_val, void* req_data) { char iocTag[32]; snprintf(iocTag, sizeof(iocTag), "ioc_%u", req_type); logNvramStage(iocTag); switch (req_type) { case APPLE80211_IOC_CARD_CAPABILITIES: { apple80211_capability_data *cap = (apple80211_capability_data *)req_data; if (cap) { bzero(cap, sizeof(*cap)); cap->version = APPLE80211_VERSION; #define SET_CAP(c) (cap->capabilities[(c) / 8] |= (1 << ((c) % 8))) SET_CAP(APPLE80211_CAP_WEP); SET_CAP(APPLE80211_CAP_TKIP); SET_CAP(APPLE80211_CAP_AES_CCM); SET_CAP(APPLE80211_CAP_WPA1); SET_CAP(APPLE80211_CAP_WPA2); SET_CAP(APPLE80211_CAP_WPA); SET_CAP(APPLE80211_CAP_SHORT_GI_20MHZ); SET_CAP(APPLE80211_CAP_SHORT_GI_40MHZ); SET_CAP(APPLE80211_CAP_BURST); SET_CAP(APPLE80211_CAP_WME); SET_CAP(APPLE80211_CAP_PMGT); SET_CAP(APPLE80211_CAP_SHPREAMBLE); SET_CAP(APPLE80211_CAP_SHSLOT); #undef SET_CAP return 0; } break; } case APPLE80211_IOC_STATE: { apple80211_state_data *state = (apple80211_state_data *)req_data; if (state) { state->version = APPLE80211_VERSION; state->state = fLinkActive ? APPLE80211_S_RUN : APPLE80211_S_INIT; return 0; } break; } case APPLE80211_IOC_PHY_MODE: { apple80211_phymode_data *pm = (apple80211_phymode_data *)req_data; if (pm) { pm->version = APPLE80211_VERSION; pm->phy_mode = APPLE80211_MODE_11AX; pm->active_phy_mode = APPLE80211_MODE_11AX; return 0; } break; } case APPLE80211_IOC_OP_MODE: { apple80211_opmode_data *om = (apple80211_opmode_data *)req_data; if (om) { om->version = APPLE80211_VERSION; om->op_mode = APPLE80211_M_STA; return 0; } break; } case APPLE80211_IOC_CHANNEL: { apple80211_channel_data *cd = (apple80211_channel_data *)req_data; if (cd) { if (req_val == 1 && cd->channel.channel > 0) { fCurrentChannel = (UInt16)cd->channel.channel; IOLog("QualcommWifi: Changement de canal Wi-Fi vers %u\n", fCurrentChannel); } cd->version = APPLE80211_VERSION; cd->channel.version = APPLE80211_VERSION; cd->channel.channel = fCurrentChannel > 0 ? fCurrentChannel : 1; cd->channel.flags = (cd->channel.channel <= 14) ? (APPLE80211_C_FLAG_2GHZ | APPLE80211_C_FLAG_20MHZ) : (APPLE80211_C_FLAG_5GHZ | APPLE80211_C_FLAG_80MHZ); return 0; } break; } case APPLE80211_IOC_TXPOWER: { apple80211_txpower_data *tx = (apple80211_txpower_data *)req_data; if (tx) { tx->version = APPLE80211_VERSION; tx->txpower_unit = APPLE80211_UNIT_DBM; tx->txpower = 20; // 20 dBm (100 mW) return 0; } break; } case APPLE80211_IOC_PROTMODE: case APPLE80211_IOC_POWERSAVE: case APPLE80211_IOC_SCANCACHE_CLEAR: { return 0; } case APPLE80211_IOC_RSSI: { apple80211_rssi_data *rd = (apple80211_rssi_data *)req_data; if (rd) { rd->version = APPLE80211_VERSION; rd->num_radios = 1; rd->rssi_unit = APPLE80211_UNIT_DBM; rd->rssi[0] = fLastRssi ? fLastRssi : -50; rd->aggregate_rssi = rd->rssi[0]; return 0; } break; } case APPLE80211_IOC_NOISE: { apple80211_noise_data *nd = (apple80211_noise_data *)req_data; if (nd) { nd->version = APPLE80211_VERSION; nd->num_radios = 1; nd->noise_unit = APPLE80211_UNIT_DBM; nd->noise[0] = -95; nd->aggregate_noise = -95; return 0; } break; } case APPLE80211_IOC_SSID: { apple80211_ssid_data *sd = (apple80211_ssid_data *)req_data; if (sd) { sd->version = APPLE80211_VERSION; sd->ssid_len = fSsidLen; memcpy(sd->ssid_bytes, fSsid, fSsidLen); return 0; } break; } case APPLE80211_IOC_BSSID: { apple80211_bssid_data *bd = (apple80211_bssid_data *)req_data; if (bd) { bd->version = APPLE80211_VERSION; memcpy(bd->bssid.octet, fBssid, 6); return 0; } break; } case APPLE80211_IOC_RATE: { apple80211_rate_data *rate = (apple80211_rate_data *)req_data; if (rate) { rate->version = APPLE80211_VERSION; rate->num_radios = 1; rate->rate[0] = 1200; return 0; } break; } case APPLE80211_IOC_SUPPORTED_CHANNELS: { apple80211_sup_channel_data *sc = (apple80211_sup_channel_data *)req_data; if (sc) { sc->version = APPLE80211_VERSION; static const UInt16 chs[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165 }; sc->num_channels = sizeof(chs)/sizeof(chs[0]); for (int i = 0; i < sc->num_channels; i++) { sc->supported_channels[i].version = APPLE80211_VERSION; sc->supported_channels[i].channel = chs[i]; sc->supported_channels[i].flags = (chs[i] <= 14) ? (APPLE80211_C_FLAG_2GHZ | APPLE80211_C_FLAG_20MHZ) : (APPLE80211_C_FLAG_5GHZ | APPLE80211_C_FLAG_80MHZ); } return 0; } break; } case APPLE80211_IOC_SCAN_REQ: case APPLE80211_IOC_SCAN_REQ_MULTIPLE: { IOLog("QualcommWifi: Requete de scan Wi-Fi recue depuis airportd !\n"); fScanInProgress = true; return 0; } case APPLE80211_IOC_SCAN_RESULT: { if (req_data) { *(void **)req_data = nullptr; } fScanInProgress = false; return 0; } case APPLE80211_IOC_POWER: { apple80211_power_data *pwr = (apple80211_power_data *)req_data; if (pwr) { pwr->version = APPLE80211_VERSION; pwr->num_radios = 1; if (req_val == 0 || req_val == 1) { fLinkActive = (req_val == 1); IOLog("QualcommWifi: Bascule alimentation radio Wi-Fi : %s\n", fLinkActive ? "ACTIVE" : "COUPEE"); } pwr->power_state[0] = fLinkActive ? 1 : 0; return 0; } break; } case APPLE80211_IOC_COUNTRY_CODE: { apple80211_country_code_data *cc = (apple80211_country_code_data *)req_data; if (cc) { cc->version = APPLE80211_VERSION; cc->cc[0] = 'F'; cc->cc[1] = 'R'; cc->cc[2] = 0; return 0; } break; } case APPLE80211_IOC_LOCALE: { apple80211_locale_data *loc = (apple80211_locale_data *)req_data; if (loc) { loc->version = APPLE80211_VERSION; loc->locale = APPLE80211_LOCALE_ETSI; return 0; } break; } case APPLE80211_IOC_DRIVER_VERSION: case APPLE80211_IOC_HARDWARE_VERSION: { apple80211_version_data *vd = (apple80211_version_data *)req_data; if (vd) { vd->version = APPLE80211_VERSION; const char *verStr = "Qualcomm FastConnect 7800 Wi-Fi 7 (v1.1.0)"; vd->string_len = (u_int16_t)strlen(verStr); strlcpy(vd->string, verStr, sizeof(vd->string)); return 0; } break; } case APPLE80211_IOC_ASSOCIATE: { apple80211_assoc_data *assoc = (apple80211_assoc_data *)req_data; if (assoc) { IOLog("QualcommWifi: Demande d'association au reseau Wi-Fi : %.*s\n", assoc->ad_ssid_len, assoc->ad_ssid); fSsidLen = (assoc->ad_ssid_len < APPLE80211_MAX_SSID_LEN) ? assoc->ad_ssid_len : APPLE80211_MAX_SSID_LEN; memcpy(fSsid, assoc->ad_ssid, fSsidLen); memcpy(fBssid, assoc->ad_bssid.octet, 6); fLinkActive = true; return 0; } break; } case APPLE80211_IOC_DISASSOCIATE: { IOLog("QualcommWifi: Deconnexion Wi-Fi demandee par macOS.\n"); fLinkActive = false; fSsidLen = 0; return 0; } default: break; } return 0; }