@stoprocent/bleno 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jshintrc +5 -0
- package/LICENSE +20 -0
- package/README.md +409 -0
- package/binding.gyp +17 -0
- package/examples/battery-service/README.md +14 -0
- package/examples/battery-service/battery-level-characteristic.js +45 -0
- package/examples/battery-service/battery-service.js +16 -0
- package/examples/battery-service/main.js +28 -0
- package/examples/battery-service/package-lock.json +1314 -0
- package/examples/battery-service/package.json +20 -0
- package/examples/blink1/README.md +44 -0
- package/examples/blink1/blink1-fade-rgb-characteristic.js +42 -0
- package/examples/blink1/blink1-rgb-characteristic.js +38 -0
- package/examples/blink1/blink1-service.js +19 -0
- package/examples/blink1/device-information-service.js +19 -0
- package/examples/blink1/hardware-revision-characteristic.js +32 -0
- package/examples/blink1/main.js +32 -0
- package/examples/blink1/serial-number-characteristic.js +21 -0
- package/examples/echo/characteristic.js +45 -0
- package/examples/echo/main.js +33 -0
- package/examples/pizza/README.md +16 -0
- package/examples/pizza/peripheral.js +57 -0
- package/examples/pizza/pizza-bake-characteristic.js +40 -0
- package/examples/pizza/pizza-crust-characteristic.js +52 -0
- package/examples/pizza/pizza-service.js +20 -0
- package/examples/pizza/pizza-toppings-characteristic.js +41 -0
- package/examples/pizza/pizza.js +58 -0
- package/examples/uart/main.js +23 -0
- package/index.d.ts +153 -0
- package/index.js +1 -0
- package/lib/bleno.js +231 -0
- package/lib/characteristic.js +91 -0
- package/lib/descriptor.js +17 -0
- package/lib/hci-socket/acl-stream.js +37 -0
- package/lib/hci-socket/bindings.js +219 -0
- package/lib/hci-socket/crypto.js +74 -0
- package/lib/hci-socket/gap.js +212 -0
- package/lib/hci-socket/gatt.js +1028 -0
- package/lib/hci-socket/hci-status.json +67 -0
- package/lib/hci-socket/hci.js +796 -0
- package/lib/hci-socket/mgmt.js +89 -0
- package/lib/hci-socket/smp.js +160 -0
- package/lib/hci-socket/vs.js +156 -0
- package/lib/mac/binding.gyp +39 -0
- package/lib/mac/bindings.js +12 -0
- package/lib/mac/src/ble_peripheral_manager.h +32 -0
- package/lib/mac/src/ble_peripheral_manager.mm +241 -0
- package/lib/mac/src/bleno_mac.h +26 -0
- package/lib/mac/src/bleno_mac.mm +167 -0
- package/lib/mac/src/callbacks.h +76 -0
- package/lib/mac/src/callbacks.mm +124 -0
- package/lib/mac/src/napi_objc.h +30 -0
- package/lib/mac/src/napi_objc.mm +286 -0
- package/lib/mac/src/noble_mac.h +34 -0
- package/lib/mac/src/noble_mac.mm +260 -0
- package/lib/mac/src/objc_cpp.h +27 -0
- package/lib/mac/src/objc_cpp.mm +144 -0
- package/lib/mac/src/peripheral.h +23 -0
- package/lib/primary-service.js +19 -0
- package/lib/resolve-bindings.js +19 -0
- package/lib/uuid-util.js +7 -0
- package/package.json +77 -0
- package/prebuilds/android-arm/node.napi.armv7.node +0 -0
- package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
- package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-ia32/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/test/characteristic.test.js +174 -0
- package/test/descriptor.test.js +46 -0
- package/test/mocha.setup.js +0 -0
- package/with-bindings.js +5 -0
- package/with-custom-binding.js +6 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
const debug = require('debug')('hci');
|
|
2
|
+
|
|
3
|
+
const { EventEmitter } = require('events');
|
|
4
|
+
|
|
5
|
+
const BluetoothHciSocket = require('@stoprocent/bluetooth-hci-socket');
|
|
6
|
+
const vendorSpecific = require('./vs');
|
|
7
|
+
|
|
8
|
+
const HCI_COMMAND_PKT = 0x01;
|
|
9
|
+
const HCI_ACLDATA_PKT = 0x02;
|
|
10
|
+
const HCI_EVENT_PKT = 0x04;
|
|
11
|
+
|
|
12
|
+
const ACL_START_NO_FLUSH = 0x00;
|
|
13
|
+
const ACL_CONT = 0x01;
|
|
14
|
+
const ACL_START = 0x02;
|
|
15
|
+
|
|
16
|
+
const EVT_DISCONN_COMPLETE = 0x05;
|
|
17
|
+
const EVT_ENCRYPT_CHANGE = 0x08;
|
|
18
|
+
const EVT_CMD_COMPLETE = 0x0e;
|
|
19
|
+
const EVT_CMD_STATUS = 0x0f;
|
|
20
|
+
const EVT_NUMBER_OF_COMPLETED_PACKETS = 0x13;
|
|
21
|
+
const EVT_LE_META_EVENT = 0x3e;
|
|
22
|
+
|
|
23
|
+
const EVT_LE_CONN_COMPLETE = 0x01;
|
|
24
|
+
const EVT_LE_CONN_UPDATE_COMPLETE = 0x03;
|
|
25
|
+
|
|
26
|
+
const OGF_LINK_CTL = 0x01;
|
|
27
|
+
const OCF_DISCONNECT = 0x0006;
|
|
28
|
+
|
|
29
|
+
const OGF_HOST_CTL = 0x03;
|
|
30
|
+
const OCF_SET_EVENT_MASK = 0x0001;
|
|
31
|
+
const OCF_RESET = 0x0003;
|
|
32
|
+
const OCF_READ_LE_HOST_SUPPORTED = 0x006c;
|
|
33
|
+
const OCF_WRITE_LE_HOST_SUPPORTED = 0x006d;
|
|
34
|
+
|
|
35
|
+
const OGF_INFO_PARAM = 0x04;
|
|
36
|
+
const OCF_READ_LOCAL_VERSION = 0x0001;
|
|
37
|
+
const OCF_READ_BUFFER_SIZE = 0x0005;
|
|
38
|
+
const OCF_READ_BD_ADDR = 0x0009;
|
|
39
|
+
|
|
40
|
+
const OGF_STATUS_PARAM = 0x05;
|
|
41
|
+
const OCF_READ_RSSI = 0x0005;
|
|
42
|
+
|
|
43
|
+
const OGF_LE_CTL = 0x08;
|
|
44
|
+
const OCF_LE_SET_EVENT_MASK = 0x0001;
|
|
45
|
+
const OCF_LE_READ_BUFFER_SIZE = 0x0002;
|
|
46
|
+
const OCF_LE_SET_ADVERTISING_PARAMETERS = 0x0006;
|
|
47
|
+
const OCF_LE_SET_ADVERTISING_DATA = 0x0008;
|
|
48
|
+
const OCF_LE_SET_SCAN_RESPONSE_DATA = 0x0009;
|
|
49
|
+
const OCF_LE_SET_ADVERTISE_ENABLE = 0x000a;
|
|
50
|
+
const OCF_LE_LTK_NEG_REPLY = 0x001B;
|
|
51
|
+
|
|
52
|
+
const DISCONNECT_CMD = OCF_DISCONNECT | OGF_LINK_CTL << 10;
|
|
53
|
+
|
|
54
|
+
const SET_EVENT_MASK_CMD = OCF_SET_EVENT_MASK | OGF_HOST_CTL << 10;
|
|
55
|
+
const RESET_CMD = OCF_RESET | OGF_HOST_CTL << 10;
|
|
56
|
+
const READ_LE_HOST_SUPPORTED_CMD = OCF_READ_LE_HOST_SUPPORTED | OGF_HOST_CTL << 10;
|
|
57
|
+
const WRITE_LE_HOST_SUPPORTED_CMD = OCF_WRITE_LE_HOST_SUPPORTED | OGF_HOST_CTL << 10;
|
|
58
|
+
|
|
59
|
+
const READ_LOCAL_VERSION_CMD = OCF_READ_LOCAL_VERSION | (OGF_INFO_PARAM << 10);
|
|
60
|
+
const READ_BUFFER_SIZE_CMD = OCF_READ_BUFFER_SIZE | (OGF_INFO_PARAM << 10);
|
|
61
|
+
const READ_BD_ADDR_CMD = OCF_READ_BD_ADDR | (OGF_INFO_PARAM << 10);
|
|
62
|
+
|
|
63
|
+
const READ_RSSI_CMD = OCF_READ_RSSI | OGF_STATUS_PARAM << 10;
|
|
64
|
+
|
|
65
|
+
const LE_SET_EVENT_MASK_CMD = OCF_LE_SET_EVENT_MASK | OGF_LE_CTL << 10;
|
|
66
|
+
const LE_READ_BUFFER_SIZE_CMD = OCF_LE_READ_BUFFER_SIZE | OGF_LE_CTL << 10;
|
|
67
|
+
const LE_SET_ADVERTISING_PARAMETERS_CMD = OCF_LE_SET_ADVERTISING_PARAMETERS | OGF_LE_CTL << 10;
|
|
68
|
+
const LE_SET_ADVERTISING_DATA_CMD = OCF_LE_SET_ADVERTISING_DATA | OGF_LE_CTL << 10;
|
|
69
|
+
const LE_SET_SCAN_RESPONSE_DATA_CMD = OCF_LE_SET_SCAN_RESPONSE_DATA | OGF_LE_CTL << 10;
|
|
70
|
+
const LE_SET_ADVERTISE_ENABLE_CMD = OCF_LE_SET_ADVERTISE_ENABLE | OGF_LE_CTL << 10;
|
|
71
|
+
const LE_LTK_NEG_REPLY_CMD = OCF_LE_LTK_NEG_REPLY | OGF_LE_CTL << 10;
|
|
72
|
+
|
|
73
|
+
const HCI_OE_USER_ENDED_CONNECTION = 0x13;
|
|
74
|
+
|
|
75
|
+
const STATUS_MAPPER = require('./hci-status');
|
|
76
|
+
|
|
77
|
+
class Hci extends EventEmitter {
|
|
78
|
+
constructor (options) {
|
|
79
|
+
super();
|
|
80
|
+
|
|
81
|
+
options = options || {};
|
|
82
|
+
this._manufacturer = null;
|
|
83
|
+
this._socket = new BluetoothHciSocket();
|
|
84
|
+
this._isDevUp = null;
|
|
85
|
+
this._state = null;
|
|
86
|
+
this._deviceId = null;
|
|
87
|
+
// le-u min payload size + l2cap header size
|
|
88
|
+
// see Bluetooth spec 4.2 [Vol 3, Part A, Chapter 4]
|
|
89
|
+
this._aclMtu = 23 + 4;
|
|
90
|
+
this._aclMaxInProgress = 1;
|
|
91
|
+
this._bindParams = 'bindParams' in options ? options.bindParams : undefined;
|
|
92
|
+
|
|
93
|
+
this.resetBuffers();
|
|
94
|
+
|
|
95
|
+
this.on('stateChange', this.onStateChange.bind(this));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
init () {
|
|
99
|
+
this._socket.on('data', this.onSocketData.bind(this));
|
|
100
|
+
this._socket.on('error', this.onSocketError.bind(this));
|
|
101
|
+
|
|
102
|
+
const deviceId = process.env.BLENO_HCI_DEVICE_ID ? parseInt(process.env.BLENO_HCI_DEVICE_ID) : undefined;
|
|
103
|
+
|
|
104
|
+
if (process.env.HCI_CHANNEL_USER) {
|
|
105
|
+
this._deviceId = this._socket.bindUser(deviceId, this._bindParams);
|
|
106
|
+
|
|
107
|
+
this._socket.start();
|
|
108
|
+
|
|
109
|
+
this.reset();
|
|
110
|
+
} else {
|
|
111
|
+
this._deviceId = this._socket.bindRaw(deviceId, this._bindParams);
|
|
112
|
+
this._socket.start();
|
|
113
|
+
|
|
114
|
+
this.pollIsDevUp();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
resetBuffers () {
|
|
119
|
+
this._mainHandle = null;
|
|
120
|
+
this._handleAclsInProgress = {};
|
|
121
|
+
this._handleBuffers = {};
|
|
122
|
+
this._aclOutQueue = [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pollIsDevUp () {
|
|
126
|
+
const isDevUp = this._socket.isDevUp();
|
|
127
|
+
|
|
128
|
+
if (this._isDevUp !== isDevUp) {
|
|
129
|
+
if (isDevUp) {
|
|
130
|
+
this.setSocketFilter();
|
|
131
|
+
this.initDev();
|
|
132
|
+
} else {
|
|
133
|
+
this.emit('stateChange', 'poweredOff');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this._isDevUp = isDevUp;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
setTimeout(this.pollIsDevUp.bind(this), 1000);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
initDev () {
|
|
143
|
+
this.resetBuffers();
|
|
144
|
+
this.setEventMask();
|
|
145
|
+
this.setLeEventMask();
|
|
146
|
+
this.readLocalVersion();
|
|
147
|
+
this.writeLeHostSupported();
|
|
148
|
+
this.readLeHostSupported();
|
|
149
|
+
this.readBdAddr();
|
|
150
|
+
this.leReadBufferSize();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
setSocketFilter () {
|
|
154
|
+
const filter = Buffer.alloc(14);
|
|
155
|
+
const typeMask = (1 << HCI_EVENT_PKT) | (1 << HCI_ACLDATA_PKT);
|
|
156
|
+
const eventMask1 = (1 << EVT_DISCONN_COMPLETE) | (1 << EVT_ENCRYPT_CHANGE) | (1 << EVT_CMD_COMPLETE) | (1 << EVT_CMD_STATUS) | (1 << EVT_NUMBER_OF_COMPLETED_PACKETS);
|
|
157
|
+
const eventMask2 = (1 << (EVT_LE_META_EVENT - 32));
|
|
158
|
+
const opcode = 0;
|
|
159
|
+
|
|
160
|
+
filter.writeUInt32LE(typeMask, 0);
|
|
161
|
+
filter.writeUInt32LE(eventMask1, 4);
|
|
162
|
+
filter.writeUInt32LE(eventMask2, 8);
|
|
163
|
+
filter.writeUInt16LE(opcode, 12);
|
|
164
|
+
|
|
165
|
+
debug('setting filter to: ' + filter.toString('hex'));
|
|
166
|
+
this._socket.setFilter(filter);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setEventMask () {
|
|
170
|
+
const cmd = Buffer.alloc(12);
|
|
171
|
+
const eventMask = Buffer.from('fffffbff07f8bf3d', 'hex');
|
|
172
|
+
|
|
173
|
+
// header
|
|
174
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
175
|
+
cmd.writeUInt16LE(SET_EVENT_MASK_CMD, 1);
|
|
176
|
+
|
|
177
|
+
// length
|
|
178
|
+
cmd.writeUInt8(eventMask.length, 3);
|
|
179
|
+
|
|
180
|
+
eventMask.copy(cmd, 4);
|
|
181
|
+
|
|
182
|
+
debug('set event mask - writing: ' + cmd.toString('hex'));
|
|
183
|
+
this._socket.write(cmd);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
setAddress (address) {
|
|
187
|
+
// Command
|
|
188
|
+
const addrCmd = vendorSpecific.setAddressCmd(this._manufacturer, address);
|
|
189
|
+
|
|
190
|
+
if (addrCmd !== null && Buffer.isBuffer(addrCmd)) {
|
|
191
|
+
// Make Command Buffer
|
|
192
|
+
const cmd = Buffer.alloc(1 + addrCmd.byteLength);
|
|
193
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
194
|
+
addrCmd.copy(cmd, 1);
|
|
195
|
+
|
|
196
|
+
debug(`set address - writing: ${cmd.toString('hex')}`);
|
|
197
|
+
this._socket.write(cmd);
|
|
198
|
+
this.readBdAddr();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
reset () {
|
|
203
|
+
const cmd = Buffer.alloc(4);
|
|
204
|
+
|
|
205
|
+
// header
|
|
206
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
207
|
+
cmd.writeUInt16LE(OCF_RESET | OGF_HOST_CTL << 10, 1);
|
|
208
|
+
|
|
209
|
+
// length
|
|
210
|
+
cmd.writeUInt8(0x00, 3);
|
|
211
|
+
|
|
212
|
+
debug('reset - writing: ' + cmd.toString('hex'));
|
|
213
|
+
this._socket.write(cmd);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
readLeHostSupported () {
|
|
217
|
+
const cmd = Buffer.alloc(4);
|
|
218
|
+
|
|
219
|
+
// header
|
|
220
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
221
|
+
cmd.writeUInt16LE(READ_LE_HOST_SUPPORTED_CMD, 1);
|
|
222
|
+
|
|
223
|
+
// length
|
|
224
|
+
cmd.writeUInt8(0x00, 3);
|
|
225
|
+
|
|
226
|
+
debug('read LE host supported - writing: ' + cmd.toString('hex'));
|
|
227
|
+
this._socket.write(cmd);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
writeLeHostSupported () {
|
|
231
|
+
const cmd = Buffer.alloc(6);
|
|
232
|
+
|
|
233
|
+
// header
|
|
234
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
235
|
+
cmd.writeUInt16LE(WRITE_LE_HOST_SUPPORTED_CMD, 1);
|
|
236
|
+
|
|
237
|
+
// length
|
|
238
|
+
cmd.writeUInt8(0x02, 3);
|
|
239
|
+
|
|
240
|
+
// data
|
|
241
|
+
cmd.writeUInt8(0x01, 4); // le
|
|
242
|
+
cmd.writeUInt8(0x00, 5); // simul
|
|
243
|
+
|
|
244
|
+
debug('write LE host supported - writing: ' + cmd.toString('hex'));
|
|
245
|
+
this._socket.write(cmd);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
readLocalVersion () {
|
|
249
|
+
const cmd = Buffer.alloc(4);
|
|
250
|
+
|
|
251
|
+
// header
|
|
252
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
253
|
+
cmd.writeUInt16LE(READ_LOCAL_VERSION_CMD, 1);
|
|
254
|
+
|
|
255
|
+
// length
|
|
256
|
+
cmd.writeUInt8(0x0, 3);
|
|
257
|
+
|
|
258
|
+
debug('read local version - writing: ' + cmd.toString('hex'));
|
|
259
|
+
this._socket.write(cmd);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
readBdAddr () {
|
|
263
|
+
const cmd = Buffer.alloc(4);
|
|
264
|
+
|
|
265
|
+
// header
|
|
266
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
267
|
+
cmd.writeUInt16LE(READ_BD_ADDR_CMD, 1);
|
|
268
|
+
|
|
269
|
+
// length
|
|
270
|
+
cmd.writeUInt8(0x0, 3);
|
|
271
|
+
|
|
272
|
+
debug('read bd addr - writing: ' + cmd.toString('hex'));
|
|
273
|
+
this._socket.write(cmd);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
setLeEventMask () {
|
|
277
|
+
const cmd = Buffer.alloc(12);
|
|
278
|
+
const leEventMask = Buffer.from('1f00000000000000', 'hex');
|
|
279
|
+
|
|
280
|
+
// header
|
|
281
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
282
|
+
cmd.writeUInt16LE(LE_SET_EVENT_MASK_CMD, 1);
|
|
283
|
+
|
|
284
|
+
// length
|
|
285
|
+
cmd.writeUInt8(leEventMask.length, 3);
|
|
286
|
+
|
|
287
|
+
leEventMask.copy(cmd, 4);
|
|
288
|
+
|
|
289
|
+
debug('set le event mask - writing: ' + cmd.toString('hex'));
|
|
290
|
+
this._socket.write(cmd);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
setAdvertisingParameters () {
|
|
294
|
+
const cmd = Buffer.alloc(19);
|
|
295
|
+
|
|
296
|
+
// header
|
|
297
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
298
|
+
cmd.writeUInt16LE(LE_SET_ADVERTISING_PARAMETERS_CMD, 1);
|
|
299
|
+
|
|
300
|
+
// length
|
|
301
|
+
cmd.writeUInt8(15, 3);
|
|
302
|
+
|
|
303
|
+
const advertisementInterval = Math.floor((process.env.BLENO_ADVERTISING_INTERVAL ? parseFloat(process.env.BLENO_ADVERTISING_INTERVAL) : 100) * 1.6);
|
|
304
|
+
|
|
305
|
+
// data
|
|
306
|
+
cmd.writeUInt16LE(advertisementInterval, 4); // min interval
|
|
307
|
+
cmd.writeUInt16LE(advertisementInterval, 6); // max interval
|
|
308
|
+
cmd.writeUInt8(0x00, 8); // adv type
|
|
309
|
+
cmd.writeUInt8(0x00, 9); // own addr typ
|
|
310
|
+
cmd.writeUInt8(0x00, 10); // direct addr type
|
|
311
|
+
(Buffer.from('000000000000', 'hex')).copy(cmd, 11); // direct addr
|
|
312
|
+
cmd.writeUInt8(0x07, 17);
|
|
313
|
+
cmd.writeUInt8(0x00, 18);
|
|
314
|
+
|
|
315
|
+
debug('set advertisement parameters - writing: ' + cmd.toString('hex'));
|
|
316
|
+
this._socket.write(cmd);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
setAdvertisingData (data) {
|
|
320
|
+
const cmd = Buffer.alloc(36);
|
|
321
|
+
|
|
322
|
+
cmd.fill(0x00);
|
|
323
|
+
|
|
324
|
+
// header
|
|
325
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
326
|
+
cmd.writeUInt16LE(LE_SET_ADVERTISING_DATA_CMD, 1);
|
|
327
|
+
|
|
328
|
+
// length
|
|
329
|
+
cmd.writeUInt8(32, 3);
|
|
330
|
+
|
|
331
|
+
// data
|
|
332
|
+
cmd.writeUInt8(data.length, 4);
|
|
333
|
+
data.copy(cmd, 5);
|
|
334
|
+
|
|
335
|
+
debug('set advertisement data - writing: ' + cmd.toString('hex'));
|
|
336
|
+
this._socket.write(cmd);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
setScanResponseData (data) {
|
|
340
|
+
const cmd = Buffer.alloc(36);
|
|
341
|
+
|
|
342
|
+
cmd.fill(0x00);
|
|
343
|
+
|
|
344
|
+
// header
|
|
345
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
346
|
+
cmd.writeUInt16LE(LE_SET_SCAN_RESPONSE_DATA_CMD, 1);
|
|
347
|
+
|
|
348
|
+
// length
|
|
349
|
+
cmd.writeUInt8(32, 3);
|
|
350
|
+
|
|
351
|
+
// data
|
|
352
|
+
cmd.writeUInt8(data.length, 4);
|
|
353
|
+
data.copy(cmd, 5);
|
|
354
|
+
|
|
355
|
+
debug('set scan response data - writing: ' + cmd.toString('hex'));
|
|
356
|
+
this._socket.write(cmd);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
setAdvertiseEnable (enabled) {
|
|
360
|
+
const cmd = Buffer.alloc(5);
|
|
361
|
+
|
|
362
|
+
// header
|
|
363
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
364
|
+
cmd.writeUInt16LE(LE_SET_ADVERTISE_ENABLE_CMD, 1);
|
|
365
|
+
|
|
366
|
+
// length
|
|
367
|
+
cmd.writeUInt8(0x01, 3);
|
|
368
|
+
|
|
369
|
+
// data
|
|
370
|
+
cmd.writeUInt8(enabled ? 0x01 : 0x00, 4); // enable: 0 -> disabled, 1 -> enabled
|
|
371
|
+
|
|
372
|
+
debug('set advertise enable - writing: ' + cmd.toString('hex'));
|
|
373
|
+
this._socket.write(cmd);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
disconnect (handle, reason) {
|
|
377
|
+
const cmd = Buffer.alloc(7);
|
|
378
|
+
|
|
379
|
+
reason = reason || HCI_OE_USER_ENDED_CONNECTION;
|
|
380
|
+
|
|
381
|
+
// header
|
|
382
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
383
|
+
cmd.writeUInt16LE(DISCONNECT_CMD, 1);
|
|
384
|
+
|
|
385
|
+
// length
|
|
386
|
+
cmd.writeUInt8(0x03, 3);
|
|
387
|
+
|
|
388
|
+
// data
|
|
389
|
+
cmd.writeUInt16LE(handle, 4); // handle
|
|
390
|
+
cmd.writeUInt8(reason, 6); // reason
|
|
391
|
+
|
|
392
|
+
debug('disconnect - writing: ' + cmd.toString('hex'));
|
|
393
|
+
this._socket.write(cmd);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
readRssi (handle) {
|
|
397
|
+
const cmd = Buffer.alloc(6);
|
|
398
|
+
|
|
399
|
+
// header
|
|
400
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
401
|
+
cmd.writeUInt16LE(READ_RSSI_CMD, 1);
|
|
402
|
+
|
|
403
|
+
// length
|
|
404
|
+
cmd.writeUInt8(0x02, 3);
|
|
405
|
+
|
|
406
|
+
// data
|
|
407
|
+
cmd.writeUInt16LE(handle, 4); // handle
|
|
408
|
+
|
|
409
|
+
debug('read rssi - writing: ' + cmd.toString('hex'));
|
|
410
|
+
this._socket.write(cmd);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
leReadBufferSize () {
|
|
414
|
+
const cmd = Buffer.alloc(4);
|
|
415
|
+
|
|
416
|
+
// header
|
|
417
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
418
|
+
cmd.writeUInt16LE(LE_READ_BUFFER_SIZE_CMD, 1);
|
|
419
|
+
|
|
420
|
+
// length
|
|
421
|
+
cmd.writeUInt8(0x0, 3);
|
|
422
|
+
|
|
423
|
+
debug('le read buffer size - writing: ' + cmd.toString('hex'));
|
|
424
|
+
this._socket.write(cmd);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
readBufferSize () {
|
|
428
|
+
const cmd = Buffer.alloc(4);
|
|
429
|
+
|
|
430
|
+
// header
|
|
431
|
+
cmd.writeUInt8(HCI_COMMAND_PKT, 0);
|
|
432
|
+
cmd.writeUInt16LE(READ_BUFFER_SIZE_CMD, 1);
|
|
433
|
+
|
|
434
|
+
// length
|
|
435
|
+
cmd.writeUInt8(0x0, 3);
|
|
436
|
+
|
|
437
|
+
debug('read buffer size - writing: ' + cmd.toString('hex'));
|
|
438
|
+
this._socket.write(cmd);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
queueAclDataPkt (handle, cid, data) {
|
|
442
|
+
let hf = handle | ACL_START_NO_FLUSH << 12;
|
|
443
|
+
// l2cap pdu may be fragmented on hci level
|
|
444
|
+
let l2capPdu = Buffer.alloc(4 + data.length);
|
|
445
|
+
l2capPdu.writeUInt16LE(data.length, 0);
|
|
446
|
+
l2capPdu.writeUInt16LE(cid, 2);
|
|
447
|
+
data.copy(l2capPdu, 4);
|
|
448
|
+
let fragId = 0;
|
|
449
|
+
|
|
450
|
+
while (l2capPdu.length) {
|
|
451
|
+
const frag = l2capPdu.slice(0, this._aclMtu);
|
|
452
|
+
l2capPdu = l2capPdu.slice(frag.length);
|
|
453
|
+
const pkt = Buffer.alloc(5 + frag.length);
|
|
454
|
+
|
|
455
|
+
// hci header
|
|
456
|
+
pkt.writeUInt8(HCI_ACLDATA_PKT, 0);
|
|
457
|
+
pkt.writeUInt16LE(hf, 1);
|
|
458
|
+
hf |= ACL_CONT << 12;
|
|
459
|
+
pkt.writeUInt16LE(frag.length, 3); // hci pdu length
|
|
460
|
+
|
|
461
|
+
frag.copy(pkt, 5);
|
|
462
|
+
|
|
463
|
+
this._aclOutQueue.push({
|
|
464
|
+
handle,
|
|
465
|
+
pkt,
|
|
466
|
+
fragId: fragId++
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
this.pushAclOutQueue();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
pushAclOutQueue () {
|
|
474
|
+
let inProgress = 0;
|
|
475
|
+
for (const handle in this._handleAclsInProgress) {
|
|
476
|
+
inProgress += this._handleAclsInProgress[handle];
|
|
477
|
+
}
|
|
478
|
+
while (inProgress < this._aclMaxInProgress && this._aclOutQueue.length) {
|
|
479
|
+
inProgress++;
|
|
480
|
+
this.writeOneAclDataPkt();
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (inProgress >= this._aclMaxInProgress && this._aclOutQueue.length) {
|
|
484
|
+
debug('acl out queue congested');
|
|
485
|
+
debug('\tin progress = ' + inProgress);
|
|
486
|
+
debug('\twaiting = ' + this._aclOutQueue.length);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
writeOneAclDataPkt () {
|
|
491
|
+
const pkt = this._aclOutQueue.shift();
|
|
492
|
+
this._handleAclsInProgress[pkt.handle]++;
|
|
493
|
+
debug('write acl data pkt frag ' + pkt.fragId + ' handle ' + pkt.handle + ' - writing: ' + pkt.pkt.toString('hex'));
|
|
494
|
+
this._socket.write(pkt.pkt);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
onSocketData (data) {
|
|
498
|
+
debug('onSocketData: ' + data.toString('hex'));
|
|
499
|
+
|
|
500
|
+
const eventType = data.readUInt8(0);
|
|
501
|
+
let handle;
|
|
502
|
+
|
|
503
|
+
debug('\tevent type = ' + eventType);
|
|
504
|
+
|
|
505
|
+
if (HCI_EVENT_PKT === eventType) {
|
|
506
|
+
const subEventType = data.readUInt8(1);
|
|
507
|
+
|
|
508
|
+
debug('\tsub event type = ' + subEventType);
|
|
509
|
+
|
|
510
|
+
if (subEventType === EVT_DISCONN_COMPLETE) {
|
|
511
|
+
handle = data.readUInt16LE(4);
|
|
512
|
+
debug('\t\thandle = ' + handle);
|
|
513
|
+
if (handle !== this._mainHandle) {
|
|
514
|
+
debug('\tignoring event because handle is unknown to bleno.');
|
|
515
|
+
debug('This might be OK in a multi role scenario in which the handle is part of a noble connection.');
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const reason = data.readUInt8(6);
|
|
520
|
+
debug('\t\treason = ' + reason);
|
|
521
|
+
|
|
522
|
+
/* As per Bluetooth Core specs:
|
|
523
|
+
When the Host receives a Disconnection Complete, Disconnection Physical
|
|
524
|
+
Link Complete or Disconnection Logical Link Complete event, the Host shall
|
|
525
|
+
assume that all unacknowledged HCI Data Packets that have been sent to the
|
|
526
|
+
Controller for the returned Handle have been flushed, and that the
|
|
527
|
+
corresponding data buffers have been freed. */
|
|
528
|
+
delete this._handleAclsInProgress[handle];
|
|
529
|
+
this._mainHandle = null;
|
|
530
|
+
const aclOutQueue = [];
|
|
531
|
+
let discarded = 0;
|
|
532
|
+
for (const i in this._aclOutQueue) {
|
|
533
|
+
if (this._aclOutQueue[i].handle !== handle) {
|
|
534
|
+
aclOutQueue.push(this._aclOutQueue[i]);
|
|
535
|
+
} else {
|
|
536
|
+
discarded++;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (discarded) {
|
|
540
|
+
debug('\t\tacls discarded = ' + discarded);
|
|
541
|
+
}
|
|
542
|
+
this._aclOutQueue = aclOutQueue;
|
|
543
|
+
this.pushAclOutQueue();
|
|
544
|
+
this.emit('disconnComplete', handle, reason);
|
|
545
|
+
} else if (subEventType === EVT_ENCRYPT_CHANGE) {
|
|
546
|
+
handle = data.readUInt16LE(4);
|
|
547
|
+
const encrypt = data.readUInt8(6);
|
|
548
|
+
|
|
549
|
+
debug('\t\thandle = ' + handle);
|
|
550
|
+
debug('\t\tencrypt = ' + encrypt);
|
|
551
|
+
|
|
552
|
+
this.emit('encryptChange', handle, encrypt);
|
|
553
|
+
} else if (subEventType === EVT_CMD_COMPLETE) {
|
|
554
|
+
const ncmd = data.readUInt8(3);
|
|
555
|
+
const cmd = data.readUInt16LE(4);
|
|
556
|
+
const status = data.readUInt8(6);
|
|
557
|
+
const result = data.slice(7);
|
|
558
|
+
|
|
559
|
+
debug('\t\tncmd = ' + ncmd);
|
|
560
|
+
debug('\t\tcmd = ' + cmd);
|
|
561
|
+
debug('\t\tstatus = ' + status);
|
|
562
|
+
debug('\t\tresult = ' + result.toString('hex'));
|
|
563
|
+
|
|
564
|
+
this.processCmdCompleteEvent(cmd, status, result);
|
|
565
|
+
} else if (subEventType === EVT_LE_META_EVENT) {
|
|
566
|
+
const leMetaEventType = data.readUInt8(3);
|
|
567
|
+
const leMetaEventStatus = data.readUInt8(4);
|
|
568
|
+
const leMetaEventData = data.slice(5);
|
|
569
|
+
|
|
570
|
+
debug('\t\tLE meta event type = ' + leMetaEventType);
|
|
571
|
+
debug('\t\tLE meta event status = ' + leMetaEventStatus);
|
|
572
|
+
debug('\t\tLE meta event data = ' + leMetaEventData.toString('hex'));
|
|
573
|
+
|
|
574
|
+
this.processLeMetaEvent(leMetaEventType, leMetaEventStatus, leMetaEventData);
|
|
575
|
+
} else if (subEventType === EVT_NUMBER_OF_COMPLETED_PACKETS) {
|
|
576
|
+
const handles = data.readUInt8(3);
|
|
577
|
+
for (let j = 0; j < handles; j++) {
|
|
578
|
+
const handle_ = data.readUInt16LE(4 + j * 4);
|
|
579
|
+
const pkts = data.readUInt16LE(6 + j * 4);
|
|
580
|
+
debug('\thandle = ' + handle_);
|
|
581
|
+
debug('\t\tcompleted = ' + pkts);
|
|
582
|
+
if (this._handleAclsInProgress[handle_] === undefined) {
|
|
583
|
+
debug('\t\talready closed');
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
if (pkts > this._handleAclsInProgress[handle_]) {
|
|
587
|
+
// Linux kernel may send acl packets by itself, so be ready for underflow
|
|
588
|
+
this._handleAclsInProgress[handle_] = 0;
|
|
589
|
+
} else {
|
|
590
|
+
this._handleAclsInProgress[handle_] -= pkts;
|
|
591
|
+
}
|
|
592
|
+
debug('\t\tin progress = ' + this._handleAclsInProgress[handle_]);
|
|
593
|
+
}
|
|
594
|
+
this.pushAclOutQueue();
|
|
595
|
+
}
|
|
596
|
+
} else if (HCI_ACLDATA_PKT === eventType) {
|
|
597
|
+
const flags = data.readUInt16LE(1) >> 12;
|
|
598
|
+
handle = data.readUInt16LE(1) & 0x0fff;
|
|
599
|
+
|
|
600
|
+
if (ACL_START === flags) {
|
|
601
|
+
const cid = data.readUInt16LE(7);
|
|
602
|
+
|
|
603
|
+
const length = data.readUInt16LE(5);
|
|
604
|
+
const pktData = data.slice(9);
|
|
605
|
+
|
|
606
|
+
debug('\t\tcid = ' + cid);
|
|
607
|
+
|
|
608
|
+
if (length === pktData.length) {
|
|
609
|
+
debug('\t\thandle = ' + handle);
|
|
610
|
+
debug('\t\tdata = ' + pktData.toString('hex'));
|
|
611
|
+
|
|
612
|
+
this.emit('aclDataPkt', handle, cid, pktData);
|
|
613
|
+
} else {
|
|
614
|
+
this._handleBuffers[handle] = {
|
|
615
|
+
length,
|
|
616
|
+
cid,
|
|
617
|
+
data: pktData
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
} else if (ACL_CONT === flags) {
|
|
621
|
+
if (!this._handleBuffers[handle] || !this._handleBuffers[handle].data) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
this._handleBuffers[handle].data = Buffer.concat([
|
|
626
|
+
this._handleBuffers[handle].data,
|
|
627
|
+
data.slice(5)
|
|
628
|
+
]);
|
|
629
|
+
|
|
630
|
+
if (this._handleBuffers[handle].data.length === this._handleBuffers[handle].length) {
|
|
631
|
+
this.emit('aclDataPkt', handle, this._handleBuffers[handle].cid, this._handleBuffers[handle].data);
|
|
632
|
+
|
|
633
|
+
delete this._handleBuffers[handle];
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
onSocketError (error) {
|
|
640
|
+
debug('onSocketError: ' + error.message);
|
|
641
|
+
|
|
642
|
+
if (error.code === 'EPERM') {
|
|
643
|
+
this.emit('stateChange', 'unauthorized');
|
|
644
|
+
} else if (error.message === 'Network is down') {
|
|
645
|
+
// no-op
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
processCmdCompleteEvent (cmd, status, result) {
|
|
650
|
+
if (cmd === RESET_CMD) {
|
|
651
|
+
this.initDev();
|
|
652
|
+
} else if (cmd === READ_LE_HOST_SUPPORTED_CMD) {
|
|
653
|
+
if (status === 0) {
|
|
654
|
+
const le = result.readUInt8(0);
|
|
655
|
+
const simul = result.readUInt8(1);
|
|
656
|
+
|
|
657
|
+
debug('\t\t\tle = ' + le);
|
|
658
|
+
debug('\t\t\tsimul = ' + simul);
|
|
659
|
+
}
|
|
660
|
+
} else if (cmd === READ_LOCAL_VERSION_CMD) {
|
|
661
|
+
const hciVer = result.readUInt8(0);
|
|
662
|
+
const hciRev = result.readUInt16LE(1);
|
|
663
|
+
const lmpVer = result.readInt8(3);
|
|
664
|
+
const manufacturer = result.readUInt16LE(4);
|
|
665
|
+
const lmpSubVer = result.readUInt16LE(6);
|
|
666
|
+
|
|
667
|
+
// Set manufacturer
|
|
668
|
+
this._manufacturer = manufacturer;
|
|
669
|
+
|
|
670
|
+
if (hciVer < 0x06) {
|
|
671
|
+
this.emit('stateChange', 'unsupported');
|
|
672
|
+
} else if (this._state !== 'poweredOn') {
|
|
673
|
+
this.setAdvertiseEnable(false);
|
|
674
|
+
this.setAdvertisingParameters();
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
this.emit('readLocalVersion', hciVer, hciRev, lmpVer, manufacturer, lmpSubVer);
|
|
678
|
+
} else if (cmd === READ_BD_ADDR_CMD) {
|
|
679
|
+
this.addressType = 'public';
|
|
680
|
+
this.address = result.toString('hex').match(/.{1,2}/g).reverse().join(':');
|
|
681
|
+
|
|
682
|
+
debug('address = ' + this.address);
|
|
683
|
+
|
|
684
|
+
this.emit('addressChange', this.address);
|
|
685
|
+
} else if (cmd === LE_SET_ADVERTISING_PARAMETERS_CMD) {
|
|
686
|
+
this.emit('stateChange', 'poweredOn');
|
|
687
|
+
|
|
688
|
+
this.emit('leAdvertisingParametersSet', status);
|
|
689
|
+
} else if (cmd === LE_SET_ADVERTISING_DATA_CMD) {
|
|
690
|
+
this.emit('leAdvertisingDataSet', status);
|
|
691
|
+
} else if (cmd === LE_SET_SCAN_RESPONSE_DATA_CMD) {
|
|
692
|
+
this.emit('leScanResponseDataSet', status);
|
|
693
|
+
} else if (cmd === LE_SET_ADVERTISE_ENABLE_CMD) {
|
|
694
|
+
this.emit('leAdvertiseEnableSet', status);
|
|
695
|
+
} else if (cmd === READ_RSSI_CMD) {
|
|
696
|
+
const handle = result.readUInt16LE(0);
|
|
697
|
+
const rssi = result.readInt8(2);
|
|
698
|
+
|
|
699
|
+
debug('\t\t\thandle = ' + handle);
|
|
700
|
+
debug('\t\t\trssi = ' + rssi);
|
|
701
|
+
|
|
702
|
+
this.emit('rssiRead', handle, rssi);
|
|
703
|
+
} else if (cmd === LE_LTK_NEG_REPLY_CMD) {
|
|
704
|
+
const handle = result.readUInt16LE(0);
|
|
705
|
+
|
|
706
|
+
debug('\t\t\thandle = ' + handle);
|
|
707
|
+
this.emit('leLtkNegReply', handle);
|
|
708
|
+
} else if (cmd === LE_READ_BUFFER_SIZE_CMD) {
|
|
709
|
+
if (!status) {
|
|
710
|
+
this.processLeReadBufferSize(result);
|
|
711
|
+
}
|
|
712
|
+
} else if (cmd === READ_BUFFER_SIZE_CMD) {
|
|
713
|
+
if (!status) {
|
|
714
|
+
const aclMtu = result.readUInt16LE(0);
|
|
715
|
+
const aclMaxInProgress = result.readUInt16LE(3);
|
|
716
|
+
// sanity
|
|
717
|
+
if (aclMtu && aclMaxInProgress) {
|
|
718
|
+
debug('br/edr acl mtu = ' + aclMtu);
|
|
719
|
+
debug('br/edr acl max pkts = ' + aclMaxInProgress);
|
|
720
|
+
this._aclMtu = aclMtu;
|
|
721
|
+
this._aclMaxInProgress = aclMaxInProgress;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
processLeReadBufferSize (result) {
|
|
728
|
+
const aclMtu = result.readUInt16LE(0);
|
|
729
|
+
const aclMaxInProgress = result.readUInt8(2);
|
|
730
|
+
if (!aclMtu) {
|
|
731
|
+
// as per Bluetooth specs
|
|
732
|
+
debug('falling back to br/edr buffer size');
|
|
733
|
+
this.readBufferSize();
|
|
734
|
+
} else {
|
|
735
|
+
debug('le acl mtu = ' + aclMtu);
|
|
736
|
+
debug('le acl max in progress = ' + aclMaxInProgress);
|
|
737
|
+
this._aclMtu = aclMtu;
|
|
738
|
+
this._aclMaxInProgress = aclMaxInProgress;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
processLeMetaEvent (eventType, status, data) {
|
|
743
|
+
if (eventType === EVT_LE_CONN_COMPLETE) {
|
|
744
|
+
this.processLeConnComplete(status, data);
|
|
745
|
+
} else if (eventType === EVT_LE_CONN_UPDATE_COMPLETE) {
|
|
746
|
+
this.processLeConnUpdateComplete(status, data);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
processLeConnComplete (status, data) {
|
|
751
|
+
const handle = data.readUInt16LE(0);
|
|
752
|
+
const role = data.readUInt8(2);
|
|
753
|
+
const addressType = data.readUInt8(3) === 0x01 ? 'random' : 'public';
|
|
754
|
+
const address = data.slice(4, 10).toString('hex').match(/.{1,2}/g).reverse().join(':');
|
|
755
|
+
const interval = data.readUInt16LE(10) * 1.25;
|
|
756
|
+
const latency = data.readUInt16LE(12); // TODO: multiplier?
|
|
757
|
+
const supervisionTimeout = data.readUInt16LE(14) * 10;
|
|
758
|
+
const masterClockAccuracy = data.readUInt8(16); // TODO: multiplier?
|
|
759
|
+
|
|
760
|
+
debug('\t\t\thandle = ' + handle);
|
|
761
|
+
debug('\t\t\trole = ' + role);
|
|
762
|
+
debug('\t\t\taddress type = ' + addressType);
|
|
763
|
+
debug('\t\t\taddress = ' + address);
|
|
764
|
+
debug('\t\t\tinterval = ' + interval);
|
|
765
|
+
debug('\t\t\tlatency = ' + latency);
|
|
766
|
+
debug('\t\t\tsupervision timeout = ' + supervisionTimeout);
|
|
767
|
+
debug('\t\t\tmaster clock accuracy = ' + masterClockAccuracy);
|
|
768
|
+
|
|
769
|
+
this._mainHandle = handle;
|
|
770
|
+
this._handleAclsInProgress[handle] = 0;
|
|
771
|
+
|
|
772
|
+
this.emit('leConnComplete', status, handle, role, addressType, address, interval, latency, supervisionTimeout, masterClockAccuracy);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
processLeConnUpdateComplete (status, data) {
|
|
776
|
+
const handle = data.readUInt16LE(0);
|
|
777
|
+
const interval = data.readUInt16LE(2) * 1.25;
|
|
778
|
+
const latency = data.readUInt16LE(4); // TODO: multiplier?
|
|
779
|
+
const supervisionTimeout = data.readUInt16LE(6) * 10;
|
|
780
|
+
|
|
781
|
+
debug('\t\t\thandle = ' + handle);
|
|
782
|
+
debug('\t\t\tinterval = ' + interval);
|
|
783
|
+
debug('\t\t\tlatency = ' + latency);
|
|
784
|
+
debug('\t\t\tsupervision timeout = ' + supervisionTimeout);
|
|
785
|
+
|
|
786
|
+
this.emit('leConnUpdateComplete', status, handle, interval, latency, supervisionTimeout);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
onStateChange (state) {
|
|
790
|
+
this._state = state;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
Hci.STATUS_MAPPER = Hci.prototype.STATUS_MAPPER = STATUS_MAPPER;
|
|
795
|
+
|
|
796
|
+
module.exports = Hci;
|