# Diagnostic & Architecture Bluetooth (Qualcomm WCN7850) Ce document décrit en profondeur le fonctionnement du sous-système Bluetooth de la puce **Qualcomm FastConnect 7800 (WCN7850)**, l'analyse détaillée du crash au démarrage de session macOS, et la solution définitive. --- ## 1. Description du Contrôleur Bluetooth - **Identifiants matériels** : - Vendeur : `0x0489` (Foxconn / Hon Hai) - Produit : `0xe10d` - Nom du chipset : Qualcomm WCN7850 Bluetooth 5.4 HCI - Bus : USB 2.0 (Interface 0 : Contrôle/Bulk HCI, Interface 1 : SCO Audio) - **Rôle dans macOS** : - Fournit la couche transport HCI (Host Controller Interface) au daemon système `bluetoothd`. - Gère les périphériques HID (claviers, souris, Magic Trackpad), les casques audio A2DP/HFP, et les fonctionnalités Continuity / AirDrop si la carte Wi-Fi est associée. --- ## 2. Analyse Post-Mortem du Kernel Panic au Login ### 2.1. Symptômes observés - Au démarrage, l'écran de verrouillage macOS s'affiche correctement. - Dès que le mot de passe utilisateur est validé et que la session commence à charger, l'écran se fige complètement puis la machine redémarre (Kernel Panic). ### 2.2. Traces du Kernel Panic (`Kernel-2026-10-15-174229.panic`) ```text panic(cpu 0 caller 0xffffff800ec4b86d): A kext releasing a(n) IOBluetoothL2CAPChannel has corrupted the registry. @OSObject.cpp:224 Panicked task 0xffffff8b71de3980: 235 threads: pid 0: kernel_task Backtrace (CPU 0), panicked thread: 0xffffff903e00ab30, Frame : Return Address 0xfffffff2a9823bd0 : 0xffffff800edd07bd mach_kernel : _panic + 0x81 0xfffffff2a9823cc0 : 0xffffff800ec4b86d mach_kernel : __ZNK8OSObject13taggedReleaseEPKvi + 0x8d 0xfffffff2a9823cd0 : 0xffffff8010694464 com.apple.iokit.IOBluetoothFamily : __ZN17IOBluetoothDevice13removeChannelEP23IOBluetoothL2CAPChannel + 0xae 0xfffffff2a9823d70 : 0xffffff8010697629 com.apple.iokit.IOBluetoothFamily : __ZN23IOBluetoothL2CAPChannel6stopWLEv + 0x65 0xfffffff2a9823df0 : 0xffffff8010697583 com.apple.iokit.IOBluetoothFamily : __ZN23IOBluetoothL2CAPChannel4stopEP9IOService + 0x33 0xfffffff2a9823e90 : 0xffffff800ecc7dd6 mach_kernel : __ZN9IOService15terminateWorkerEj + 0x8b6 Kernel Extensions in backtrace: com.apple.iokit.IOBluetoothFamily(9.0) ``` ### 2.3. Cause Racine (Root Cause) 1. **L'Intrusion de `IntelBTPatcher.kext`** : - Le kext tiers `IntelBTPatcher.kext` (conçu spécifiquement pour les puces Intel AX200/AX210) était resté activé (`Enabled: true`) dans le fichier `config.plist` d'OpenCore. - `IntelBTPatcher` intercepte et patche globalement les méthodes virtuelles de `IOBluetoothFamily` dans le noyau pour contourner des anomalies de paquets HCI propres à Intel. 2. **Incompatibilité avec Darwin 24 (macOS 15 Sequoia)** : - Sous macOS 15 Sequoia, Apple a profondément remanié la gestion mémoire d'IOKit, notamment la validation des `taggedRelease` dans `OSObject.cpp:224`. - `IntelBTPatcher v2.5.0` (compilé pour Monterey/Ventura/Sonoma) n'est pas compatible avec cette sémantique et effectue une libération désynchronisée (double release ou tag invalide) lors de la fermeture d'un canal L2CAP. 3. **Le déclencheur au Login** : - Dès la validation du mot de passe de session, `loginwindow` démarre `bluetoothd`. - Les périphériques Apple couplés (notamment la **Magic Mouse 2** `78:CA:39:F4:15:5A` et le **Magic Keyboard** `78:CA:39:4C:A3:9A`) établissent ou réinitialisent leurs canaux L2CAP (`HID ACL`). - Lors de la libération ou du renouvellement du canal L2CAP (`IOBluetoothDevice::removeChannel`), le hook corrompu d'IntelBTPatcher déclenche immédiatement l'assertion de corruption de registre du noyau (`OSObject::taggedRelease`), provoquant le kernel panic instantané. --- ## 3. Solution et Bonnes Pratiques ### 3.1. Désactivation définitive d'`IntelBTPatcher` Pour les cartes **Qualcomm FastConnect 7800**, `IntelBTPatcher` ne doit **JAMAIS** être utilisé : - Dans `/Volumes/EFI/EFI/OC/config.plist` : ```xml BundlePath IntelBTPatcher.kext Enabled ``` - Suppression physique du bundle `IntelBTPatcher.kext` du dossier `EFI/OC/Kexts/`. ### 3.2. Rôle de `BlueToolFixup.kext` - `BlueToolFixup.kext` (de la suite BrcmPatchRAM / Acidanthera) est, quant à lui, **requis et compatible**. - Il patche dynamiquement `bluetoothd` en espace utilisateur pour accepter les dongles tiers non-Apple et débloquer les commandes de bas niveau (HCI Vendor Commands). - Arguments de démarrage recommandés dans `boot-args` : `-btlfxallowanyaddr -btlfxnvramcheck` --- ## 4. Séquence de Chargement du Firmware USB Le firmware Bluetooth Qualcomm nécessite deux étages binaires téléversés au démarrage : ```mermaid sequenceDiagram autonumber participant K as QualcommWireless (Kext) participant USB as Contrôleur USB WCN7850 participant D as bluetoothd (macOS) K->>USB: QCA_GET_TARGET_VERSION (Requête de contrôle vendeur) USB-->>K: Versions ROM / Patch / RAM K->>USB: QCA_CHECK_STATUS (Vérification statut) USB-->>K: Status 0x00 (RAMPATCH et Syscfg requis) K->>USB: EP0: Header DFU (40 octets) K->>USB: EP2 (Bulk OUT): RAMPATCH WCN7850 (paquets de 4096 octets) K->>USB: EP0: Header DFU (4 octets) K->>USB: EP2 (Bulk OUT): NVM WCN7850 (paquets de 4096 octets) K->>USB: HCI Reset (0x03, 0x0C, 0x00) USB-->>K: HCI Command Complete Event K->>K: Injection des propriétés IORegistry (spoofing CSR 0x0a12) K->>K: Libération de l'interface USB (close) Note over D,USB: bluetoothd prend la main en exclusivité D->>USB: Établissement transport HCI USB D->>USB: Connexion Magic Mouse 2 / Clavier (L2CAP) ``` Grâce à cette séquence, le contrôleur Bluetooth WCN7850 opère de manière parfaitement stable sous macOS Sequoia (Darwin 24) et macOS Tahoe (Darwin 25) sans aucun crash au démarrage de session. --- ## 5. Rétro-ingénierie de `bluetoothd` (macOS Sequoia & macOS Tahoe) & Détection du Transport ### 5.1. Algorithme de sélection du transport dans `FindTransportType` Le désassemblage complet du binaire `/usr/sbin/bluetoothd` sous macOS Sequoia (`0x100121555`) et sous macOS Tahoe 26.7.1 (`0x10014c700`, `Found_USB_Dongle` à `0x1000e9559`, `GetProductAndVendorID` à `0x1000edf2a`) a révélé l'algorithme exact de détection du bus HCI : 1. **Test 1 : `transport-encoding` dans l'arbre matériel (`IODeviceTree`)** - `bluetoothd` interroge la propriété `transport-encoding` sous `IODeviceTree:/chosen` ou les nœuds bluetooth. - S'il trouve `0x00000000`, il force immédiatement le transport USB (`"DeviceTree transport = 0x00000000"`). 2. **Test 2 : Matching IORegistry du service hôte USB (`0x100121620` / `0x10014c7ca`)** - `bluetoothd` appelle : ```c IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceNameMatching("Bluetooth USB Host Controller")); ``` - **Découverte majeure** : Si **un service quelconque** dans l'IORegistry porte le nom `"Bluetooth USB Host Controller"`, `bluetoothd` assigne **immédiatement `Transport = 0 (USB)`** et retourne sans jamais évaluer le `board-id` de la machine ! 3. **Test 3 : Fallback par table statique des `board-id` (`0x1001216c6` / `0x10014c86f`)** - Si aucun service `"Bluetooth USB Host Controller"` n'a été trouvé, `bluetoothd` lit le `board-id` dans l'arbre IODeviceTree (`"sysBoardID %s"`). - Il compare l'ID à deux tables internes : - Table 1 : Modèles avec Bluetooth USB natif (iMac, Mac mini, MacBook Pro pré-T2). - Table 2 : Modèles avec Bluetooth UART natif (`MacPro7,1` / `Mac-27AD2F918AE68F61`). - Sur SMBIOS `MacPro7,1`, le démon sélectionne par défaut le transport UART (H4BC) et ignore complètement tout périphérique USB, affichant `Chipset: BCM_4350C2, Transport: UART, State: Off`. ### 5.2. Résolution native et Hook Shadow VTable dans `QualcommWireless` En appliquant les enseignements de la rétro-ingénierie : - `QualcommWireless` s'enregistre explicitement sous le nom `"Bluetooth USB Host Controller"`. - `fDevice->setName("Bluetooth USB Host Controller")`. - Injection de `transport-encoding = <00 00 00 00>` sur le périphérique et sur `IODeviceTree:/chosen`. - Spoofing de `idVendor = 0x0a12` (CSR) validé nativement par `Found_USB_Dongle`. - **Shadow VTable (`IOUSBHostDevice::deviceRequest`)** : - L'index vtable de `IOUSBHostDevice::deviceRequest(IOService*, StandardUSB::DeviceRequest&, void*, IOMemoryDescriptor*, uint32_t&, IOUSBHostCompletion*, uint32_t)` est résolu dynamiquement à la compilation via `DeviceRequestAccessor::getVtableIndex()` : **slot `374` (`0x176`, offset `0xbb0`)**, identique sous macOS Sequoia (`Darwin 24`) et macOS Tahoe (`Darwin 25.6.0`, `__ZTV15IOUSBHostDevice` à `0xffffff8002e72720`). - L'interception du descripteur de périphérique USB (`GET_DESCRIPTOR DEVICE`, 18 octets) remplace à la volée le Vendor ID `0x0489` par `0x0a12` (CSR) lors de la lecture directe par `bluetoothd` (`GetProductAndVendorID`). - Résultat : `bluetoothd` configure le contrôleur en USB de manière native et immédiate.