Files
QualcommWireless/QualcommWifi/QualcommWifi.cpp
T
Erwan 1a6562c229 feat(wifi): implement NVRAM stage logging and safe crash-proof mode for WCN7850
### Breakthroughs & Rétro-ingénierie
- **Analyse des réinitialisations matérielles instantanées sur AMD Ryzen** :
  L'arrêt brutal (coupure d'alimentation instantanée de 5 secondes au boot) sur plateforme AMD Ryzen/AM4 provient d'un Machine Check / PCIe Unsupported Request / AER déclenché lors d'accès MMIO (BAR0) lorsque la carte WCN7850 est en état basse consommation D3hot (CurrentPowerState=2 dans IORegistry).
- **Traces persistantes dans la NVRAM matérielle (AppleEFINVRAM)** :
  Comme un reset matériel coupe l'alimentation avant l'écriture des journaux sur disque ou la génération d'un Kernel Panic (pas de /Library/Logs/DiagnosticReports), la fonction logNvramStage() écrit directement chaque jalon franchi dans l'entrée '/options' du plan d'arbre de périphériques gIODTPlane. Cette trace survit aux redémarrages et est consultable via 'nvram -p | grep qcom_wifi_log'.
- **Mode Safe / Diagnostic sans crash** :
  Introduction d'une barrière d'accès MMIO pilotée par l'argument de démarrage 'qcom_mmio'. Sans cet argument, le pilote active le contrôleur PCIe en D0 sans toucher aux registres MMIO sensibles, publiant les interfaces et services IO80211Controller pour validation visuelle sans aucun risque de boot loop.

### Modifications apportées
- **QualcommWifi/QualcommWifi.cpp** :
  - Ajout de l'include <IOKit/IODeviceTreeSupport.h> et implémentation de logNvramStage().
  - Instrumentation des méthodes init(), probe(), start(), createWorkQueue(), getHardwareAddress(), enable(), disable(), dataLinkLayerAttachComplete(), setPOWER() et handleApple80211Request().
  - Ajout du réveil formel en D0 via fPciDevice->enablePCIPowerManagement(kPCIPMCSPowerStateD0).
  - Conditionnement de l'accès BAR0/MHI/BHI sous PE_parse_boot_argn("qcom_mmio").
  - Suppression des reliquats de détachement d'interface ethernet.

### Validation
- Compilation réussie avec clang 17 et SDK macOS Tahoe 26.x.
- Validation des dépendances et symboles à 100% via kmutil libraries contre BootKernelExtensions.kc.
- Déploiement du kext sur /Volumes/EFI/EFI/OC/Kexts/ et activation dans config.plist.
2026-10-16 16:53:58 +02:00

1023 lines
40 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>
static char gLogBuffer[2048] = "QCOM_BOOT:";
static void logNvramStage(const char *tag) {
size_t curLen = strlen(gLogBuffer);
if (curLen + strlen(tag) + 2 < sizeof(gLogBuffer)) {
strcat(gLogBuffer, ">");
strcat(gLogBuffer, tag);
}
IORegistryEntry *options = IORegistryEntry::fromPath("/options", gIODTPlane);
if (options) {
OSString *val = OSString::withCString(gLogBuffer);
if (val) {
options->setProperty("qcom_wifi_log", val);
val->release();
}
}
IOLog("QualcommWifi: >>> [STAGE] %s <<<\n", tag);
}
#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;
return true;
}
void QualcommWifi::free(void) {
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 (!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");
// Réveil formel du contrôleur PCIe vers l'état d'alimentation D0
logNvramStage("pci_enable_d0");
fPciDevice->enablePCIPowerManagement(kPCIPMCSPowerStateD0);
UInt16 pciCommand = fPciDevice->configRead16(kIOPCIConfigCommand);
pciCommand |= kIOPCICommandMemorySpace;
pciCommand &= ~kIOPCICommandBusMaster; // Protection absolue contre tout accès DMA non configuré sur AMD-Vi
logNvramStage("pci_cmd_write");
fPciDevice->configWrite16(kIOPCIConfigCommand, pciCommand);
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);
}
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;
}