Skip to content

Fix EINVAL when connecting LE L2CAP client sockets - #64

Merged
colemancda merged 1 commit into
masterfrom
fix/le-client-att-psm
Oct 28, 2026
Merged

colemancda merged 1 commit into
masterfrom
fix/le-client-att-psm

Conversation

@colemancda

Copy link
Copy Markdown
Member

L2CAPSocket.lowEnergyClient built the destination address with both protocolServiceMultiplexer: .att (l2_psm = 0x1F) and channel: .att (l2_cid = 4). The kernel's l2cap_sock_connect() returns EINVAL when both are set, so every LE GATT connection failed immediately with "Invalid argument" before anything reached the controller.

LE ATT connects over the fixed channel (CID 4) with PSM 0, the same as BlueZ's gatttool. This change drops the PSM from the destination address.

Testing

  • Before: GATTCentral.connect(to:) against a Govee H5072 failed in about 0.1 s with Invalid argument, and btmon showed no HCI traffic.
  • After: the socket is accepted and the kernel queues the LE connection (LE Add Device → passive scan → LE Create Connection in btmon). Debian 13, kernel 6.12, CSR 4.0 USB dongle, Swift 6.4.1.
  • swift test: 86 tests, 1 failure. L2CAPSocketAddressTests.testClassicAddressDecoding also fails on master and is unrelated to this change.

lowEnergyClient set both the ATT PSM and the ATT CID on the destination
address. l2cap_sock_connect() rejects an address with both l2_psm and
l2_cid set, so every LE GATT connection failed immediately with EINVAL.
LE ATT uses the fixed channel (CID 4) with PSM 0.
@colemancda
colemancda merged commit 32328d3 into master Oct 28, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant