-
Notifications
You must be signed in to change notification settings - Fork 198
Expand file tree
/
Copy pathadapter_windows.go
More file actions
140 lines (115 loc) · 4.15 KB
/
Copy pathadapter_windows.go
File metadata and controls
140 lines (115 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package bluetooth
import (
"errors"
"fmt"
"strings"
"syscall"
"unsafe"
"github.com/go-ole/go-ole"
"github.com/saltosystems/winrt-go"
"github.com/saltosystems/winrt-go/windows/devices/bluetooth/advertisement"
"github.com/saltosystems/winrt-go/windows/foundation"
"golang.org/x/sys/windows"
)
var _ BLEAdapter = (*Adapter)(nil)
type Adapter struct {
watcher *advertisement.BluetoothLEAdvertisementWatcher
connectHandler func(device Device, connected bool)
defaultAdvertisement *Advertisement
}
// DefaultAdapter is the default adapter on the system.
//
// Make sure to call Enable() before using it to initialize the adapter.
var DefaultAdapter = &Adapter{
connectHandler: func(device Device, connected bool) {
return
},
}
// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
func (a *Adapter) Enable() error {
return ole.RoInitialize(1) // initialize with multithreading enabled
}
// Reset is a no-op on Windows. Provided for interface symmetry.
func (a *Adapter) Reset() error {
return nil
}
func awaitAsyncOperation(asyncOperation *foundation.IAsyncOperation, genericParamSignature string) error {
var status foundation.AsyncStatus
// We need to obtain the GUID of the AsyncOperationCompletedHandler, but its a generic delegate
// so we also need the generic parameter type's signature:
// AsyncOperationCompletedHandler<genericParamSignature>
iid := winrt.ParameterizedInstanceGUID(foundation.GUIDAsyncOperationCompletedHandler, genericParamSignature)
// Wait until the async operation completes.
waitChan := make(chan struct{})
handler := foundation.NewAsyncOperationCompletedHandler(ole.NewGUID(iid), func(instance *foundation.AsyncOperationCompletedHandler, asyncInfo *foundation.IAsyncOperation, asyncStatus foundation.AsyncStatus) {
status = asyncStatus
close(waitChan)
})
defer handler.Release()
asyncOperation.SetCompleted(handler)
// Wait until async operation has stopped, and finish.
<-waitChan
if status != foundation.AsyncStatusCompleted {
if err := getAsyncError(asyncOperation); err != nil {
return fmt.Errorf("async operation failed with status %d: %w", status, err)
}
return fmt.Errorf("async operation failed with status %d", status)
}
return nil
}
// getAsyncError queries IAsyncInfo from an IAsyncOperation to retrieve
// the error code of a failed async operation. If the HRESULT corresponds
// to a Bluetooth ATT error (facility 0x65), it returns an AttributeProtocolError.
func getAsyncError(asyncOperation *foundation.IAsyncOperation) error {
iid := ole.NewGUID(foundation.GUIDIAsyncInfo)
var asyncInfo *foundation.IAsyncInfo
hr, _, _ := syscall.SyscallN(
asyncOperation.VTable().QueryInterface,
uintptr(unsafe.Pointer(asyncOperation)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&asyncInfo)),
)
if hr != 0 {
return nil
}
defer asyncInfo.Release()
result, err := asyncInfo.GetErrorCode()
if err != nil {
return err
}
if result.Value == 0 {
return nil
}
return hresultToError(uint32(result.Value))
}
// hresultToError converts an HRESULT to an appropriate error type.
// For Bluetooth ATT errors (facility 0x65), it returns an error with the ATT error code.
// For other errors, it returns a generic error with the HRESULT value.
func hresultToError(hr uint32) error {
facility := (hr >> 16) & 0x1FFF
code := hr & 0xFFFF
if facility == 0x65 { // FACILITY_BLUETOOTH_ATT
return fmt.Errorf("Bluetooth ATT error (code 0x%04X)", code)
}
// For FACILITY_WIN32 the lower 16 bits are a plain Win32 error code that
// FormatMessage understands; for other facilities try the full HRESULT.
errCode := hr
if facility == 0x7 { // FACILITY_WIN32
errCode = code
}
buf := make([]uint16, 512)
n, err := windows.FormatMessage(
windows.FORMAT_MESSAGE_FROM_SYSTEM|windows.FORMAT_MESSAGE_IGNORE_INSERTS,
0, errCode, 0, buf, nil,
)
if err == nil && n > 0 {
msg := strings.TrimRight(windows.UTF16ToString(buf[:n]), " \r\n")
return fmt.Errorf("HRESULT 0x%08X: %s", hr, msg)
}
return fmt.Errorf("HRESULT 0x%08X", hr)
}
func (a *Adapter) Address() (MACAddress, error) {
// TODO: get mac address
return MACAddress{}, errors.New("not implemented")
}