Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 110 additions & 5 deletions gap_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"sync"
"unsafe"

"github.com/go-ole/go-ole"
Expand Down Expand Up @@ -295,6 +296,28 @@ type Device struct {
session *genericattributeprofile.GattSession
connectionStatusListenerToken foundation.EventRegistrationToken
connectionStatusListener *foundation.TypedEventHandler
connParams *connectionParamsState
}

// connectionParamsState keeps the open connection parameters request.
// Windows applies the request only while the object is open:
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice.requestpreferredconnectionparameters
type connectionParamsState struct {
mu sync.Mutex
request *bluetooth.BluetoothLEPreferredConnectionParametersRequest
}

// replace closes the open request and then keeps the new one.
// A nil request only closes the open request and restores the system defaults.
func (s *connectionParamsState) replace(request *bluetooth.BluetoothLEPreferredConnectionParametersRequest) {
s.mu.Lock()
defer s.mu.Unlock()

if s.request != nil {
_ = s.request.Close()
s.request.Release()
}
s.request = request
}

// Connect starts a connection attempt to the given peripheral device address.
Expand Down Expand Up @@ -367,8 +390,9 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err

Address: address,

device: bleDevice,
session: newSession,
device: bleDevice,
session: newSession,
connParams: &connectionParamsState{},
}

// https://learn.microsoft.com/es-es/uwp/api/windows.devices.bluetooth.bluetoothledevice.connectionstatuschanged?view=winrt-26100
Expand Down Expand Up @@ -417,6 +441,11 @@ func (d Device) Disconnect() error {

d.cancel()

// Close the request while the device is still open.
if d.connParams != nil {
d.connParams.replace(nil)
}

if err := d.session.Close(); err != nil {
return err
}
Expand Down Expand Up @@ -447,13 +476,89 @@ func (d Device) Connected() (bool, error) {
// Whether or not the device will actually honor this, depends on the device and
// on the specific parameters.
//
// On Windows, this call doesn't do anything.
// Windows uses only ConnectionParams.Priority. It ignores MinInterval,
// MaxInterval and Timeout. It needs Windows 11 build 22000 or later.
func (d Device) RequestConnectionParams(params ConnectionParams) error {
// TODO: implement this using
// BluetoothLEDevice.RequestPreferredConnectionParameters.
if params.Priority == ConnectionPriorityUnspecified {
return nil
}
if d.device == nil || d.connParams == nil {
return errors.New("bluetooth: device is not connected")
}

preferred, err := preferredConnectionParameters(params.Priority)
if err != nil {
return err
}
defer preferred.Release()

request, err := d.device.RequestPreferredConnectionParameters(preferred)
if err != nil {
return err
}

// The status shows only that the system accepted the request. The agreed
// parameters come later, in the ConnectionParametersChanged event.
status, err := request.GetStatus()
if err != nil {
discardConnectionParamsRequest(request)
return err
}
if status != bluetooth.BluetoothLEPreferredConnectionParametersRequestStatusSuccess {
discardConnectionParamsRequest(request)
return fmt.Errorf("bluetooth: connection parameters request was not accepted: %s",
connectionParamsRequestStatusString(status))
}

// Keep the request. Windows applies it only while the object is open.
d.connParams.replace(request)

return nil
}

// preferredConnectionParameters returns the Windows preset for the priority.
// The caller must release the result.
func preferredConnectionParameters(priority ConnectionPriority) (*bluetooth.BluetoothLEPreferredConnectionParameters, error) {
var (
preferred *bluetooth.BluetoothLEPreferredConnectionParameters
err error
)
switch priority {
case ConnectionPriorityThroughput:
preferred, err = bluetooth.BluetoothLEPreferredConnectionParametersGetThroughputOptimized()
case ConnectionPriorityBalanced:
preferred, err = bluetooth.BluetoothLEPreferredConnectionParametersGetBalanced()
case ConnectionPriorityPowerSaving:
preferred, err = bluetooth.BluetoothLEPreferredConnectionParametersGetPowerOptimized()
default:
return nil, fmt.Errorf("bluetooth: unknown connection priority %d", priority)
}
if err != nil {
// Windows 10 does not have the activation factory for these presets.
return nil, fmt.Errorf("bluetooth: connection priorities need Windows 11 build 22000 or later: %w", err)
}
return preferred, nil
}

// discardConnectionParamsRequest closes a request that the device does not keep.
func discardConnectionParamsRequest(request *bluetooth.BluetoothLEPreferredConnectionParametersRequest) {
_ = request.Close()
request.Release()
}

func connectionParamsRequestStatusString(status bluetooth.BluetoothLEPreferredConnectionParametersRequestStatus) string {
switch status {
case bluetooth.BluetoothLEPreferredConnectionParametersRequestStatusSuccess:
return "success"
case bluetooth.BluetoothLEPreferredConnectionParametersRequestStatusDeviceNotAvailable:
return "device not available"
case bluetooth.BluetoothLEPreferredConnectionParametersRequestStatusAccessDenied:
return "access denied"
default:
return "unspecified"
}
}

// SetRandomAddress sets the random address to be used for advertising.
func (a *Adapter) SetRandomAddress(mac MAC) error {
return errors.ErrUnsupported
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22.1
require (
github.com/go-ole/go-ole v1.2.6
github.com/godbus/dbus/v5 v5.1.0
github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b
github.com/saltosystems/winrt-go v0.0.0-20260513072510-45f10383b2b8
github.com/soypat/cyw43439 v0.0.0-20250505012923-830110c8f4af
github.com/tinygo-org/cbgo v0.0.4
golang.org/x/crypto v0.12.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b h1:du3zG5fd8snsFN6RBoLA7fpaYV9ZQIsyH9snlk2Zvik=
github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b/go.mod h1:CIltaIm7qaANUIvzr0Vmz71lmQMAIbGJ7cvgzX7FMfA=
github.com/saltosystems/winrt-go v0.0.0-20260513072510-45f10383b2b8 h1:CpUxfPAWwKKHDCH8tKBzAe6lC3c2mDVspXPP10Z+4IQ=
github.com/saltosystems/winrt-go v0.0.0-20260513072510-45f10383b2b8/go.mod h1:CIltaIm7qaANUIvzr0Vmz71lmQMAIbGJ7cvgzX7FMfA=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
Expand Down
Loading