-
Notifications
You must be signed in to change notification settings - Fork 0
/
device.go
172 lines (139 loc) · 3.63 KB
/
device.go
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//go:build linux
package main
import (
"bytes"
"log"
"net"
"sync"
"tinygo.org/x/bluetooth"
)
type Device struct {
adapter *bluetooth.Adapter
rx, tx *bluetooth.Characteristic
// Currently, the SetConnectHandler on Linux does not work,
// Hence we do not know when our device is connected or disconnected.
// The control characteristic is sent from Host to let us know that
// a new connection is being made.
ctrl *bluetooth.Characteristic
connection chan struct{}
// To close a connection, close this channel.
closed chan struct{}
mu sync.Mutex
rxBuf *bytes.Buffer
rxBufCh chan struct{}
}
func NewDevice() *Device {
d := &Device{
adapter: bluetooth.DefaultAdapter,
rx: &bluetooth.Characteristic{},
tx: &bluetooth.Characteristic{},
ctrl: &bluetooth.Characteristic{},
connection: make(chan struct{}),
closed: make(chan struct{}),
rxBuf: bytes.NewBuffer(nil),
rxBufCh: make(chan struct{}),
}
// TODO: not work
// https://github.com/tinygo-org/bluetooth/issues/290
d.adapter.SetConnectHandler(func(device bluetooth.Device, connected bool) {
log.Println(`Connect:`, device, connected)
})
Must(d.adapter.Enable())
service := bluetooth.Service{
UUID: uuidService,
Characteristics: []bluetooth.CharacteristicConfig{
{
Handle: d.rx,
UUID: uuidRx,
Flags: bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission,
WriteEvent: d.onRecv,
},
{
Handle: d.tx,
UUID: uuidTx,
Flags: bluetooth.CharacteristicNotifyPermission | bluetooth.CharacteristicReadPermission,
},
{
Handle: d.ctrl,
UUID: uuidCtrl,
Flags: bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission,
WriteEvent: d.writeControl,
},
},
}
Must(d.adapter.AddService(&service))
a := d.adapter.DefaultAdvertisement()
Must(a.Configure(bluetooth.AdvertisementOptions{
ServiceUUIDs: []bluetooth.UUID{uuidService},
}))
return d
}
func (d *Device) writeControl(client bluetooth.Connection, offset int, p []byte) {
close(d.closed)
d.connection <- struct{}{}
}
func (d *Device) onRecv(client bluetooth.Connection, offset int, p []byte) {
d.mu.Lock()
defer d.mu.Unlock()
d.rxBuf.Write(p)
select {
case d.rxBufCh <- struct{}{}:
default:
}
}
func (d *Device) Address() string {
return Must1(d.adapter.Address()).String()
}
// 超时控制默认为“0”,即不超时,永远广播。
// https://github.com/tinygo-org/bluetooth/blob/a668e1b0a062612faa41ac354f7edd5b25428101/gap_linux.go#L79-L84
func (d *Device) StartAdvertisement() {
a := d.adapter.DefaultAdvertisement()
Must(a.Start())
}
func (d *Device) Write(p []byte) (int, error) {
select {
case <-d.closed:
return 0, errConnClosed
default:
}
return splitWrite(d.tx, p)
}
func (d *Device) Read(p []byte) (int, error) {
d.mu.Lock()
if d.rxBuf.Len() <= 0 {
d.mu.Unlock()
select {
case <-d.rxBufCh:
case <-d.closed:
return 0, errConnClosed
}
d.mu.Lock()
}
n, err := d.rxBuf.Read(p)
d.mu.Unlock()
return n, err
}
func (d *Device) WaitForConnection() {
<-d.connection
d.closed = make(chan struct{})
d.rxBuf.Reset()
}
func main() {
log.SetFlags(log.Flags() | log.Lshortfile)
d := NewDevice()
log.Println(`Address:`, d.Address())
// 库代码硬编码成了无超时,所以只需要调用一次。
log.Println(`Start advertisement`)
d.StartAdvertisement()
for {
log.Println(`Waiting for Connection`)
d.WaitForConnection()
log.Println(`Connected`)
conn := Must1(net.Dial(`tcp4`, `localhost:22`))
go func() {
<-d.closed
conn.Close()
}()
Stream(conn, d)
}
}