Files
QualcommWireless/QualcommWifi/QualcommWifi.cpp
T
Erwan 0988463366 fix(wifi): sécuriser logNvramStage sans locks IORegistry et découper l'étape 6 en 3 pauses balisées
- Breakthroughs & Rétro-ingénierie :
  * Analyse du crash survenu au seuil de l'Étape 6 immédiatement après la pause visuelle de 10s sous qcom_step=6 qcom_substep=1.
  * Identification d'une cause majeure de panique/deadlock sous Power Management : logNvramStage() interrogeait le plan du Device Tree IOKit via IORegistryEntry::fromPath("/options", gIODTPlane). Durant IO80211SkywalkInterface::start(), macOS enchaîne PMinit(), joinPMtree(this), registerPowerDriver() puis changePowerStateToPriv(1) et changePowerStateTo(0). L'exécution de setPowerState() sous verrou PM tout en tentant d'acquérir le verrou gIODTPlane engendre un risque critique de deadlock ou de faute d'accès en ring 0.
  * Résolution : bascule exclusive de logNvramStage() sur PEWriteNVRAMProperty() (Platform Expert direct, aucun verrou de plan IORegistry).
  * Résolution de l'écrasement silencieux de diagnostic : lors du reboot sous qcom_step=5, QualcommWifi::init() et start() s'exécutaient avec succès et écrasaient qcom_stage par 'start_done'. Implémentation d'une détection dans init() qui recopie automatiquement qcom_stage vers qcom_last_crash et le nouveau fil d'Ariane continu qcom_trail vers qcom_last_trail.
  * Découpage de l'Étape 6 en 3 pauses d'observation sous qcom_substep>=1 : Pause 1/3 (3s avant start(this)), Pause 2/3 (5s après start(this) réussi avec bannière [SUCCES MAJEUR]), et Pause 3/3 (3s avant de terminer QualcommWifi::start()).

- Modifications apportées :
  * QualcommWifi/QualcommWifi.cpp :
    - Élimination des locks IORegistry dans logNvramStage(), maintien du fil d'Ariane global gNvramTrail.
    - Ajout dans init() de la lecture non volatile de qcom_stage / qcom_trail et sauvegarde vers qcom_last_crash / qcom_last_trail.
    - Séquençage en 3 pauses visuelles balisées pour l'Étape 6.
  * docs/ISSUES_AND_POSTMORTEMS.md : rédaction détaillée du Postmortem 18.
  * EFI OpenCore : déploiement du binaire recompilé dans /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext et configuration boot-args qcom_step=6 qcom_substep=1 dans config.plist.

- Validation :
  * Compilation sans aucun avertissement via ./build_wifi.sh.
  * kmutil libraries -p build/Release/QualcommWifi.kext validé à 100% contre BootKernelExtensions.kc.
  * Déploiement vérifié sur /Volumes/EFI/EFI/OC/.
2026-10-19 10:07:13 +02:00

1413 lines
58 KiB
C++

#include "QualcommWifi.hpp"
#include <IOKit/IOLib.h>
#include <libkern/OSByteOrder.h>
#include <kern/task.h>
#include <string.h>
// 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 <pexpert/pexpert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IOLocks.h>
// Buffer pour le fil d'Ariane persistant (chaînage des balises d'étapes sans verrous)
static char gNvramTrail[512] = {0};
static int sGetWorkQueueCount = 0;
static inline void logNvramStage(const char *tag) {
if (!tag) return;
IOLog("QualcommWifi: >>> [STAGE] %s <<<\n", tag);
PEWriteNVRAMProperty("qcom_stage", tag, (unsigned int)strlen(tag));
// Mise à jour continue du fil d'Ariane
if (gNvramTrail[0] != '\0') {
strlcat(gNvramTrail, ">", sizeof(gNvramTrail));
}
strlcat(gNvramTrail, tag, sizeof(gNvramTrail));
PEWriteNVRAMProperty("qcom_trail", gNvramTrail, (unsigned int)strlen(gNvramTrail));
}
// Classe simulant un FaultReporter IOKit valide (attendu par IO80211Controller::findAndAttachToFaultReporter)
// Conforme à l'ABI CCFaultReporter / IOService (267 slots de vtable, taille 0x90)
class FakeFaultReporter : public IOService {
OSDeclareDefaultStructors(FakeFaultReporter)
private:
uint8_t _pad[0x90 - sizeof(IOService)];
public:
virtual bool init() override {
if (!IOService::init()) return false;
bzero(_pad, sizeof(_pad));
return true;
}
static FakeFaultReporter* create() {
FakeFaultReporter *inst = new FakeFaultReporter;
if (inst && !inst->init()) {
inst->release();
return nullptr;
}
return inst;
}
};
OSDefineMetaClassAndStructors(FakeFaultReporter, IOService)
static_assert(sizeof(FakeFaultReporter) == 0x90, "FakeFaultReporter size must match CCFaultReporter ABI (0x90)");
// Buffers statiques globaux BSS : garantis alloués et mappés dès le chargement du kext,
// immunisés contre tout échec de heap dynamique durant le boot précoce.
static uint8_t gStreamMem[0x100] __attribute__((aligned(64)));
static uint8_t gP1Mem[0x1000] __attribute__((aligned(64)));
static uint8_t gP2Mem[0x100] __attribute__((aligned(64)));
static uint8_t gDummyReporter[0x100] __attribute__((aligned(64)));
static void *gDummyReporterVtable[256];
static IOReturn dummyReporterFunc(void *self, ...) {
return kIOReturnSuccess;
}
// Classe de simulation binaire conforme pour CCLogStream (CoreCapture) sous macOS Tahoe
// - Hérite de IOService (slots 0..265)
// - Déclare les 14 méthodes virtuelles de CCLogStream (slots 266..279) pour un total exact de 280 slots
// évitant tout déréférencement hors-limites de vtable (ex: setLevel @ 0x868, setFlags @ 0x878, setConsoleLogFlag @ 0x8b0)
// - Offsets internes conformes :
// +0x88 (streamData) -> gStreamMem, dont +0x48 -> gDummyReporter (évite #PF dans CCLogStream::logv -> incrementValue)
// +0x90 (p1) -> gP1Mem, dont +0x58 -> gP2Mem (évite #PF dans CCLogStream::shouldLog)
class FakeLogStream : public IOService {
OSDeclareDefaultStructors(FakeLogStream)
public:
void *streamData; // offset 0x88
void *p1; // offset 0x90
virtual bool init() override {
if (!IOService::init()) return false;
// Vtable factice pour le reporter afin que tout appel virtuel
// (ex: IOSimpleReporter::incrementValue appelant slot 45 / +0x168) réussisse sans faute
for (int i = 0; i < 256; i++) {
gDummyReporterVtable[i] = (void *)dummyReporterFunc;
}
*(void **)gDummyReporter = (void *)gDummyReporterVtable;
bzero(gStreamMem, sizeof(gStreamMem));
*(void **)(gStreamMem + 0x48) = gDummyReporter;
streamData = gStreamMem;
bzero(gP1Mem, sizeof(gP1Mem));
bzero(gP2Mem, sizeof(gP2Mem));
*(void **)(gP1Mem + 0x58) = gP2Mem;
p1 = gP1Mem;
return true;
}
// Méthodes virtuelles CCLogStream (slots 266 à 279 dans __ZTV11CCLogStream)
virtual void profileLoaded() {}
virtual void profileRemoved() {}
virtual bool initWithPipeAndName(void *pipe, const char *name, void *opt) { return true; }
virtual void setLevel(int level) {}
virtual int getLevel() { return 0; }
virtual void setFlags(uint64_t flags) {}
virtual uint64_t getFlags() { return 0; }
virtual void setLogFlag(uint64_t flag) {}
virtual void clearLogFlag(uint64_t flag) {}
virtual void setConsoleLevel(int level) {}
virtual void setConsoleFlags(uint64_t flags) {}
virtual uint64_t getConsoleFlags() { return 0; }
virtual void setConsoleLogFlag(uint64_t flag) {}
virtual void clearConsoleLogFlag(uint64_t flag) {}
static FakeLogStream* create() {
FakeLogStream *inst = new FakeLogStream;
if (inst && !inst->init()) {
inst->release();
return nullptr;
}
return inst;
}
};
OSDefineMetaClassAndStructors(FakeLogStream, IOService)
static_assert(__builtin_offsetof(FakeLogStream, streamData) == 0x88, "streamData must be at offset 0x88 for CCStream ABI");
static_assert(__builtin_offsetof(FakeLogStream, p1) == 0x90, "p1 must be at offset 0x90 for CCLogStream ABI");
static_assert(sizeof(FakeLogStream) == 0x98, "FakeLogStream size must match CCLogStream ABI (0x98)");
extern "C" {
void* _ZN23IO80211SkywalkInterfacenwEm(unsigned long size);
void _ZN23IO80211SkywalkInterfaceC1EPK11OSMetaClass(void *thisPtr, const void *meta);
void _ZN23IO80211SkywalkInterfacedlEPvm(void *ptr, unsigned long size);
bool _ZN23IO80211SkywalkInterface9initIvarsEv(void *thisPtr);
bool _ZN23IO80211SkywalkInterface4initEv(void *thisPtr);
extern const void * const _ZN23IO80211SkywalkInterface10gMetaClassE;
}
static IO80211SkywalkInterface* createSkywalkInterface(void) {
void *mem = _ZN23IO80211SkywalkInterfacenwEm(0x128);
if (!mem) {
IOLog("QualcommWifi: Erreur - operator new(IO80211SkywalkInterface) a echoue !\n");
return nullptr;
}
bzero(mem, 0x128);
_ZN23IO80211SkywalkInterfaceC1EPK11OSMetaClass(mem, &_ZN23IO80211SkywalkInterface10gMetaClassE);
// Initialisation complète via IO80211SkywalkInterface::init() :
// Alloue IOService::init (fPropertyTable, fRegistryTable, fLock),
// le lock IOSkywalkEthernetInterface (+0x118), et ivars (+0x120) !
if (!_ZN23IO80211SkywalkInterface4initEv(mem)) {
IOLog("QualcommWifi: IO80211SkywalkInterface::init() a echoue, repli sur initIvars()...\n");
_ZN23IO80211SkywalkInterface9initIvarsEv(mem);
} else {
IOLog("QualcommWifi: IO80211SkywalkInterface::init() reussi (fPropertyTable + lock + ivars alloues) !\n");
}
return (IO80211SkywalkInterface *)mem;
}
#define super IO80211Controller
OSDefineMetaClassAndStructors(QualcommWifi, IO80211Controller)
bool QualcommWifi::init(OSDictionary *dictionary) {
// 1. Sauvegarde non volatile de l'état du boot précédent si un crash a eu lieu
char prevStage[128] = {0};
unsigned int prevLen = sizeof(prevStage) - 1;
if (PEReadNVRAMProperty("qcom_stage", prevStage, &prevLen) && prevLen > 0) {
prevStage[prevLen] = '\0';
if (strcmp(prevStage, "start_done") != 0 && strcmp(prevStage, "clean") != 0) {
PEWriteNVRAMProperty("qcom_last_crash", prevStage, (unsigned int)strlen(prevStage));
IOLog("QualcommWifi: >>> [ALERTE] Crash boot précédent détecté au tag: '%s' (sauvegardé dans qcom_last_crash) <<<\n", prevStage);
}
}
char prevTrail[512] = {0};
unsigned int prevTrailLen = sizeof(prevTrail) - 1;
if (PEReadNVRAMProperty("qcom_trail", prevTrail, &prevTrailLen) && prevTrailLen > 0) {
prevTrail[prevTrailLen] = '\0';
bool hadStartDone = false;
size_t tLen = strlen(prevTrail);
const char *doneTag = "start_done";
size_t dLen = strlen(doneTag);
if (tLen >= dLen) {
for (size_t i = 0; i <= tLen - dLen; i++) {
if (memcmp(prevTrail + i, doneTag, dLen) == 0) {
hadStartDone = true;
break;
}
}
}
if (!hadStartDone) {
PEWriteNVRAMProperty("qcom_last_trail", prevTrail, (unsigned int)strlen(prevTrail));
IOLog("QualcommWifi: >>> [ALERTE] Fil d'Ariane du crash précédent: '%s' (sauvegardé dans qcom_last_trail) <<<\n", prevTrail);
}
}
bzero(gNvramTrail, sizeof(gNvramTrail));
sGetWorkQueueCount = 0;
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;
fWorkLoop = nullptr;
fWorkQueue = nullptr;
fNetif = nullptr;
fSkywalkInterface = nullptr;
fSkywalkCtrlAttached = false;
fSkywalkAttached = false;
fSkywalkStarted = false;
// 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();
logNvramStage("init_done");
return true;
}
void QualcommWifi::free(void) {
logNvramStage("free_enter");
OSSafeReleaseNULL(fSkywalkInterface);
OSSafeReleaseNULL(fFaultReporter);
OSSafeReleaseNULL(fLogger);
OSSafeReleaseNULL(fWorkLoop);
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<IOOptionBits>(kIODirectionInOut) | static_cast<IOOptionBits>(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<IOOptionBits>(kIODirectionInOut) | static_cast<IOOptionBits>(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<IOOptionBits>(kIODirectionInOut) | static_cast<IOOptionBits>(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 (!createWorkLoop()) {
logNvramStage("workLoop_failed");
IOLog("QualcommWifi: Erreur - Impossible de creer IOWorkLoop !\n");
return false;
}
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");
// Initialisation complète de l'arborescence Power Management (requis pour fSkywalkInterface->joinPMtree(this))
logNvramStage("pm_init");
PMinit();
joinPMtree(provider);
static IOPMPowerState sPowerStates[2] = {
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, IOPMDeviceUsable | IOPMPowerOn, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
registerPowerDriver(this, sPowerStates, 2);
changePowerStateToPriv(1);
IOLog("QualcommWifi: Power Management initialise avec succes (PMinit + joinPMtree + registerPowerDriver) !\n");
// 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");
fPciDevice->setMemoryEnable(true);
fPciDevice->setBusMasterEnable(true);
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);
IOLog("QualcommWifi: MMIO BAR0 mappe (0x%p). MHI_Status=0x%08x, MHI_Ctrl=0x%08x, BHI_Off=0x%08x, BHIE_Off=0x%08x\n",
fBar0Virt, mhiStatus, mhiCtrl, bhiOff, 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);
IOLog("QualcommWifi: BHI Version=%u.%u, ExecEnv=0x%x, Status=0x%x, Serial=0x%08x\n",
bhiMajor, bhiMinor, bhiEe, bhiStat, bhiSerial);
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 {
IOLog("QualcommWifi: Erreur - Impossible de mapper BAR0 !\n");
}
} 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);
// Instanciation et attachement de l'interface réseau Skywalk native (IO80211SkywalkInterface)
// Contrôlé par boot-arg "qcom_skywalk=1" (activé par défaut)
int allow_skywalk = 1;
PE_parse_boot_argn("qcom_skywalk", &allow_skywalk, sizeof(allow_skywalk));
// Étape maximale Skywalk contrôlée par boot-arg "qcom_step" (par défaut: 1 = allocation + initIvars sécurisée)
// 1: createSkywalkInterface (allocation + initIvars)
// 2: init(this, &mac) (initialisation avec copie adresse MAC)
// 3: setInterfaceRole(1)
// 4: attachInterface(fSkywalkInterface, this) [slot 441 sur controller]
// 5: fSkywalkInterface->attach(this) [attachement IORegistry]
// 6: fSkywalkInterface->start(this) [démarrage Skywalk, joinPMtree, workQueue, etc.]
// 7: fSkywalkInterface->registerService() [matching IOKit / airportd]
int skywalk_step = 1;
PE_parse_boot_argn("qcom_step", &skywalk_step, sizeof(skywalk_step));
if (allow_skywalk) {
logNvramStage("skywalk_alloc");
fSkywalkInterface = createSkywalkInterface();
if (fSkywalkInterface) {
IOLog("QualcommWifi: createSkywalkInterface réussi (0x%p) !\n", fSkywalkInterface);
if (skywalk_step >= 2) {
logNvramStage("skywalk_init");
if (fSkywalkInterface->init(this, (ether_addr *)&fMacAddress)) {
IOLog("QualcommWifi: fSkywalkInterface->init(...) réussi !\n");
if (skywalk_step >= 3) {
logNvramStage("skywalk_role");
fSkywalkInterface->setInterfaceRole(1); // 1 = kIO80211InterfaceRoleInfra (Primary Wi-Fi)
IOLog("QualcommWifi: fSkywalkInterface->setInterfaceRole(1) configuré !\n");
}
if (skywalk_step >= 4) {
logNvramStage("skywalk_attach_ctrl");
fSkywalkCtrlAttached = attachInterface(fSkywalkInterface, this); // slot 441 in IO80211Controller
IOLog("QualcommWifi: this->attachInterface(fSkywalkInterface, this) = %d\n", fSkywalkCtrlAttached);
}
if (skywalk_step >= 5) {
logNvramStage("skywalk_attach");
if (fSkywalkInterface->attach(this)) {
fSkywalkAttached = true;
IOLog("QualcommWifi: fSkywalkInterface->attach(this) réussi !\n");
if (skywalk_step >= 6) {
int substep = 0;
PE_parse_boot_argn("qcom_substep", &substep, sizeof(substep));
logNvramStage("s6_0_before_start");
IOLog("QualcommWifi: ========================================================\n");
IOLog("QualcommWifi: >>> ETAPE 6 : Appel de fSkywalkInterface->start(this) (substep=%d) <<<\n", substep);
IOLog("QualcommWifi: ========================================================\n");
if (substep >= 1) {
IOLog("QualcommWifi: [PAUSE 1/3] Pause de 3s AVANT start(this) pour lecture visuelle...\n");
IOSleep(3000);
}
logNvramStage("s6_1_calling_start");
bool startRes = fSkywalkInterface->start(this);
if (startRes) {
fSkywalkStarted = true;
logNvramStage("s6_2_start_ok");
IOLog("QualcommWifi: ========================================================\n");
IOLog("QualcommWifi: >>> [SUCCES MAJEUR] fSkywalkInterface->start(this) A REUSSI ! <<<\n");
IOLog("QualcommWifi: ========================================================\n");
if (substep >= 1) {
IOLog("QualcommWifi: [PAUSE 2/3] Pause de 5s APRES start(this) reussi pour observation...\n");
IOSleep(5000);
}
if (skywalk_step >= 7) {
logNvramStage("s7_0_before_register");
fSkywalkInterface->registerService();
logNvramStage("s7_1_register_done");
IOLog("QualcommWifi: fSkywalkInterface->registerService() publié !\n");
}
} else {
logNvramStage("s6_2_start_err");
IOLog("QualcommWifi: AVERTISSEMENT - fSkywalkInterface->start(this) a echoue (interface preservee pour diagnostic) !\n");
}
logNvramStage("s6_3_post_start");
if (substep >= 1) {
IOLog("QualcommWifi: [PAUSE 3/3] Pause de 3s avant de terminer QualcommWifi::start()...\n");
IOSleep(3000);
}
}
} else {
logNvramStage("skywalk_attach_err");
IOLog("QualcommWifi: Erreur - fSkywalkInterface->attach(this) a echoue !\n");
}
}
} else {
logNvramStage("skywalk_init_err");
IOLog("QualcommWifi: Erreur - fSkywalkInterface->init(...) a echoue !\n");
}
}
}
}
int allow_register = 0;
PE_parse_boot_argn("qcom_register", &allow_register, sizeof(allow_register));
if (allow_register) {
logNvramStage("register_service");
registerService();
} else {
logNvramStage("register_bypassed");
}
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");
if (fSkywalkInterface) {
if (fSkywalkCtrlAttached) {
detachInterface(fSkywalkInterface, false); // slot 442 in IO80211Controller
fSkywalkCtrlAttached = false;
}
if (fSkywalkStarted) {
fSkywalkInterface->stop(this);
fSkywalkStarted = false;
}
if (fSkywalkAttached) {
fSkywalkInterface->detach(this);
fSkywalkAttached = false;
}
OSSafeReleaseNULL(fSkywalkInterface);
}
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);
}
if (fWorkLoop) {
OSSafeReleaseNULL(fWorkLoop);
}
if (fWorkQueue) {
OSSafeReleaseNULL(fWorkQueue);
}
PMstop();
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 {
sGetWorkQueueCount++;
char tag[64];
snprintf(tag, sizeof(tag), "cb_getWorkQueue_%d", sGetWorkQueueCount);
logNvramStage(tag);
IOLog("QualcommWifi: >>> CALLBACK getWorkQueue(#%d) -> 0x%p <<<\n", sGetWorkQueueCount, fWorkQueue);
return fWorkQueue;
}
IORecursiveLock* QualcommWifi::allocIO80211RecursiveLock(void) {
logNvramStage("cb_allocLock");
IOLog("QualcommWifi: >>> CALLBACK allocIO80211RecursiveLock() <<<\n");
IORecursiveLock *lock = super::allocIO80211RecursiveLock();
IOLog("QualcommWifi: super::allocIO80211RecursiveLock() = 0x%p\n", lock);
if (!lock) {
logNvramStage("cb_allocLock_null");
IOLog("QualcommWifi: AVERTISSEMENT - allocIO80211RecursiveLock a renvoye NULL, repli sur IORecursiveLockAlloc()...\n");
lock = IORecursiveLockAlloc();
IOLog("QualcommWifi: IORecursiveLockAlloc() = 0x%p\n", lock);
}
return lock;
}
bool QualcommWifi::createWorkLoop(void) {
logNvramStage("createWorkLoop");
if (fWorkLoop) {
return true;
}
fWorkLoop = IOWorkLoop::workLoop();
if (!fWorkLoop) {
logNvramStage("workLoop_alloc_failed");
IOLog("QualcommWifi: Erreur - Impossible d'allouer IOWorkLoop !\n");
return false;
}
logNvramStage("workLoop_ok");
IOLog("QualcommWifi: IOWorkLoop allouee avec succes (0x%p) !\n", fWorkLoop);
return true;
}
IOWorkLoop* QualcommWifi::getWorkLoop(void) const {
return fWorkLoop ? fWorkLoop : (getProvider() ? getProvider()->getWorkLoop() : nullptr);
}
IOReturn QualcommWifi::getHardwareAddress(IOEthernetAddress *addrP) {
logNvramStage("cb_getHwAddr");
if (!addrP) return kIOReturnBadArgument;
bcopy(&fMacAddress, addrP, sizeof(IOEthernetAddress));
return kIOReturnSuccess;
}
IOReturn QualcommWifi::enable(IONetworkInterface *netif) {
logNvramStage("cb_netif_enable");
IOLog("QualcommWifi: >>> CALLBACK netif enable() <<<\n");
const IONetworkMedium *medium = getSelectedMedium();
setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, medium);
return kIOReturnSuccess;
}
IOReturn QualcommWifi::disable(IONetworkInterface *netif) {
logNvramStage("cb_netif_disable");
IOLog("QualcommWifi: >>> CALLBACK netif disable() <<<\n");
setLinkStatus(kIONetworkLinkValid, nullptr);
return kIOReturnSuccess;
}
UInt32 QualcommWifi::outputPacket(mbuf_t m, void *param) {
logNvramStage("cb_outputPacket");
if (m) {
freePacket(m);
}
return kIOReturnOutputDropped;
}
void QualcommWifi::dataLinkLayerAttachComplete(void) {
logNvramStage("cb_dataLinkAttachDone");
IOLog("QualcommWifi: >>> CALLBACK dataLinkLayerAttachComplete (Skywalk Tahoe) <<<\n");
}
IOReturn QualcommWifi::handleCardSpecific(IO80211SkywalkInterface *iface, unsigned long req_type, void *req_data, bool isSet) {
logNvramStage("cb_cardSpecific");
IOLog("QualcommWifi: >>> CALLBACK handleCardSpecific(req=0x%lx, isSet=%d) <<<\n", req_type, isSet);
return (IOReturn)handleApple80211Request((unsigned int)req_type, isSet ? 1 : 0, req_data);
}
void* QualcommWifi::getLogger(void) const {
logNvramStage("cb_getLogger");
return (void*)fLogger;
}
void* QualcommWifi::getFaultReporterFromDriver(void) {
logNvramStage("cb_getFaultReporter");
return (void*)fFaultReporter;
}
UInt32 QualcommWifi::getActionFramePoolCapacity(void) {
return 0x100;
}
IO80211SkywalkInterface* QualcommWifi::getPrimarySkywalkInterface(void) {
logNvramStage("cb_getPrimarySkywalk");
return fSkywalkInterface;
}
IOReturn QualcommWifi::setMulticastList(ether_addr const *addrs, UInt32 count) {
logNvramStage("cb_setMulticast");
return kIOReturnSuccess;
}
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;
}
IOReturn QualcommWifi::setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice) {
logNvramStage(powerStateOrdinal == 1 ? "cb_pm_on" : "cb_pm_off");
IOLog("QualcommWifi: >>> CALLBACK setPowerState(ordinal=%lu, whatDevice=0x%p) <<<\n",
powerStateOrdinal, whatDevice);
return IOPMAckImplied;
}