/* * Hci.hpp * Bluetooth HCI (Host Controller Interface) layer * HCI transport over USB bulk/interrupt/control endpoints * Copyright (c) 2026 Daniel Hammer */ #pragma once #include namespace Drivers::USB::Bluetooth::Hci { // ========================================================================= // HCI packet types (for USB transport) // ========================================================================= // USB transport uses different endpoints for each packet type: // Commands -> Control EP0 (class request) // ACL data -> Bulk OUT / Bulk IN // Events -> Interrupt IN // ========================================================================= // HCI command opcodes (OGF << 10 | OCF) // ========================================================================= // Link Control (OGF 0x01) constexpr uint16_t OP_INQUIRY = 0x0401; constexpr uint16_t OP_INQUIRY_CANCEL = 0x0402; constexpr uint16_t OP_CREATE_CONNECTION = 0x0405; constexpr uint16_t OP_DISCONNECT = 0x0406; constexpr uint16_t OP_ACCEPT_CONN_REQ = 0x0409; constexpr uint16_t OP_REJECT_CONN_REQ = 0x040A; constexpr uint16_t OP_LINK_KEY_REQ_REPLY = 0x040B; constexpr uint16_t OP_LINK_KEY_REQ_NEG_REPLY = 0x040C; constexpr uint16_t OP_AUTH_REQUESTED = 0x0411; constexpr uint16_t OP_SET_CONN_ENCRYPT = 0x0413; constexpr uint16_t OP_IO_CAPABILITY_REPLY = 0x042B; constexpr uint16_t OP_USER_CONFIRM_REPLY = 0x042C; // Link Policy (OGF 0x02) constexpr uint16_t OP_WRITE_DEFAULT_LP = 0x080F; constexpr uint16_t OP_SNIFF_MODE = 0x0803; // Controller & Baseband (OGF 0x03) constexpr uint16_t OP_RESET = 0x0C03; constexpr uint16_t OP_SET_EVENT_FILTER = 0x0C05; constexpr uint16_t OP_WRITE_LOCAL_NAME = 0x0C13; constexpr uint16_t OP_READ_LOCAL_NAME = 0x0C14; constexpr uint16_t OP_WRITE_SCAN_ENABLE = 0x0C1A; constexpr uint16_t OP_WRITE_CLASS_OF_DEVICE = 0x0C24; constexpr uint16_t OP_WRITE_SSP_MODE = 0x0C56; constexpr uint16_t OP_WRITE_INQUIRY_MODE = 0x0C45; constexpr uint16_t OP_WRITE_PAGE_TIMEOUT = 0x0C18; constexpr uint16_t OP_WRITE_AUTH_ENABLE = 0x0C20; constexpr uint16_t OP_SET_EVENT_MASK = 0x0C01; // Informational Parameters (OGF 0x04) constexpr uint16_t OP_READ_BD_ADDR = 0x1009; constexpr uint16_t OP_READ_LOCAL_VERSION = 0x1001; constexpr uint16_t OP_READ_LOCAL_FEATURES = 0x1003; constexpr uint16_t OP_READ_BUFFER_SIZE = 0x1005; // Intel vendor commands (OGF 0x3F) constexpr uint16_t OP_INTEL_READ_VERSION = 0xFC05; constexpr uint16_t OP_INTEL_RESET = 0xFC01; constexpr uint16_t OP_INTEL_SET_EVENT_MASK = 0xFC52; constexpr uint16_t OP_INTEL_DDC_CONFIG_WRITE = 0xFC8B; constexpr uint16_t OP_INTEL_SECURE_SEND = 0xFC09; constexpr uint16_t OP_INTEL_WRITE_BD_ADDR = 0xFC31; // set adapter BD_ADDR // ========================================================================= // HCI event codes // ========================================================================= constexpr uint8_t EVT_INQUIRY_COMPLETE = 0x01; constexpr uint8_t EVT_INQUIRY_RESULT = 0x02; constexpr uint8_t EVT_CONNECTION_COMPLETE = 0x03; constexpr uint8_t EVT_CONNECTION_REQUEST = 0x04; constexpr uint8_t EVT_DISCONNECTION_COMPLETE = 0x05; constexpr uint8_t EVT_AUTH_COMPLETE = 0x06; constexpr uint8_t EVT_ENCRYPT_CHANGE = 0x08; constexpr uint8_t EVT_LINK_KEY_REQUEST = 0x17; constexpr uint8_t EVT_LINK_KEY_NOTIFICATION = 0x18; constexpr uint8_t EVT_COMMAND_COMPLETE = 0x0E; constexpr uint8_t EVT_COMMAND_STATUS = 0x0F; constexpr uint8_t EVT_NUM_COMPLETED_PACKETS = 0x13; constexpr uint8_t EVT_IO_CAPABILITY_REQUEST = 0x31; constexpr uint8_t EVT_IO_CAPABILITY_RESPONSE = 0x32; constexpr uint8_t EVT_USER_CONFIRM_REQUEST = 0x33; constexpr uint8_t EVT_SIMPLE_PAIRING_COMPLETE = 0x36; constexpr uint8_t EVT_INQUIRY_RESULT_RSSI = 0x22; constexpr uint8_t EVT_EXTENDED_INQUIRY_RESULT = 0x2F; constexpr uint8_t EVT_VENDOR_SPECIFIC = 0xFF; // ========================================================================= // Inquiry result storage // ========================================================================= struct InquiryDevice { uint8_t BdAddr[6]; uint8_t _pad[2]; uint32_t ClassOfDevice; int8_t Rssi; uint8_t _pad2[3]; char Name[64]; // From Extended Inquiry Result or Remote Name Request }; constexpr int MAX_INQUIRY_RESULTS = 16; // ========================================================================= // HCI packet headers // ========================================================================= struct CommandHeader { uint16_t Opcode; uint8_t ParamLength; } __attribute__((packed)); struct EventHeader { uint8_t EventCode; uint8_t ParamLength; } __attribute__((packed)); struct AclHeader { uint16_t HandleFlags; // bits 11:0 = handle, 13:12 = PB flag, 15:14 = BC flag uint16_t DataLength; } __attribute__((packed)); // ACL PB (Packet Boundary) flag values constexpr uint16_t ACL_PB_FIRST_NON_FLUSH = 0x0000; // First non-auto-flushable constexpr uint16_t ACL_PB_CONTINUING = 0x1000; // Continuing fragment constexpr uint16_t ACL_PB_FIRST_FLUSH = 0x2000; // First auto-flushable // ========================================================================= // HCI connection info // ========================================================================= struct ConnectionInfo { bool Active; uint16_t Handle; uint8_t BdAddr[6]; uint8_t LinkType; // 0x01 = ACL bool Encrypted; }; constexpr int MAX_CONNECTIONS = 4; // A persisted (bonded/paired) device, as stored in the link-key store. struct BondInfo { uint8_t Addr[6]; }; // ========================================================================= // Intel Bluetooth version info // ========================================================================= struct IntelVersion { uint8_t Status; uint8_t HwPlatform; uint8_t HwVariant; uint8_t HwRevision; uint8_t FwVariant; // 0x06 = bootloader, 0x23 = operational uint8_t FwRevision; uint8_t FwBuildNum; uint8_t FwBuildWw; uint8_t FwBuildYy; uint8_t FwPatchNum; } __attribute__((packed)); // ========================================================================= // Public API // ========================================================================= // Initialize HCI transport over USB for the given slot void Initialize(uint8_t slotId); // Start receiving HCI events and ACL data (call after HCI init sequence). // Arms both the interrupt IN and the bulk IN; the bulk IN must stay armed // through the firmware download (it absorbs the device's ~635 KB cc=4 glitch // and keeps the event pipe alive -- see the definition). void StartEventPipe(); // Send an HCI command via USB control transfer (EP0) bool SendCommand(uint16_t opcode, const uint8_t* params, uint8_t paramLen); // Wait for a Command Complete event matching the given opcode // Returns true if received within timeout, fills outParams (excluding status byte) bool WaitCommandComplete(uint16_t opcode, uint8_t* outParams = nullptr, uint8_t maxLen = 0, uint32_t timeoutMs = 2000); // Wait for a Command Status event matching the given opcode bool WaitCommandStatus(uint16_t opcode, uint32_t timeoutMs = 2000); // Send ACL data via USB bulk OUT bool SendAcl(uint16_t handle, uint16_t pbFlag, const uint8_t* data, uint16_t len); // Process an HCI event received on the interrupt IN endpoint void ProcessEvent(const uint8_t* data, uint32_t len); // Process ACL data received on the bulk IN endpoint void ProcessAcl(const uint8_t* data, uint32_t len); // Get connection info ConnectionInfo* GetConnection(uint16_t handle); ConnectionInfo* GetActiveConnection(); ConnectionInfo* GetConnectionByIndex(int index); // 0..MAX_CONNECTIONS-1 // HCI Reset command bool Reset(); // Read local BD_ADDR bool ReadBdAddr(uint8_t* addr); // Read standard HCI local version info struct LocalVersion { uint8_t Status; uint8_t HciVersion; uint16_t HciRevision; uint8_t LmpVersion; uint16_t Manufacturer; uint16_t LmpSubversion; } __attribute__((packed)); bool ReadLocalVersion(LocalVersion* ver); // Read Intel-specific version info bool ReadIntelVersion(IntelVersion* ver); // ========================================================================= // Intel firmware download primitives (bootloader mode) // ========================================================================= // Read the Intel version response in TLV format (0xFC05 with parameter // 0xFF). Copies the raw return parameters (byte 0 = status, followed by // the TLV stream) into outBuf, bounded by the number of bytes actually // received from the controller. Returns that length, or -1 on failure. int ReadIntelVersionTlv(uint8_t* outBuf, int maxLen); // Intel "Secure Send" (0xFC09): pushes one logical fragment to the // bootloader, split into <=252-byte chunks each prefixed with the // fragment type (0x00 CSS init, 0x01 firmware data, 0x02 signature, // 0x03 public key). The bootloader does not Command-Complete these; pacing // is by USB transfer completion and the result arrives asynchronously as a // 0xFF/0x06 secure-send result event (see WaitSecureSendResult). bool IntelSecureSend(uint8_t fragmentType, const uint8_t* data, uint32_t len); // Reset / await the Intel "secure send result" vendor event (0xFF/0x06). // Call ClearSecureSendResult() before a download phase, then // WaitSecureSendResult() to read the outcome (result/status, 0 = success). void ClearSecureSendResult(); bool WaitSecureSendResult(uint32_t timeoutMs, uint8_t* outResult, uint8_t* outStatus); // Bonded-device link key persistence (so pairings survive reboots). // LoadLinkKeys(): read the on-disk store once VFS is up. // FlushLinkKeys(): write the store to disk if it changed -- call from // process context (NOT an event handler), since it does blocking disk I/O. void LoadLinkKeys(); void FlushLinkKeys(); // Enumerate the stored bonds (paired devices) into buf; returns the count // written (<= maxCount). Used to list paired-but-disconnected devices. int ListBonds(BondInfo* buf, int maxCount); // Remove a stored bond by BD_ADDR and persist the removal (process context). // Returns true if a matching bond was found and forgotten. bool ForgetBond(const uint8_t* addr); // Toggle the bounded firmware-phase interrupt-IN trace (one log line per // completion, capped) plus mailbox-overwrite flags. Enabled by the // deferred bring-up so a failing hardware boot log shows exactly what the // event pipe delivered. void SetFwTrace(bool on); // Non-blocking peek at the most recent 0xFF/0x06 secure-send result without // consuming it. Returns true if one has arrived since the last // ClearSecureSendResult(). The payload loop uses this to catch a mid-stream // rejection -- a healthy bootloader stays silent until the final fragment. bool PeekSecureSendResult(uint8_t* outResult, uint8_t* outStatus); // Reset the controller into operational firmware at bootAddr (0xFC01) and // wait for the Intel "bootup" vendor event. Returns true once booted. bool IntelBootFirmware(uint32_t bootAddr, uint32_t timeoutMs = 5000); // Apply one DDC parameter record (record[0] = payload length) via 0xFC8B. bool IntelWriteDdcRecord(const uint8_t* record, uint8_t recordLen); // Configure the Intel vendor event mask (0xFC52). bool IntelSetEventMask(); // Set local name bool WriteLocalName(const char* name); // Set class of device bool WriteClassOfDevice(uint32_t cod); // Enable scan (inquiry + page) bool WriteScanEnable(uint8_t mode); // Write Simple Secure Pairing mode bool WriteSSPMode(uint8_t mode); // Accept an incoming connection bool AcceptConnection(const uint8_t* bdAddr, uint8_t role); // Request authentication on an ACL link (we are the connection initiator). // Drives Link Key Request -> pairing; needed for bonded-device reconnects. bool AuthenticateLink(uint16_t handle); // Set the adapter's BD_ADDR (Intel vendor command 0xFC31). Used to dodge a // remote that holds a stale, un-clearable bond to our real address. bool SetBdAddr(const uint8_t* addr); // Send any queued pairing replies (IO-cap / user-confirm / link-key) with // real confirmed transfers. Call from top-level (e.g. the connect loop), // NOT from an event handler -- event handlers only enqueue. void ProcessPendingCommands(); // ACL TX flow control: outstanding (un-acked) ACL packets, and the // controller's ACL buffer count (Number-Of-Completed-Packets credits). The // media writer throttles on these so it never overruns the controller. uint16_t AclPendingCount(); uint16_t AclMaxPackets(); // ACL packets handed to the xHCI whose bulk OUT completion has not been // reaped yet (each one still owns a TX DMA ring slot). uint32_t AclTxInFlight(); // True when SendAcl can be called without overrunning either the // controller's ACL buffers (NOCP credits) or the TX DMA ring. The media // writer polls events until this is true before sending each frame. bool AclTxReady(); // Lost-NOCP recovery: zero the credit count after a prolonged stall with // the USB side drained (completions presumed lost). void AclResetCredits(); // Disconnect a connection bool Disconnect(uint16_t handle, uint8_t reason); // Read ACL buffer size from controller bool ReadBufferSize(uint16_t* aclLen, uint8_t* scoLen, uint16_t* aclNum, uint16_t* scoNum); // Inquiry (device discovery) bool StartInquiry(uint8_t durationUnits); // duration in 1.28s units (e.g., 8 = ~10s) bool CancelInquiry(); int GetInquiryResults(InquiryDevice* buf, int maxCount); void ClearInquiryResults(); bool IsInquiryActive(); // Create ACL connection to a remote device bool CreateConnection(const uint8_t* bdAddr); // Drain any pending HCI events (call in poll loops that aren't inside // WaitCommandComplete/WaitCommandStatus) void DrainEvents(); }