You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2017/08/29 21:44:40 UTC
[mynewt-newtmgr] branch master updated: Re-add OS-specific vendored
packages.
This is an automated email from the ASF dual-hosted git repository.
ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newtmgr.git
The following commit(s) were added to refs/heads/master by this push:
new c704a92 Re-add OS-specific vendored packages.
c704a92 is described below
commit c704a92b2144a3a320ed58fb1d2ba8abac5260da
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Aug 29 14:42:33 2017 -0700
Re-add OS-specific vendored packages.
---
Gopkg.lock | 14 +-
Gopkg.toml | 4 +
README.md | 6 +-
vendor/github.com/currantlabs/ble/.gitignore | 5 +
vendor/github.com/currantlabs/ble/LICENSE | 27 +
vendor/github.com/currantlabs/ble/README.md | 3 +
vendor/github.com/currantlabs/ble/addr.go | 20 +
vendor/github.com/currantlabs/ble/adv.go | 28 +
vendor/github.com/currantlabs/ble/client.go | 69 +
vendor/github.com/currantlabs/ble/conn.go | 39 +
vendor/github.com/currantlabs/ble/const.go | 33 +
vendor/github.com/currantlabs/ble/darwin/adv.go | 73 +
vendor/github.com/currantlabs/ble/darwin/client.go | 282 ++++
vendor/github.com/currantlabs/ble/darwin/conn.go | 135 ++
vendor/github.com/currantlabs/ble/darwin/device.go | 521 +++++++
vendor/github.com/currantlabs/ble/darwin/log.go | 7 +
vendor/github.com/currantlabs/ble/darwin/msg.go | 64 +
vendor/github.com/currantlabs/ble/darwin/option.go | 20 +
vendor/github.com/currantlabs/ble/darwin/state.go | 26 +
vendor/github.com/currantlabs/ble/darwin/util.go | 11 +
vendor/github.com/currantlabs/ble/dev.go | 42 +
vendor/github.com/currantlabs/ble/error.go | 75 +
.../ble/examples/basic/advertiser/main.go | 46 +
.../ble/examples/basic/explorer/main.go | 182 +++
.../currantlabs/ble/examples/basic/scanner/main.go | 68 +
.../currantlabs/ble/examples/basic/server/main.go | 55 +
.../currantlabs/ble/examples/blesh/README.md | 235 +++
.../currantlabs/ble/examples/blesh/adv.go | 34 +
.../currantlabs/ble/examples/blesh/exp.go | 57 +
.../currantlabs/ble/examples/blesh/filter.go | 32 +
.../currantlabs/ble/examples/blesh/flg.go | 17 +
.../currantlabs/ble/examples/blesh/lnx.go | 52 +
.../currantlabs/ble/examples/blesh/main.go | 425 ++++++
.../currantlabs/ble/examples/blesh/util.go | 54 +
.../currantlabs/ble/examples/lib/battery.go | 23 +
.../currantlabs/ble/examples/lib/count.go | 65 +
.../ble/examples/lib/dev/default_darwin.go | 11 +
.../ble/examples/lib/dev/default_linux.go | 11 +
.../currantlabs/ble/examples/lib/dev/dev.go | 8 +
.../currantlabs/ble/examples/lib/echo.go | 58 +
.../currantlabs/ble/examples/lib/uuids.go | 11 +
vendor/github.com/currantlabs/ble/gatt.go | 188 +++
vendor/github.com/currantlabs/ble/handler.go | 188 +++
.../github.com/currantlabs/ble/linux/adv/const.go | 57 +
.../github.com/currantlabs/ble/linux/adv/packet.go | 289 ++++
.../github.com/currantlabs/ble/linux/att/README.md | 40 +
vendor/github.com/currantlabs/ble/linux/att/att.go | 30 +
.../currantlabs/ble/linux/att/att_gen.go | 639 ++++++++
.../github.com/currantlabs/ble/linux/att/attr.go | 14 +
.../github.com/currantlabs/ble/linux/att/client.go | 560 +++++++
vendor/github.com/currantlabs/ble/linux/att/db.go | 210 +++
vendor/github.com/currantlabs/ble/linux/att/log.go | 7 +
.../github.com/currantlabs/ble/linux/att/server.go | 597 ++++++++
vendor/github.com/currantlabs/ble/linux/device.go | 165 +++
.../currantlabs/ble/linux/gatt/README.md | 47 +
.../currantlabs/ble/linux/gatt/client.go | 385 +++++
.../currantlabs/ble/linux/gatt/server.go | 90 ++
.../github.com/currantlabs/ble/linux/hci/README.md | 116 ++
vendor/github.com/currantlabs/ble/linux/hci/adv.go | 135 ++
.../github.com/currantlabs/ble/linux/hci/buffer.go | 74 +
.../currantlabs/ble/linux/hci/cmd/cmd.go | 31 +
.../currantlabs/ble/linux/hci/cmd/cmd_gen.go | 1562 ++++++++++++++++++++
.../github.com/currantlabs/ble/linux/hci/conn.go | 338 +++++
.../github.com/currantlabs/ble/linux/hci/const.go | 30 +
.../github.com/currantlabs/ble/linux/hci/error.go | 161 ++
.../currantlabs/ble/linux/hci/evt/evt.go | 55 +
.../currantlabs/ble/linux/hci/evt/evt_gen.go | 227 +++
vendor/github.com/currantlabs/ble/linux/hci/gap.go | 214 +++
vendor/github.com/currantlabs/ble/linux/hci/hci.go | 522 +++++++
vendor/github.com/currantlabs/ble/linux/hci/log.go | 7 +
.../github.com/currantlabs/ble/linux/hci/option.go | 42 +
.../github.com/currantlabs/ble/linux/hci/params.go | 55 +
.../github.com/currantlabs/ble/linux/hci/signal.go | 223 +++
.../currantlabs/ble/linux/hci/signal_gen.go | 205 +++
vendor/github.com/currantlabs/ble/linux/hci/smp.go | 61 +
.../currantlabs/ble/linux/hci/socket/dummy.go | 10 +
.../currantlabs/ble/linux/hci/socket/socket.go | 146 ++
.../currantlabs/ble/linux/tools/codegen/Makefile | 12 +
.../currantlabs/ble/linux/tools/codegen/att.json | 431 ++++++
.../currantlabs/ble/linux/tools/codegen/att.tmpl | 7 +
.../currantlabs/ble/linux/tools/codegen/cmd.json | 1246 ++++++++++++++++
.../currantlabs/ble/linux/tools/codegen/cmd.tmpl | 29 +
.../currantlabs/ble/linux/tools/codegen/codegen.go | 301 ++++
.../currantlabs/ble/linux/tools/codegen/evt.json | 327 ++++
.../currantlabs/ble/linux/tools/codegen/evt.tmpl | 10 +
.../ble/linux/tools/codegen/signal.json | 137 ++
.../ble/linux/tools/codegen/signal.tmpl | 19 +
vendor/github.com/currantlabs/ble/profile.go | 221 +++
vendor/github.com/currantlabs/ble/uuid.go | 217 +++
vendor/github.com/raff/goble/.gitignore | 23 +
vendor/github.com/raff/goble/LICENSE | 20 +
vendor/github.com/raff/goble/README.md | 20 +
vendor/github.com/raff/goble/characteristics.go | 77 +
vendor/github.com/raff/goble/descriptors.go | 14 +
vendor/github.com/raff/goble/emitter.go | 82 +
vendor/github.com/raff/goble/examples/beacon.go | 38 +
.../github.com/raff/goble/examples/discoverer.go | 74 +
vendor/github.com/raff/goble/examples/explorer.go | 284 ++++
vendor/github.com/raff/goble/examples/main.go | 129 ++
vendor/github.com/raff/goble/goble.go | 717 +++++++++
vendor/github.com/raff/goble/services.go | 25 +
vendor/github.com/raff/goble/xpc/xpc.go | 381 +++++
vendor/github.com/raff/goble/xpc/xpc_test.go | 106 ++
vendor/github.com/raff/goble/xpc/xpc_wrapper.c | 85 ++
vendor/github.com/raff/goble/xpc/xpc_wrapper.h | 33 +
105 files changed, 15727 insertions(+), 6 deletions(-)
diff --git a/Gopkg.lock b/Gopkg.lock
index 1293e60..aaad4de 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -8,6 +8,12 @@
version = "v1.0.3"
[[projects]]
+ branch = "master"
+ name = "github.com/currantlabs/ble"
+ packages = [".","darwin","examples/lib/dev","linux","linux/adv","linux/att","linux/gatt","linux/hci","linux/hci/cmd","linux/hci/evt","linux/hci/socket"]
+ revision = "ba3b00e87511deec53b2d0e8df691426a1d37bf7"
+
+[[projects]]
name = "github.com/fatih/structs"
packages = ["."]
revision = "a720dfa8df582c51dee1b36feabb906bde1588bd"
@@ -81,6 +87,12 @@
[[projects]]
branch = "master"
+ name = "github.com/raff/goble"
+ packages = ["xpc"]
+ revision = "b12b34f940c4cf4363660073539b5fa9fd96bd16"
+
+[[projects]]
+ branch = "master"
name = "github.com/runtimeco/go-coap"
packages = ["."]
revision = "e869704e8ca4a662cdde0e67e006daadd523b2b3"
@@ -154,6 +166,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "852b2af2b4da6688403961270fc59f01e519ca103325797388b678239b75b3f6"
+ inputs-digest = "e608391fefffe106d6f8148f2c92789f96ee5425c775a3437266586da886dff7"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index debfb80..81152be 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -26,6 +26,10 @@
version = "1.0.3"
[[constraint]]
+ branch = "master"
+ name = "github.com/currantlabs/ble"
+
+[[constraint]]
name = "github.com/fatih/structs"
version = "1.0.0"
diff --git a/README.md b/README.md
index 24e2c3b..3240108 100644
--- a/README.md
+++ b/README.md
@@ -25,11 +25,7 @@ Newt Manager (newtmgr) is the application tool that enables a user to communicat
### Building
-newtmgr is vendored using the dep tool (https://github.com/golang/dep). Some
-dependencies are intentially excluded from the vendor directory. The reason
-these are not vendored is to prevent errors during elicited by `go get
-mynewt.apache.org/newtmgr/...` on linux. These two dependencies contain
-OS-specific code, something `go get` seems to trip over when it is vendored.
+newtmgr is vendored using the dep tool (https://github.com/golang/dep).
To build newtmgr from source, you will need to manually acquire the missing
dependencies. OS-specific instructions are below:
diff --git a/vendor/github.com/currantlabs/ble/.gitignore b/vendor/github.com/currantlabs/ble/.gitignore
new file mode 100644
index 0000000..f8a9d56
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/.gitignore
@@ -0,0 +1,5 @@
+.*.swp
+.tags
+.tags1
+
+/examples/bin/*
diff --git a/vendor/github.com/currantlabs/ble/LICENSE b/vendor/github.com/currantlabs/ble/LICENSE
new file mode 100644
index 0000000..0d29b8e
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2016 Currant Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Currant Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/currantlabs/ble/README.md b/vendor/github.com/currantlabs/ble/README.md
new file mode 100644
index 0000000..28f954d
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/README.md
@@ -0,0 +1,3 @@
+# bt
+
+bt is a Bluetooth library for Go; its API is still under active development, and may change without warning.
diff --git a/vendor/github.com/currantlabs/ble/addr.go b/vendor/github.com/currantlabs/ble/addr.go
new file mode 100644
index 0000000..d19317a
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/addr.go
@@ -0,0 +1,20 @@
+package ble
+
+import "strings"
+
+// Addr represents a network end point address.
+// It's MAC address on Linux or Device UUID on OS X.
+type Addr interface {
+ String() string
+}
+
+// NewAddr creates an Addr from string
+func NewAddr(s string) Addr {
+ return addr(strings.ToLower(s))
+}
+
+type addr string
+
+func (a addr) String() string {
+ return string(a)
+}
diff --git a/vendor/github.com/currantlabs/ble/adv.go b/vendor/github.com/currantlabs/ble/adv.go
new file mode 100644
index 0000000..24c1228
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/adv.go
@@ -0,0 +1,28 @@
+package ble
+
+// AdvHandler handles advertisement.
+type AdvHandler func(a Advertisement)
+
+// AdvFilter returns true if the advertisement matches specified condition.
+type AdvFilter func(a Advertisement) bool
+
+// Advertisement ...
+type Advertisement interface {
+ LocalName() string
+ ManufacturerData() []byte
+ ServiceData() []ServiceData
+ Services() []UUID
+ OverflowService() []UUID
+ TxPowerLevel() int
+ Connectable() bool
+ SolicitedService() []UUID
+
+ RSSI() int
+ Address() Addr
+}
+
+// ServiceData ...
+type ServiceData struct {
+ UUID UUID
+ Data []byte
+}
diff --git a/vendor/github.com/currantlabs/ble/client.go b/vendor/github.com/currantlabs/ble/client.go
new file mode 100644
index 0000000..e21ccf1
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/client.go
@@ -0,0 +1,69 @@
+package ble
+
+// A Client is a GATT client.
+type Client interface {
+ // Address returns platform specific unique ID of the remote peripheral, e.g. MAC on Linux, Client UUID on OS X.
+ Address() Addr
+
+ // Name returns the name of the remote peripheral.
+ // This can be the advertised name, if exists, or the GAP device name, which takes priority.
+ Name() string
+
+ // Profile returns discovered profile.
+ Profile() *Profile
+
+ // DiscoverProfile discovers the whole hierachy of a server.
+ DiscoverProfile(force bool) (*Profile, error)
+
+ // DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1]
+ // If filter is specified, only filtered services are returned.
+ DiscoverServices(filter []UUID) ([]*Service, error)
+
+ // DiscoverIncludedServices finds the included services of a service. [Vol 3, Part G, 4.5.1]
+ // If filter is specified, only filtered services are returned.
+ DiscoverIncludedServices(filter []UUID, s *Service) ([]*Service, error)
+
+ // DiscoverCharacteristics finds all the characteristics within a service. [Vol 3, Part G, 4.6.1]
+ // If filter is specified, only filtered characteristics are returned.
+ DiscoverCharacteristics(filter []UUID, s *Service) ([]*Characteristic, error)
+
+ // DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1]
+ // If filter is specified, only filtered descriptors are returned.
+ DiscoverDescriptors(filter []UUID, c *Characteristic) ([]*Descriptor, error)
+
+ // ReadCharacteristic reads a characteristic value from a server. [Vol 3, Part G, 4.8.1]
+ ReadCharacteristic(c *Characteristic) ([]byte, error)
+
+ // ReadLongCharacteristic reads a characteristic value which is longer than the MTU. [Vol 3, Part G, 4.8.3]
+ ReadLongCharacteristic(c *Characteristic) ([]byte, error)
+
+ // WriteCharacteristic writes a characteristic value to a server. [Vol 3, Part G, 4.9.3]
+ WriteCharacteristic(c *Characteristic, value []byte, noRsp bool) error
+
+ // ReadDescriptor reads a characteristic descriptor from a server. [Vol 3, Part G, 4.12.1]
+ ReadDescriptor(d *Descriptor) ([]byte, error)
+
+ // WriteDescriptor writes a characteristic descriptor to a server. [Vol 3, Part G, 4.12.3]
+ WriteDescriptor(d *Descriptor, v []byte) error
+
+ // ReadRSSI retrieves the current RSSI value of remote peripheral. [Vol 2, Part E, 7.5.4]
+ ReadRSSI() int
+
+ // ExchangeMTU set the ATT_MTU to the maximum possible value that can be supported by both devices [Vol 3, Part G, 4.3.1]
+ ExchangeMTU(rxMTU int) (txMTU int, err error)
+
+ // Subscribe subscribes to indication (if ind is set true), or notification of a characteristic value. [Vol 3, Part G, 4.10 & 4.11]
+ Subscribe(c *Characteristic, ind bool, h NotificationHandler) error
+
+ // Unsubscribe unsubscribes to indication (if ind is set true), or notification of a specified characteristic value. [Vol 3, Part G, 4.10 & 4.11]
+ Unsubscribe(c *Characteristic, ind bool) error
+
+ // ClearSubscriptions clears all subscriptions to notifications and indications.
+ ClearSubscriptions() error
+
+ // CancelConnection disconnects the connection.
+ CancelConnection() error
+
+ // Disconnected returns a receiving channel, which is closed when the client disconnects.
+ Disconnected() <-chan struct{}
+}
diff --git a/vendor/github.com/currantlabs/ble/conn.go b/vendor/github.com/currantlabs/ble/conn.go
new file mode 100644
index 0000000..1ddb3a9
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/conn.go
@@ -0,0 +1,39 @@
+package ble
+
+import (
+ "io"
+
+ "golang.org/x/net/context"
+)
+
+// Conn implements a L2CAP connection.
+type Conn interface {
+ io.ReadWriteCloser
+
+ // Context returns the context that is used by this Conn.
+ Context() context.Context
+
+ // SetContext sets the context that is used by this Conn.
+ SetContext(ctx context.Context)
+
+ // LocalAddr returns local device's address.
+ LocalAddr() Addr
+
+ // RemoteAddr returns remote device's address.
+ RemoteAddr() Addr
+
+ // RxMTU returns the ATT_MTU which the local device is capable of accepting.
+ RxMTU() int
+
+ // SetRxMTU sets the ATT_MTU which the local device is capable of accepting.
+ SetRxMTU(mtu int)
+
+ // TxMTU returns the ATT_MTU which the remote device is capable of accepting.
+ TxMTU() int
+
+ // SetTxMTU sets the ATT_MTU which the remote device is capable of accepting.
+ SetTxMTU(mtu int)
+
+ // Disconnected returns a receiving channel, which is closed when the connection disconnects.
+ Disconnected() <-chan struct{}
+}
diff --git a/vendor/github.com/currantlabs/ble/const.go b/vendor/github.com/currantlabs/ble/const.go
new file mode 100644
index 0000000..f4c142b
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/const.go
@@ -0,0 +1,33 @@
+package ble
+
+// DefaultMTU defines the default MTU of ATT protocol including 3 bytes of ATT header.
+const DefaultMTU = 23
+
+// MaxMTU is maximum of ATT_MTU, which is 512 bytes of value length, plus 3 bytes of ATT header.
+// The maximum length of an attribute value shall be 512 octets [Vol 3, Part F, 3.2.9]
+const MaxMTU = 512 + 3
+
+// UUIDs ...
+var (
+ GAPUUID = UUID16(0x1800) // Generic Access
+ GATTUUID = UUID16(0x1801) // Generic Attribute
+ CurrentTimeUUID = UUID16(0x1805) // Current Time Service
+ DeviceInfoUUID = UUID16(0x180A) // Device Information
+ BatteryUUID = UUID16(0x180F) // Battery Service
+ HIDUUID = UUID16(0x1812) // Human Interface Device
+
+ PrimaryServiceUUID = UUID16(0x2800)
+ SecondaryServiceUUID = UUID16(0x2801)
+ IncludeUUID = UUID16(0x2802)
+ CharacteristicUUID = UUID16(0x2803)
+
+ ClientCharacteristicConfigUUID = UUID16(0x2902)
+ ServerCharacteristicConfigUUID = UUID16(0x2903)
+
+ DeviceNameUUID = UUID16(0x2A00)
+ AppearanceUUID = UUID16(0x2A01)
+ PeripheralPrivacyUUID = UUID16(0x2A02)
+ ReconnectionAddrUUID = UUID16(0x2A03)
+ PeferredParamsUUID = UUID16(0x2A04)
+ ServiceChangedUUID = UUID16(0x2A05)
+)
diff --git a/vendor/github.com/currantlabs/ble/darwin/adv.go b/vendor/github.com/currantlabs/ble/darwin/adv.go
new file mode 100644
index 0000000..b7af334
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/adv.go
@@ -0,0 +1,73 @@
+package darwin
+
+import (
+ "github.com/currantlabs/ble"
+ "github.com/raff/goble/xpc"
+)
+
+type adv struct {
+ args xpc.Dict
+ ad xpc.Dict
+}
+
+func (a *adv) LocalName() string {
+ return a.ad.GetString("kCBAdvDataLocalName", a.args.GetString("kCBMsgArgName", ""))
+}
+
+func (a *adv) ManufacturerData() []byte {
+ return a.ad.GetBytes("kCBAdvDataManufacturerData", nil)
+}
+
+func (a *adv) ServiceData() []ble.ServiceData {
+ xSDs, ok := a.ad["kCBAdvDataServiceData"]
+ if !ok {
+ return nil
+ }
+
+ xSD := xSDs.(xpc.Array)
+ var sd []ble.ServiceData
+ for i := 0; i < len(xSD); i += 2 {
+ sd = append(
+ sd, ble.ServiceData{
+ UUID: ble.UUID(xSD[i].([]byte)),
+ Data: xSD[i+1].([]byte),
+ })
+ }
+ return sd
+}
+
+func (a *adv) Services() []ble.UUID {
+ xUUIDs, ok := a.ad["kCBAdvDataServiceUUIDs"]
+ if !ok {
+ return nil
+ }
+ var uuids []ble.UUID
+ for _, xUUID := range xUUIDs.(xpc.Array) {
+ uuids = append(uuids, ble.UUID(ble.Reverse(xUUID.([]byte))))
+ }
+ return uuids
+}
+
+func (a *adv) OverflowService() []ble.UUID {
+ return nil // TODO
+}
+
+func (a *adv) TxPowerLevel() int {
+ return a.ad.GetInt("kCBAdvDataTxPowerLevel", 0)
+}
+
+func (a *adv) SolicitedService() []ble.UUID {
+ return nil // TODO
+}
+
+func (a *adv) Connectable() bool {
+ return a.ad.GetInt("kCBAdvDataIsConnectable", 0) > 0
+}
+
+func (a *adv) RSSI() int {
+ return a.args.GetInt("kCBMsgArgRssi", 0)
+}
+
+func (a *adv) Address() ble.Addr {
+ return xpc.UUID(a.args.MustGetUUID("kCBMsgArgDeviceUUID"))
+}
diff --git a/vendor/github.com/currantlabs/ble/darwin/client.go b/vendor/github.com/currantlabs/ble/darwin/client.go
new file mode 100644
index 0000000..668b408
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/client.go
@@ -0,0 +1,282 @@
+package darwin
+
+import (
+ "fmt"
+
+ "github.com/currantlabs/ble"
+ "github.com/raff/goble/xpc"
+)
+
+// A Client is a GATT client.
+type Client struct {
+ profile *ble.Profile
+ name string
+
+ id xpc.UUID
+ conn *conn
+}
+
+// NewClient ...
+func NewClient(c ble.Conn) (*Client, error) {
+ return &Client{
+ conn: c.(*conn),
+ id: xpc.MakeUUID(c.RemoteAddr().String()),
+ }, nil
+}
+
+// Address returns UUID of the remote peripheral.
+func (cln *Client) Address() ble.Addr {
+ return cln.conn.RemoteAddr()
+}
+
+// Name returns the name of the remote peripheral.
+// This can be the advertised name, if exists, or the GAP device name, which takes priority.
+func (cln *Client) Name() string {
+ return cln.name
+}
+
+// Profile returns the discovered profile.
+func (cln *Client) Profile() *ble.Profile {
+ return cln.profile
+}
+
+// DiscoverProfile discovers the whole hierachy of a server.
+func (cln *Client) DiscoverProfile(force bool) (*ble.Profile, error) {
+ if cln.profile != nil && !force {
+ return cln.profile, nil
+ }
+ ss, err := cln.DiscoverServices(nil)
+ if err != nil {
+ return nil, fmt.Errorf("can't discover services: %s\n", err)
+ }
+ for _, s := range ss {
+ cs, err := cln.DiscoverCharacteristics(nil, s)
+ if err != nil {
+ return nil, fmt.Errorf("can't discover characteristics: %s\n", err)
+ }
+ for _, c := range cs {
+ _, err := cln.DiscoverDescriptors(nil, c)
+ if err != nil {
+ return nil, fmt.Errorf("can't discover descriptors: %s\n", err)
+ }
+ }
+ }
+ cln.profile = &ble.Profile{Services: ss}
+ return cln.profile, nil
+}
+
+// DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1]
+// If filter is specified, only filtered services are returned.
+func (cln *Client) DiscoverServices(ss []ble.UUID) ([]*ble.Service, error) {
+ rsp := cln.conn.sendReq(45, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgUUIDs": uuidSlice(ss),
+ })
+ if rsp.err() != nil {
+ return nil, rsp.err()
+ }
+ svcs := []*ble.Service{}
+ for _, xss := range rsp.services() {
+ xs := msg(xss.(xpc.Dict))
+ svcs = append(svcs, &ble.Service{
+ UUID: ble.MustParse(xs.uuid()),
+ Handle: uint16(xs.serviceStartHandle()),
+ EndHandle: uint16(xs.serviceEndHandle()),
+ })
+ }
+ if cln.profile == nil {
+ cln.profile = &ble.Profile{Services: svcs}
+ }
+ return svcs, nil
+}
+
+// DiscoverIncludedServices finds the included services of a service. [Vol 3, Part G, 4.5.1]
+// If filter is specified, only filtered services are returned.
+func (cln *Client) DiscoverIncludedServices(ss []ble.UUID, s *ble.Service) ([]*ble.Service, error) {
+ rsp := cln.conn.sendReq(60, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgServiceStartHandle": s.Handle,
+ "kCBMsgArgServiceEndHandle": s.EndHandle,
+ "kCBMsgArgUUIDs": uuidSlice(ss),
+ })
+ if rsp.err() != nil {
+ return nil, rsp.err()
+ }
+ return nil, ble.ErrNotImplemented
+}
+
+// DiscoverCharacteristics finds all the characteristics within a service. [Vol 3, Part G, 4.6.1]
+// If filter is specified, only filtered characteristics are returned.
+func (cln *Client) DiscoverCharacteristics(cs []ble.UUID, s *ble.Service) ([]*ble.Characteristic, error) {
+ rsp := cln.conn.sendReq(62, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgServiceStartHandle": s.Handle,
+ "kCBMsgArgServiceEndHandle": s.EndHandle,
+ "kCBMsgArgUUIDs": uuidSlice(cs),
+ })
+ if rsp.err() != nil {
+ return nil, rsp.err()
+ }
+ for _, xcs := range rsp.characteristics() {
+ xc := msg(xcs.(xpc.Dict))
+ s.Characteristics = append(s.Characteristics, &ble.Characteristic{
+ UUID: ble.MustParse(xc.uuid()),
+ Property: ble.Property(xc.characteristicProperties()),
+ Handle: uint16(xc.characteristicHandle()),
+ ValueHandle: uint16(xc.characteristicValueHandle()),
+ })
+ }
+ return s.Characteristics, nil
+}
+
+// DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1]
+// If filter is specified, only filtered descriptors are returned.
+func (cln *Client) DiscoverDescriptors(ds []ble.UUID, c *ble.Characteristic) ([]*ble.Descriptor, error) {
+ rsp := cln.conn.sendReq(70, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgCharacteristicHandle": c.Handle,
+ "kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
+ "kCBMsgArgUUIDs": uuidSlice(ds),
+ })
+ for _, xds := range rsp.descriptors() {
+ xd := msg(xds.(xpc.Dict))
+ c.Descriptors = append(c.Descriptors, &ble.Descriptor{
+ UUID: ble.MustParse(xd.uuid()),
+ Handle: uint16(xd.descriptorHandle()),
+ })
+ }
+ return c.Descriptors, nil
+}
+
+// ReadCharacteristic reads a characteristic value from a server. [Vol 3, Part G, 4.8.1]
+func (cln *Client) ReadCharacteristic(c *ble.Characteristic) ([]byte, error) {
+ rsp := cln.conn.sendReq(65, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgCharacteristicHandle": c.Handle,
+ "kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
+ })
+ if rsp.err() != nil {
+ return nil, rsp.err()
+ }
+ return rsp.data(), nil
+}
+
+// ReadLongCharacteristic reads a characteristic value which is longer than the MTU. [Vol 3, Part G, 4.8.3]
+func (cln *Client) ReadLongCharacteristic(c *ble.Characteristic) ([]byte, error) {
+ return nil, ble.ErrNotImplemented
+}
+
+// WriteCharacteristic writes a characteristic value to a server. [Vol 3, Part G, 4.9.3]
+func (cln *Client) WriteCharacteristic(c *ble.Characteristic, b []byte, noRsp bool) error {
+ args := xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgCharacteristicHandle": c.Handle,
+ "kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
+ "kCBMsgArgData": b,
+ "kCBMsgArgType": map[bool]int{false: 0, true: 1}[noRsp],
+ }
+ if noRsp {
+ cln.conn.sendCmd(66, args)
+ return nil
+ }
+ return cln.conn.sendReq(66, args).err()
+}
+
+// ReadDescriptor reads a characteristic descriptor from a server. [Vol 3, Part G, 4.12.1]
+func (cln *Client) ReadDescriptor(d *ble.Descriptor) ([]byte, error) {
+ rsp := cln.conn.sendReq(77, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgDescriptorHandle": d.Handle,
+ })
+ if rsp.err() != nil {
+ return nil, rsp.err()
+ }
+ return rsp.data(), nil
+}
+
+// WriteDescriptor writes a characteristic descriptor to a server. [Vol 3, Part G, 4.12.3]
+func (cln *Client) WriteDescriptor(d *ble.Descriptor, b []byte) error {
+ rsp := cln.conn.sendReq(78, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgDescriptorHandle": d.Handle,
+ "kCBMsgArgData": b,
+ })
+ return rsp.err()
+}
+
+// ReadRSSI retrieves the current RSSI value of remote peripheral. [Vol 2, Part E, 7.5.4]
+func (cln *Client) ReadRSSI() int {
+ rsp := cln.conn.sendReq(44, xpc.Dict{"kCBMsgArgDeviceUUID": cln.id})
+ if rsp.err() != nil {
+ return 0
+ }
+ return rsp.rssi()
+}
+
+// ExchangeMTU set the ATT_MTU to the maximum possible value that can be
+// supported by both devices [Vol 3, Part G, 4.3.1]
+func (cln *Client) ExchangeMTU(mtu int) (int, error) {
+ // TODO: find the xpc command to tell OS X the rxMTU we can handle.
+ return cln.conn.TxMTU(), nil
+}
+
+// Subscribe subscribes to indication (if ind is set true), or notification of a
+// characteristic value. [Vol 3, Part G, 4.10 & 4.11]
+func (cln *Client) Subscribe(c *ble.Characteristic, ind bool, fn ble.NotificationHandler) error {
+ cln.conn.Lock()
+ defer cln.conn.Unlock()
+ cln.conn.subs[c.Handle] = &sub{fn: fn, char: c}
+ rsp := cln.conn.sendReq(68, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgCharacteristicHandle": c.Handle,
+ "kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
+ "kCBMsgArgState": 1,
+ })
+ if rsp.err() != nil {
+ delete(cln.conn.subs, c.Handle)
+ return rsp.err()
+ }
+ return nil
+}
+
+// Unsubscribe unsubscribes to indication (if ind is set true), or notification
+// of a specified characteristic value. [Vol 3, Part G, 4.10 & 4.11]
+func (cln *Client) Unsubscribe(c *ble.Characteristic, ind bool) error {
+ rsp := cln.conn.sendReq(68, xpc.Dict{
+ "kCBMsgArgDeviceUUID": cln.id,
+ "kCBMsgArgCharacteristicHandle": c.Handle,
+ "kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
+ "kCBMsgArgState": 0,
+ })
+ if rsp.err() != nil {
+ return rsp.err()
+ }
+ cln.conn.Lock()
+ defer cln.conn.Unlock()
+ delete(cln.conn.subs, c.Handle)
+ return nil
+}
+
+// ClearSubscriptions clears all subscriptions to notifications and indications.
+func (cln *Client) ClearSubscriptions() error {
+ for _, s := range cln.conn.subs {
+ cln.Unsubscribe(s.char, false)
+ }
+ return nil
+}
+
+// CancelConnection disconnects the connection.
+func (cln *Client) CancelConnection() error {
+ rsp := cln.conn.sendReq(32, xpc.Dict{"kCBMsgArgDeviceUUID": cln.id})
+ return rsp.err()
+}
+
+// Disconnected returns a receiving channel, which is closed when the client disconnects.
+func (cln *Client) Disconnected() <-chan struct{} {
+ return cln.conn.Disconnected()
+}
+
+type sub struct {
+ fn ble.NotificationHandler
+ char *ble.Characteristic
+}
diff --git a/vendor/github.com/currantlabs/ble/darwin/conn.go b/vendor/github.com/currantlabs/ble/darwin/conn.go
new file mode 100644
index 0000000..c0abf35
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/conn.go
@@ -0,0 +1,135 @@
+package darwin
+
+import (
+ "sync"
+
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "github.com/raff/goble/xpc"
+)
+
+func newConn(d *Device, a ble.Addr) *conn {
+ return &conn{
+ dev: d,
+ rxMTU: 23,
+ txMTU: 23,
+ addr: a,
+ done: make(chan struct{}),
+
+ notifiers: make(map[uint16]ble.Notifier),
+ subs: make(map[uint16]*sub),
+
+ rspc: make(chan msg),
+ }
+}
+
+type conn struct {
+ sync.RWMutex
+
+ dev *Device
+ role int
+ ctx context.Context
+ rxMTU int
+ txMTU int
+ addr ble.Addr
+ done chan struct{}
+
+ rspc chan msg
+
+ connInterval int
+ connLatency int
+ supervisionTimeout int
+
+ notifiers map[uint16]ble.Notifier // central connection only
+
+ subs map[uint16]*sub
+}
+
+func (c *conn) Context() context.Context {
+ return c.ctx
+}
+
+func (c *conn) SetContext(ctx context.Context) {
+ c.ctx = ctx
+}
+
+func (c *conn) LocalAddr() ble.Addr {
+ // return c.dev.Address()
+ return c.addr // FIXME
+}
+
+func (c *conn) RemoteAddr() ble.Addr {
+ return c.addr
+}
+
+func (c *conn) RxMTU() int {
+ return c.rxMTU
+}
+
+func (c *conn) SetRxMTU(mtu int) {
+ c.rxMTU = mtu
+}
+
+func (c *conn) TxMTU() int {
+ return c.txMTU
+}
+
+func (c *conn) SetTxMTU(mtu int) {
+ c.txMTU = mtu
+}
+
+func (c *conn) Read(b []byte) (int, error) {
+ return 0, nil
+}
+
+func (c *conn) Write(b []byte) (int, error) {
+ return 0, nil
+}
+
+func (c *conn) Close() error {
+ return nil
+}
+
+// Disconnected returns a receiving channel, which is closed when the connection disconnects.
+func (c *conn) Disconnected() <-chan struct{} {
+ return c.done
+}
+
+// server (peripheral)
+func (c *conn) subscribed(char *ble.Characteristic) {
+ h := char.Handle
+ if _, found := c.notifiers[h]; found {
+ return
+ }
+ send := func(b []byte) (int, error) {
+ c.dev.sendCmd(c.dev.pm, 15, xpc.Dict{
+ "kCBMsgArgUUIDs": [][]byte{},
+ "kCBMsgArgAttributeID": h,
+ "kCBMsgArgData": b,
+ })
+ return len(b), nil
+ }
+ n := ble.NewNotifier(send)
+ c.notifiers[h] = n
+ req := ble.NewRequest(c, nil, 0) // convey *conn to user handler.
+ go char.NotifyHandler.ServeNotify(req, n)
+}
+
+// server (peripheral)
+func (c *conn) unsubscribed(char *ble.Characteristic) {
+ if n, found := c.notifiers[char.Handle]; found {
+ n.Close()
+ delete(c.notifiers, char.Handle)
+ }
+}
+
+func (c *conn) sendReq(id int, args xpc.Dict) msg {
+ c.dev.sendCmd(c.dev.cm, id, args)
+ m := <-c.rspc
+ return msg(m.args())
+}
+
+func (c *conn) sendCmd(id int, args xpc.Dict) {
+ c.dev.sendCmd(c.dev.pm, id, args)
+}
diff --git a/vendor/github.com/currantlabs/ble/darwin/device.go b/vendor/github.com/currantlabs/ble/darwin/device.go
new file mode 100644
index 0000000..b60a48d
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/device.go
@@ -0,0 +1,521 @@
+package darwin
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/pkg/errors"
+ "github.com/raff/goble/xpc"
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "sync"
+)
+
+const (
+ evtStateChanged = 6
+ evtAdvertisingStarted = 16
+ evtAdvertisingStopped = 17
+ evtServiceAdded = 18
+ evtReadRequest = 19
+ evtWriteRequest = 20
+ evtSubscribe = 21
+ evtUnubscribe = 22
+ evtConfirmation = 23
+ evtPeripheralDiscovered = 37
+ evtPeripheralConnected = 38
+ evtPeripheralDisconnected = 40
+ evtATTMTU = 53
+ evtRSSIRead = 55
+ evtServiceDiscovered = 56
+ evtIncludedServicesDiscovered = 63
+ evtCharacteristicsDiscovered = 64
+ evtCharacteristicRead = 71
+ evtCharacteristicWritten = 72
+ evtNotificationValueSet = 74
+ evtDescriptorsDiscovered = 76
+ evtDescriptorRead = 79
+ evtDescriptorWritten = 80
+ evtSleveConnectionComplete = 81
+ evtMasterConnectionComplete = 82
+)
+
+// Device is either a Peripheral or Central device.
+type Device struct {
+ pm xpc.XPC // peripheralManager
+ cm xpc.XPC // centralManager
+
+ role int // 1: peripheralManager (server), 0: centralManager (client)
+
+ rspc chan msg
+
+ conns map[string]*conn
+ connLock sync.Mutex
+
+ // Only used in client/centralManager implementation
+ advHandler ble.AdvHandler
+ chConn chan *conn
+
+ // Only used in server/peripheralManager implementation
+ chars map[int]*ble.Characteristic
+ base int
+}
+
+// NewDevice returns a BLE device.
+func NewDevice(opts ...Option) (*Device, error) {
+ d := &Device{
+ rspc: make(chan msg),
+ conns: make(map[string]*conn),
+ chConn: make(chan *conn),
+ chars: make(map[int]*ble.Characteristic),
+ base: 1,
+ }
+ if err := d.Option(opts...); err != nil {
+ return nil, err
+ }
+
+ d.pm = xpc.XpcConnect("com.apple.blued", d)
+ d.cm = xpc.XpcConnect("com.apple.blued", d)
+
+ return d, errors.Wrap(d.Init(), "can't init")
+}
+
+// Option sets the options specified.
+func (d *Device) Option(opts ...Option) error {
+ var err error
+ for _, opt := range opts {
+ err = opt(d)
+ }
+ return err
+}
+
+// Init ...
+func (d *Device) Init() error {
+ rsp := d.sendReq(d.cm, 1, xpc.Dict{
+ "kCBMsgArgName": fmt.Sprintf("gopher-%v", time.Now().Unix()),
+ "kCBMsgArgOptions": xpc.Dict{
+ "kCBInitOptionShowPowerAlert": 1,
+ },
+ "kCBMsgArgType": 0,
+ })
+ s := State(rsp.state())
+ if s != StatePoweredOn {
+ return fmt.Errorf("state: %s", s)
+ }
+
+ rsp = d.sendReq(d.pm, 1, xpc.Dict{
+ "kCBMsgArgName": fmt.Sprintf("gopher-%v", time.Now().Unix()),
+ "kCBMsgArgOptions": xpc.Dict{
+ "kCBInitOptionShowPowerAlert": 1,
+ },
+ "kCBMsgArgType": 1,
+ })
+ s = State(rsp.state())
+ if s != StatePoweredOn {
+ return fmt.Errorf("state: %s", s)
+ }
+ return nil
+}
+
+// AdvertiseMfgData ...
+func (d *Device) AdvertiseMfgData(ctx context.Context, id uint16, md []byte) error {
+ l := len(md)
+ b := []byte{byte(l + 3), 0xFF, uint8(id), uint8(id >> 8)}
+ if err := d.sendReq(d.pm, 8, xpc.Dict{
+ "kCBAdvDataAppleMfgData": append(b, md...),
+ }).err(); err != nil {
+ return errors.Wrap(err, "can't advertise")
+ }
+ <-ctx.Done()
+ return ctx.Err()
+}
+
+// AdvertiseServiceData16 advertises data associated with a 16bit service uuid
+func (d *Device) AdvertiseServiceData16(ctx context.Context, id uint16, b []byte) error {
+ l := len(b)
+ prefix := []byte{
+ 0x03, 0x03, uint8(id), uint8(id >> 8),
+ byte(l + 3), 0x16, uint8(id), uint8(id >> 8),
+ }
+ if err := d.sendReq(d.pm, 8, xpc.Dict{
+ "kCBAdvDataAppleMfgData": append(prefix, b...),
+ }).err(); err != nil {
+ return errors.Wrap(err, "can't advertise")
+ }
+ <-ctx.Done()
+ return ctx.Err()
+}
+
+// AdvertiseNameAndServices advertises name and specifid service UUIDs.
+func (d *Device) AdvertiseNameAndServices(ctx context.Context, name string, ss ...ble.UUID) error {
+ if err := d.sendReq(d.pm, 8, xpc.Dict{
+ "kCBAdvDataLocalName": name,
+ "kCBAdvDataServiceUUIDs": uuidSlice(ss)},
+ ).err(); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ d.stopAdvertising()
+ return ctx.Err()
+}
+
+// AdvertiseIBeaconData advertises iBeacon packet with specified manufacturer data.
+func (d *Device) AdvertiseIBeaconData(ctx context.Context, md []byte) error {
+ var utsname xpc.Utsname
+ xpc.Uname(&utsname)
+
+ if utsname.Release >= "14." {
+ ibeaconCode := []byte{0x02, 0x15}
+ return d.AdvertiseMfgData(ctx, 0x004C, append(ibeaconCode, md...))
+ }
+ if err := d.sendReq(d.pm, 8, xpc.Dict{"kCBAdvDataAppleBeaconKey": md}).err(); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ return d.stopAdvertising()
+}
+
+// AdvertiseIBeacon advertises iBeacon packet.
+func (d *Device) AdvertiseIBeacon(ctx context.Context, u ble.UUID, major, minor uint16, pwr int8) error {
+ b := make([]byte, 21)
+ copy(b, ble.Reverse(u)) // Big endian
+ binary.BigEndian.PutUint16(b[16:], major) // Big endian
+ binary.BigEndian.PutUint16(b[18:], minor) // Big endian
+ b[20] = uint8(pwr) // Measured Tx Power
+ return d.AdvertiseIBeaconData(ctx, b)
+}
+
+// stopAdvertising stops advertising.
+func (d *Device) stopAdvertising() error {
+ return errors.Wrap(d.sendReq(d.pm, 9, nil).err(), "can't stop advertising")
+}
+
+// Scan ...
+func (d *Device) Scan(ctx context.Context, allowDup bool, h ble.AdvHandler) error {
+ d.advHandler = h
+ if err := d.sendCmd(d.cm, 29, xpc.Dict{
+ // "kCBMsgArgUUIDs": uuidSlice(ss),
+ "kCBMsgArgOptions": xpc.Dict{
+ "kCBScanOptionAllowDuplicates": map[bool]int{true: 1, false: 0}[allowDup],
+ },
+ }); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ if err := d.stopScanning(); err != nil {
+ return errors.Wrap(ctx.Err(), err.Error())
+ }
+ return ctx.Err()
+}
+
+// stopAdvertising stops advertising.
+func (d *Device) stopScanning() error {
+ return errors.Wrap(d.sendCmd(d.cm, 30, nil), "can't stop scanning")
+}
+
+// RemoveAllServices removes all services of device's
+func (d *Device) RemoveAllServices() error {
+ return d.sendCmd(d.pm, 12, nil)
+}
+
+// AddService adds a service to device's database.
+// The following services are ignored as they are provided by OS X.
+//
+// 0x1800 (Generic Access)
+// 0x1801 (Generic Attribute)
+// 0x1805 (Current Time Service)
+// 0x180A (Device Information)
+// 0x180F (Battery Service)
+// 0x1812 (Human Interface Device)
+func (d *Device) AddService(s *ble.Service) error {
+ if s.UUID.Equal(ble.GAPUUID) ||
+ s.UUID.Equal(ble.GATTUUID) ||
+ s.UUID.Equal(ble.CurrentTimeUUID) ||
+ s.UUID.Equal(ble.DeviceInfoUUID) ||
+ s.UUID.Equal(ble.BatteryUUID) ||
+ s.UUID.Equal(ble.HIDUUID) {
+ return nil
+ }
+ xs := xpc.Dict{
+ "kCBMsgArgAttributeID": d.base,
+ "kCBMsgArgAttributeIDs": []int{},
+ "kCBMsgArgCharacteristics": nil,
+ "kCBMsgArgType": 1, // 1 => primary, 0 => excluded
+ "kCBMsgArgUUID": ble.Reverse(s.UUID),
+ }
+ d.base++
+
+ xcs := xpc.Array{}
+ for _, c := range s.Characteristics {
+ props := 0
+ perm := 0
+ if c.Property&ble.CharRead != 0 {
+ props |= 0x02
+ if ble.CharRead&c.Secure != 0 {
+ perm |= 0x04
+ } else {
+ perm |= 0x01
+ }
+ }
+ if c.Property&ble.CharWriteNR != 0 {
+ props |= 0x04
+ if c.Secure&ble.CharWriteNR != 0 {
+ perm |= 0x08
+ } else {
+ perm |= 0x02
+ }
+ }
+ if c.Property&ble.CharWrite != 0 {
+ props |= 0x08
+ if c.Secure&ble.CharWrite != 0 {
+ perm |= 0x08
+ } else {
+ perm |= 0x02
+ }
+ }
+ if c.Property&ble.CharNotify != 0 {
+ if c.Secure&ble.CharNotify != 0 {
+ props |= 0x100
+ } else {
+ props |= 0x10
+ }
+ }
+ if c.Property&ble.CharIndicate != 0 {
+ if c.Secure&ble.CharIndicate != 0 {
+ props |= 0x200
+ } else {
+ props |= 0x20
+ }
+ }
+
+ xc := xpc.Dict{
+ "kCBMsgArgAttributeID": d.base,
+ "kCBMsgArgUUID": ble.Reverse(c.UUID),
+ "kCBMsgArgAttributePermissions": perm,
+ "kCBMsgArgCharacteristicProperties": props,
+ "kCBMsgArgData": c.Value,
+ }
+ c.Handle = uint16(d.base)
+ d.chars[d.base] = c
+ d.base++
+
+ xds := xpc.Array{}
+ for _, d := range c.Descriptors {
+ if d.UUID.Equal(ble.ClientCharacteristicConfigUUID) {
+ // skip CCCD
+ continue
+ }
+ xd := xpc.Dict{
+ "kCBMsgArgData": d.Value,
+ "kCBMsgArgUUID": ble.Reverse(d.UUID),
+ }
+ xds = append(xds, xd)
+ }
+ xc["kCBMsgArgDescriptors"] = xds
+ xcs = append(xcs, xc)
+ }
+ xs["kCBMsgArgCharacteristics"] = xcs
+
+ return d.sendReq(d.pm, 10, xs).err()
+}
+
+// SetServices ...
+func (d *Device) SetServices(ss []*ble.Service) error {
+ if err := d.RemoveAllServices(); err != nil {
+ return nil
+ }
+ for _, s := range ss {
+ if err := d.AddService(s); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Dial ...
+func (d *Device) Dial(ctx context.Context, a ble.Addr) (ble.Client, error) {
+ d.sendCmd(d.cm, 31, xpc.Dict{
+ "kCBMsgArgDeviceUUID": xpc.MakeUUID(a.String()),
+ "kCBMsgArgOptions": xpc.Dict{
+ "kCBConnectOptionNotifyOnDisconnection": 1,
+ },
+ })
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case c := <-d.chConn:
+ c.SetContext(ctx)
+ return NewClient(c)
+ }
+}
+
+// Stop ...
+func (d *Device) Stop() error {
+ return nil
+}
+
+// HandleXpcEvent process Device events and asynchronous errors.
+func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
+ if err != nil {
+ log.Println("error:", err)
+ return
+ }
+ m := msg(event)
+ args := msg(msg(event).args())
+ logger.Info("recv", "id", m.id(), "args", fmt.Sprintf("%v", m.args()))
+
+ switch m.id() {
+ case // Device event
+ evtStateChanged,
+ evtAdvertisingStarted,
+ evtAdvertisingStopped,
+ evtServiceAdded:
+ d.rspc <- args
+
+ case evtPeripheralDiscovered:
+ if d.advHandler == nil {
+ break
+ }
+ a := &adv{args: m.args(), ad: args.advertisementData()}
+ go d.advHandler(a)
+
+ case evtConfirmation:
+ // log.Printf("confirmed: %d", args.attributeID())
+
+ case evtATTMTU:
+ d.conn(args).SetTxMTU(args.attMTU())
+
+ case evtSleveConnectionComplete:
+ // remote peripheral is connected.
+ fallthrough
+ case evtMasterConnectionComplete:
+ // remote central is connected.
+
+ // Could be LEConnectionComplete or LEConnectionUpdateComplete.
+ c := d.conn(args)
+ c.connInterval = args.connectionInterval()
+ c.connLatency = args.connectionLatency()
+ c.supervisionTimeout = args.supervisionTimeout()
+
+ case evtReadRequest:
+ aid := args.attributeID()
+ char := d.chars[aid]
+ v := char.Value
+ if v == nil {
+ c := d.conn(args)
+ req := ble.NewRequest(c, nil, args.offset())
+ buf := bytes.NewBuffer(make([]byte, 0, c.txMTU-1))
+ rsp := ble.NewResponseWriter(buf)
+ char.ReadHandler.ServeRead(req, rsp)
+ v = buf.Bytes()
+ }
+
+ d.sendCmd(d.pm, 13, xpc.Dict{
+ "kCBMsgArgAttributeID": aid,
+ "kCBMsgArgData": v,
+ "kCBMsgArgTransactionID": args.transactionID(),
+ "kCBMsgArgResult": 0,
+ })
+
+ case evtWriteRequest:
+ for _, xxw := range args.attWrites() {
+ xw := msg(xxw.(xpc.Dict))
+ aid := xw.attributeID()
+ char := d.chars[aid]
+ req := ble.NewRequest(d.conn(args), xw.data(), xw.offset())
+ char.WriteHandler.ServeWrite(req, nil)
+ if xw.ignoreResponse() == 1 {
+ continue
+ }
+ d.sendCmd(d.pm, 13, xpc.Dict{
+ "kCBMsgArgAttributeID": aid,
+ "kCBMsgArgData": nil,
+ "kCBMsgArgTransactionID": args.transactionID(),
+ "kCBMsgArgResult": 0,
+ })
+ }
+
+ case evtSubscribe:
+ // characteristic is subscribed by remote central.
+ d.conn(args).subscribed(d.chars[args.attributeID()])
+
+ case evtUnubscribe:
+ // characteristic is unsubscribed by remote central.
+ d.conn(args).unsubscribed(d.chars[args.attributeID()])
+
+ case evtPeripheralConnected:
+ d.chConn <- d.conn(args)
+
+ case evtPeripheralDisconnected:
+ c := d.conn(args)
+ select {
+ case c.rspc <- m:
+ // Canceled by local central synchronously
+ default:
+ // Canceled by remote peripheral asynchronously.
+ }
+ d.connLock.Lock()
+ delete(d.conns, c.RemoteAddr().String())
+ d.connLock.Unlock()
+ close(c.done)
+
+ case evtCharacteristicRead:
+ // Notification
+ c := d.conn(args)
+ if args.isNotification() != 0 {
+ sub := c.subs[uint16(args.characteristicHandle())]
+ if sub == nil {
+ log.Printf("notified by unsubscribed handle")
+ // FIXME: should terminate the connection?
+ } else {
+ sub.fn(args.data())
+ }
+ break
+ }
+ c.rspc <- m
+
+ case // Peripheral events
+ evtRSSIRead,
+ evtServiceDiscovered,
+ evtIncludedServicesDiscovered,
+ evtCharacteristicsDiscovered,
+ evtCharacteristicWritten,
+ evtNotificationValueSet,
+ evtDescriptorsDiscovered,
+ evtDescriptorRead,
+ evtDescriptorWritten:
+
+ d.conn(args).rspc <- m
+
+ default:
+ log.Printf("Unhandled event: %#v", event)
+ }
+}
+
+func (d *Device) conn(m msg) *conn {
+ // Convert xpc.UUID to ble.UUID.
+ a := ble.MustParse(m.deviceUUID().String())
+ d.connLock.Lock()
+ c, ok := d.conns[a.String()]
+ if !ok {
+ c = newConn(d, a)
+ d.conns[a.String()] = c
+ }
+ d.connLock.Unlock()
+ return c
+}
+
+// sendReq sends a message and waits for its reply.
+func (d *Device) sendReq(x xpc.XPC, id int, args xpc.Dict) msg {
+ d.sendCmd(x, id, args)
+ return <-d.rspc
+}
+
+func (d *Device) sendCmd(x xpc.XPC, id int, args xpc.Dict) error {
+ logger.Info("send", "id", id, "args", fmt.Sprintf("%v", args))
+ x.Send(xpc.Dict{"kCBMsgId": id, "kCBMsgArgs": args}, false)
+ return nil
+}
diff --git a/vendor/github.com/currantlabs/ble/darwin/log.go b/vendor/github.com/currantlabs/ble/darwin/log.go
new file mode 100644
index 0000000..40b0411
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/log.go
@@ -0,0 +1,7 @@
+package darwin
+
+import (
+ "github.com/mgutz/logxi/v1"
+)
+
+var logger = log.New("darwin")
diff --git a/vendor/github.com/currantlabs/ble/darwin/msg.go b/vendor/github.com/currantlabs/ble/darwin/msg.go
new file mode 100644
index 0000000..7ad170b
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/msg.go
@@ -0,0 +1,64 @@
+package darwin
+
+import (
+ "github.com/currantlabs/ble"
+ "github.com/raff/goble/xpc"
+)
+
+type msg xpc.Dict
+
+func (m msg) id() int { return xpc.Dict(m).MustGetInt("kCBMsgId") }
+func (m msg) args() xpc.Dict { return xpc.Dict(m).MustGetDict("kCBMsgArgs") }
+func (m msg) advertisementData() xpc.Dict {
+ return xpc.Dict(m).MustGetDict("kCBMsgArgAdvertisementData")
+}
+func (m msg) attMTU() int { return xpc.Dict(m).MustGetInt("kCBMsgArgATTMTU") }
+func (m msg) attWrites() xpc.Array { return xpc.Dict(m).MustGetArray("kCBMsgArgATTWrites") }
+func (m msg) attributeID() int { return xpc.Dict(m).MustGetInt("kCBMsgArgAttributeID") }
+func (m msg) characteristicHandle() int {
+ return xpc.Dict(m).MustGetInt("kCBMsgArgCharacteristicHandle")
+}
+func (m msg) data() []byte {
+ // return xpc.Dict(m).MustGetBytes("kCBMsgArgData")
+ v := m["kCBMsgArgData"]
+ switch v.(type) {
+ case string:
+ return []byte(v.(string))
+ case []byte:
+ return v.([]byte)
+ default:
+ return nil
+ }
+}
+
+func (m msg) deviceUUID() xpc.UUID { return xpc.Dict(m).MustGetUUID("kCBMsgArgDeviceUUID") }
+func (m msg) ignoreResponse() int { return xpc.Dict(m).MustGetInt("kCBMsgArgIgnoreResponse") }
+func (m msg) offset() int { return xpc.Dict(m).MustGetInt("kCBMsgArgOffset") }
+func (m msg) isNotification() int { return xpc.Dict(m).GetInt("kCBMsgArgIsNotification", 0) }
+func (m msg) result() int { return xpc.Dict(m).MustGetInt("kCBMsgArgResult") }
+func (m msg) state() int { return xpc.Dict(m).MustGetInt("kCBMsgArgState") }
+func (m msg) rssi() int { return xpc.Dict(m).MustGetInt("kCBMsgArgData") }
+func (m msg) transactionID() int { return xpc.Dict(m).MustGetInt("kCBMsgArgTransactionID") }
+func (m msg) uuid() string { return xpc.Dict(m).MustGetHexBytes("kCBMsgArgUUID") }
+func (m msg) serviceStartHandle() int { return xpc.Dict(m).MustGetInt("kCBMsgArgServiceStartHandle") }
+func (m msg) serviceEndHandle() int { return xpc.Dict(m).MustGetInt("kCBMsgArgServiceEndHandle") }
+func (m msg) services() xpc.Array { return xpc.Dict(m).MustGetArray("kCBMsgArgServices") }
+func (m msg) characteristics() xpc.Array { return xpc.Dict(m).MustGetArray("kCBMsgArgCharacteristics") }
+func (m msg) characteristicProperties() int {
+ return xpc.Dict(m).MustGetInt("kCBMsgArgCharacteristicProperties")
+}
+func (m msg) characteristicValueHandle() int {
+ return xpc.Dict(m).MustGetInt("kCBMsgArgCharacteristicValueHandle")
+}
+func (m msg) descriptors() xpc.Array { return xpc.Dict(m).MustGetArray("kCBMsgArgDescriptors") }
+func (m msg) descriptorHandle() int { return xpc.Dict(m).MustGetInt("kCBMsgArgDescriptorHandle") }
+func (m msg) connectionInterval() int { return xpc.Dict(m).MustGetInt("kCBMsgArgConnectionInterval") }
+func (m msg) connectionLatency() int { return xpc.Dict(m).MustGetInt("kCBMsgArgConnectionLatency") }
+func (m msg) supervisionTimeout() int { return xpc.Dict(m).MustGetInt("kCBMsgArgSupervisionTimeout") }
+
+func (m msg) err() error {
+ if code := m.result(); code != 0 {
+ return ble.ATTError(code)
+ }
+ return nil
+}
diff --git a/vendor/github.com/currantlabs/ble/darwin/option.go b/vendor/github.com/currantlabs/ble/darwin/option.go
new file mode 100644
index 0000000..924d1d5
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/option.go
@@ -0,0 +1,20 @@
+package darwin
+
+// An Option is a configuration function, which configures the device.
+type Option func(*Device) error
+
+// OptPeripheralRole configures the device to perform Peripheral tasks.
+func OptPeripheralRole() Option {
+ return func(d *Device) error {
+ d.role = 1
+ return nil
+ }
+}
+
+// OptCentralRole configures the device to perform Central tasks.
+func OptCentralRole() Option {
+ return func(d *Device) error {
+ d.role = 0
+ return nil
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/darwin/state.go b/vendor/github.com/currantlabs/ble/darwin/state.go
new file mode 100644
index 0000000..8034e39
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/state.go
@@ -0,0 +1,26 @@
+package darwin
+
+// State ...
+type State int
+
+// State ...
+const (
+ StateUnknown State = 0
+ StateResetting State = 1
+ StateUnsupported State = 2
+ StateUnauthorized State = 3
+ StatePoweredOff State = 4
+ StatePoweredOn State = 5
+)
+
+func (s State) String() string {
+ str := []string{
+ "Unknown",
+ "Resetting",
+ "Unsupported",
+ "Unauthorized",
+ "PoweredOff",
+ "PoweredOn",
+ }
+ return str[int(s)]
+}
diff --git a/vendor/github.com/currantlabs/ble/darwin/util.go b/vendor/github.com/currantlabs/ble/darwin/util.go
new file mode 100644
index 0000000..8fdb251
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/darwin/util.go
@@ -0,0 +1,11 @@
+package darwin
+
+import "github.com/currantlabs/ble"
+
+func uuidSlice(uu []ble.UUID) [][]byte {
+ us := [][]byte{}
+ for _, u := range uu {
+ us = append(us, ble.Reverse(u))
+ }
+ return us
+}
diff --git a/vendor/github.com/currantlabs/ble/dev.go b/vendor/github.com/currantlabs/ble/dev.go
new file mode 100644
index 0000000..8d293cd
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/dev.go
@@ -0,0 +1,42 @@
+package ble
+
+import "golang.org/x/net/context"
+
+// Device ...
+type Device interface {
+ // AddService adds a service to database.
+ AddService(svc *Service) error
+
+ // RemoveAllServices removes all services that are currently in the database.
+ RemoveAllServices() error
+
+ // SetServices set the specified service to the database.
+ // It removes all currently added services, if any.
+ SetServices(svcs []*Service) error
+
+ // Stop detatch the GATT server from a peripheral device.
+ Stop() error
+
+ // AdvertiseNameAndServices advertises device name, and specified service UUIDs.
+ // It tres to fit the UUIDs in the advertising packet as much as possi
+ // If name doesn't fit in the advertising packet, it will be put in scan response.
+ AdvertiseNameAndServices(ctx context.Context, name string, uuids ...UUID) error
+
+ // AdvertiseMfgData avertises the given manufacturer data.
+ AdvertiseMfgData(ctx context.Context, id uint16, b []byte) error
+
+ // AdvertiseServiceData16 advertises data associated with a 16bit service uuid
+ AdvertiseServiceData16(ctx context.Context, id uint16, b []byte) error
+
+ // AdvertiseIBeaconData advertise iBeacon with given manufacturer data.
+ AdvertiseIBeaconData(ctx context.Context, b []byte) error
+
+ // AdvertiseIBeacon advertises iBeacon with specified parameters.
+ AdvertiseIBeacon(ctx context.Context, u UUID, major, minor uint16, pwr int8) error
+
+ // Scan starts scanning. Duplicated advertisements will be filtered out if allowDup is set to false.
+ Scan(ctx context.Context, allowDup bool, h AdvHandler) error
+
+ // Dial ...
+ Dial(ctx context.Context, a Addr) (Client, error)
+}
diff --git a/vendor/github.com/currantlabs/ble/error.go b/vendor/github.com/currantlabs/ble/error.go
new file mode 100644
index 0000000..b66295e
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/error.go
@@ -0,0 +1,75 @@
+package ble
+
+import (
+ "errors"
+ "fmt"
+)
+
+// ErrEIRPacketTooLong is the error returned when an AdvertisingPacket
+// or ScanResponsePacket is too long.
+var ErrEIRPacketTooLong = errors.New("max packet length is 31")
+
+// ErrNotImplemented means the functionality is not implemented.
+var ErrNotImplemented = errors.New("not implemented")
+
+// ATTError is the error code of Attribute Protocol [Vol 3, Part F, 3.4.1.1].
+type ATTError byte
+
+// ATTError is the error code of Attribute Protocol [Vol 3, Part F, 3.4.1.1].
+const (
+ ErrSuccess ATTError = 0x00 // ErrSuccess measn the operation is success.
+ ErrInvalidHandle ATTError = 0x01 // ErrInvalidHandle means the attribute handle given was not valid on this server.
+ ErrReadNotPerm ATTError = 0x02 // ErrReadNotPerm eans the attribute cannot be read.
+ ErrWriteNotPerm ATTError = 0x03 // ErrWriteNotPerm eans the attribute cannot be written.
+ ErrInvalidPDU ATTError = 0x04 // ErrInvalidPDU means the attribute PDU was invalid.
+ ErrAuthentication ATTError = 0x05 // ErrAuthentication means the attribute requires authentication before it can be read or written.
+ ErrReqNotSupp ATTError = 0x06 // ErrReqNotSupp means the attribute server does not support the request received from the client.
+ ErrInvalidOffset ATTError = 0x07 // ErrInvalidOffset means the specified was past the end of the attribute.
+ ErrAuthorization ATTError = 0x08 // ErrAuthorization means the attribute requires authorization before it can be read or written.
+ ErrPrepQueueFull ATTError = 0x09 // ErrPrepQueueFull means too many prepare writes have been queued.
+ ErrAttrNotFound ATTError = 0x0a // ErrAttrNotFound means no attribute found within the given attribute handle range.
+ ErrAttrNotLong ATTError = 0x0b // ErrAttrNotLong means the attribute cannot be read or written using the Read Blob Request.
+ ErrInsuffEncrKeySize ATTError = 0x0c // ErrInsuffEncrKeySize means the Encryption Key Size used for encrypting this link is insufficient.
+ ErrInvalAttrValueLen ATTError = 0x0d // ErrInvalAttrValueLen means the attribute value length is invalid for the operation.
+ ErrUnlikely ATTError = 0x0e // ErrUnlikely means the attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested.
+ ErrInsuffEnc ATTError = 0x0f // ErrInsuffEnc means the attribute requires encryption before it can be read or written.
+ ErrUnsuppGrpType ATTError = 0x10 // ErrUnsuppGrpType means the attribute type is not a supported grouping attribute as defined by a higher layer specification.
+ ErrInsuffResources ATTError = 0x11 // ErrInsuffResources means insufficient resources to complete the request.
+)
+
+func (e ATTError) Error() string {
+ switch i := int(e); {
+ case i < 0x11:
+ return errName[e]
+ case i >= 0x12 && i <= 0x7F: // Reserved for future use.
+ return fmt.Sprintf("reserved error code (0x%02X)", i)
+ case i >= 0x80 && i <= 0x9F: // Application error, defined by higher level.
+ return fmt.Sprintf("application error code (0x%02X)", i)
+ case i >= 0xA0 && i <= 0xDF: // Reserved for future use.
+ return fmt.Sprintf("reserved error code (0x%02X)", i)
+ case i >= 0xE0 && i <= 0xFF: // Common profile and service error codes.
+ return "profile or service error"
+ }
+ return "unkown error"
+}
+
+var errName = map[ATTError]string{
+ ErrSuccess: "success",
+ ErrInvalidHandle: "invalid handle",
+ ErrReadNotPerm: "read not permitted",
+ ErrWriteNotPerm: "write not permitted",
+ ErrInvalidPDU: "invalid PDU",
+ ErrAuthentication: "insufficient authentication",
+ ErrReqNotSupp: "request not supported",
+ ErrInvalidOffset: "invalid offset",
+ ErrAuthorization: "insufficient authorization",
+ ErrPrepQueueFull: "prepare queue full",
+ ErrAttrNotFound: "attribute not found",
+ ErrAttrNotLong: "attribute not long",
+ ErrInsuffEncrKeySize: "insufficient encryption key size",
+ ErrInvalAttrValueLen: "invalid attribute value length",
+ ErrUnlikely: "unlikely error",
+ ErrInsuffEnc: "insufficient encryption",
+ ErrUnsuppGrpType: "unsupported group type",
+ ErrInsuffResources: "insufficient resources",
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/basic/advertiser/main.go b/vendor/github.com/currantlabs/ble/examples/basic/advertiser/main.go
new file mode 100644
index 0000000..f9aad6b
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/basic/advertiser/main.go
@@ -0,0 +1,46 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/examples/lib/dev"
+)
+
+var (
+ device = flag.String("device", "default", "implementation of ble")
+ du = flag.Duration("du", 5*time.Second, "advertising duration, 0 for indefinitely")
+)
+
+func main() {
+ flag.Parse()
+
+ d, err := dev.NewDevice("default")
+ if err != nil {
+ log.Fatalf("can't new device : %s", err)
+ }
+ ble.SetDefaultDevice(d)
+
+ // Advertise for specified durantion, or until interrupted by user.
+ fmt.Printf("Advertising for %s...\n", *du)
+ ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), *du))
+ chkErr(ble.AdvertiseNameAndServices(ctx, "Gopher"))
+}
+
+func chkErr(err error) {
+ switch errors.Cause(err) {
+ case nil:
+ case context.DeadlineExceeded:
+ fmt.Printf("done\n")
+ case context.Canceled:
+ fmt.Printf("canceled\n")
+ default:
+ log.Fatalf(err.Error())
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/basic/explorer/main.go b/vendor/github.com/currantlabs/ble/examples/basic/explorer/main.go
new file mode 100644
index 0000000..4ff3777
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/basic/explorer/main.go
@@ -0,0 +1,182 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "strings"
+ "time"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/examples/lib/dev"
+)
+
+var (
+ device = flag.String("device", "default", "implementation of ble")
+ name = flag.String("name", "Gopher", "name of remote peripheral")
+ addr = flag.String("addr", "", "address of remote peripheral (MAC on Linux, UUID on OS X)")
+ sub = flag.Duration("sub", 0, "subscribe to notification and indication for a specified period")
+ sd = flag.Duration("sd", 5*time.Second, "scanning duration, 0 for indefinitely")
+)
+
+func main() {
+ flag.Parse()
+
+ d, err := dev.NewDevice(*device)
+ if err != nil {
+ log.Fatalf("can't new device : %s", err)
+ }
+ ble.SetDefaultDevice(d)
+
+ // Default to search device with name of Gopher (or specified by user).
+ filter := func(a ble.Advertisement) bool {
+ return strings.ToUpper(a.LocalName()) == strings.ToUpper(*name)
+ }
+
+ // If addr is specified, search for addr instead.
+ if len(*addr) != 0 {
+ filter = func(a ble.Advertisement) bool {
+ return strings.ToUpper(a.Address().String()) == strings.ToUpper(*addr)
+ }
+ }
+
+ // Scan for specified durantion, or until interrupted by user.
+ fmt.Printf("Scanning for %s...\n", *sd)
+ ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), *sd))
+ cln, err := ble.Connect(ctx, filter)
+ if err != nil {
+ log.Fatalf("can't connect : %s", err)
+ }
+
+ // Make sure we had the chance to print out the message.
+ done := make(chan struct{})
+ // Normally, the connection is disconnected by us after our exploration.
+ // However, it can be asynchronously disconnected by the remote peripheral.
+ // So we wait(detect) the disconnection in the go routine.
+ go func() {
+ <-cln.Disconnected()
+ fmt.Printf("[ %s ] is disconnected \n", cln.Address())
+ close(done)
+ }()
+
+ fmt.Printf("Discovering profile...\n")
+ p, err := cln.DiscoverProfile(true)
+ if err != nil {
+ log.Fatalf("can't discover profile: %s", err)
+ }
+
+ // Start the exploration.
+ explore(cln, p)
+
+ // Disconnect the connection. (On OS X, this might take a while.)
+ fmt.Printf("Disconnecting [ %s ]... (this might take up to few seconds on OS X)\n", cln.Address())
+ cln.CancelConnection()
+
+ <-done
+}
+
+func explore(cln ble.Client, p *ble.Profile) error {
+ for _, s := range p.Services {
+ fmt.Printf(" Service: %s %s, Handle (0x%02X)\n", s.UUID, ble.Name(s.UUID), s.Handle)
+
+ for _, c := range s.Characteristics {
+ fmt.Printf(" Characteristic: %s %s, Property: 0x%02X (%s), Handle(0x%02X), VHandle(0x%02X)\n",
+ c.UUID, ble.Name(c.UUID), c.Property, propString(c.Property), c.Handle, c.ValueHandle)
+ if (c.Property & ble.CharRead) != 0 {
+ b, err := cln.ReadCharacteristic(c)
+ if err != nil {
+ fmt.Printf("Failed to read characteristic: %s\n", err)
+ continue
+ }
+ fmt.Printf(" Value %x | %q\n", b, b)
+ }
+
+ for _, d := range c.Descriptors {
+ fmt.Printf(" Descriptor: %s %s, Handle(0x%02x)\n", d.UUID, ble.Name(d.UUID), d.Handle)
+ b, err := cln.ReadDescriptor(d)
+ if err != nil {
+ fmt.Printf("Failed to read descriptor: %s\n", err)
+ continue
+ }
+ fmt.Printf(" Value %x | %q\n", b, b)
+ }
+
+ if *sub != 0 {
+ // Don't bother to subscribe the Service Changed characteristics.
+ if c.UUID.Equal(ble.ServiceChangedUUID) {
+ continue
+ }
+
+ // Don't touch the Apple-specific Service/Characteristic.
+ // Service: D0611E78BBB44591A5F8487910AE4366
+ // Characteristic: 8667556C9A374C9184ED54EE27D90049, Property: 0x18 (WN),
+ // Descriptor: 2902, Client Characteristic Configuration
+ // Value 0000 | "\x00\x00"
+ if c.UUID.Equal(ble.MustParse("8667556C9A374C9184ED54EE27D90049")) {
+ continue
+ }
+
+ if (c.Property & ble.CharNotify) != 0 {
+ fmt.Printf("\n-- Subscribe to notification for %s --\n", *sub)
+ h := func(req []byte) { fmt.Printf("Notified: %q [ % X ]\n", string(req), req) }
+ if err := cln.Subscribe(c, false, h); err != nil {
+ log.Fatalf("subscribe failed: %s", err)
+ }
+ time.Sleep(*sub)
+ if err := cln.Unsubscribe(c, false); err != nil {
+ log.Fatalf("unsubscribe failed: %s", err)
+ }
+ fmt.Printf("-- Unsubscribe to notification --\n")
+ }
+ if (c.Property & ble.CharIndicate) != 0 {
+ fmt.Printf("\n-- Subscribe to indication of %s --\n", *sub)
+ h := func(req []byte) { fmt.Printf("Indicated: %q [ % X ]\n", string(req), req) }
+ if err := cln.Subscribe(c, true, h); err != nil {
+ log.Fatalf("subscribe failed: %s", err)
+ }
+ time.Sleep(*sub)
+ if err := cln.Unsubscribe(c, true); err != nil {
+ log.Fatalf("unsubscribe failed: %s", err)
+ }
+ fmt.Printf("-- Unsubscribe to indication --\n")
+ }
+ }
+ }
+ fmt.Printf("\n")
+ }
+ return nil
+}
+
+func propString(p ble.Property) string {
+ var s string
+ for k, v := range map[ble.Property]string{
+ ble.CharBroadcast: "B",
+ ble.CharRead: "R",
+ ble.CharWriteNR: "w",
+ ble.CharWrite: "W",
+ ble.CharNotify: "N",
+ ble.CharIndicate: "I",
+ ble.CharSignedWrite: "S",
+ ble.CharExtended: "E",
+ } {
+ if p&k != 0 {
+ s += v
+ }
+ }
+ return s
+}
+
+func chkErr(err error) {
+ switch errors.Cause(err) {
+ case nil:
+ case context.DeadlineExceeded:
+ fmt.Printf("done\n")
+ case context.Canceled:
+ fmt.Printf("canceled\n")
+ default:
+ log.Fatalf(err.Error())
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/basic/scanner/main.go b/vendor/github.com/currantlabs/ble/examples/basic/scanner/main.go
new file mode 100644
index 0000000..4445e92
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/basic/scanner/main.go
@@ -0,0 +1,68 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/examples/lib/dev"
+)
+
+var (
+ device = flag.String("device", "default", "implementation of ble")
+ du = flag.Duration("du", 5*time.Second, "scanning duration")
+ dup = flag.Bool("dup", true, "allow duplicate reported")
+)
+
+func main() {
+ flag.Parse()
+
+ d, err := dev.NewDevice(*device)
+ if err != nil {
+ log.Fatalf("can't new device : %s", err)
+ }
+ ble.SetDefaultDevice(d)
+
+ // Scan for specified durantion, or until interrupted by user.
+ fmt.Printf("Scanning for %s...\n", *du)
+ ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), *du))
+ chkErr(ble.Scan(ctx, *dup, advHandler, nil))
+}
+
+func advHandler(a ble.Advertisement) {
+ if a.Connectable() {
+ fmt.Printf("[%s] C %3d:", a.Address(), a.RSSI())
+ } else {
+ fmt.Printf("[%s] N %3d:", a.Address(), a.RSSI())
+ }
+ comma := ""
+ if len(a.LocalName()) > 0 {
+ fmt.Printf(" Name: %s", a.LocalName())
+ comma = ","
+ }
+ if len(a.Services()) > 0 {
+ fmt.Printf("%s Svcs: %v", comma, a.Services())
+ comma = ","
+ }
+ if len(a.ManufacturerData()) > 0 {
+ fmt.Printf("%s MD: %X", comma, a.ManufacturerData())
+ }
+ fmt.Printf("\n")
+}
+
+func chkErr(err error) {
+ switch errors.Cause(err) {
+ case nil:
+ case context.DeadlineExceeded:
+ fmt.Printf("done\n")
+ case context.Canceled:
+ fmt.Printf("canceled\n")
+ default:
+ log.Fatalf(err.Error())
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/basic/server/main.go b/vendor/github.com/currantlabs/ble/examples/basic/server/main.go
new file mode 100644
index 0000000..39e0737
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/basic/server/main.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/examples/lib"
+ "github.com/currantlabs/ble/examples/lib/dev"
+)
+
+var (
+ device = flag.String("device", "default", "implementation of ble")
+ du = flag.Duration("du", 5*time.Second, "advertising duration, 0 for indefinitely")
+)
+
+func main() {
+ flag.Parse()
+
+ d, err := dev.NewDevice(*device)
+ if err != nil {
+ log.Fatalf("can't new device : %s", err)
+ }
+ ble.SetDefaultDevice(d)
+
+ testSvc := ble.NewService(lib.TestSvcUUID)
+ testSvc.AddCharacteristic(lib.NewCountChar())
+ testSvc.AddCharacteristic(lib.NewEchoChar())
+
+ if err := ble.AddService(testSvc); err != nil {
+ log.Fatalf("can't add service: %s", err)
+ }
+
+ // Advertise for specified durantion, or until interrupted by user.
+ fmt.Printf("Advertising for %s...\n", *du)
+ ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), *du))
+ chkErr(ble.AdvertiseNameAndServices(ctx, "Gopher", testSvc.UUID))
+}
+
+func chkErr(err error) {
+ switch errors.Cause(err) {
+ case nil:
+ case context.DeadlineExceeded:
+ fmt.Printf("done\n")
+ case context.Canceled:
+ fmt.Printf("canceled\n")
+ default:
+ log.Fatalf(err.Error())
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/blesh/README.md b/vendor/github.com/currantlabs/ble/examples/blesh/README.md
new file mode 100644
index 0000000..4b72ec9
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/blesh/README.md
@@ -0,0 +1,235 @@
+
+``` blesh -h ```
+
+```
+NAME:
+ blesh - A CLI tool for ble
+
+USAGE:
+ blesh [global options] command [command options] [arguments...]
+
+VERSION:
+ 0.0.1
+
+COMMANDS:
+ status, st Display current status
+ adv, a Advertise name, UUIDs, iBeacon (TODO)
+ serve, sv Start the GATT Server
+ scan, s Scan surrounding with specified filter
+ connect, c Connect to a peripheral device
+ disconnect, x Disconnect a connected peripheral device
+ discover, d Discover profile on connected device
+ explore, e Display discovered profile
+ read, r Read value from a characteristic or descriptor
+ write, w Write value to a characteristic or descriptor
+ sub Subscribe to notification (or indication)
+ unsub Unsubscribe to notification (or indication)
+ shell, sh Enter interactive mode
+ help, h Shows a list of commands or help for one command
+
+GLOBAL OPTIONS:
+ --help, -h show help
+ --version, -v print the version
+```
+
+*** Quick Tutorial ***
+
+We will start two instances of blesh; one as a peripheral running a simple
+GATT server while another one acting as client connecting to the server, and explore it's profile.
+
+** Build **
+
+blesh supports both OS X and Linux, and can be cross-compiled easily.
+Let's build two binaries and have them running on different platforms.
+(Or you can run two instances on a Linux if you have more than one ble devices.)
+```
+GOOS=darwin go build -o blesh_osx *.go
+GOOS=linux go build -o blesh_lnx *.go
+```
+
+Start a GATT server on the Linux platform (with a usb ble dongle) for 1 hour.
+(We'll leave it sit there for the rest of tutoruial)
+```
+sudo ./blesh_lnx sv -tmo 1h
+
+Initializing device ...
+Serving GATT Server for 1h0m0s...
+```
+
+Start another instance on the OS X, or Linux with another ble device. Use the shell subcommand to enter interactive mode.
+
+```
+./blesh_osx shell
+
+Initializing device ...
+blesh >
+```
+
+Scan for surrounding devices
+
+```
+blesh > scan -tmo 2s
+Scanning for 2s...
+[41a2e12c9265407a82e25379df839e0d] C -47: Name: Gopher, Svcs: [0001000000011000800000805F9B34FB]
+[41a2e12c9265407a82e25379df839e0d] C -47: Name: Gopher
+[41a2e12c9265407a82e25379df839e0d] C -47: Name: Gopher, Svcs: [0001000000011000800000805F9B34FB]
+[41a2e12c9265407a82e25379df839e0d] C -48: Name: Gopher
+[1a7026ac7be8402680926d95ef2f0661] C 86: Name: Bose QuietComfort 35, Svcs: [FEBE], MD: 1001400C0141ACBC32794E58
+[1a7026ac7be8402680926d95ef2f0661] C 86: Name: Bose QuietComfort 35
+[41a2e12c9265407a82e25379df839e0d] C -48: Name: Gopher, Svcs: [0001000000011000800000805F9B34FB]
+[41a2e12c9265407a82e25379df839e0d] C -48: Name: Gopher
+...
+```
+
+The peripheral instance is advertising with "Gopher" as its name, and a service with UUID "0001000000011000800000805F9B34FB".
+
+Now, let's scan only for device that has name of "Gopher" for 1 second. And don't show duplicate.
+
+```
+blesh > scan -name Gopher -dup=false -tmo 1s
+Scanning for 1s...
+[41a2e12c9265407a82e25379df839e0d] C -48: Name: Gopher, Svcs: [0001000000011000800000805F9B34FB]
+[41a2e12c9265407a82e25379df839e0d] C -58: Name: Gopher
+```
+
+The result is much cleaner now.
+
+```
+blesh > status
+Current status:
+ Initialized: yes
+ Address: 41a2e12c9265407a82e25379df839e0d
+ Connected:
+ Profile:
+ UUID:
+```
+
+Status shows some cached values, which can serve as default values in some commands. For example, now we can issue "connect" command without explicitly specifying which device to connect.
+
+```
+blesh > connect
+Connecting ...
+```
+
+Or we can still connect to device with specified condition.
+
+```
+blesh > disconnect
+Disconnecting [ 41A2E12C9265407A82E25379DF839E0D ]... (this might take up to few seconds on OS X)
+
+blesh > connect -addr 41A2E12C9265407A82E25379DF839E0D
+Connecting ...
+```
+
+```
+blesh > status
+Current status:
+ Initialized: yes
+ Address: 41A2E12C9265407A82E25379DF839E0D
+ Connected: 41A2E12C9265407A82E25379DF839E0D
+ Profile:
+ UUID:
+```
+
+Now we're connected, and let's discover the profile (service/char/...) on the server.
+
+```
+blesh > discover
+Discovering profile...
+```
+
+```
+blesh > status
+Current status:
+ Initialized: yes
+ Address: 41A2E12C9265407A82E25379DF839E0D
+ Connected: 41A2E12C9265407A82E25379DF839E0D
+ Profile:
+
+ Service: 0001000000011000800000805F9B34FB , Handle (0x10)
+ Characteristic: 0001000000021000800000805F9B34FB , Property: 0x3E (wWNIR), Handle(0x11), VHandle(0x12)
+ Value 636f756e743a20526561642030 | "count: Read 0"
+ Descriptor: 2902 Client Characteristic Configuration, Handle(0x13)
+ Value 0000 | "\x00\x00"
+ Characteristic: 0002000000021000800000805F9B34FB , Property: 0x3C (IwWN), Handle(0x14), VHandle(0x15)
+ Descriptor: 2902 Client Characteristic Configuration, Handle(0x16)
+ Value 0000 | "\x00\x00"
+
+ UUID:
+```
+
+To display the profile only, use the explore command.
+```
+blesh > explore
+Discovering profile...
+ Service: 0001000000011000800000805F9B34FB , Handle (0x10)
+ Characteristic: 0001000000021000800000805F9B34FB , Property: 0x3E (IRwWN), Handle(0x11), VHandle(0x12)
+ Value 636f756e743a20526561642031 | "count: Read 1"
+ Descriptor: 2902 Client Characteristic Configuration, Handle(0x13)
+ Value 0000 | "\x00\x00"
+ Characteristic: 0002000000021000800000805F9B34FB , Property: 0x3C (wWNI), Handle(0x14), VHandle(0x15)
+ Descriptor: 2902 Client Characteristic Configuration, Handle(0x16)
+ Value 0000 | "\x00\x00"
+```
+
+Notice that the value read from characteristic is changed (our sample characteristic simply increment the counter when it is read).
+
+Use the "read" command to read the characteristic value again.
+
+```
+blesh > read -uuid 0001000000021000800000805F9B34FB
+ Value 636f756e743a20526561642032 | "count: Read 2"
+```
+
+```
+blesh > st
+Current status:
+ Initialized: yes
+ Address: 41A2E12C9265407A82E25379DF839E0D
+ Connected: 41A2E12C9265407A82E25379DF839E0D
+ Profile:
+
+ Service: 0001000000011000800000805F9B34FB , Handle (0x10)
+ Characteristic: 0001000000021000800000805F9B34FB , Property: 0x3E (RwWNI), Handle(0x11), VHandle(0x12)
+ Value 636f756e743a20526561642033 | "count: Read 3"
+ Descriptor: 2902 Client Characteristic Configuration, Handle(0x13)
+ Value 0000 | "\x00\x00"
+ Characteristic: 0002000000021000800000805F9B34FB , Property: 0x3C (wWNI), Handle(0x14), VHandle(0x15)
+ Descriptor: 2902 Client Characteristic Configuration, Handle(0x16)
+ Value 0000 | "\x00\x00"
+
+ UUID: 0001000000021000800000805F9B34FB
+```
+
+Notice now the UUID has been cached, too. So, we can "subscribe" (or unsubscribe) to the characteristic without explicitly specifying it.
+
+```
+blesh > sub
+blesh >
+notified: 436f756e743a2030 | "Count: 0"
+notified: 436f756e743a2031 | "Count: 1"
+notified: 436f756e743a2032 | "Count: 2"
+notified: 436f756e743a2033 | "Count: 3"
+notified: 436f756e743a2034 | "Count: 4"
+unsub
+blesh >
+```
+
+Disconnect, and quit the shell.
+
+```
+blesh > disconnect
+Disconnecting [ 41A2E12C9265407A82E25379DF839E0D ]... (this might take up to few seconds on OS X)
+
+blesh > quit
+```
+
+Given that we've know what filter to use for connecting, and which UUID to manipulate, we can use the one-liner to read the value directly.
+
+```
+./blesh_osx read -name Gopher -uuid 0001000000021000800000805F9B34FB
+Initializing device ...
+Connecting...
+Discovering profile...
+ Value 636f756e743a20526561642034 | "count: Read 4"
+```
diff --git a/vendor/github.com/currantlabs/ble/examples/blesh/adv.go b/vendor/github.com/currantlabs/ble/examples/blesh/adv.go
new file mode 100644
index 0000000..25b1770
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/blesh/adv.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/currantlabs/ble"
+)
+
+func advHandler(a ble.Advertisement) {
+ curr.addr = a.Address()
+ if a.Connectable() {
+ fmt.Printf("[%s] C %3d:", a.Address(), a.RSSI())
+ } else {
+ fmt.Printf("[%s] N %3d:", a.Address(), a.RSSI())
+ }
+ comma := ""
+ if len(a.LocalName()) > 0 {
+ fmt.Printf(" Name: %s", a.LocalName())
+ comma = ","
+ }
+ if len(a.Services()) > 0 {
+ fmt.Printf("%s Svcs: %v", comma, a.Services())
+ comma = ","
+ }
+ if len(a.ManufacturerData()) > 0 {
+ fmt.Printf("%s MD: %X", comma, a.ManufacturerData())
+ }
+ fmt.Printf("\n")
+}
+
+// ServiceData() []ServiceData
+// OverflowService() []UUID
+// TxPowerLevel() int
+// SolicitedService() []UUID
diff --git a/vendor/github.com/currantlabs/ble/examples/blesh/exp.go b/vendor/github.com/currantlabs/ble/examples/blesh/exp.go
new file mode 100644
index 0000000..47fe634
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/blesh/exp.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/currantlabs/ble"
+)
+
+func explore(cln ble.Client, p *ble.Profile) error {
+ for _, s := range p.Services {
+ fmt.Printf(" Service: %s %s, Handle (0x%02X)\n", s.UUID, ble.Name(s.UUID), s.Handle)
+
+ for _, c := range s.Characteristics {
+ fmt.Printf(" Characteristic: %s %s, Property: 0x%02X (%s), Handle(0x%02X), VHandle(0x%02X)\n",
+ c.UUID, ble.Name(c.UUID), c.Property, propString(c.Property), c.Handle, c.ValueHandle)
+ if (c.Property & ble.CharRead) != 0 {
+ b, err := cln.ReadCharacteristic(c)
+ if err != nil {
+ fmt.Printf("Failed to read characteristic: %s\n", err)
+ continue
+ }
+ fmt.Printf(" Value %x | %q\n", b, b)
+ }
+
+ for _, d := range c.Descriptors {
+ fmt.Printf(" Descriptor: %s %s, Handle(0x%02x)\n", d.UUID, ble.Name(d.UUID), d.Handle)
+ b, err := cln.ReadDescriptor(d)
+ if err != nil {
+ fmt.Printf("Failed to read descriptor: %s\n", err)
+ continue
+ }
+ fmt.Printf(" Value %x | %q\n", b, b)
+ }
+ }
+ fmt.Printf("\n")
+ }
+ return nil
+}
+
+func propString(p ble.Property) string {
+ var s string
+ for k, v := range map[ble.Property]string{
+ ble.CharBroadcast: "B",
+ ble.CharRead: "R",
+ ble.CharWriteNR: "w",
+ ble.CharWrite: "W",
+ ble.CharNotify: "N",
+ ble.CharIndicate: "I",
+ ble.CharSignedWrite: "S",
+ ble.CharExtended: "E",
+ } {
+ if p&k != 0 {
+ s += v
+ }
+ }
+ return s
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/blesh/filter.go b/vendor/github.com/currantlabs/ble/examples/blesh/filter.go
new file mode 100644
index 0000000..52283c2
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/blesh/filter.go
@@ -0,0 +1,32 @@
+package main
+
+import (
+ "strings"
+
+ "github.com/currantlabs/ble"
+ "github.com/urfave/cli"
+)
+
+func filter(c *cli.Context) ble.AdvFilter {
+ if c.String("name") != "" {
+ return func(a ble.Advertisement) bool {
+ return strings.ToLower(a.LocalName()) == strings.ToLower(c.String("name"))
+ }
+ }
+ if c.String("addr") != "" {
+ return func(a ble.Advertisement) bool {
+ return a.Address().String() == strings.ToLower(c.String("addr"))
+ }
+ }
+ if svc := strings.ToLower(c.String("svc")); svc != "" {
+ return func(a ble.Advertisement) bool {
+ for _, s := range a.Services() {
+ if s.String() == svc {
+ return true
+ }
+ }
+ return false
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/blesh/flg.go b/vendor/github.com/currantlabs/ble/examples/blesh/flg.go
new file mode 100644
index 0000000..d695155
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/blesh/flg.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "time"
+
+ "github.com/urfave/cli"
+)
+
+var (
+ flgTimeout = cli.DurationFlag{Name: "tmo, t", Value: time.Second * 5, Usage: "Timeout for the command"}
+ flgName = cli.StringFlag{Name: "name, n", Usage: "Name of remote device"}
+ flgAddr = cli.StringFlag{Name: "addr, a", Usage: "Address of remote device"}
+ flgSvc = cli.StringFlag{Name: "svc, s", Usage: "Services of remote device"}
+ flgAllowDup = cli.BoolFlag{Name: "dup", Usage: "Allow duplicate in scanning result"}
+ flgUUID = cli.StringFlag{Name: "uuid, u", Usage: "UUID"}
+ flgInd = cli.BoolFlag{Name: "ind", Usage: "Indication"}
+)
diff --git a/vendor/github.com/currantlabs/ble/examples/blesh/lnx.go b/vendor/github.com/currantlabs/ble/examples/blesh/lnx.go
new file mode 100644
index 0000000..f68ba49
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/blesh/lnx.go
@@ -0,0 +1,52 @@
+package main
+
+import (
+ "github.com/currantlabs/ble/linux"
+ "github.com/currantlabs/ble/linux/hci"
+ "github.com/currantlabs/ble/linux/hci/cmd"
+ "github.com/pkg/errors"
+)
+
+func updateLinuxParam(d *linux.Device) error {
+ if err := d.HCI.Send(&cmd.LESetAdvertisingParameters{
+ AdvertisingIntervalMin: 0x0020, // 0x0020 - 0x4000; N * 0.625 msec
+ AdvertisingIntervalMax: 0x0020, // 0x0020 - 0x4000; N * 0.625 msec
+ AdvertisingType: 0x00, // 00: ADV_IND, 0x01: DIRECT(HIGH), 0x02: SCAN, 0x03: NONCONN, 0x04: DIRECT(LOW)
+ OwnAddressType: 0x00, // 0x00: public, 0x01: random
+ DirectAddressType: 0x00, // 0x00: public, 0x01: random
+ DirectAddress: [6]byte{}, // Public or Random Address of the Device to be connected
+ AdvertisingChannelMap: 0x7, // 0x07 0x01: ch37, 0x2: ch38, 0x4: ch39
+ AdvertisingFilterPolicy: 0x00,
+ }, nil); err != nil {
+ return errors.Wrap(err, "can't set advertising param")
+ }
+
+ if err := d.HCI.Send(&cmd.LESetScanParameters{
+ LEScanType: 0x01, // 0x00: passive, 0x01: active
+ LEScanInterval: 0x0004, // 0x0004 - 0x4000; N * 0.625msec
+ LEScanWindow: 0x0004, // 0x0004 - 0x4000; N * 0.625msec
+ OwnAddressType: 0x00, // 0x00: public, 0x01: random
+ ScanningFilterPolicy: 0x00, // 0x00: accept all, 0x01: ignore non-white-listed.
+ }, nil); err != nil {
+ return errors.Wrap(err, "can't set scan param")
+ }
+
+ if err := d.HCI.Option(hci.OptConnParams(
+ cmd.LECreateConnection{
+ LEScanInterval: 0x0004, // 0x0004 - 0x4000; N * 0.625 msec
+ LEScanWindow: 0x0004, // 0x0004 - 0x4000; N * 0.625 msec
+ InitiatorFilterPolicy: 0x00, // White list is not used
+ PeerAddressType: 0x00, // Public Device Address
+ PeerAddress: [6]byte{}, //
+ OwnAddressType: 0x00, // Public Device Address
+ ConnIntervalMin: 0x0006, // 0x0006 - 0x0C80; N * 1.25 msec
+ ConnIntervalMax: 0x0006, // 0x0006 - 0x0C80; N * 1.25 msec
+ ConnLatency: 0x0000, // 0x0000 - 0x01F3; N * 1.25 msec
+ SupervisionTimeout: 0x0048, // 0x000A - 0x0C80; N * 10 msec
+ MinimumCELength: 0x0000, // 0x0000 - 0xFFFF; N * 0.625 msec
+ MaximumCELength: 0x0000, // 0x0000 - 0xFFFF; N * 0.625 msec
+ })); err != nil {
+ return errors.Wrap(err, "can't set connection param")
+ }
+ return nil
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/blesh/main.go b/vendor/github.com/currantlabs/ble/examples/blesh/main.go
new file mode 100644
index 0000000..b7f9363
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/blesh/main.go
@@ -0,0 +1,425 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "os/signal"
+ "strings"
+ "syscall"
+
+ "golang.org/x/net/context"
+
+ "github.com/pkg/errors"
+ "github.com/urfave/cli"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/examples/lib"
+ "github.com/currantlabs/ble/examples/lib/dev"
+ "github.com/currantlabs/ble/linux"
+)
+
+var curr struct {
+ device ble.Device
+ client ble.Client
+ clients map[string]ble.Client
+ uuid ble.UUID
+ addr ble.Addr
+ profile *ble.Profile
+}
+
+var (
+ errNotConnected = fmt.Errorf("not connected")
+ errNoProfile = fmt.Errorf("no profile")
+ errNoUUID = fmt.Errorf("no UUID")
+ errInvalidUUID = fmt.Errorf("invalid UUID")
+)
+
+func main() {
+ curr.clients = make(map[string]ble.Client)
+
+ app := cli.NewApp()
+
+ app.Name = "blesh"
+ app.Usage = "A CLI tool for ble"
+ app.Version = "0.0.1"
+ app.Action = cli.ShowAppHelp
+
+ app.Commands = []cli.Command{
+ {
+ Name: "status",
+ Aliases: []string{"st"},
+ Usage: "Display current status",
+ Before: setup,
+ Action: cmdStatus,
+ },
+ {
+ Name: "adv",
+ Aliases: []string{"a"},
+ Usage: "Advertise name, UUIDs, iBeacon (TODO)",
+ Before: setup,
+ Action: cmdAdv,
+ Flags: []cli.Flag{flgTimeout, flgName},
+ },
+ {
+ Name: "serve",
+ Aliases: []string{"sv"},
+ Usage: "Start the GATT Server",
+ Before: setup,
+ Action: cmdServe,
+ Flags: []cli.Flag{flgTimeout, flgName},
+ },
+ {
+ Name: "scan",
+ Aliases: []string{"s"},
+ Usage: "Scan surrounding with specified filter",
+ Before: setup,
+ Action: cmdScan,
+ Flags: []cli.Flag{flgTimeout, flgName, flgAddr, flgSvc, flgAllowDup},
+ },
+ {
+ Name: "connect",
+ Aliases: []string{"c"},
+ Usage: "Connect to a peripheral device",
+ Before: setup,
+ Action: cmdConnect,
+ Flags: []cli.Flag{flgTimeout, flgName, flgAddr, flgSvc},
+ },
+ {
+ Name: "disconnect",
+ Aliases: []string{"x"},
+ Usage: "Disconnect a connected peripheral device",
+ Before: setup,
+ Action: cmdDisconnect,
+ Flags: []cli.Flag{flgAddr},
+ },
+ {
+ Name: "discover",
+ Aliases: []string{"d"},
+ Usage: "Discover profile on connected device",
+ Before: setup,
+ Action: cmdDiscover,
+ Flags: []cli.Flag{flgTimeout, flgName, flgAddr},
+ },
+ {
+ Name: "explore",
+ Aliases: []string{"e"},
+ Usage: "Display discovered profile",
+ Before: setup,
+ Action: cmdExplore,
+ Flags: []cli.Flag{flgTimeout, flgName, flgAddr},
+ },
+ {
+ Name: "read",
+ Aliases: []string{"r"},
+ Usage: "Read value from a characteristic or descriptor",
+ Before: setup,
+ Action: cmdRead,
+ Flags: []cli.Flag{flgUUID, flgTimeout, flgName, flgAddr},
+ },
+ {
+ Name: "write",
+ Aliases: []string{"w"},
+ Usage: "Write value to a characteristic or descriptor",
+ Before: setup,
+ Action: cmdWrite,
+ Flags: []cli.Flag{flgUUID, flgTimeout, flgName, flgAddr},
+ },
+ {
+ Name: "sub",
+ Usage: "Subscribe to notification (or indication)",
+ Before: setup,
+ Action: cmdSub,
+ Flags: []cli.Flag{flgUUID, flgInd, flgTimeout, flgName, flgAddr},
+ },
+ {
+ Name: "unsub",
+ Usage: "Unsubscribe to notification (or indication)",
+ Before: setup,
+ Action: cmdUnsub,
+ Flags: []cli.Flag{flgUUID, flgInd, flgAddr},
+ },
+ {
+ Name: "shell",
+ Aliases: []string{"sh"},
+ Usage: "Enter interactive mode",
+ Before: setup,
+ Action: func(c *cli.Context) { cmdShell(app) },
+ },
+ }
+
+ // app.Before = setup
+ app.Run(os.Args)
+}
+
+func setup(c *cli.Context) error {
+ if curr.device != nil {
+ return nil
+ }
+ fmt.Printf("Initializing device ...\n")
+ d, err := dev.NewDevice("default")
+ if err != nil {
+ return errors.Wrap(err, "can't new device")
+ }
+ ble.SetDefaultDevice(d)
+ curr.device = d
+
+ // Optinal. Demostrate changing HCI parameters on Linux.
+ if dev, ok := d.(*linux.Device); ok {
+ return errors.Wrap(updateLinuxParam(dev), "can't update hci parameters")
+ }
+
+ return nil
+}
+func cmdStatus(c *cli.Context) error {
+ m := map[bool]string{true: "yes", false: "no"}
+ fmt.Printf("Current status:\n")
+ fmt.Printf(" Initialized: %s\n", m[curr.device != nil])
+
+ if curr.addr != nil {
+ fmt.Printf(" Address: %s\n", curr.addr)
+ } else {
+ fmt.Printf(" Address:\n")
+ }
+
+ fmt.Printf(" Connected:")
+ for k := range curr.clients {
+ fmt.Printf(" %s", k)
+ }
+ fmt.Printf("\n")
+
+ fmt.Printf(" Profile:\n")
+ if curr.profile != nil {
+ fmt.Printf("\n")
+ explore(curr.client, curr.profile)
+ }
+
+ if curr.uuid != nil {
+ fmt.Printf(" UUID: %s\n", curr.uuid)
+ } else {
+ fmt.Printf(" UUID:\n")
+ }
+
+ return nil
+}
+
+func cmdAdv(c *cli.Context) error {
+ fmt.Printf("Advertising for %s...\n", c.Duration("tmo"))
+ ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), c.Duration("tmo")))
+ return chkErr(ble.AdvertiseNameAndServices(ctx, "Gopher"))
+}
+
+func cmdScan(c *cli.Context) error {
+ fmt.Printf("Scanning for %s...\n", c.Duration("tmo"))
+ ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), c.Duration("tmo")))
+ return chkErr(ble.Scan(ctx, c.Bool("dup"), advHandler, filter(c)))
+}
+
+func cmdServe(c *cli.Context) error {
+ testSvc := ble.NewService(lib.TestSvcUUID)
+ testSvc.AddCharacteristic(lib.NewCountChar())
+ testSvc.AddCharacteristic(lib.NewEchoChar())
+
+ if err := ble.AddService(testSvc); err != nil {
+ return errors.Wrap(err, "can't add service")
+ }
+
+ fmt.Printf("Serving GATT Server for %s...\n", c.Duration("tmo"))
+ ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), c.Duration("tmo")))
+ return chkErr(ble.AdvertiseNameAndServices(ctx, "Gopher", testSvc.UUID))
+}
+
+func cmdConnect(c *cli.Context) error {
+ curr.client = nil
+
+ var cln ble.Client
+ var err error
+
+ ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), c.Duration("tmo")))
+ if c.String("addr") != "" {
+ curr.addr = ble.NewAddr(c.String("addr"))
+ fmt.Printf("Dialing to specified address: %s\n", curr.addr)
+ cln, err = ble.Dial(ctx, curr.addr)
+ } else if filter(c) != nil {
+ fmt.Printf("Scanning with filter...\n")
+ if cln, err = ble.Connect(ctx, filter(c)); err == nil {
+ curr.addr = cln.Address()
+ fmt.Printf("Connected to %s\n", curr.addr)
+
+ }
+ } else if curr.addr != nil {
+ fmt.Printf("Dialing to implicit address: %s\n", curr.addr)
+ cln, err = ble.Dial(ctx, curr.addr)
+ } else {
+ return fmt.Errorf("no filter specified, and cached peripheral address")
+ }
+ if err == nil {
+ curr.client = cln
+ curr.clients[cln.Address().String()] = cln
+ }
+ go func() {
+ <-cln.Disconnected()
+ delete(curr.clients, cln.Address().String())
+ curr.client = nil
+ fmt.Printf("\n%s disconnected\n", cln.Address().String())
+ }()
+ return err
+}
+
+func cmdDisconnect(c *cli.Context) error {
+ if c.String("addr") != "" {
+ curr.client = curr.clients[c.String("addr")]
+ }
+ if curr.client == nil {
+ return errNotConnected
+ }
+ defer func() {
+ delete(curr.clients, curr.client.Address().String())
+ curr.client = nil
+ curr.profile = nil
+ }()
+
+ fmt.Printf("Disconnecting [ %s ]... (this might take up to few seconds on OS X)\n", curr.client.Address())
+ return curr.client.CancelConnection()
+}
+
+func cmdDiscover(c *cli.Context) error {
+ curr.profile = nil
+ if curr.client == nil {
+ if err := cmdConnect(c); err != nil {
+ return errors.Wrap(err, "can't connect")
+ }
+ }
+
+ fmt.Printf("Discovering profile...\n")
+ p, err := curr.client.DiscoverProfile(true)
+ if err != nil {
+ return errors.Wrap(err, "can't discover profile")
+ }
+
+ curr.profile = p
+ return nil
+}
+
+func cmdExplore(c *cli.Context) error {
+ if curr.client == nil {
+ if err := cmdConnect(c); err != nil {
+ return errors.Wrap(err, "can't connect")
+ }
+ }
+ if curr.profile == nil {
+ if err := cmdDiscover(c); err != nil {
+ return errors.Wrap(err, "can't discover profile")
+ }
+ }
+ return explore(curr.client, curr.profile)
+}
+
+func cmdRead(c *cli.Context) error {
+ if err := doGetUUID(c); err != nil {
+ return err
+ }
+ if err := doConnect(c); err != nil {
+ return err
+ }
+ if err := doDiscover(c); err != nil {
+ return err
+ }
+ if u := curr.profile.Find(ble.NewCharacteristic(curr.uuid)); u != nil {
+ b, err := curr.client.ReadCharacteristic(u.(*ble.Characteristic))
+ if err != nil {
+ return errors.Wrap(err, "can't read characteristic")
+ }
+ fmt.Printf(" Value %x | %q\n", b, b)
+ return nil
+ }
+ if u := curr.profile.Find(ble.NewDescriptor(curr.uuid)); u != nil {
+ b, err := curr.client.ReadDescriptor(u.(*ble.Descriptor))
+ if err != nil {
+ return errors.Wrap(err, "can't read descriptor")
+ }
+ fmt.Printf(" Value %x | %q\n", b, b)
+ return nil
+ }
+ return errNoUUID
+}
+
+func cmdWrite(c *cli.Context) error {
+ if err := doGetUUID(c); err != nil {
+ return err
+ }
+ if err := doConnect(c); err != nil {
+ return err
+ }
+ if err := doDiscover(c); err != nil {
+ return err
+ }
+ if u := curr.profile.Find(ble.NewCharacteristic(curr.uuid)); u != nil {
+ err := curr.client.WriteCharacteristic(u.(*ble.Characteristic), []byte("hello"), true)
+ return errors.Wrap(err, "can't write characteristic")
+ }
+ if u := curr.profile.Find(ble.NewDescriptor(curr.uuid)); u != nil {
+ err := curr.client.WriteDescriptor(u.(*ble.Descriptor), []byte("fixme"))
+ return errors.Wrap(err, "can't write descriptor")
+ }
+ return errNoUUID
+}
+
+func cmdSub(c *cli.Context) error {
+ if err := doGetUUID(c); err != nil {
+ return err
+ }
+ if err := doConnect(c); err != nil {
+ return err
+ }
+ if err := doDiscover(c); err != nil {
+ return err
+ }
+ // NotificationHandler
+ h := func(req []byte) { fmt.Printf("notified: %x | %q\n", req, req) }
+ if u := curr.profile.Find(ble.NewCharacteristic(curr.uuid)); u != nil {
+ err := curr.client.Subscribe(u.(*ble.Characteristic), c.Bool("ind"), h)
+ return errors.Wrap(err, "can't subscribe to characteristic")
+ }
+ return errNoUUID
+}
+
+func cmdUnsub(c *cli.Context) error {
+ if err := doGetUUID(c); err != nil {
+ return err
+ }
+ if err := doConnect(c); err != nil {
+ return err
+ }
+ if u := curr.profile.Find(ble.NewCharacteristic(curr.uuid)); u != nil {
+ err := curr.client.Unsubscribe(u.(*ble.Characteristic), c.Bool("ind"))
+ return errors.Wrap(err, "can't unsubscribe to characteristic")
+ }
+ return errNoUUID
+}
+
+func cmdShell(app *cli.App) {
+ cli.OsExiter = func(c int) {}
+ reader := bufio.NewReader(os.Stdin)
+ sigs := make(chan os.Signal, 1)
+ go func() {
+ for range sigs {
+ fmt.Printf("\n(type quit or q to exit)\n\nblesh >")
+ }
+ }()
+ defer close(sigs)
+ signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+ for {
+ fmt.Print("blesh > ")
+ text, _ := reader.ReadString('\n')
+ text = strings.TrimSpace(text)
+ if text == "" {
+ continue
+ }
+ if text == "quit" || text == "q" {
+ break
+ }
+ app.Run(append(os.Args[1:], strings.Split(text, " ")...))
+ }
+ signal.Stop(sigs)
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/blesh/util.go b/vendor/github.com/currantlabs/ble/examples/blesh/util.go
new file mode 100644
index 0000000..34e3741
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/blesh/util.go
@@ -0,0 +1,54 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/currantlabs/ble"
+ "github.com/pkg/errors"
+ "github.com/urfave/cli"
+ "golang.org/x/net/context"
+)
+
+func doGetUUID(c *cli.Context) error {
+ if c.String("uuid") != "" {
+ u, err := ble.Parse(c.String("uuid"))
+ if err != nil {
+ return errInvalidUUID
+ }
+ curr.uuid = u
+ }
+ if curr.uuid == nil {
+ return errNoUUID
+ }
+ return nil
+}
+
+func doConnect(c *cli.Context) error {
+ if c.String("addr") != "" {
+ curr.addr = ble.NewAddr(c.String("addr"))
+ curr.client = curr.clients[curr.addr.String()]
+ }
+ if curr.client != nil {
+ return nil
+ }
+ return cmdConnect(c)
+}
+
+func doDiscover(c *cli.Context) error {
+ if curr.profile != nil {
+ return nil
+ }
+ return cmdDiscover(c)
+}
+
+func chkErr(err error) error {
+ switch errors.Cause(err) {
+ case context.DeadlineExceeded:
+ // Sepcified duration passed, which is the expected case.
+ return nil
+ case context.Canceled:
+ fmt.Printf("\n(Canceled)\n")
+ return nil
+ }
+ return err
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/lib/battery.go b/vendor/github.com/currantlabs/ble/examples/lib/battery.go
new file mode 100644
index 0000000..47a0b1f
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/lib/battery.go
@@ -0,0 +1,23 @@
+package lib
+
+import "github.com/currantlabs/ble"
+
+// NewBatteryService ...
+func NewBatteryService() *ble.Service {
+ lv := byte(100)
+ s := ble.NewService(ble.UUID16(0x180F))
+ c := s.NewCharacteristic(ble.UUID16(0x2A19))
+ c.HandleRead(
+ ble.ReadHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
+ rsp.Write([]byte{lv})
+ lv--
+ }))
+
+ // Characteristic User Description
+ c.NewDescriptor(ble.UUID16(0x2901)).SetValue([]byte("Battery level between 0 and 100 percent"))
+
+ // Characteristic Presentation Format
+ c.NewDescriptor(ble.UUID16(0x2904)).SetValue([]byte{4, 1, 39, 173, 1, 0, 0})
+
+ return s
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/lib/count.go b/vendor/github.com/currantlabs/ble/examples/lib/count.go
new file mode 100644
index 0000000..ea1e2c7
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/lib/count.go
@@ -0,0 +1,65 @@
+package lib
+
+import (
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/currantlabs/ble"
+)
+
+// NewCountChar ...
+func NewCountChar() *ble.Characteristic {
+ n := 0
+ c := ble.NewCharacteristic(CountCharUUID)
+ c.HandleRead(ble.ReadHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
+ fmt.Fprintf(rsp, "count: Read %d", n)
+ log.Printf("count: Read %d", n)
+ n++
+ }))
+
+ c.HandleWrite(ble.WriteHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
+ log.Printf("count: Wrote %s", string(req.Data()))
+ }))
+
+ c.HandleNotify(ble.NotifyHandlerFunc(func(req ble.Request, n ble.Notifier) {
+ cnt := 0
+ log.Printf("count: Notification subscribed")
+ for {
+ select {
+ case <-n.Context().Done():
+ log.Printf("count: Notification unsubscribed")
+ return
+ case <-time.After(time.Second):
+ log.Printf("count: Notify: %d", cnt)
+ if _, err := fmt.Fprintf(n, "Count: %d", cnt); err != nil {
+ // Client disconnected prematurely before unsubscription.
+ log.Printf("count: Failed to notify : %s", err)
+ return
+ }
+ cnt++
+ }
+ }
+ }))
+
+ c.HandleIndicate(ble.NotifyHandlerFunc(func(req ble.Request, n ble.Notifier) {
+ cnt := 0
+ log.Printf("count: Indication subscribed")
+ for {
+ select {
+ case <-n.Context().Done():
+ log.Printf("count: Indication unsubscribed")
+ return
+ case <-time.After(time.Second):
+ log.Printf("count: Indicate: %d", cnt)
+ if _, err := fmt.Fprintf(n, "Count: %d", cnt); err != nil {
+ // Client disconnected prematurely before unsubscription.
+ log.Printf("count: Failed to indicate : %s", err)
+ return
+ }
+ cnt++
+ }
+ }
+ }))
+ return c
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/lib/dev/default_darwin.go b/vendor/github.com/currantlabs/ble/examples/lib/dev/default_darwin.go
new file mode 100644
index 0000000..5200989
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/lib/dev/default_darwin.go
@@ -0,0 +1,11 @@
+package dev
+
+import (
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/darwin"
+)
+
+// DefaultDevice ...
+func DefaultDevice() (d ble.Device, err error) {
+ return darwin.NewDevice()
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/lib/dev/default_linux.go b/vendor/github.com/currantlabs/ble/examples/lib/dev/default_linux.go
new file mode 100644
index 0000000..7d31388
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/lib/dev/default_linux.go
@@ -0,0 +1,11 @@
+package dev
+
+import (
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/linux"
+)
+
+// DefaultDevice ...
+func DefaultDevice() (d ble.Device, err error) {
+ return linux.NewDevice()
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/lib/dev/dev.go b/vendor/github.com/currantlabs/ble/examples/lib/dev/dev.go
new file mode 100644
index 0000000..6ff46c3
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/lib/dev/dev.go
@@ -0,0 +1,8 @@
+package dev
+
+import "github.com/currantlabs/ble"
+
+// NewDevice ...
+func NewDevice(impl string) (d ble.Device, err error) {
+ return DefaultDevice()
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/lib/echo.go b/vendor/github.com/currantlabs/ble/examples/lib/echo.go
new file mode 100644
index 0000000..472471d
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/lib/echo.go
@@ -0,0 +1,58 @@
+package lib
+
+import (
+ "log"
+ "sync"
+ "time"
+
+ "github.com/currantlabs/ble"
+)
+
+// NewEchoChar ...
+func NewEchoChar() *ble.Characteristic {
+ e := &echoChar{m: make(map[string]chan []byte)}
+ c := ble.NewCharacteristic(EchoCharUUID)
+ c.HandleWrite(ble.WriteHandlerFunc(e.written))
+ c.HandleNotify(ble.NotifyHandlerFunc(e.echo))
+ c.HandleIndicate(ble.NotifyHandlerFunc(e.echo))
+ return c
+}
+
+type echoChar struct {
+ sync.Mutex
+ m map[string]chan []byte
+}
+
+func (e *echoChar) written(req ble.Request, rsp ble.ResponseWriter) {
+ e.Lock()
+ e.m[req.Conn().RemoteAddr().String()] <- req.Data()
+ e.Unlock()
+}
+
+func (e *echoChar) echo(req ble.Request, n ble.Notifier) {
+ ch := make(chan []byte)
+ e.Lock()
+ e.m[req.Conn().RemoteAddr().String()] = ch
+ e.Unlock()
+ log.Printf("echo: Notification subscribed")
+ defer func() {
+ e.Lock()
+ delete(e.m, req.Conn().RemoteAddr().String())
+ e.Unlock()
+ }()
+ for {
+ select {
+ case <-n.Context().Done():
+ log.Printf("echo: Notification unsubscribed")
+ return
+ case <-time.After(time.Second * 20):
+ log.Printf("echo: timeout")
+ return
+ case msg := <-ch:
+ if _, err := n.Write(msg); err != nil {
+ log.Printf("echo: can't indicate: %s", err)
+ return
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/examples/lib/uuids.go b/vendor/github.com/currantlabs/ble/examples/lib/uuids.go
new file mode 100644
index 0000000..15d546d
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/examples/lib/uuids.go
@@ -0,0 +1,11 @@
+package lib
+
+import "github.com/currantlabs/ble"
+
+// Private 128-bit UUIDs, which avoids the base of pre-defined 16/32-bits UUIDS
+// xxxxxxxx-0000-1000-8000-00805F9B34FB [Vol 3, Part B, 2.5.1].
+var (
+ TestSvcUUID = ble.MustParse("00010000-0001-1000-8000-00805F9B34FB")
+ CountCharUUID = ble.MustParse("00010000-0002-1000-8000-00805F9B34FB")
+ EchoCharUUID = ble.MustParse("00020000-0002-1000-8000-00805F9B34FB")
+)
diff --git a/vendor/github.com/currantlabs/ble/gatt.go b/vendor/github.com/currantlabs/ble/gatt.go
new file mode 100644
index 0000000..e157d29
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/gatt.go
@@ -0,0 +1,188 @@
+package ble
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+
+ "github.com/pkg/errors"
+
+ "golang.org/x/net/context"
+)
+
+// ErrDefaultDevice ...
+var ErrDefaultDevice = errors.New("default device is not set")
+
+var defaultDevice Device
+
+// SetDefaultDevice returns the default HCI device.
+func SetDefaultDevice(d Device) {
+ defaultDevice = d
+}
+
+// AddService adds a service to database.
+func AddService(svc *Service) error {
+ if defaultDevice == nil {
+ return ErrDefaultDevice
+ }
+ return defaultDevice.AddService(svc)
+}
+
+// RemoveAllServices removes all services that are currently in the database.
+func RemoveAllServices() error {
+ if defaultDevice == nil {
+ return ErrDefaultDevice
+ }
+ return defaultDevice.RemoveAllServices()
+}
+
+// SetServices set the specified service to the database.
+// It removes all currently added services, if any.
+func SetServices(svcs []*Service) error {
+ if defaultDevice == nil {
+ return ErrDefaultDevice
+ }
+ return defaultDevice.SetServices(svcs)
+}
+
+// Stop detatch the GATT server from a peripheral device.
+func Stop() error {
+ if defaultDevice == nil {
+ return ErrDefaultDevice
+ }
+ return defaultDevice.Stop()
+}
+
+// AdvertiseNameAndServices advertises device name, and specified service UUIDs.
+// It tres to fit the UUIDs in the advertising packet as much as possi
+// If name doesn't fit in the advertising packet, it will be put in scan response.
+func AdvertiseNameAndServices(ctx context.Context, name string, uuids ...UUID) error {
+ if defaultDevice == nil {
+ return ErrDefaultDevice
+ }
+ defer untrap(trap(ctx))
+ return defaultDevice.AdvertiseNameAndServices(ctx, name, uuids...)
+}
+
+// AdvertiseIBeaconData advertise iBeacon with given manufacturer data.
+func AdvertiseIBeaconData(ctx context.Context, b []byte) error {
+ if defaultDevice == nil {
+ return ErrDefaultDevice
+ }
+ defer untrap(trap(ctx))
+ return defaultDevice.AdvertiseIBeaconData(ctx, b)
+}
+
+// AdvertiseIBeacon advertises iBeacon with specified parameters.
+func AdvertiseIBeacon(ctx context.Context, u UUID, major, minor uint16, pwr int8) error {
+ if defaultDevice == nil {
+ return ErrDefaultDevice
+ }
+ defer untrap(trap(ctx))
+ return defaultDevice.AdvertiseIBeacon(ctx, u, major, minor, pwr)
+}
+
+// Scan starts scanning. Duplicated advertisements will be filtered out if allowDup is set to false.
+func Scan(ctx context.Context, allowDup bool, h AdvHandler, f AdvFilter) error {
+ if defaultDevice == nil {
+ return ErrDefaultDevice
+ }
+ defer untrap(trap(ctx))
+
+ if f == nil {
+ return defaultDevice.Scan(ctx, allowDup, h)
+ }
+
+ h2 := func(a Advertisement) {
+ if f(a) {
+ h(a)
+ }
+ }
+ return defaultDevice.Scan(ctx, allowDup, h2)
+}
+
+// Find ...
+func Find(ctx context.Context, allowDup bool, f AdvFilter) ([]Advertisement, error) {
+ if defaultDevice == nil {
+ return nil, ErrDefaultDevice
+ }
+ var advs []Advertisement
+ h := func(a Advertisement) {
+ advs = append(advs, a)
+ }
+ defer untrap(trap(ctx))
+ return advs, Scan(ctx, allowDup, h, f)
+}
+
+// Dial ...
+func Dial(ctx context.Context, a Addr) (Client, error) {
+ if defaultDevice == nil {
+ return nil, ErrDefaultDevice
+ }
+ defer untrap(trap(ctx))
+ return defaultDevice.Dial(ctx, a)
+}
+
+// Connect searches for and connects to a Peripheral which matches specified condition.
+func Connect(ctx context.Context, f AdvFilter) (Client, error) {
+ ctx2, cancel := context.WithCancel(ctx)
+ go func() {
+ select {
+ case <-ctx.Done():
+ cancel()
+ case <-ctx2.Done():
+ }
+ }()
+
+ ch := make(chan Advertisement)
+ fn := func(a Advertisement) {
+ cancel()
+ ch <- a
+ }
+ if err := Scan(ctx2, false, fn, f); err != nil {
+ if err != context.Canceled {
+ return nil, errors.Wrap(err, "can't scan")
+ }
+ }
+
+ cln, err := Dial(ctx, (<-ch).Address())
+ return cln, errors.Wrap(err, "can't dial")
+}
+
+// A NotificationHandler handles notification or indication from a server.
+type NotificationHandler func(req []byte)
+
+// WithSigHandler ...
+func WithSigHandler(ctx context.Context, cancel func()) context.Context {
+ return context.WithValue(ctx, "sig", cancel)
+}
+
+// Cleanup for the interrupted case.
+func trap(ctx context.Context) chan<- os.Signal {
+ v := ctx.Value("sig")
+ if v == nil {
+ return nil
+ }
+ cancel, ok := v.(func())
+ if cancel == nil || !ok {
+ return nil
+ }
+
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+ go func() {
+ select {
+ case <-sigs:
+ cancel()
+ case <-ctx.Done():
+ }
+ }()
+ return sigs
+}
+
+func untrap(sigs chan<- os.Signal) {
+ if sigs == nil {
+ return
+ }
+ signal.Stop(sigs)
+}
diff --git a/vendor/github.com/currantlabs/ble/handler.go b/vendor/github.com/currantlabs/ble/handler.go
new file mode 100644
index 0000000..b3673fb
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/handler.go
@@ -0,0 +1,188 @@
+package ble
+
+import (
+ "bytes"
+ "io"
+
+ "golang.org/x/net/context"
+)
+
+// A ReadHandler handles GATT requests.
+type ReadHandler interface {
+ ServeRead(req Request, rsp ResponseWriter)
+}
+
+// ReadHandlerFunc is an adapter to allow the use of ordinary functions as Handlers.
+type ReadHandlerFunc func(req Request, rsp ResponseWriter)
+
+// ServeRead returns f(r, maxlen, offset).
+func (f ReadHandlerFunc) ServeRead(req Request, rsp ResponseWriter) {
+ f(req, rsp)
+}
+
+// A WriteHandler handles GATT requests.
+type WriteHandler interface {
+ ServeWrite(req Request, rsp ResponseWriter)
+}
+
+// WriteHandlerFunc is an adapter to allow the use of ordinary functions as Handlers.
+type WriteHandlerFunc func(req Request, rsp ResponseWriter)
+
+// ServeWrite returns f(r, maxlen, offset).
+func (f WriteHandlerFunc) ServeWrite(req Request, rsp ResponseWriter) {
+ f(req, rsp)
+}
+
+// A NotifyHandler handles GATT requests.
+type NotifyHandler interface {
+ ServeNotify(req Request, n Notifier)
+}
+
+// NotifyHandlerFunc is an adapter to allow the use of ordinary functions as Handlers.
+type NotifyHandlerFunc func(req Request, n Notifier)
+
+// ServeNotify returns f(r, maxlen, offset).
+func (f NotifyHandlerFunc) ServeNotify(req Request, n Notifier) {
+ f(req, n)
+}
+
+// Request ...
+type Request interface {
+ Conn() Conn
+ Data() []byte
+ Offset() int
+}
+
+// NewRequest returns a default implementation of Request.
+func NewRequest(conn Conn, data []byte, offset int) Request {
+ return &request{conn: conn, data: data, offset: offset}
+}
+
+// Default implementation of request.
+type request struct {
+ conn Conn
+ data []byte
+ offset int
+}
+
+func (r *request) Conn() Conn { return r.conn }
+func (r *request) Data() []byte { return r.data }
+func (r *request) Offset() int { return r.offset }
+
+// ResponseWriter ...
+type ResponseWriter interface {
+ // Write writes data to return as the characteristic value.
+ Write(b []byte) (int, error)
+
+ // Status reports the result of the request.
+ Status() ATTError
+
+ // SetStatus reports the result of the request.
+ SetStatus(status ATTError)
+
+ // Len ...
+ Len() int
+
+ // Cap ...
+ Cap() int
+}
+
+// NewResponseWriter ...
+func NewResponseWriter(buf *bytes.Buffer) ResponseWriter {
+ return &responseWriter{buf: buf}
+}
+
+// responseWriter implements Response
+type responseWriter struct {
+ buf *bytes.Buffer
+ status ATTError
+}
+
+// Status reports the result of the request.
+func (r *responseWriter) Status() ATTError {
+ return r.status
+}
+
+// SetStatus reports the result of the request.
+func (r *responseWriter) SetStatus(status ATTError) {
+ r.status = status
+}
+
+// Len returns length of the buffer.
+// Len returns 0 if it is a dummy write response for WriteCommand.
+func (r *responseWriter) Len() int {
+ if r.buf == nil {
+ return 0
+ }
+ return r.buf.Len()
+}
+
+// Cap returns capacity of the buffer.
+// Cap returns 0 if it is a dummy write response for WriteCommand.
+func (r *responseWriter) Cap() int {
+ if r.buf == nil {
+ return 0
+ }
+ return r.buf.Cap()
+}
+
+// Write writes data to return as the characteristic value.
+// Cap returns 0 with error set to ErrReqNotSupp if it is a dummy write response for WriteCommand.
+func (r *responseWriter) Write(b []byte) (int, error) {
+ if r.buf == nil {
+ return 0, ErrReqNotSupp
+ }
+ if len(b) > r.buf.Cap()-r.buf.Len() {
+ return 0, io.ErrShortWrite
+ }
+
+ return r.buf.Write(b)
+}
+
+// Notifier ...
+type Notifier interface {
+ // Context sends data to the central.
+ Context() context.Context
+
+ // Write sends data to the central.
+ Write(b []byte) (int, error)
+
+ // Close ...
+ Close() error
+
+ // Cap returns the maximum number of bytes that may be sent in a single notification.
+ Cap() int
+}
+
+type notifier struct {
+ ctx context.Context
+ maxlen int
+ cancel func()
+ send func([]byte) (int, error)
+}
+
+// NewNotifier ...
+func NewNotifier(send func([]byte) (int, error)) Notifier {
+ n := ¬ifier{}
+ n.ctx, n.cancel = context.WithCancel(context.Background())
+ n.send = send
+ // n.maxlen = cap
+ return n
+}
+
+func (n *notifier) Context() context.Context {
+ return n.ctx
+}
+
+func (n *notifier) Write(b []byte) (int, error) {
+ return n.send(b)
+}
+
+func (n *notifier) Close() error {
+ n.cancel()
+ return nil
+}
+
+func (n *notifier) Cap() int {
+ return n.maxlen
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/adv/const.go b/vendor/github.com/currantlabs/ble/linux/adv/const.go
new file mode 100644
index 0000000..284934a
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/adv/const.go
@@ -0,0 +1,57 @@
+package adv
+
+import "errors"
+
+// MaxEIRPacketLength is the maximum allowed AdvertisingPacket
+// and ScanResponsePacket length.
+const MaxEIRPacketLength = 31
+
+// ErrNotFit ...
+var (
+ ErrInvalid = errors.New("invalid argument")
+ ErrNotFit = errors.New("data not fit")
+)
+
+// Advertising flags
+const (
+ FlagLimitedDiscoverable = 0x01 // LE Limited Discoverable Mode
+ FlagGeneralDiscoverable = 0x02 // LE General Discoverable Mode
+ FlagLEOnly = 0x04 // BR/EDR Not Supported. Bit 37 of LMP Feature Mask Definitions (Page 0)
+ FlagBothController = 0x08 // Simultaneous LE and BR/EDR to Same Device Capable (Controller).
+ FlagBothHost = 0x10 // Simultaneous LE and BR/EDR to Same Device Capable (Host).
+)
+
+// Advertising data field s
+const (
+ flags = 0x01 // Flags
+ someUUID16 = 0x02 // Incomplete List of 16-bit Service Class UUIDs
+ allUUID16 = 0x03 // Complete List of 16-bit Service Class UUIDs
+ someUUID32 = 0x04 // Incomplete List of 32-bit Service Class UUIDs
+ allUUID32 = 0x05 // Complete List of 32-bit Service Class UUIDs
+ someUUID128 = 0x06 // Incomplete List of 128-bit Service Class UUIDs
+ allUUID128 = 0x07 // Complete List of 128-bit Service Class UUIDs
+ shortName = 0x08 // Shortened Local Name
+ completeName = 0x09 // Complete Local Name
+ txPower = 0x0A // Tx Power Level
+ classOfDevice = 0x0D // Class of Device
+ simplePairingC192 = 0x0E // Simple Pairing Hash C-192
+ simplePairingR192 = 0x0F // Simple Pairing Randomizer R-192
+ secManagerTK = 0x10 // Security Manager TK Value
+ secManagerOOB = 0x11 // Security Manager Out of Band Flags
+ slaveConnInt = 0x12 // Slave Connection Interval Range
+ serviceSol16 = 0x14 // List of 16-bit Service Solicitation UUIDs
+ serviceSol128 = 0x15 // List of 128-bit Service Solicitation UUIDs
+ serviceData16 = 0x16 // Service Data - 16-bit UUID
+ pubTargetAddr = 0x17 // Public Target Address
+ randTargetAddr = 0x18 // Random Target Address
+ appearance = 0x19 // Appearance
+ advInterval = 0x1A // Advertising Interval
+ leDeviceAddr = 0x1B // LE Bluetooth Device Address
+ leRole = 0x1C // LE Role
+ serviceSol32 = 0x1F // List of 32-bit Service Solicitation UUIDs
+ serviceData32 = 0x20 // Service Data - 32-bit UUID
+ serviceData128 = 0x21 // Service Data - 128-bit UUID
+ leSecConfirm = 0x22 // LE Secure Connections Confirmation Value
+ leSecRandom = 0x23 // LE Secure Connections Random Value
+ manufacturerData = 0xFF // Manufacturer Specific Data
+)
diff --git a/vendor/github.com/currantlabs/ble/linux/adv/packet.go b/vendor/github.com/currantlabs/ble/linux/adv/packet.go
new file mode 100644
index 0000000..9f38e4b
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/adv/packet.go
@@ -0,0 +1,289 @@
+package adv
+
+import (
+ "encoding/binary"
+
+ "github.com/currantlabs/ble"
+)
+
+// Packet is an implemntation of ble.AdvPacket for crafting or parsing an advertising packet or scan response.
+// Refer to Supplement to Bluetooth Core Specification | CSSv6, Part A.
+type Packet struct {
+ b []byte
+}
+
+// Bytes returns the bytes of the packet.
+func (p *Packet) Bytes() []byte {
+ return p.b
+}
+
+// Len returns the length of the packet.
+func (p *Packet) Len() int {
+ return len(p.b)
+}
+
+// NewPacket returns a new advertising Packet.
+func NewPacket(fields ...Field) (*Packet, error) {
+ p := &Packet{b: make([]byte, 0, MaxEIRPacketLength)}
+ for _, f := range fields {
+ if err := f(p); err != nil {
+ return nil, err
+ }
+ }
+ return p, nil
+}
+
+// NewRawPacket returns a new advertising Packet.
+func NewRawPacket(bytes ...[]byte) *Packet {
+ p := &Packet{b: make([]byte, 0, MaxEIRPacketLength)}
+ for _, b := range bytes {
+ p.b = append(p.b, b...)
+ }
+ return p
+}
+
+// Field is an advertising field which can be appended to a packet.
+type Field func(p *Packet) error
+
+// Append appends a field to the packet. It returns ErrNotFit if the field
+// doesn't fit into the packet, and leaves the packet intact.
+func (p *Packet) Append(f Field) error {
+ return f(p)
+}
+
+// appends appends a field to the packet. It returns ErrNotFit if the field
+// doesn't fit into the packet, and leaves the packet intact.
+func (p *Packet) append(typ byte, b []byte) error {
+ if p.Len()+1+1+len(b) > MaxEIRPacketLength {
+ return ErrNotFit
+ }
+ p.b = append(p.b, byte(len(b)+1))
+ p.b = append(p.b, typ)
+ p.b = append(p.b, b...)
+ return nil
+}
+
+// Raw appends the bytes to the current packet.
+// This is helpful for creating new packet from existing packets.
+func Raw(b []byte) Field {
+ return func(p *Packet) error {
+ if p.Len()+len(b) > MaxEIRPacketLength {
+ return ErrNotFit
+ }
+ p.b = append(p.b, b...)
+ return nil
+ }
+}
+
+// IBeaconData returns an iBeacon advertising packet with specified parameters.
+func IBeaconData(md []byte) Field {
+ return func(p *Packet) error {
+ return ManufacturerData(0x004C, md)(p)
+ }
+}
+
+// IBeacon returns an iBeacon advertising packet with specified parameters.
+func IBeacon(u ble.UUID, major, minor uint16, pwr int8) Field {
+ return func(p *Packet) error {
+ if u.Len() != 16 {
+ return ErrInvalid
+ }
+ md := make([]byte, 23)
+ md[0] = 0x02 // Data type: iBeacon
+ md[1] = 0x15 // Data length: 21 bytes
+ copy(md[2:], ble.Reverse(u)) // Big endian
+ binary.BigEndian.PutUint16(md[18:], major) // Big endian
+ binary.BigEndian.PutUint16(md[20:], minor) // Big endian
+ md[22] = uint8(pwr) // Measured Tx Power
+ return ManufacturerData(0x004C, md)(p)
+ }
+}
+
+// Flags is a flags.
+func Flags(f byte) Field {
+ return func(p *Packet) error {
+ return p.append(flags, []byte{f})
+ }
+}
+
+// ShortName is a short local name.
+func ShortName(n string) Field {
+ return func(p *Packet) error {
+ return p.append(shortName, []byte(n))
+ }
+}
+
+// CompleteName is a compelete local name.
+func CompleteName(n string) Field {
+ return func(p *Packet) error {
+ return p.append(completeName, []byte(n))
+ }
+}
+
+// ManufacturerData is manufacturer specific data.
+func ManufacturerData(id uint16, b []byte) Field {
+ return func(p *Packet) error {
+ d := append([]byte{uint8(id), uint8(id >> 8)}, b...)
+ return p.append(manufacturerData, d)
+ }
+}
+
+// AllUUID is one of the complete service UUID list.
+func AllUUID(u ble.UUID) Field {
+ return func(p *Packet) error {
+ if u.Len() == 2 {
+ return p.append(allUUID16, u)
+ }
+ if u.Len() == 4 {
+ return p.append(allUUID32, u)
+ }
+ return p.append(allUUID128, u)
+ }
+}
+
+// SomeUUID is one of the incomplete service UUID list.
+func SomeUUID(u ble.UUID) Field {
+ return func(p *Packet) error {
+ if u.Len() == 2 {
+ return p.append(someUUID16, u)
+ }
+ if u.Len() == 4 {
+ return p.append(someUUID32, u)
+ }
+ return p.append(someUUID128, u)
+ }
+}
+
+// ServiceData16 is service data for a 16bit service uuid
+func ServiceData16(id uint16, b []byte) Field {
+ return func(p *Packet) error {
+ uuid := ble.UUID16(id)
+ if err := p.append(allUUID16, uuid); err != nil {
+ return err
+ }
+ return p.append(serviceData16, append(uuid, b...))
+ }
+}
+
+// Field returns the field data (excluding the initial length and typ byte).
+// It returns nil, if the specified field is not found.
+func (p *Packet) Field(typ byte) []byte {
+ b := p.b
+ for len(b) > 0 {
+ if len(b) < 2 {
+ return nil
+ }
+ l, t := b[0], b[1]
+ if int(l) < 1 || len(b) < int(1+l) {
+ return nil
+ }
+ if t == typ {
+ return b[2 : 2+l-1]
+ }
+ b = b[1+l:]
+ }
+ return nil
+}
+
+// Flags returns the flags of the packet.
+func (p *Packet) Flags() (flags byte, present bool) {
+ b := p.Field(flags)
+ if len(b) < 2 {
+ return 0, false
+ }
+ return b[2], true
+}
+
+// LocalName returns the ShortName or CompleteName if it presents.
+func (p *Packet) LocalName() string {
+ if b := p.Field(shortName); b != nil {
+ return string(b)
+ }
+ return string(p.Field(completeName))
+}
+
+// TxPower returns the TxPower, if it presents.
+func (p *Packet) TxPower() (power int, present bool) {
+ b := p.Field(txPower)
+ if len(b) < 3 {
+ return 0, false
+ }
+ return int(int8(b[2])), true
+}
+
+// UUIDs returns a list of service UUIDs.
+func (p *Packet) UUIDs() []ble.UUID {
+ var u []ble.UUID
+ if b := p.Field(someUUID16); b != nil {
+ u = uuidList(u, b, 2)
+ }
+ if b := p.Field(allUUID16); b != nil {
+ u = uuidList(u, b, 2)
+ }
+ if b := p.Field(someUUID32); b != nil {
+ u = uuidList(u, b, 4)
+ }
+ if b := p.Field(allUUID32); b != nil {
+ u = uuidList(u, b, 4)
+ }
+ if b := p.Field(someUUID128); b != nil {
+ u = uuidList(u, b, 16)
+ }
+ if b := p.Field(allUUID128); b != nil {
+ u = uuidList(u, b, 16)
+ }
+ return u
+}
+
+// ServiceSol ...
+func (p *Packet) ServiceSol() []ble.UUID {
+ var u []ble.UUID
+ if b := p.Field(serviceSol16); b != nil {
+ u = uuidList(u, b, 2)
+ }
+ if b := p.Field(serviceSol32); b != nil {
+ u = uuidList(u, b, 16)
+ }
+ if b := p.Field(serviceSol128); b != nil {
+ u = uuidList(u, b, 16)
+ }
+ return u
+}
+
+// ServiceData ...
+func (p *Packet) ServiceData() []ble.ServiceData {
+ var s []ble.ServiceData
+ if b := p.Field(serviceData16); b != nil {
+ s = serviceDataList(s, b, 2)
+ }
+ if b := p.Field(serviceData32); b != nil {
+ s = serviceDataList(s, b, 4)
+ }
+ if b := p.Field(serviceData128); b != nil {
+ s = serviceDataList(s, b, 16)
+ }
+ return s
+}
+
+// ManufacturerData returns the ManufacturerData field if it presents.
+func (p *Packet) ManufacturerData() []byte {
+ return p.Field(manufacturerData)
+}
+
+// Utility function for creating a list of uuids.
+func uuidList(u []ble.UUID, d []byte, w int) []ble.UUID {
+ for len(d) > 0 {
+ u = append(u, ble.UUID(d[:w]))
+ d = d[w:]
+ }
+ return u
+}
+
+func serviceDataList(sd []ble.ServiceData, d []byte, w int) []ble.ServiceData {
+ serviceData := ble.ServiceData{
+ UUID: ble.UUID(d[:w]),
+ Data: make([]byte, len(d)-w),
+ }
+ copy(serviceData.Data, d[2:])
+ return append(sd, serviceData)
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/att/README.md b/vendor/github.com/currantlabs/ble/linux/att/README.md
new file mode 100644
index 0000000..25024f8
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/att/README.md
@@ -0,0 +1,40 @@
+## Attribute Protocol (ATT)
+
+This package implement Attribute Protocol (ATT) [Vol 3, Part F]
+
+#### Check list for ATT Server implementation.
+ - [x] Error Response [3.4.1.1]
+ - [x] Exchange MTU Request [3.4.2.1 & 3.4.2.2]
+ - [x] Find Information Request [3.4.3.1 & 3.4.3.2]
+ - [x] Find By Type Value Request [3.4.3.3 & 3.4.3.4]
+ - [x] Read By Type Request [3.4.4.1 & 3.4.4.2]
+ - [x] Read Request [3.4.4.3 & 3.4.4.4]
+ - [x] Read Blob Request [3.4.4.5 & 3.4.4.6]
+ - [ ] Read Multiple Request [3.4.4.7 & 3.4.4.8]
+ - [x] Read By Group Type Request [3.4.4.9 & 3.4.4.10]
+ - [x] Write Request [3.4.5.1 & 3.4.5.2]
+ - [x] Write Command [3.4.5.3]
+ - [ ] Signed Write Command [3.4.5.4]
+ - [ ] Prepare Write Request [3.4.6.1 & 3.4.6.2]
+ - [ ] Execute Write Request [3.4.6.3]
+ - [x] Handle Value Notification [3.4.7.1]
+ - [x] Handle Value Indication [3.4.7.2 & 3.4.7.3]
+
+#### Check list for ATT Client implementation.
+
+ - [x] Error Response [3.4.1.1]
+ - [x] Exchange MTU Request [3.4.2.1 & 3.4.2.2]
+ - [x] Find Information Request [3.4.3.1 & 3.4.3.2]
+ - [ ] Find By Type Value Request [3.4.3.3 & 3.4.3.4]
+ - [x] Read By Type Request [3.4.4.1 & 3.4.4.2]
+ - [x] Read Request [3.4.4.3 & 3.4.4.4]
+ - [x] Read Blob Request [3.4.4.5 & 3.4.4.6]
+ - [ ] Read Multiple Request [3.4.4.7 & 3.4.4.8]
+ - [x] Read By Group Type Request [3.4.4.9 & 3.4.4.10]
+ - [x] Write Request [3.4.5.1 & 3.4.5.2]
+ - [x] Write Command [3.4.5.3]
+ - [ ] Signed Write Command [3.4.5.4]
+ - [ ] Prepare Write Request [3.4.6.1 & 3.4.6.2]
+ - [ ] Execute Write Request [3.4.6.3]
+ - [x] Handle Value Notification [3.4.7.1]
+ - [x] Handle Value Indication [3.4.7.2 & 3.4.7.3]
diff --git a/vendor/github.com/currantlabs/ble/linux/att/att.go b/vendor/github.com/currantlabs/ble/linux/att/att.go
new file mode 100644
index 0000000..584acbf
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/att/att.go
@@ -0,0 +1,30 @@
+package att
+
+import "errors"
+
+var (
+ // ErrInvalidArgument means one or more of the arguments are invalid.
+ ErrInvalidArgument = errors.New("invalid argument")
+
+ // ErrInvalidResponse means one or more of the response fields are invalid.
+ ErrInvalidResponse = errors.New("invalid response")
+
+ // ErrSeqProtoTimeout means the request hasn't been acknowledged in 30 seconds.
+ // [Vol 3, Part F, 3.3.3]
+ ErrSeqProtoTimeout = errors.New("req timeout")
+)
+
+var rspOfReq = map[byte]byte{
+ ExchangeMTURequestCode: ExchangeMTUResponseCode,
+ FindInformationRequestCode: FindInformationResponseCode,
+ FindByTypeValueRequestCode: FindByTypeValueResponseCode,
+ ReadByTypeRequestCode: ReadByTypeResponseCode,
+ ReadRequestCode: ReadResponseCode,
+ ReadBlobRequestCode: ReadBlobResponseCode,
+ ReadMultipleRequestCode: ReadMultipleResponseCode,
+ ReadByGroupTypeRequestCode: ReadByGroupTypeResponseCode,
+ WriteRequestCode: WriteResponseCode,
+ PrepareWriteRequestCode: PrepareWriteResponseCode,
+ ExecuteWriteRequestCode: ExecuteWriteResponseCode,
+ HandleValueIndicationCode: HandleValueConfirmationCode,
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/att/att_gen.go b/vendor/github.com/currantlabs/ble/linux/att/att_gen.go
new file mode 100644
index 0000000..df29642
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/att/att_gen.go
@@ -0,0 +1,639 @@
+package att
+
+import "encoding/binary"
+
+// ErrorResponseCode ...
+const ErrorResponseCode = 0x01
+
+// ErrorResponse implements Error Response (0x01) [Vol 3, Part E, 3.4.1.1].
+type ErrorResponse []byte
+
+// AttributeOpcode ...
+func (r ErrorResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ErrorResponse) SetAttributeOpcode() { r[0] = 0x01 }
+
+// RequestOpcodeInError ...
+func (r ErrorResponse) RequestOpcodeInError() uint8 { return r[1] }
+
+// SetRequestOpcodeInError ...
+func (r ErrorResponse) SetRequestOpcodeInError(v uint8) { r[1] = v }
+
+// AttributeInError ...
+func (r ErrorResponse) AttributeInError() uint16 { return binary.LittleEndian.Uint16(r[2:]) }
+
+// SetAttributeInError ...
+func (r ErrorResponse) SetAttributeInError(v uint16) { binary.LittleEndian.PutUint16(r[2:], v) }
+
+// ErrorCode ...
+func (r ErrorResponse) ErrorCode() uint8 { return r[4] }
+
+// SetErrorCode ...
+func (r ErrorResponse) SetErrorCode(v uint8) { r[4] = v }
+
+// ExchangeMTURequestCode ...
+const ExchangeMTURequestCode = 0x02
+
+// ExchangeMTURequest implements Exchange MTU Request (0x02) [Vol 3, Part E, 3.4.2.1].
+type ExchangeMTURequest []byte
+
+// AttributeOpcode ...
+func (r ExchangeMTURequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ExchangeMTURequest) SetAttributeOpcode() { r[0] = 0x02 }
+
+// ClientRxMTU ...
+func (r ExchangeMTURequest) ClientRxMTU() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetClientRxMTU ...
+func (r ExchangeMTURequest) SetClientRxMTU(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// ExchangeMTUResponseCode ...
+const ExchangeMTUResponseCode = 0x03
+
+// ExchangeMTUResponse implements Exchange MTU Response (0x03) [Vol 3, Part E, 3.4.2.2].
+type ExchangeMTUResponse []byte
+
+// AttributeOpcode ...
+func (r ExchangeMTUResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ExchangeMTUResponse) SetAttributeOpcode() { r[0] = 0x03 }
+
+// ServerRxMTU ...
+func (r ExchangeMTUResponse) ServerRxMTU() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetServerRxMTU ...
+func (r ExchangeMTUResponse) SetServerRxMTU(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// FindInformationRequestCode ...
+const FindInformationRequestCode = 0x04
+
+// FindInformationRequest implements Find Information Request (0x04) [Vol 3, Part E, 3.4.3.1].
+type FindInformationRequest []byte
+
+// AttributeOpcode ...
+func (r FindInformationRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r FindInformationRequest) SetAttributeOpcode() { r[0] = 0x04 }
+
+// StartingHandle ...
+func (r FindInformationRequest) StartingHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetStartingHandle ...
+func (r FindInformationRequest) SetStartingHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// EndingHandle ...
+func (r FindInformationRequest) EndingHandle() uint16 { return binary.LittleEndian.Uint16(r[3:]) }
+
+// SetEndingHandle ...
+func (r FindInformationRequest) SetEndingHandle(v uint16) { binary.LittleEndian.PutUint16(r[3:], v) }
+
+// FindInformationResponseCode ...
+const FindInformationResponseCode = 0x05
+
+// FindInformationResponse implements Find Information Response (0x05) [Vol 3, Part E, 3.4.3.2].
+type FindInformationResponse []byte
+
+// AttributeOpcode ...
+func (r FindInformationResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r FindInformationResponse) SetAttributeOpcode() { r[0] = 0x05 }
+
+// Format ...
+func (r FindInformationResponse) Format() uint8 { return r[1] }
+
+// SetFormat ...
+func (r FindInformationResponse) SetFormat(v uint8) { r[1] = v }
+
+// InformationData ...
+func (r FindInformationResponse) InformationData() []byte { return r[2:] }
+
+// SetInformationData ...
+func (r FindInformationResponse) SetInformationData(v []byte) { copy(r[2:], v) }
+
+// FindByTypeValueRequestCode ...
+const FindByTypeValueRequestCode = 0x06
+
+// FindByTypeValueRequest implements Find By Type Value Request (0x06) [Vol 3, Part E, 3.4.3.3].
+type FindByTypeValueRequest []byte
+
+// AttributeOpcode ...
+func (r FindByTypeValueRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r FindByTypeValueRequest) SetAttributeOpcode() { r[0] = 0x06 }
+
+// StartingHandle ...
+func (r FindByTypeValueRequest) StartingHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetStartingHandle ...
+func (r FindByTypeValueRequest) SetStartingHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// EndingHandle ...
+func (r FindByTypeValueRequest) EndingHandle() uint16 { return binary.LittleEndian.Uint16(r[3:]) }
+
+// SetEndingHandle ...
+func (r FindByTypeValueRequest) SetEndingHandle(v uint16) { binary.LittleEndian.PutUint16(r[3:], v) }
+
+// AttributeType ...
+func (r FindByTypeValueRequest) AttributeType() uint16 { return binary.LittleEndian.Uint16(r[5:]) }
+
+// SetAttributeType ...
+func (r FindByTypeValueRequest) SetAttributeType(v uint16) { binary.LittleEndian.PutUint16(r[5:], v) }
+
+// AttributeValue ...
+func (r FindByTypeValueRequest) AttributeValue() []byte { return r[7:] }
+
+// SetAttributeValue ...
+func (r FindByTypeValueRequest) SetAttributeValue(v []byte) { copy(r[7:], v) }
+
+// FindByTypeValueResponseCode ...
+const FindByTypeValueResponseCode = 0x07
+
+// FindByTypeValueResponse implements Find By Type Value Response (0x07) [Vol 3, Part E, 3.4.3.4].
+type FindByTypeValueResponse []byte
+
+// AttributeOpcode ...
+func (r FindByTypeValueResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r FindByTypeValueResponse) SetAttributeOpcode() { r[0] = 0x07 }
+
+// HandleInformationList ...
+func (r FindByTypeValueResponse) HandleInformationList() []byte { return r[1:] }
+
+// SetHandleInformationList ...
+func (r FindByTypeValueResponse) SetHandleInformationList(v []byte) { copy(r[1:], v) }
+
+// ReadByTypeRequestCode ...
+const ReadByTypeRequestCode = 0x08
+
+// ReadByTypeRequest implements Read By Type Request (0x08) [Vol 3, Part E, 3.4.4.1].
+type ReadByTypeRequest []byte
+
+// AttributeOpcode ...
+func (r ReadByTypeRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadByTypeRequest) SetAttributeOpcode() { r[0] = 0x08 }
+
+// StartingHandle ...
+func (r ReadByTypeRequest) StartingHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetStartingHandle ...
+func (r ReadByTypeRequest) SetStartingHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// EndingHandle ...
+func (r ReadByTypeRequest) EndingHandle() uint16 { return binary.LittleEndian.Uint16(r[3:]) }
+
+// SetEndingHandle ...
+func (r ReadByTypeRequest) SetEndingHandle(v uint16) { binary.LittleEndian.PutUint16(r[3:], v) }
+
+// AttributeType ...
+func (r ReadByTypeRequest) AttributeType() []byte { return r[5:] }
+
+// SetAttributeType ...
+func (r ReadByTypeRequest) SetAttributeType(v []byte) { copy(r[5:], v) }
+
+// ReadByTypeResponseCode ...
+const ReadByTypeResponseCode = 0x09
+
+// ReadByTypeResponse implements Read By Type Response (0x09) [Vol 3, Part E, 3.4.4.2].
+type ReadByTypeResponse []byte
+
+// AttributeOpcode ...
+func (r ReadByTypeResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadByTypeResponse) SetAttributeOpcode() { r[0] = 0x09 }
+
+// Length ...
+func (r ReadByTypeResponse) Length() uint8 { return r[1] }
+
+// SetLength ...
+func (r ReadByTypeResponse) SetLength(v uint8) { r[1] = v }
+
+// AttributeDataList ...
+func (r ReadByTypeResponse) AttributeDataList() []byte { return r[2:] }
+
+// SetAttributeDataList ...
+func (r ReadByTypeResponse) SetAttributeDataList(v []byte) { copy(r[2:], v) }
+
+// ReadRequestCode ...
+const ReadRequestCode = 0x0A
+
+// ReadRequest implements Read Request (0x0A) [Vol 3, Part E, 3.4.4.3].
+type ReadRequest []byte
+
+// AttributeOpcode ...
+func (r ReadRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadRequest) SetAttributeOpcode() { r[0] = 0x0A }
+
+// AttributeHandle ...
+func (r ReadRequest) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r ReadRequest) SetAttributeHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// ReadResponseCode ...
+const ReadResponseCode = 0x0B
+
+// ReadResponse implements Read Response (0x0B) [Vol 3, Part E, 3.4.4.4].
+type ReadResponse []byte
+
+// AttributeOpcode ...
+func (r ReadResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadResponse) SetAttributeOpcode() { r[0] = 0x0B }
+
+// AttributeValue ...
+func (r ReadResponse) AttributeValue() []byte { return r[1:] }
+
+// SetAttributeValue ...
+func (r ReadResponse) SetAttributeValue(v []byte) { copy(r[1:], v) }
+
+// ReadBlobRequestCode ...
+const ReadBlobRequestCode = 0x0C
+
+// ReadBlobRequest implements Read Blob Request (0x0C) [Vol 3, Part E, 3.4.4.5].
+type ReadBlobRequest []byte
+
+// AttributeOpcode ...
+func (r ReadBlobRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadBlobRequest) SetAttributeOpcode() { r[0] = 0x0C }
+
+// AttributeHandle ...
+func (r ReadBlobRequest) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r ReadBlobRequest) SetAttributeHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// ValueOffset ...
+func (r ReadBlobRequest) ValueOffset() uint16 { return binary.LittleEndian.Uint16(r[3:]) }
+
+// SetValueOffset ...
+func (r ReadBlobRequest) SetValueOffset(v uint16) { binary.LittleEndian.PutUint16(r[3:], v) }
+
+// ReadBlobResponseCode ...
+const ReadBlobResponseCode = 0x0D
+
+// ReadBlobResponse implements Read Blob Response (0x0D) [Vol 3, Part E, 3.4.4.6].
+type ReadBlobResponse []byte
+
+// AttributeOpcode ...
+func (r ReadBlobResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadBlobResponse) SetAttributeOpcode() { r[0] = 0x0D }
+
+// PartAttributeValue ...
+func (r ReadBlobResponse) PartAttributeValue() []byte { return r[1:] }
+
+// SetPartAttributeValue ...
+func (r ReadBlobResponse) SetPartAttributeValue(v []byte) { copy(r[1:], v) }
+
+// ReadMultipleRequestCode ...
+const ReadMultipleRequestCode = 0x0E
+
+// ReadMultipleRequest implements Read Multiple Request (0x0E) [Vol 3, Part E, 3.4.4.7].
+type ReadMultipleRequest []byte
+
+// AttributeOpcode ...
+func (r ReadMultipleRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadMultipleRequest) SetAttributeOpcode() { r[0] = 0x0E }
+
+// SetOfHandles ...
+func (r ReadMultipleRequest) SetOfHandles() []byte { return r[1:] }
+
+// SetSetOfHandles ...
+func (r ReadMultipleRequest) SetSetOfHandles(v []byte) { copy(r[1:], v) }
+
+// ReadMultipleResponseCode ...
+const ReadMultipleResponseCode = 0x0F
+
+// ReadMultipleResponse implements Read Multiple Response (0x0F) [Vol 3, Part E, 3.4.4.8].
+type ReadMultipleResponse []byte
+
+// AttributeOpcode ...
+func (r ReadMultipleResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadMultipleResponse) SetAttributeOpcode() { r[0] = 0x0F }
+
+// SetOfValues ...
+func (r ReadMultipleResponse) SetOfValues() []byte { return r[1:] }
+
+// SetSetOfValues ...
+func (r ReadMultipleResponse) SetSetOfValues(v []byte) { copy(r[1:], v) }
+
+// ReadByGroupTypeRequestCode ...
+const ReadByGroupTypeRequestCode = 0x10
+
+// ReadByGroupTypeRequest implements Read By Group Type Request (0x10) [Vol 3, Part E, 3.4.4.9].
+type ReadByGroupTypeRequest []byte
+
+// AttributeOpcode ...
+func (r ReadByGroupTypeRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadByGroupTypeRequest) SetAttributeOpcode() { r[0] = 0x10 }
+
+// StartingHandle ...
+func (r ReadByGroupTypeRequest) StartingHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetStartingHandle ...
+func (r ReadByGroupTypeRequest) SetStartingHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// EndingHandle ...
+func (r ReadByGroupTypeRequest) EndingHandle() uint16 { return binary.LittleEndian.Uint16(r[3:]) }
+
+// SetEndingHandle ...
+func (r ReadByGroupTypeRequest) SetEndingHandle(v uint16) { binary.LittleEndian.PutUint16(r[3:], v) }
+
+// AttributeGroupType ...
+func (r ReadByGroupTypeRequest) AttributeGroupType() []byte { return r[5:] }
+
+// SetAttributeGroupType ...
+func (r ReadByGroupTypeRequest) SetAttributeGroupType(v []byte) { copy(r[5:], v) }
+
+// ReadByGroupTypeResponseCode ...
+const ReadByGroupTypeResponseCode = 0x11
+
+// ReadByGroupTypeResponse implements Read By Group Type Response (0x11) [Vol 3, Part E, 3.4.4.10].
+type ReadByGroupTypeResponse []byte
+
+// AttributeOpcode ...
+func (r ReadByGroupTypeResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ReadByGroupTypeResponse) SetAttributeOpcode() { r[0] = 0x11 }
+
+// Length ...
+func (r ReadByGroupTypeResponse) Length() uint8 { return r[1] }
+
+// SetLength ...
+func (r ReadByGroupTypeResponse) SetLength(v uint8) { r[1] = v }
+
+// AttributeDataList ...
+func (r ReadByGroupTypeResponse) AttributeDataList() []byte { return r[2:] }
+
+// SetAttributeDataList ...
+func (r ReadByGroupTypeResponse) SetAttributeDataList(v []byte) { copy(r[2:], v) }
+
+// WriteRequestCode ...
+const WriteRequestCode = 0x12
+
+// WriteRequest implements Write Request (0x12) [Vol 3, Part E, 3.4.5.1].
+type WriteRequest []byte
+
+// AttributeOpcode ...
+func (r WriteRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r WriteRequest) SetAttributeOpcode() { r[0] = 0x12 }
+
+// AttributeHandle ...
+func (r WriteRequest) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r WriteRequest) SetAttributeHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// AttributeValue ...
+func (r WriteRequest) AttributeValue() []byte { return r[3:] }
+
+// SetAttributeValue ...
+func (r WriteRequest) SetAttributeValue(v []byte) { copy(r[3:], v) }
+
+// WriteResponseCode ...
+const WriteResponseCode = 0x13
+
+// WriteResponse implements Write Response (0x13) [Vol 3, Part E, 3.4.5.2].
+type WriteResponse []byte
+
+// AttributeOpcode ...
+func (r WriteResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r WriteResponse) SetAttributeOpcode() { r[0] = 0x13 }
+
+// WriteCommandCode ...
+const WriteCommandCode = 0x52
+
+// WriteCommand implements Write Command (0x52) [Vol 3, Part E, 3.4.5.3].
+type WriteCommand []byte
+
+// AttributeOpcode ...
+func (r WriteCommand) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r WriteCommand) SetAttributeOpcode() { r[0] = 0x52 }
+
+// AttributeHandle ...
+func (r WriteCommand) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r WriteCommand) SetAttributeHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// AttributeValue ...
+func (r WriteCommand) AttributeValue() []byte { return r[3:] }
+
+// SetAttributeValue ...
+func (r WriteCommand) SetAttributeValue(v []byte) { copy(r[3:], v) }
+
+// SignedWriteCommandCode ...
+const SignedWriteCommandCode = 0xD2
+
+// SignedWriteCommand implements Signed Write Command (0xD2) [Vol 3, Part E, 3.4.5.4].
+type SignedWriteCommand []byte
+
+// AttributeOpcode ...
+func (r SignedWriteCommand) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r SignedWriteCommand) SetAttributeOpcode() { r[0] = 0xD2 }
+
+// AttributeHandle ...
+func (r SignedWriteCommand) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r SignedWriteCommand) SetAttributeHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// AttributeValue ...
+func (r SignedWriteCommand) AttributeValue() []byte { return r[3:] }
+
+// SetAttributeValue ...
+func (r SignedWriteCommand) SetAttributeValue(v []byte) { copy(r[3:], v) }
+
+// AuthenticationSignature ...
+func (r SignedWriteCommand) AuthenticationSignature() [12]byte {
+ b := [12]byte{}
+ copy(b[:], r[3:])
+ return b
+}
+
+// SetAuthenticationSignature ...
+func (r SignedWriteCommand) SetAuthenticationSignature(v [12]byte) { copy(r[3:3+12], v[:]) }
+
+// PrepareWriteRequestCode ...
+const PrepareWriteRequestCode = 0x16
+
+// PrepareWriteRequest implements Prepare Write Request (0x16) [Vol 3, Part E, 3.4.6.1].
+type PrepareWriteRequest []byte
+
+// AttributeOpcode ...
+func (r PrepareWriteRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r PrepareWriteRequest) SetAttributeOpcode() { r[0] = 0x16 }
+
+// AttributeHandle ...
+func (r PrepareWriteRequest) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r PrepareWriteRequest) SetAttributeHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// ValueOffset ...
+func (r PrepareWriteRequest) ValueOffset() uint16 { return binary.LittleEndian.Uint16(r[3:]) }
+
+// SetValueOffset ...
+func (r PrepareWriteRequest) SetValueOffset(v uint16) { binary.LittleEndian.PutUint16(r[3:], v) }
+
+// PartAttributeValue ...
+func (r PrepareWriteRequest) PartAttributeValue() []byte { return r[5:] }
+
+// SetPartAttributeValue ...
+func (r PrepareWriteRequest) SetPartAttributeValue(v []byte) { copy(r[5:], v) }
+
+// PrepareWriteResponseCode ...
+const PrepareWriteResponseCode = 0x17
+
+// PrepareWriteResponse implements Prepare Write Response (0x17) [Vol 3, Part E, 3.4.6.2].
+type PrepareWriteResponse []byte
+
+// AttributeOpcode ...
+func (r PrepareWriteResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r PrepareWriteResponse) SetAttributeOpcode() { r[0] = 0x17 }
+
+// AttributeHandle ...
+func (r PrepareWriteResponse) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r PrepareWriteResponse) SetAttributeHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// ValueOffset ...
+func (r PrepareWriteResponse) ValueOffset() uint16 { return binary.LittleEndian.Uint16(r[3:]) }
+
+// SetValueOffset ...
+func (r PrepareWriteResponse) SetValueOffset(v uint16) { binary.LittleEndian.PutUint16(r[3:], v) }
+
+// PartAttributeValue ...
+func (r PrepareWriteResponse) PartAttributeValue() []byte { return r[5:] }
+
+// SetPartAttributeValue ...
+func (r PrepareWriteResponse) SetPartAttributeValue(v []byte) { copy(r[5:], v) }
+
+// ExecuteWriteRequestCode ...
+const ExecuteWriteRequestCode = 0x18
+
+// ExecuteWriteRequest implements Execute Write Request (0x18) [Vol 3, Part E, 3.4.6.3].
+type ExecuteWriteRequest []byte
+
+// AttributeOpcode ...
+func (r ExecuteWriteRequest) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ExecuteWriteRequest) SetAttributeOpcode() { r[0] = 0x18 }
+
+// Flags ...
+func (r ExecuteWriteRequest) Flags() uint8 { return r[1] }
+
+// SetFlags ...
+func (r ExecuteWriteRequest) SetFlags(v uint8) { r[1] = v }
+
+// ExecuteWriteResponseCode ...
+const ExecuteWriteResponseCode = 0x19
+
+// ExecuteWriteResponse implements Execute Write Response (0x19) [Vol 3, Part E, 3.4.6.4].
+type ExecuteWriteResponse []byte
+
+// AttributeOpcode ...
+func (r ExecuteWriteResponse) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r ExecuteWriteResponse) SetAttributeOpcode() { r[0] = 0x19 }
+
+// HandleValueNotificationCode ...
+const HandleValueNotificationCode = 0x1B
+
+// HandleValueNotification implements Handle Value Notification (0x1B) [Vol 3, Part E, 3.4.7.1].
+type HandleValueNotification []byte
+
+// AttributeOpcode ...
+func (r HandleValueNotification) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r HandleValueNotification) SetAttributeOpcode() { r[0] = 0x1B }
+
+// AttributeHandle ...
+func (r HandleValueNotification) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r HandleValueNotification) SetAttributeHandle(v uint16) {
+ binary.LittleEndian.PutUint16(r[1:], v)
+}
+
+// AttributeValue ...
+func (r HandleValueNotification) AttributeValue() []byte { return r[3:] }
+
+// SetAttributeValue ...
+func (r HandleValueNotification) SetAttributeValue(v []byte) { copy(r[3:], v) }
+
+// HandleValueIndicationCode ...
+const HandleValueIndicationCode = 0x1D
+
+// HandleValueIndication implements Handle Value Indication (0x1D) [Vol 3, Part E, 3.4.7.2].
+type HandleValueIndication []byte
+
+// AttributeOpcode ...
+func (r HandleValueIndication) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r HandleValueIndication) SetAttributeOpcode() { r[0] = 0x1D }
+
+// AttributeHandle ...
+func (r HandleValueIndication) AttributeHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+// SetAttributeHandle ...
+func (r HandleValueIndication) SetAttributeHandle(v uint16) { binary.LittleEndian.PutUint16(r[1:], v) }
+
+// AttributeValue ...
+func (r HandleValueIndication) AttributeValue() []byte { return r[3:] }
+
+// SetAttributeValue ...
+func (r HandleValueIndication) SetAttributeValue(v []byte) { copy(r[3:], v) }
+
+// HandleValueConfirmationCode ...
+const HandleValueConfirmationCode = 0x1E
+
+// HandleValueConfirmation implements Handle Value Confirmation (0x1E) [Vol 3, Part E, 3.4.7.3].
+type HandleValueConfirmation []byte
+
+// AttributeOpcode ...
+func (r HandleValueConfirmation) AttributeOpcode() uint8 { return r[0] }
+
+// SetAttributeOpcode ...
+func (r HandleValueConfirmation) SetAttributeOpcode() { r[0] = 0x1E }
diff --git a/vendor/github.com/currantlabs/ble/linux/att/attr.go b/vendor/github.com/currantlabs/ble/linux/att/attr.go
new file mode 100644
index 0000000..94b0fe2
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/att/attr.go
@@ -0,0 +1,14 @@
+package att
+
+import "github.com/currantlabs/ble"
+
+// attr is a BLE attribute.
+type attr struct {
+ h uint16
+ endh uint16
+ typ ble.UUID
+
+ v []byte
+ rh ble.ReadHandler
+ wh ble.WriteHandler
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/att/client.go b/vendor/github.com/currantlabs/ble/linux/att/client.go
new file mode 100644
index 0000000..fa6c9c4
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/att/client.go
@@ -0,0 +1,560 @@
+package att
+
+import (
+ "encoding/binary"
+ "fmt"
+ "time"
+
+ "github.com/currantlabs/ble"
+ "github.com/pkg/errors"
+)
+
+// NotificationHandler handles notification or indication.
+type NotificationHandler interface {
+ HandleNotification(req []byte)
+}
+
+// Client implementa an Attribute Protocol Client.
+type Client struct {
+ l2c ble.Conn
+ rspc chan []byte
+
+ rxBuf []byte
+ chTxBuf chan []byte
+ chErr chan error
+ handler NotificationHandler
+}
+
+// NewClient returns an Attribute Protocol Client.
+func NewClient(l2c ble.Conn, h NotificationHandler) *Client {
+ c := &Client{
+ l2c: l2c,
+ rspc: make(chan []byte),
+ chTxBuf: make(chan []byte, 1),
+ rxBuf: make([]byte, ble.MaxMTU),
+ chErr: make(chan error, 1),
+ handler: h,
+ }
+ c.chTxBuf <- make([]byte, l2c.TxMTU(), l2c.TxMTU())
+ return c
+}
+
+// ExchangeMTU informs the server of the client’s maximum receive MTU size and
+// request the server to respond with its maximum receive MTU size. [Vol 3, Part F, 3.4.2.1]
+func (c *Client) ExchangeMTU(clientRxMTU int) (serverRxMTU int, err error) {
+ if clientRxMTU < ble.DefaultMTU || clientRxMTU > ble.MaxMTU {
+ return 0, ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ // The same txBuf, or a newly allocate one, if the txMTU is changed,
+ // will be released back to the channel.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ // Let L2CAP know the MTU we can handle.
+ c.l2c.SetRxMTU(clientRxMTU)
+
+ req := ExchangeMTURequest(txBuf[:3])
+ req.SetAttributeOpcode()
+ req.SetClientRxMTU(uint16(clientRxMTU))
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return 0, err
+ }
+
+ // Convert and validate the response.
+ rsp := ExchangeMTUResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return 0, ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ fallthrough
+ case len(rsp) != 3:
+ return 0, ErrInvalidResponse
+ }
+
+ txMTU := int(rsp.ServerRxMTU())
+ if len(txBuf) != txMTU {
+ // Let L2CAP know the MTU that the remote device can handle.
+ c.l2c.SetTxMTU(txMTU)
+ // Put a re-allocated txBuf back to the channel.
+ // The txBuf has been captured in deferred function.
+ txBuf = make([]byte, txMTU, txMTU)
+ }
+
+ return txMTU, nil
+}
+
+// FindInformation obtains the mapping of attribute handles with their associated types.
+// This allows a Client to discover the list of attributes and their types on a server.
+// [Vol 3, Part F, 3.4.3.1 & 3.4.3.2]
+func (c *Client) FindInformation(starth, endh uint16) (fmt int, data []byte, err error) {
+ if starth == 0 || starth > endh {
+ return 0x00, nil, ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := FindInformationRequest(txBuf[:5])
+ req.SetAttributeOpcode()
+ req.SetStartingHandle(starth)
+ req.SetEndingHandle(endh)
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return 0x00, nil, err
+ }
+
+ // Convert and validate the response.
+ rsp := FindInformationResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return 0x00, nil, ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ fallthrough
+ case len(rsp) < 6:
+ fallthrough
+ case rsp.Format() == 0x01 && ((len(rsp)-2)%4) != 0:
+ fallthrough
+ case rsp.Format() == 0x02 && ((len(rsp)-2)%18) != 0:
+ return 0x00, nil, ErrInvalidResponse
+ }
+ return int(rsp.Format()), rsp.InformationData(), nil
+}
+
+// // HandleInformationList ...
+// type HandleInformationList []byte
+//
+// // FoundAttributeHandle ...
+// func (l HandleInformationList) FoundAttributeHandle() []byte { return l[:2] }
+//
+// // GroupEndHandle ...
+// func (l HandleInformationList) GroupEndHandle() []byte { return l[2:4] }
+//
+// // FindByTypeValue ...
+// func (c *Client) FindByTypeValue(starth, endh, attrType uint16, value []byte) ([]HandleInformationList, error) {
+// return nil, nil
+// }
+
+// ReadByType obtains the values of attributes where the attribute type is known
+// but the handle is not known. [Vol 3, Part F, 3.4.4.1 & 3.4.4.2]
+func (c *Client) ReadByType(starth, endh uint16, uuid ble.UUID) (int, []byte, error) {
+ if starth > endh || (len(uuid) != 2 && len(uuid) != 16) {
+ return 0, nil, ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := ReadByTypeRequest(txBuf[:5+len(uuid)])
+ req.SetAttributeOpcode()
+ req.SetStartingHandle(starth)
+ req.SetEndingHandle(endh)
+ req.SetAttributeType(uuid)
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ // Convert and validate the response.
+ rsp := ReadByTypeResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return 0, nil, ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ fallthrough
+ case len(rsp) < 4 || len(rsp.AttributeDataList())%int(rsp.Length()) != 0:
+ return 0, nil, ErrInvalidResponse
+ }
+ return int(rsp.Length()), rsp.AttributeDataList(), nil
+}
+
+// Read requests the server to read the value of an attribute and return its
+// value in a Read Response. [Vol 3, Part F, 3.4.4.3 & 3.4.4.4]
+func (c *Client) Read(handle uint16) ([]byte, error) {
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := ReadRequest(txBuf[:3])
+ req.SetAttributeOpcode()
+ req.SetAttributeHandle(handle)
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return nil, err
+ }
+
+ // Convert and validate the response.
+ rsp := ReadResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return nil, ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ fallthrough
+ case len(rsp) < 1:
+ return nil, ErrInvalidResponse
+ }
+ return rsp.AttributeValue(), nil
+}
+
+// ReadBlob requests the server to read part of the value of an attribute at a
+// given offset and return a specific part of the value in a Read Blob Response.
+// [Vol 3, Part F, 3.4.4.5 & 3.4.4.6]
+func (c *Client) ReadBlob(handle, offset uint16) ([]byte, error) {
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := ReadBlobRequest(txBuf[:5])
+ req.SetAttributeOpcode()
+ req.SetAttributeHandle(handle)
+ req.SetValueOffset(offset)
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return nil, err
+ }
+
+ // Convert and validate the response.
+ rsp := ReadBlobResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return nil, ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ fallthrough
+ case len(rsp) < 1:
+ return nil, ErrInvalidResponse
+ }
+ return rsp.PartAttributeValue(), nil
+}
+
+// ReadMultiple requests the server to read two or more values of a set of
+// attributes and return their values in a Read Multiple Response.
+// Only values that have a known fixed size can be read, with the exception of
+// the last value that can have a variable length. The knowledge of whether
+// attributes have a known fixed size is defined in a higher layer specification.
+// [Vol 3, Part F, 3.4.4.7 & 3.4.4.8]
+func (c *Client) ReadMultiple(handles []uint16) ([]byte, error) {
+ // Should request to read two or more values.
+ if len(handles) < 2 || len(handles)*2 > c.l2c.TxMTU()-1 {
+ return nil, ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := ReadMultipleRequest(txBuf[:1+len(handles)*2])
+ req.SetAttributeOpcode()
+ p := req.SetOfHandles()
+ for _, h := range handles {
+ binary.LittleEndian.PutUint16(p, h)
+ p = p[2:]
+ }
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return nil, err
+ }
+
+ // Convert and validate the response.
+ rsp := ReadMultipleResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return nil, ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ fallthrough
+ case len(rsp) < 1:
+ return nil, ErrInvalidResponse
+ }
+ return rsp.SetOfValues(), nil
+}
+
+// ReadByGroupType obtains the values of attributes where the attribute type is known,
+// the type of a grouping attribute as defined by a higher layer specification, but
+// the handle is not known. [Vol 3, Part F, 3.4.4.9 & 3.4.4.10]
+func (c *Client) ReadByGroupType(starth, endh uint16, uuid ble.UUID) (int, []byte, error) {
+ if starth > endh || (len(uuid) != 2 && len(uuid) != 16) {
+ return 0, nil, ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := ReadByGroupTypeRequest(txBuf[:5+len(uuid)])
+ req.SetAttributeOpcode()
+ req.SetStartingHandle(starth)
+ req.SetEndingHandle(endh)
+ req.SetAttributeGroupType(uuid)
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ // Convert and validate the response.
+ rsp := ReadByGroupTypeResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return 0, nil, ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ fallthrough
+ case len(rsp) < 4:
+ fallthrough
+ case len(rsp.AttributeDataList())%int(rsp.Length()) != 0:
+ return 0, nil, ErrInvalidResponse
+ }
+
+ return int(rsp.Length()), rsp.AttributeDataList(), nil
+}
+
+// Write requests the server to write the value of an attribute and acknowledge that
+// this has been achieved in a Write Response. [Vol 3, Part F, 3.4.5.1 & 3.4.5.2]
+func (c *Client) Write(handle uint16, value []byte) error {
+ if len(value) > c.l2c.TxMTU()-3 {
+ return ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := WriteRequest(txBuf[:3+len(value)])
+ req.SetAttributeOpcode()
+ req.SetAttributeHandle(handle)
+ req.SetAttributeValue(value)
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return err
+ }
+
+ // Convert and validate the response.
+ rsp := WriteResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ return ErrInvalidResponse
+ }
+ return nil
+}
+
+// WriteCommand requests the server to write the value of an attribute, typically
+// into a control-point attribute. [Vol 3, Part F, 3.4.5.3]
+func (c *Client) WriteCommand(handle uint16, value []byte) error {
+ if len(value) > c.l2c.TxMTU()-3 {
+ return ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := WriteCommand(txBuf[:3+len(value)])
+ req.SetAttributeOpcode()
+ req.SetAttributeHandle(handle)
+ req.SetAttributeValue(value)
+
+ return c.sendCmd(req)
+}
+
+// SignedWrite requests the server to write the value of an attribute with an authentication
+// signature, typically into a control-point attribute. [Vol 3, Part F, 3.4.5.4]
+func (c *Client) SignedWrite(handle uint16, value []byte, signature [12]byte) error {
+ if len(value) > c.l2c.TxMTU()-15 {
+ return ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := SignedWriteCommand(txBuf[:15+len(value)])
+ req.SetAttributeOpcode()
+ req.SetAttributeHandle(handle)
+ req.SetAttributeValue(value)
+ req.SetAuthenticationSignature(signature)
+
+ return c.sendCmd(req)
+}
+
+// PrepareWrite requests the server to prepare to write the value of an attribute.
+// The server will respond to this request with a Prepare Write Response, so that
+// the Client can verify that the value was received correctly.
+// [Vol 3, Part F, 3.4.6.1 & 3.4.6.2]
+func (c *Client) PrepareWrite(handle uint16, offset uint16, value []byte) (uint16, uint16, []byte, error) {
+ if len(value) > c.l2c.TxMTU()-5 {
+ return 0, 0, nil, ErrInvalidArgument
+ }
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := PrepareWriteRequest(txBuf[:5+len(value)])
+ req.SetAttributeOpcode()
+ req.SetAttributeHandle(handle)
+ req.SetValueOffset(offset)
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return 0, 0, nil, err
+ }
+
+ // Convert and validate the response.
+ rsp := PrepareWriteResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return 0, 0, nil, ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) != 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ fallthrough
+ case len(rsp) < 5:
+ return 0, 0, nil, ErrInvalidResponse
+ }
+ return rsp.AttributeHandle(), rsp.ValueOffset(), rsp.PartAttributeValue(), nil
+}
+
+// ExecuteWrite requests the server to write or cancel the write of all the prepared
+// values currently held in the prepare queue from this Client. This request shall be
+// handled by the server as an atomic operation. [Vol 3, Part F, 3.4.6.3 & 3.4.6.4]
+func (c *Client) ExecuteWrite(flags uint8) error {
+
+ // Acquire and reuse the txBuf, and release it after usage.
+ txBuf := <-c.chTxBuf
+ defer func() { c.chTxBuf <- txBuf }()
+
+ req := ExecuteWriteRequest(txBuf[:1])
+ req.SetAttributeOpcode()
+ req.SetFlags(flags)
+
+ b, err := c.sendReq(req)
+ if err != nil {
+ return err
+ }
+
+ // Convert and validate the response.
+ rsp := ExecuteWriteResponse(b)
+ switch {
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ return ble.ATTError(rsp[4])
+ case rsp[0] == ErrorResponseCode && len(rsp) == 5:
+ fallthrough
+ case rsp[0] != rsp.AttributeOpcode():
+ return ErrInvalidResponse
+ }
+ return nil
+}
+
+func (c *Client) sendCmd(b []byte) error {
+ _, err := c.l2c.Write(b)
+ return err
+}
+
+func (c *Client) sendReq(b []byte) (rsp []byte, err error) {
+ logger.Debug("client", "req", fmt.Sprintf("% X", b))
+ if _, err := c.l2c.Write(b); err != nil {
+ return nil, errors.Wrap(err, "send ATT request failed")
+ }
+ for {
+ select {
+ case rsp := <-c.rspc:
+ if rsp[0] == ErrorResponseCode || rsp[0] == rspOfReq[b[0]] {
+ return rsp, nil
+ }
+ // Sometimes when we connect to an Apple device, it sends
+ // ATT requests asynchronously to us. // In this case, we
+ // returns an ErrReqNotSupp response, and continue to wait
+ // the response to our request.
+ errRsp := newErrorResponse(rsp[0], 0x0000, ble.ErrReqNotSupp)
+ logger.Debug("client", "req", fmt.Sprintf("% X", b))
+ _, err := c.l2c.Write(errRsp)
+ if err != nil {
+ return nil, errors.Wrap(err, "unexpected ATT response recieved")
+ }
+ case err := <-c.chErr:
+ return nil, errors.Wrap(err, "ATT request failed")
+ case <-time.After(30 * time.Second):
+ return nil, errors.Wrap(ErrSeqProtoTimeout, "ATT request timeout")
+ }
+ }
+}
+
+// Loop ...
+func (c *Client) Loop() {
+
+ type asyncWork struct {
+ handle func([]byte)
+ data []byte
+ }
+
+ ch := make(chan asyncWork, 16)
+ defer close(ch)
+ go func() {
+ for w := range ch {
+ w.handle(w.data)
+ }
+ }()
+
+ confirmation := []byte{HandleValueConfirmationCode}
+ for {
+ n, err := c.l2c.Read(c.rxBuf)
+ logger.Debug("client", "rsp", fmt.Sprintf("% X", c.rxBuf[:n]))
+ if err != nil {
+ // We don't expect any error from the bearer (L2CAP ACL-U)
+ // Pass it along to the pending request, if any, and escape.
+ c.chErr <- err
+ return
+ }
+
+ b := make([]byte, n)
+ copy(b, c.rxBuf)
+
+ if (b[0] != HandleValueNotificationCode) && (b[0] != HandleValueIndicationCode) {
+ c.rspc <- b
+ continue
+ }
+
+ // Deliver the full request to upper layer.
+ select {
+ case ch <- asyncWork{handle: c.handler.HandleNotification, data: b}:
+ default:
+ // If this really happens, especially on a slow machine, enlarge the channel buffer.
+ logger.Error("client", "req", "can't enqueue incoming notification.")
+ }
+
+ // Always write aknowledgement for an indication, even it was an invalid request.
+ if b[0] == HandleValueIndicationCode {
+ logger.Debug("client", "req", fmt.Sprintf("% X", b))
+ c.l2c.Write(confirmation)
+ }
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/att/db.go b/vendor/github.com/currantlabs/ble/linux/att/db.go
new file mode 100644
index 0000000..6ddb55d
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/att/db.go
@@ -0,0 +1,210 @@
+package att
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/currantlabs/ble"
+)
+
+// A DB is a contiguous range of attributes.
+type DB struct {
+ attrs []*attr
+ base uint16 // handle for first attr in attrs
+}
+
+const (
+ tooSmall = -1
+ tooLarge = -2
+)
+
+// idx returns the idx into attrs corresponding to attr a.
+// If h is too small, idx returns tooSmall (-1).
+// If h is too large, idx returns tooLarge (-2).
+func (r *DB) idx(h int) int {
+ if h < int(r.base) {
+ return tooSmall
+ }
+ if int(h) >= int(r.base)+len(r.attrs) {
+ return tooLarge
+ }
+ return h - int(r.base)
+}
+
+// at returns attr a.
+func (r *DB) at(h uint16) (a *attr, ok bool) {
+ i := r.idx(int(h))
+ if i < 0 {
+ return nil, false
+ }
+ return r.attrs[i], true
+}
+
+// subrange returns attributes in range [start, end]; it may return an empty slice.
+// subrange does not panic for out-of-range start or end.
+func (r *DB) subrange(start, end uint16) []*attr {
+ startidx := r.idx(int(start))
+ switch startidx {
+ case tooSmall:
+ startidx = 0
+ case tooLarge:
+ return []*attr{}
+ }
+
+ endidx := r.idx(int(end) + 1) // [start, end] includes its upper bound!
+ switch endidx {
+ case tooSmall:
+ return []*attr{}
+ case tooLarge:
+ endidx = len(r.attrs)
+ }
+ return r.attrs[startidx:endidx]
+}
+
+// NewDB ...
+func NewDB(ss []*ble.Service, base uint16) *DB {
+ h := base
+ var attrs []*attr
+ var aa []*attr
+ for i, s := range ss {
+ h, aa = genSvcAttr(s, h)
+ if i == len(ss)-1 {
+ aa[0].endh = 0xFFFF
+ }
+ attrs = append(attrs, aa...)
+ }
+ DumpAttributes(attrs)
+ return &DB{attrs: attrs, base: base}
+}
+
+func genSvcAttr(s *ble.Service, h uint16) (uint16, []*attr) {
+ a := &attr{
+ h: h,
+ typ: ble.PrimaryServiceUUID,
+ v: s.UUID,
+ }
+ h++
+ attrs := []*attr{a}
+ var aa []*attr
+
+ for _, c := range s.Characteristics {
+ h, aa = genCharAttr(c, h)
+ attrs = append(attrs, aa...)
+ }
+
+ a.endh = h - 1
+ return h, attrs
+}
+
+func genCharAttr(c *ble.Characteristic, h uint16) (uint16, []*attr) {
+ vh := h + 1
+
+ a := &attr{
+ h: h,
+ typ: ble.CharacteristicUUID,
+ v: append([]byte{byte(c.Property), byte(vh), byte((vh) >> 8)}, c.UUID...),
+ }
+
+ va := &attr{
+ h: vh,
+ typ: c.UUID,
+ v: c.Value,
+ rh: c.ReadHandler,
+ wh: c.WriteHandler,
+ }
+
+ c.Handle = h
+ c.ValueHandle = vh
+ if c.NotifyHandler != nil || c.IndicateHandler != nil {
+ c.CCCD = newCCCD(c)
+ c.Descriptors = append(c.Descriptors, c.CCCD)
+ }
+
+ h += 2
+
+ attrs := []*attr{a, va}
+ for _, d := range c.Descriptors {
+ attrs = append(attrs, genDescAttr(d, h))
+ h++
+ }
+
+ a.endh = h - 1
+ return h, attrs
+}
+
+func genDescAttr(d *ble.Descriptor, h uint16) *attr {
+ return &attr{
+ h: h,
+ typ: d.UUID,
+ v: d.Value,
+ rh: d.ReadHandler,
+ wh: d.WriteHandler,
+ }
+}
+
+// DumpAttributes ...
+func DumpAttributes(aa []*attr) {
+ logger.Debug("server", "db", "Generating attribute table:")
+ logger.Debug("server", "db", "handle endh type")
+ for _, a := range aa {
+ if a.v != nil {
+ logger.Debug("server", "db", fmt.Sprintf("0x%04X 0x%04X 0x%s [% X]", a.h, a.endh, a.typ, a.v))
+ continue
+ }
+ logger.Debug("server", "db", fmt.Sprintf("0x%04X 0x%04X 0x%s", a.h, a.endh, a.typ))
+ }
+}
+
+const (
+ cccNotify = 0x0001
+ cccIndicate = 0x0002
+)
+
+func newCCCD(c *ble.Characteristic) *ble.Descriptor {
+ d := ble.NewDescriptor(ble.ClientCharacteristicConfigUUID)
+
+ d.HandleRead(ble.ReadHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
+ cccs := req.Conn().(*conn).cccs
+ ccc := cccs[c.Handle]
+ binary.Write(rsp, binary.LittleEndian, ccc)
+ }))
+
+ d.HandleWrite(ble.WriteHandlerFunc(func(req ble.Request, rsp ble.ResponseWriter) {
+ cn := req.Conn().(*conn)
+ old := cn.cccs[c.Handle]
+ ccc := binary.LittleEndian.Uint16(req.Data())
+
+ oldNotify := old&cccNotify != 0
+ oldIndicate := old&cccIndicate != 0
+ newNotify := ccc&cccNotify != 0
+ newIndicate := ccc&cccIndicate != 0
+
+ if newNotify && !oldNotify {
+ if c.Property&ble.CharNotify == 0 {
+ rsp.SetStatus(ble.ErrUnlikely)
+ return
+ }
+ send := func(b []byte) (int, error) { return cn.svr.notify(c.ValueHandle, b) }
+ cn.nn[c.Handle] = ble.NewNotifier(send)
+ go c.NotifyHandler.ServeNotify(req, cn.nn[c.Handle])
+ }
+ if !newNotify && oldNotify {
+ cn.nn[c.Handle].Close()
+ }
+
+ if newIndicate && !oldIndicate {
+ if c.Property&ble.CharIndicate == 0 {
+ rsp.SetStatus(ble.ErrUnlikely)
+ return
+ }
+ send := func(b []byte) (int, error) { return cn.svr.indicate(c.ValueHandle, b) }
+ cn.in[c.Handle] = ble.NewNotifier(send)
+ go c.IndicateHandler.ServeNotify(req, cn.in[c.Handle])
+ }
+ if !newIndicate && oldIndicate {
+ cn.in[c.Handle].Close()
+ }
+ cn.cccs[c.Handle] = ccc
+ }))
+ return d
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/att/log.go b/vendor/github.com/currantlabs/ble/linux/att/log.go
new file mode 100644
index 0000000..61d7f60
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/att/log.go
@@ -0,0 +1,7 @@
+package att
+
+import (
+ "github.com/mgutz/logxi/v1"
+)
+
+var logger = log.New("att")
diff --git a/vendor/github.com/currantlabs/ble/linux/att/server.go b/vendor/github.com/currantlabs/ble/linux/att/server.go
new file mode 100644
index 0000000..21006ac
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/att/server.go
@@ -0,0 +1,597 @@
+package att
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "time"
+
+ "github.com/currantlabs/ble"
+)
+
+type conn struct {
+ ble.Conn
+ svr *Server
+ cccs map[uint16]uint16
+ nn map[uint16]ble.Notifier
+ in map[uint16]ble.Notifier
+}
+
+// Server implementas an ATT (Attribute Protocol) server.
+type Server struct {
+ conn *conn
+ db *DB
+
+ // Refer to [Vol 3, Part F, 3.3.2 & 3.3.3] for the requirement of
+ // sequential request-response protocol, and transactions.
+ rxMTU int
+ txBuf []byte
+ chNotBuf chan []byte
+ chIndBuf chan []byte
+ chConfirm chan bool
+
+ dummyRspWriter ble.ResponseWriter
+}
+
+// NewServer returns an ATT (Attribute Protocol) server.
+func NewServer(db *DB, l2c ble.Conn) (*Server, error) {
+ mtu := l2c.RxMTU()
+ if mtu < ble.DefaultMTU || mtu > ble.MaxMTU {
+ return nil, fmt.Errorf("invalid MTU")
+ }
+ // Although the rxBuf is initialized with the capacity of rxMTU, it is
+ // not discovered, and only the default ATT_MTU (23 bytes) of it shall
+ // be used until remote central request ExchangeMTU.
+ s := &Server{
+ conn: &conn{
+ Conn: l2c,
+ cccs: make(map[uint16]uint16),
+ in: make(map[uint16]ble.Notifier),
+ nn: make(map[uint16]ble.Notifier),
+ },
+ db: db,
+
+ rxMTU: mtu,
+ txBuf: make([]byte, ble.DefaultMTU, ble.DefaultMTU),
+ chNotBuf: make(chan []byte, 1),
+ chIndBuf: make(chan []byte, 1),
+ chConfirm: make(chan bool),
+
+ dummyRspWriter: ble.NewResponseWriter(nil),
+ }
+ s.conn.svr = s
+ s.chNotBuf <- make([]byte, ble.DefaultMTU, ble.DefaultMTU)
+ s.chIndBuf <- make([]byte, ble.DefaultMTU, ble.DefaultMTU)
+ return s, nil
+}
+
+// notify sends notification to remote central.
+func (s *Server) notify(h uint16, data []byte) (int, error) {
+ // Acquire and reuse notifyBuffer. Release it after usage.
+ nBuf := <-s.chNotBuf
+ defer func() { s.chNotBuf <- nBuf }()
+
+ rsp := HandleValueNotification(nBuf)
+ rsp.SetAttributeOpcode()
+ rsp.SetAttributeHandle(h)
+ buf := bytes.NewBuffer(rsp.AttributeValue())
+ buf.Reset()
+ if len(data) > buf.Cap() {
+ data = data[:buf.Cap()]
+ }
+ buf.Write(data)
+ return s.conn.Write(rsp[:3+buf.Len()])
+}
+
+// indicate sends indication to remote central.
+func (s *Server) indicate(h uint16, data []byte) (int, error) {
+ // Acquire and reuse indicateBuffer. Release it after usage.
+ iBuf := <-s.chIndBuf
+ defer func() { s.chIndBuf <- iBuf }()
+
+ rsp := HandleValueIndication(iBuf)
+ rsp.SetAttributeOpcode()
+ rsp.SetAttributeHandle(h)
+ buf := bytes.NewBuffer(rsp.AttributeValue())
+ buf.Reset()
+ if len(data) > buf.Cap() {
+ data = data[:buf.Cap()]
+ }
+ buf.Write(data)
+ n, err := s.conn.Write(rsp[:3+buf.Len()])
+ if err != nil {
+ return n, err
+ }
+ select {
+ case _, ok := <-s.chConfirm:
+ if !ok {
+ return 0, io.ErrClosedPipe
+ }
+ return n, nil
+ case <-time.After(time.Second * 30):
+ return 0, ErrSeqProtoTimeout
+ }
+}
+
+// Loop accepts incoming ATT request, and respond response.
+func (s *Server) Loop() {
+ type sbuf struct {
+ buf []byte
+ len int
+ }
+ pool := make(chan *sbuf, 2)
+ pool <- &sbuf{buf: make([]byte, s.rxMTU)}
+ pool <- &sbuf{buf: make([]byte, s.rxMTU)}
+
+ seq := make(chan *sbuf)
+ go func() {
+ b := <-pool
+ for {
+ n, err := s.conn.Read(b.buf)
+ if n == 0 || err != nil {
+ close(seq)
+ close(s.chConfirm)
+ _ = s.conn.Close()
+ return
+ }
+ if b.buf[0] == HandleValueConfirmationCode {
+ select {
+ case s.chConfirm <- true:
+ default:
+ logger.Error("server", "recieved a spurious confirmation", nil)
+ }
+ continue
+ }
+ b.len = n
+ seq <- b // Send the current request for handling
+ b = <-pool // Swap the buffer for next incoming request.
+ }
+ }()
+ for req := range seq {
+ if rsp := s.handleRequest(req.buf[:req.len]); rsp != nil {
+ if len(rsp) != 0 {
+ s.conn.Write(rsp)
+ }
+ }
+ pool <- req
+ }
+ for h, ccc := range s.conn.cccs {
+ if ccc != 0 {
+ logger.Info("cleanup", "ccc", fmt.Sprintf("0x%02X", ccc))
+ }
+ if ccc&cccIndicate != 0 {
+ s.conn.in[h].Close()
+ }
+ if ccc&cccNotify != 0 {
+ s.conn.nn[h].Close()
+ }
+ }
+}
+
+func (s *Server) handleRequest(b []byte) []byte {
+ var resp []byte
+ logger.Debug("server", "req", fmt.Sprintf("% X", b))
+ switch reqType := b[0]; reqType {
+ case ExchangeMTURequestCode:
+ resp = s.handleExchangeMTURequest(b)
+ case FindInformationRequestCode:
+ resp = s.handleFindInformationRequest(b)
+ case FindByTypeValueRequestCode:
+ resp = s.handleFindByTypeValueRequest(b)
+ case ReadByTypeRequestCode:
+ resp = s.handleReadByTypeRequest(b)
+ case ReadRequestCode:
+ resp = s.handleReadRequest(b)
+ case ReadBlobRequestCode:
+ resp = s.handleReadBlobRequest(b)
+ case ReadByGroupTypeRequestCode:
+ resp = s.handleReadByGroupRequest(b)
+ case WriteRequestCode:
+ resp = s.handleWriteRequest(b)
+ case WriteCommandCode:
+ s.handleWriteCommand(b)
+ case ReadMultipleRequestCode,
+ PrepareWriteRequestCode,
+ ExecuteWriteRequestCode,
+ SignedWriteCommandCode:
+ fallthrough
+ default:
+ resp = newErrorResponse(reqType, 0x0000, ble.ErrReqNotSupp)
+ }
+ logger.Debug("server", "rsp", fmt.Sprintf("% X", resp))
+ return resp
+}
+
+// handle MTU Exchange request. [Vol 3, Part F, 3.4.2]
+func (s *Server) handleExchangeMTURequest(r ExchangeMTURequest) []byte {
+ // Validate the request.
+ switch {
+ case len(r) != 3:
+ fallthrough
+ case r.ClientRxMTU() < 23:
+ return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
+ }
+
+ txMTU := int(r.ClientRxMTU())
+ s.conn.SetTxMTU(txMTU)
+
+ if txMTU != len(s.txBuf) {
+ // Apply the txMTU afer this response has been sent and before
+ // any other attribute protocol PDU is sent.
+ defer func() {
+ s.txBuf = make([]byte, txMTU, txMTU)
+ <-s.chNotBuf
+ s.chNotBuf <- make([]byte, txMTU, txMTU)
+ <-s.chIndBuf
+ s.chIndBuf <- make([]byte, txMTU, txMTU)
+ }()
+ }
+
+ rsp := ExchangeMTUResponse(s.txBuf)
+ rsp.SetAttributeOpcode()
+ rsp.SetServerRxMTU(uint16(s.rxMTU))
+ return rsp[:3]
+}
+
+// handle Find Information request. [Vol 3, Part F, 3.4.3.1 & 3.4.3.2]
+func (s *Server) handleFindInformationRequest(r FindInformationRequest) []byte {
+ // Validate the request.
+ switch {
+ case len(r) != 5:
+ return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
+ case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle():
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
+ }
+
+ rsp := FindInformationResponse(s.txBuf)
+ rsp.SetAttributeOpcode()
+ rsp.SetFormat(0x00)
+ buf := bytes.NewBuffer(rsp.InformationData())
+ buf.Reset()
+
+ // Each response shall contain Types of the same format.
+ for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) {
+ if rsp.Format() == 0 {
+ rsp.SetFormat(0x01)
+ if a.typ.Len() == 16 {
+ rsp.SetFormat(0x02)
+ }
+ }
+ if rsp.Format() == 0x01 && a.typ.Len() != 2 {
+ break
+ }
+ if rsp.Format() == 0x02 && a.typ.Len() != 16 {
+ break
+ }
+
+ if buf.Len()+2+a.typ.Len() > buf.Cap() {
+ break
+ }
+ binary.Write(buf, binary.LittleEndian, a.h)
+ binary.Write(buf, binary.LittleEndian, a.typ)
+ }
+
+ // Nothing has been found.
+ if rsp.Format() == 0 {
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound)
+ }
+ return rsp[:2+buf.Len()]
+}
+
+// handle Find By Type Value request. [Vol 3, Part F, 3.4.3.3 & 3.4.3.4]
+func (s *Server) handleFindByTypeValueRequest(r FindByTypeValueRequest) []byte {
+ // Validate the request.
+ switch {
+ case len(r) < 7:
+ return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
+ case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle():
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
+ }
+
+ rsp := FindByTypeValueResponse(s.txBuf)
+ rsp.SetAttributeOpcode()
+ buf := bytes.NewBuffer(rsp.HandleInformationList())
+ buf.Reset()
+
+ for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) {
+ v, starth, endh := a.v, a.h, a.endh
+ if !(ble.UUID(a.typ).Equal(ble.UUID16(r.AttributeType()))) {
+ continue
+ }
+ if v == nil {
+ // The value shall not exceed ATT_MTU - 7 bytes.
+ // Since ResponseWriter caps the value at the capacity,
+ // we allocate one extra byte, and the written length.
+ buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-7+1))
+ e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2))
+ if e != ble.ErrSuccess || buf2.Len() > len(s.txBuf)-7 {
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
+ }
+ endh = a.h
+ }
+ if !(ble.UUID(v).Equal(ble.UUID(r.AttributeValue()))) {
+ continue
+ }
+
+ if buf.Len()+4 > buf.Cap() {
+ break
+ }
+ binary.Write(buf, binary.LittleEndian, starth)
+ binary.Write(buf, binary.LittleEndian, endh)
+ }
+ if buf.Len() == 0 {
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound)
+ }
+
+ return rsp[:1+buf.Len()]
+}
+
+// handle Read By Type request. [Vol 3, Part F, 3.4.4.1 & 3.4.4.2]
+func (s *Server) handleReadByTypeRequest(r ReadByTypeRequest) []byte {
+ // Validate the request.
+ switch {
+ case len(r) != 7 && len(r) != 21:
+ return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
+ case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle():
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
+ }
+
+ rsp := ReadByTypeResponse(s.txBuf)
+ rsp.SetAttributeOpcode()
+ buf := bytes.NewBuffer(rsp.AttributeDataList())
+ buf.Reset()
+
+ // handle length (2 bytes) + value length.
+ // Each response shall only contains values with the same size.
+ dlen := 0
+ for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) {
+ if !a.typ.Equal(ble.UUID(r.AttributeType())) {
+ continue
+ }
+ v := a.v
+ if v == nil {
+ buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-2))
+ if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess {
+ // Return if the first value read cause an error.
+ if dlen == 0 {
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), e)
+ }
+ // Otherwise, skip to the next one.
+ break
+ }
+ v = buf2.Bytes()
+ }
+ if dlen == 0 {
+ // Found the first value.
+ dlen = 2 + len(v)
+ if dlen > 255 {
+ dlen = 255
+ }
+ if dlen > buf.Cap() {
+ dlen = buf.Cap()
+ }
+ rsp.SetLength(uint8(dlen))
+ } else if 2+len(v) != dlen {
+ break
+ }
+
+ if buf.Len()+dlen > buf.Cap() {
+ break
+ }
+ binary.Write(buf, binary.LittleEndian, a.h)
+ binary.Write(buf, binary.LittleEndian, v[:dlen-2])
+ }
+ if dlen == 0 {
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound)
+ }
+ return rsp[:2+buf.Len()]
+}
+
+// handle Read request. [Vol 3, Part F, 3.4.4.3 & 3.4.4.4]
+func (s *Server) handleReadRequest(r ReadRequest) []byte {
+ // Validate the request.
+ switch {
+ case len(r) != 3:
+ return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
+ }
+
+ rsp := ReadResponse(s.txBuf)
+ rsp.SetAttributeOpcode()
+ buf := bytes.NewBuffer(rsp.AttributeValue())
+ buf.Reset()
+
+ a, ok := s.db.at(r.AttributeHandle())
+ if !ok {
+ return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrInvalidHandle)
+ }
+
+ // Simple case. Read-only, no-authorization, no-authentication.
+ if a.v != nil {
+ binary.Write(buf, binary.LittleEndian, a.v)
+ return rsp[:1+buf.Len()]
+ }
+
+ // Pass the request to upper layer with the ResponseWriter, which caps
+ // the buffer to a valid length of payload.
+ if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess {
+ return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e)
+ }
+ return rsp[:1+buf.Len()]
+}
+
+// handle Read Blob request. [Vol 3, Part F, 3.4.4.5 & 3.4.4.6]
+func (s *Server) handleReadBlobRequest(r ReadBlobRequest) []byte {
+ // Validate the request.
+ switch {
+ case len(r) != 5:
+ return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
+ }
+
+ a, ok := s.db.at(r.AttributeHandle())
+ if !ok {
+ return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrInvalidHandle)
+ }
+
+ rsp := ReadBlobResponse(s.txBuf)
+ rsp.SetAttributeOpcode()
+ buf := bytes.NewBuffer(rsp.PartAttributeValue())
+ buf.Reset()
+
+ // Simple case. Read-only, no-authorization, no-authentication.
+ if a.v != nil {
+ binary.Write(buf, binary.LittleEndian, a.v)
+ return rsp[:1+buf.Len()]
+ }
+
+ // Pass the request to upper layer with the ResponseWriter, which caps
+ // the buffer to a valid length of payload.
+ if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess {
+ return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e)
+ }
+ return rsp[:1+buf.Len()]
+}
+
+// handle Read Blob request. [Vol 3, Part F, 3.4.4.9 & 3.4.4.10]
+func (s *Server) handleReadByGroupRequest(r ReadByGroupTypeRequest) []byte {
+ // Validate the request.
+ switch {
+ case len(r) != 7 && len(r) != 21:
+ return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
+ case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle():
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
+ }
+
+ rsp := ReadByGroupTypeResponse(s.txBuf)
+ rsp.SetAttributeOpcode()
+ buf := bytes.NewBuffer(rsp.AttributeDataList())
+ buf.Reset()
+
+ dlen := 0
+ for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) {
+ v := a.v
+ if v == nil {
+ buf2 := bytes.NewBuffer(make([]byte, buf.Cap()-buf.Len()-4))
+ if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess {
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), e)
+ }
+ v = buf2.Bytes()
+ }
+ if dlen == 0 {
+ dlen = 4 + len(v)
+ if dlen > 255 {
+ dlen = 255
+ }
+ if dlen > buf.Cap() {
+ dlen = buf.Cap()
+ }
+ rsp.SetLength(uint8(dlen))
+ } else if 4+len(v) != dlen {
+ break
+ }
+
+ if buf.Len()+dlen > buf.Cap() {
+ break
+ }
+ binary.Write(buf, binary.LittleEndian, a.h)
+ binary.Write(buf, binary.LittleEndian, a.endh)
+ binary.Write(buf, binary.LittleEndian, v[:dlen-4])
+ }
+ if dlen == 0 {
+ return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound)
+ }
+ return rsp[:2+buf.Len()]
+}
+
+// handle Write request. [Vol 3, Part F, 3.4.5.1 & 3.4.5.2]
+func (s *Server) handleWriteRequest(r WriteRequest) []byte {
+ // Validate the request.
+ switch {
+ case len(r) < 3:
+ return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
+ }
+
+ a, ok := s.db.at(r.AttributeHandle())
+ if !ok {
+ return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrInvalidHandle)
+ }
+
+ // We don't support write to static value. Pass the request to upper layer.
+ if a == nil {
+ return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrWriteNotPerm)
+ }
+ if e := handleATT(a, s.conn, r, ble.NewResponseWriter(nil)); e != ble.ErrSuccess {
+ return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e)
+ }
+ return []byte{WriteResponseCode}
+}
+
+// handle Write command. [Vol 3, Part F, 3.4.5.3]
+func (s *Server) handleWriteCommand(r WriteCommand) []byte {
+ // Validate the request.
+ switch {
+ case len(r) <= 3:
+ return nil
+ }
+
+ a, ok := s.db.at(r.AttributeHandle())
+ if !ok {
+ return nil
+ }
+
+ // We don't support write to static value. Pass the request to upper layer.
+ if a == nil {
+ return nil
+ }
+ if e := handleATT(a, s.conn, r, s.dummyRspWriter); e != ble.ErrSuccess {
+ return nil
+ }
+ return nil
+}
+
+func newErrorResponse(op byte, h uint16, s ble.ATTError) []byte {
+ r := ErrorResponse(make([]byte, 5))
+ r.SetAttributeOpcode()
+ r.SetRequestOpcodeInError(op)
+ r.SetAttributeInError(h)
+ r.SetErrorCode(uint8(s))
+ return r
+}
+
+func handleATT(a *attr, conn ble.Conn, req []byte, rsp ble.ResponseWriter) ble.ATTError {
+ rsp.SetStatus(ble.ErrSuccess)
+ var offset int
+ var data []byte
+ switch req[0] {
+ case ReadByTypeRequestCode:
+ fallthrough
+ case ReadRequestCode:
+ if a.rh == nil {
+ return ble.ErrReadNotPerm
+ }
+ a.rh.ServeRead(ble.NewRequest(conn, data, offset), rsp)
+ case ReadBlobRequestCode:
+ if a.rh == nil {
+ return ble.ErrReadNotPerm
+ }
+ offset = int(ReadBlobRequest(req).ValueOffset())
+ a.rh.ServeRead(ble.NewRequest(conn, data, offset), rsp)
+ case WriteRequestCode:
+ fallthrough
+ case WriteCommandCode:
+ if a.wh == nil {
+ return ble.ErrWriteNotPerm
+ }
+ data = WriteRequest(req).AttributeValue()
+ a.wh.ServeWrite(ble.NewRequest(conn, data, offset), rsp)
+ // case PrepareWriteRequestCode:
+ // case ExecuteWriteRequestCode:
+ // case SignedWriteCommandCode:
+ // case ReadByGroupTypeRequestCode:
+ // case ReadMultipleRequestCode:
+ default:
+ return ble.ErrReqNotSupp
+ }
+
+ return rsp.Status()
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/device.go b/vendor/github.com/currantlabs/ble/linux/device.go
new file mode 100644
index 0000000..52a6fbd
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/device.go
@@ -0,0 +1,165 @@
+package linux
+
+import (
+ "log"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/linux/att"
+ "github.com/currantlabs/ble/linux/gatt"
+ "github.com/currantlabs/ble/linux/hci"
+)
+
+// NewDevice returns the default HCI device.
+func NewDevice() (*Device, error) {
+ dev, err := hci.NewHCI()
+ if err != nil {
+ return nil, errors.Wrap(err, "can't create hci")
+ }
+ if err = dev.Init(); err != nil {
+ return nil, errors.Wrap(err, "can't init hci")
+ }
+
+ s, err := gatt.NewServer()
+ if err != nil {
+ return nil, errors.Wrap(err, "can't create server")
+ }
+
+ mtu := ble.DefaultMTU
+ mtu = ble.MaxMTU // TODO: get this from user using Option.
+ if mtu > ble.MaxMTU {
+ return nil, errors.Wrapf(err, "maximum ATT_MTU is %d", ble.MaxMTU)
+ }
+
+ go func() {
+ for {
+ l2c, err := dev.Accept()
+ if err != nil {
+ log.Printf("can't accept: %s", err)
+ return
+ }
+
+ // Initialize the per-connection cccd values.
+ l2c.SetContext(context.WithValue(l2c.Context(), "ccc", make(map[uint16]uint16)))
+ l2c.SetRxMTU(mtu)
+
+ s.Lock()
+ as, err := att.NewServer(s.DB(), l2c)
+ s.Unlock()
+ if err != nil {
+ log.Printf("can't create ATT server: %s", err)
+ continue
+
+ }
+ go as.Loop()
+ }
+ }()
+ return &Device{HCI: dev, Server: s}, nil
+}
+
+// Device ...
+type Device struct {
+ HCI *hci.HCI
+ Server *gatt.Server
+}
+
+// AddService adds a service to database.
+func (d *Device) AddService(svc *ble.Service) error {
+ return d.Server.AddService(svc)
+}
+
+// RemoveAllServices removes all services that are currently in the database.
+func (d *Device) RemoveAllServices() error {
+ return d.Server.RemoveAllServices()
+}
+
+// SetServices set the specified service to the database.
+// It removes all currently added services, if any.
+func (d *Device) SetServices(svcs []*ble.Service) error {
+ return d.Server.SetServices(svcs)
+}
+
+// Stop stops gatt server.
+func (d *Device) Stop() error {
+ return d.HCI.Close()
+}
+
+// AdvertiseNameAndServices advertises device name, and specified service UUIDs.
+// It tres to fit the UUIDs in the advertising packet as much as possible.
+// If name doesn't fit in the advertising packet, it will be put in scan response.
+func (d *Device) AdvertiseNameAndServices(ctx context.Context, name string, uuids ...ble.UUID) error {
+ if err := d.HCI.AdvertiseNameAndServices(name, uuids...); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ d.HCI.StopAdvertising()
+ return ctx.Err()
+}
+
+// AdvertiseMfgData avertises the given manufacturer data.
+func (d *Device) AdvertiseMfgData(ctx context.Context, id uint16, b []byte) error {
+ if err := d.HCI.AdvertiseMfgData(id, b); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ d.HCI.StopAdvertising()
+ return ctx.Err()
+}
+
+// AdvertiseServiceData16 advertises data associated with a 16bit service uuid
+func (d *Device) AdvertiseServiceData16(ctx context.Context, id uint16, b []byte) error {
+ if err := d.HCI.AdvertiseServiceData16(id, b); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ d.HCI.StopAdvertising()
+ return ctx.Err()
+}
+
+// AdvertiseIBeaconData advertise iBeacon with given manufacturer data.
+func (d *Device) AdvertiseIBeaconData(ctx context.Context, b []byte) error {
+ if err := d.HCI.AdvertiseIBeaconData(b); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ d.HCI.StopAdvertising()
+ return ctx.Err()
+}
+
+// AdvertiseIBeacon advertises iBeacon with specified parameters.
+func (d *Device) AdvertiseIBeacon(ctx context.Context, u ble.UUID, major, minor uint16, pwr int8) error {
+ if err := d.HCI.AdvertiseIBeacon(u, major, minor, pwr); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ d.HCI.StopAdvertising()
+ return ctx.Err()
+}
+
+// Scan starts scanning. Duplicated advertisements will be filtered out if allowDup is set to false.
+func (d *Device) Scan(ctx context.Context, allowDup bool, h ble.AdvHandler) error {
+ if err := d.HCI.SetAdvHandler(h); err != nil {
+ return err
+ }
+ if err := d.HCI.Scan(allowDup); err != nil {
+ return err
+ }
+ <-ctx.Done()
+ d.HCI.StopScanning()
+ return ctx.Err()
+}
+
+// Dial ...
+func (d *Device) Dial(ctx context.Context, a ble.Addr) (ble.Client, error) {
+ // d.HCI.Dial is a blocking call, although most of time it should return immediately.
+ // But in case passing wrong device address or the device went non-connectable, it blocks.
+ cln, err := d.HCI.Dial(ctx, a)
+ return cln, errors.Wrap(err, "can't dial")
+}
+
+// Address returns the listener's device address.
+func (d *Device) Address() ble.Addr {
+ return d.HCI.Addr()
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/gatt/README.md b/vendor/github.com/currantlabs/ble/linux/gatt/README.md
new file mode 100644
index 0000000..b3a3fed
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/gatt/README.md
@@ -0,0 +1,47 @@
+## Generic Attribute Profile (GATT)
+
+This package implement Generic Attribute Profile (GATT) [Vol 3, Part G]
+
+### Check list for ATT Client implementation.
+
+#### Server Configuration [4.3]
+ - [x] Exchange MTU [4.3.1]
+
+#### Primary Service Discovery [4.4]
+ - [x] Discover All Primary Service [4.4.1]
+ - [ ] Discover Primary Service by Service UUID [4.4.2]
+
+#### Relationship Discovery [4.5]
+ - [ ] Find Included Services [4.5.1]
+
+#### Characteristic Discovery [4.6]
+ - [x] Discover All Characteristics of a Service [4.6.1]
+ - [ ] Discover Characteristics by UUID [4.6.2]
+
+#### Characteristic Descriptors Discovery [4.7]
+ - [x] Discover All Characteristic Descriptors [4.7.1]
+
+#### Characteristic Value Read [4.8]
+ - [ ] Read Characteristic Value [4.8.1]
+ - [ ] Read Using Characteristic UUID [4.8.2]
+ - [x] Read Long Characteristic Values [4.8.3]
+ - [ ] Read Multiple Characteristic Values [4.8.4]
+
+#### Characteristic Value Write [4.9]
+ - [x] Write Without Response [4.9.1]
+ - [ ] Signed Write Without Response [4.9.2]
+ - [x] Write Characteristic Value [4.9.3]
+ - [ ] Write Long Characteristic Values [4.9.4]
+ - [x] Reliable Writes [4.9.5]
+
+#### Characteristic Value Notifications [4.10]
+ - [x] Notifications [4.10.1]
+
+#### Characteristic Indications [4.11]
+ - [x] Indications [4.11.1]
+
+#### Characteristic Descriptors [4.12]
+ - [ ] Read Characteristic Descriptors [4.12.1]
+ - [ ] Read Long Characteristic Descriptors [4.12.2]
+ - [ ] Write Characteristic Descriptors [4.12.3]
+ - [ ] Write Long Characteristic Descriptors [4.12.4]
diff --git a/vendor/github.com/currantlabs/ble/linux/gatt/client.go b/vendor/github.com/currantlabs/ble/linux/gatt/client.go
new file mode 100644
index 0000000..a5c4e15
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/gatt/client.go
@@ -0,0 +1,385 @@
+package gatt
+
+import (
+ "encoding/binary"
+ "fmt"
+ "log"
+ "sync"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/linux/att"
+)
+
+const (
+ cccNotify = 0x0001
+ cccIndicate = 0x0002
+)
+
+// NewClient returns a GATT Client.
+func NewClient(conn ble.Conn) (*Client, error) {
+ p := &Client{
+ subs: make(map[uint16]*sub),
+ conn: conn,
+ }
+ p.ac = att.NewClient(conn, p)
+ go p.ac.Loop()
+ return p, nil
+}
+
+// A Client is a GATT Client.
+type Client struct {
+ sync.RWMutex
+
+ profile *ble.Profile
+ name string
+ subs map[uint16]*sub
+
+ ac *att.Client
+ conn ble.Conn
+}
+
+// Address returns the address of the client.
+func (p *Client) Address() ble.Addr {
+ p.RLock()
+ defer p.RUnlock()
+ return p.conn.RemoteAddr()
+}
+
+// Name returns the name of the client.
+func (p *Client) Name() string {
+ p.RLock()
+ defer p.RUnlock()
+ return p.name
+}
+
+// Profile returns the discovered profile.
+func (p *Client) Profile() *ble.Profile {
+ p.RLock()
+ defer p.RUnlock()
+ return p.profile
+}
+
+// DiscoverProfile discovers the whole hierachy of a server.
+func (p *Client) DiscoverProfile(force bool) (*ble.Profile, error) {
+ if p.profile != nil && !force {
+ return p.profile, nil
+ }
+ ss, err := p.DiscoverServices(nil)
+ if err != nil {
+ return nil, fmt.Errorf("can't discover services: %s\n", err)
+ }
+ for _, s := range ss {
+ cs, err := p.DiscoverCharacteristics(nil, s)
+ if err != nil {
+ return nil, fmt.Errorf("can't discover characteristics: %s\n", err)
+ }
+ for _, c := range cs {
+ _, err := p.DiscoverDescriptors(nil, c)
+ if err != nil {
+ return nil, fmt.Errorf("can't discover descriptors: %s\n", err)
+ }
+ }
+ }
+ p.profile = &ble.Profile{Services: ss}
+ return p.profile, nil
+}
+
+// DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1]
+// If filter is specified, only filtered services are returned.
+func (p *Client) DiscoverServices(filter []ble.UUID) ([]*ble.Service, error) {
+ p.Lock()
+ defer p.Unlock()
+ if p.profile == nil {
+ p.profile = &ble.Profile{}
+ }
+ start := uint16(0x0001)
+ for {
+ length, b, err := p.ac.ReadByGroupType(start, 0xFFFF, ble.PrimaryServiceUUID)
+ if err == ble.ErrAttrNotFound {
+ return p.profile.Services, nil
+ }
+ if err != nil {
+ return nil, err
+ }
+ for len(b) != 0 {
+ h := binary.LittleEndian.Uint16(b[:2])
+ endh := binary.LittleEndian.Uint16(b[2:4])
+ u := ble.UUID(b[4:length])
+ if filter == nil || ble.Contains(filter, u) {
+ s := &ble.Service{
+ UUID: u,
+ Handle: h,
+ EndHandle: endh,
+ }
+ p.profile.Services = append(p.profile.Services, s)
+ }
+ if endh == 0xFFFF {
+ return p.profile.Services, nil
+ }
+ start = endh + 1
+ b = b[length:]
+ }
+ }
+}
+
+// DiscoverIncludedServices finds the included services of a service. [Vol 3, Part G, 4.5.1]
+// If filter is specified, only filtered services are returned.
+func (p *Client) DiscoverIncludedServices(ss []ble.UUID, s *ble.Service) ([]*ble.Service, error) {
+ p.Lock()
+ defer p.Unlock()
+ return nil, nil
+}
+
+// DiscoverCharacteristics finds all the characteristics within a service. [Vol 3, Part G, 4.6.1]
+// If filter is specified, only filtered characteristics are returned.
+func (p *Client) DiscoverCharacteristics(filter []ble.UUID, s *ble.Service) ([]*ble.Characteristic, error) {
+ p.Lock()
+ defer p.Unlock()
+ start := s.Handle
+ var lastChar *ble.Characteristic
+ for start <= s.EndHandle {
+ length, b, err := p.ac.ReadByType(start, s.EndHandle, ble.CharacteristicUUID)
+ if err == ble.ErrAttrNotFound {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ for len(b) != 0 {
+ h := binary.LittleEndian.Uint16(b[:2])
+ p := ble.Property(b[2])
+ vh := binary.LittleEndian.Uint16(b[3:5])
+ u := ble.UUID(b[5:length])
+ c := &ble.Characteristic{
+ UUID: u,
+ Property: p,
+ Handle: h,
+ ValueHandle: vh,
+ EndHandle: s.EndHandle,
+ }
+ if filter == nil || ble.Contains(filter, u) {
+ s.Characteristics = append(s.Characteristics, c)
+ }
+ if lastChar != nil {
+ lastChar.EndHandle = c.Handle - 1
+ }
+ lastChar = c
+ start = vh + 1
+ b = b[length:]
+ }
+ }
+ return s.Characteristics, nil
+}
+
+// DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1]
+// If filter is specified, only filtered descriptors are returned.
+func (p *Client) DiscoverDescriptors(filter []ble.UUID, c *ble.Characteristic) ([]*ble.Descriptor, error) {
+ p.Lock()
+ defer p.Unlock()
+ start := c.ValueHandle + 1
+ for start <= c.EndHandle {
+ fmt, b, err := p.ac.FindInformation(start, c.EndHandle)
+ if err == ble.ErrAttrNotFound {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ length := 2 + 2
+ if fmt == 0x02 {
+ length = 2 + 16
+ }
+ for len(b) != 0 {
+ h := binary.LittleEndian.Uint16(b[:2])
+ u := ble.UUID(b[2:length])
+ d := &ble.Descriptor{UUID: u, Handle: h}
+ if filter == nil || ble.Contains(filter, u) {
+ c.Descriptors = append(c.Descriptors, d)
+ }
+ if u.Equal(ble.ClientCharacteristicConfigUUID) {
+ c.CCCD = d
+ }
+ start = h + 1
+ b = b[length:]
+ }
+ }
+ return c.Descriptors, nil
+}
+
+// ReadCharacteristic reads a characteristic value from a server. [Vol 3, Part G, 4.8.1]
+func (p *Client) ReadCharacteristic(c *ble.Characteristic) ([]byte, error) {
+ p.Lock()
+ defer p.Unlock()
+ return p.ac.Read(c.ValueHandle)
+}
+
+// ReadLongCharacteristic reads a characteristic value which is longer than the MTU. [Vol 3, Part G, 4.8.3]
+func (p *Client) ReadLongCharacteristic(c *ble.Characteristic) ([]byte, error) {
+ p.Lock()
+ defer p.Unlock()
+
+ // The maximum length of an attribute value shall be 512 octects [Vol 3, 3.2.9]
+ buffer := make([]byte, 0, 512)
+
+ read, err := p.ac.Read(c.ValueHandle)
+ if err != nil {
+ return nil, err
+ }
+ buffer = append(buffer, read...)
+
+ for len(read) >= p.conn.TxMTU()-1 {
+ if read, err = p.ac.ReadBlob(c.ValueHandle, uint16(len(buffer))); err != nil {
+ return nil, err
+ }
+ buffer = append(buffer, read...)
+ }
+ return buffer, nil
+}
+
+// WriteCharacteristic writes a characteristic value to a server. [Vol 3, Part G, 4.9.3]
+func (p *Client) WriteCharacteristic(c *ble.Characteristic, v []byte, noRsp bool) error {
+ p.Lock()
+ defer p.Unlock()
+ if noRsp {
+ return p.ac.WriteCommand(c.ValueHandle, v)
+ }
+ return p.ac.Write(c.ValueHandle, v)
+}
+
+// ReadDescriptor reads a characteristic descriptor from a server. [Vol 3, Part G, 4.12.1]
+func (p *Client) ReadDescriptor(d *ble.Descriptor) ([]byte, error) {
+ p.Lock()
+ defer p.Unlock()
+ return p.ac.Read(d.Handle)
+}
+
+// WriteDescriptor writes a characteristic descriptor to a server. [Vol 3, Part G, 4.12.3]
+func (p *Client) WriteDescriptor(d *ble.Descriptor, v []byte) error {
+ p.Lock()
+ defer p.Unlock()
+ return p.ac.Write(d.Handle, v)
+}
+
+// ReadRSSI retrieves the current RSSI value of remote peripheral. [Vol 2, Part E, 7.5.4]
+func (p *Client) ReadRSSI() int {
+ p.Lock()
+ defer p.Unlock()
+ // TODO:
+ return 0
+}
+
+// ExchangeMTU informs the server of the client’s maximum receive MTU size and
+// request the server to respond with its maximum receive MTU size. [Vol 3, Part F, 3.4.2.1]
+func (p *Client) ExchangeMTU(mtu int) (int, error) {
+ p.Lock()
+ defer p.Unlock()
+ return p.ac.ExchangeMTU(mtu)
+}
+
+// Subscribe subscribes to indication (if ind is set true), or notification of a
+// characteristic value. [Vol 3, Part G, 4.10 & 4.11]
+func (p *Client) Subscribe(c *ble.Characteristic, ind bool, h ble.NotificationHandler) error {
+ p.Lock()
+ defer p.Unlock()
+ if c.CCCD == nil {
+ return fmt.Errorf("CCCD not found")
+ }
+ if ind {
+ return p.setHandlers(c.CCCD.Handle, c.ValueHandle, cccIndicate, h)
+ }
+ return p.setHandlers(c.CCCD.Handle, c.ValueHandle, cccNotify, h)
+}
+
+// Unsubscribe unsubscribes to indication (if ind is set true), or notification
+// of a specified characteristic value. [Vol 3, Part G, 4.10 & 4.11]
+func (p *Client) Unsubscribe(c *ble.Characteristic, ind bool) error {
+ p.Lock()
+ defer p.Unlock()
+ if c.CCCD == nil {
+ return fmt.Errorf("CCCD not found")
+ }
+ if ind {
+ return p.setHandlers(c.CCCD.Handle, c.ValueHandle, cccIndicate, nil)
+ }
+ return p.setHandlers(c.CCCD.Handle, c.ValueHandle, cccNotify, nil)
+}
+
+func (p *Client) setHandlers(cccdh, vh, flag uint16, h ble.NotificationHandler) error {
+ s, ok := p.subs[vh]
+ if !ok {
+ s = &sub{cccdh, 0x0000, nil, nil}
+ p.subs[vh] = s
+ }
+ switch {
+ case h == nil && (s.ccc&flag) == 0:
+ return nil
+ case h != nil && (s.ccc&flag) != 0:
+ return nil
+ case h == nil && (s.ccc&flag) != 0:
+ s.ccc &= ^uint16(flag)
+ case h != nil && (s.ccc&flag) == 0:
+ s.ccc |= flag
+ }
+
+ v := make([]byte, 2)
+ binary.LittleEndian.PutUint16(v, s.ccc)
+ if flag == cccNotify {
+ s.nHandler = h
+ } else {
+ s.iHandler = h
+ }
+ return p.ac.Write(s.cccdh, v)
+}
+
+// ClearSubscriptions clears all subscriptions to notifications and indications.
+func (p *Client) ClearSubscriptions() error {
+ p.Lock()
+ defer p.Unlock()
+ zero := make([]byte, 2)
+ for vh, s := range p.subs {
+ if err := p.ac.Write(s.cccdh, zero); err != nil {
+ return err
+ }
+ delete(p.subs, vh)
+ }
+ return nil
+}
+
+// CancelConnection disconnects the connection.
+func (p *Client) CancelConnection() error {
+ p.Lock()
+ defer p.Unlock()
+ return p.conn.Close()
+}
+
+// Disconnected returns a receiving channel, which is closed when the client disconnects.
+func (p *Client) Disconnected() <-chan struct{} {
+ p.Lock()
+ defer p.Unlock()
+ return p.conn.Disconnected()
+}
+
+// HandleNotification ...
+func (p *Client) HandleNotification(req []byte) {
+ p.Lock()
+ defer p.Unlock()
+ vh := att.HandleValueIndication(req).AttributeHandle()
+ sub, ok := p.subs[vh]
+ if !ok {
+ // FIXME: disconnects and propagate an error to the user.
+ log.Printf("Got an unregistered notification")
+ return
+ }
+ fn := sub.nHandler
+ if req[0] == att.HandleValueIndicationCode {
+ fn = sub.iHandler
+ }
+ if fn != nil {
+ fn(req[3:])
+ }
+}
+
+type sub struct {
+ cccdh uint16
+ ccc uint16
+ nHandler ble.NotificationHandler
+ iHandler ble.NotificationHandler
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/gatt/server.go b/vendor/github.com/currantlabs/ble/linux/gatt/server.go
new file mode 100644
index 0000000..23f3a18
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/gatt/server.go
@@ -0,0 +1,90 @@
+package gatt
+
+import (
+ "log"
+ "sync"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/linux/att"
+)
+
+// NewServer ...
+func NewServerWithName(name string) (*Server, error) {
+ return &Server{
+ name:name,
+ svcs: defaultServices(name),
+ db: att.NewDB(defaultServices(name), uint16(1)),
+ }, nil
+}
+
+// NewServer ...
+func NewServer() (*Server, error) {
+ return NewServerWithName("Gopher")
+}
+
+// Server ...
+type Server struct {
+ sync.Mutex
+ name string
+
+ svcs []*ble.Service
+ db *att.DB
+}
+
+// AddService ...
+func (s *Server) AddService(svc *ble.Service) error {
+ s.Lock()
+ defer s.Unlock()
+ s.svcs = append(s.svcs, svc)
+ s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
+ return nil
+}
+
+// RemoveAllServices ...
+func (s *Server) RemoveAllServices() error {
+ s.Lock()
+ defer s.Unlock()
+ s.svcs = defaultServices(s.name)
+ s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
+ return nil
+}
+
+// SetServices ...
+func (s *Server) SetServices(svcs []*ble.Service) error {
+ s.Lock()
+ defer s.Unlock()
+ s.svcs = append(defaultServices(s.name), svcs...)
+ s.db = att.NewDB(s.svcs, uint16(1)) // ble attrs start at 1
+ return nil
+}
+
+// DB ...
+func (s *Server) DB() *att.DB {
+ return s.db
+}
+
+func defaultServices(name string) []*ble.Service {
+ // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.ble.appearance.xml
+ var gapCharAppearanceGenericComputer = []byte{0x00, 0x80}
+
+ gapSvc := ble.NewService(ble.GAPUUID)
+ gapSvc.NewCharacteristic(ble.DeviceNameUUID).SetValue([]byte(name))
+ gapSvc.NewCharacteristic(ble.AppearanceUUID).SetValue(gapCharAppearanceGenericComputer)
+ gapSvc.NewCharacteristic(ble.PeripheralPrivacyUUID).SetValue([]byte{0x00})
+ gapSvc.NewCharacteristic(ble.ReconnectionAddrUUID).SetValue([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+ gapSvc.NewCharacteristic(ble.PeferredParamsUUID).SetValue([]byte{0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0x07})
+
+ gattSvc := ble.NewService(ble.GATTUUID)
+ gattSvc.NewCharacteristic(ble.ServiceChangedUUID).HandleIndicate(
+ ble.NotifyHandlerFunc(func(r ble.Request, n ble.Notifier) {
+ log.Printf("TODO: indicate client when the services are changed")
+ for {
+ select {
+ case <-n.Context().Done():
+ log.Printf("count: Notification unsubscribed")
+ return
+ }
+ }
+ }))
+ return []*ble.Service{gapSvc, gattSvc}
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/README.md b/vendor/github.com/currantlabs/ble/linux/hci/README.md
new file mode 100644
index 0000000..48eecdb
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/README.md
@@ -0,0 +1,116 @@
+## LE Command Requirements
+
+List of the commands and events that a Controller supporting LE shall implement. [Vol 2 Part A.3.19]
+
+- Mandatory
+
+ - [ ] Vol 2, Part E, 7.7.14 - Command Complete Event (0x0E)
+ - [ ] Vol 2, Part E, 7.7.15 - Command Status Event (0x0F)
+ - [ ] Vol 2, Part E, 7.8.16 - LE Add Device To White List Command (0x08|0x0011)
+ - [ ] Vol 2, Part E, 7.8.15 - LE Clear White List Command (0x08|0x0010)
+ - [ ] Vol 2, Part E, 7.8.2 - LE Read Buffer Size Command (0x08|0x0002)
+ - [ ] Vol 2, Part E, 7.4.3 - Read Local Supported Features Command (0x04|0x0003)
+ - [ ] Vol 2, Part E, 7.8.27 - LE Read Supported States Command (0x08|0x001C)
+ - [ ] Vol 2, Part E, 7.8.14 - LE Read White List Size Command (0x08|0x000F)
+ - [ ] Vol 2, Part E, 7.8.17 - LE Remove Device From White List Command (0x08|0x0012)
+ - [ ] Vol 2, Part E, 7.8.1 - LE Set Event Mask Command (0x08|0x0001)
+ - [ ] Vol 2, Part E, 7.8.30 - LE Test End Command (0x08|0x001F)
+ - [ ] Vol 2, Part E, 7.4.6 - Read BD_ADDR Command (0x04|0x0009)
+ - [ ] Vol 2, Part E, 7.8.3 - LE Read Local Supported Features Command (0x08|0x0003)
+ - [ ] Vol 2, Part E, 7.4.1 - Read Local Version Information Command (0x04|0x0001)
+ - [ ] Vol 2, Part E, 7.3.2 - Reset Command (0x03|0x003)
+ - [ ] Vol 2, Part E, 7.4.2 - Read Local Supported Commands Command (0x04|0x0002)
+ - [ ] Vol 2, Part E, 7.3.1 - Set Event Mask Command (0x03|0x0001)
+
+
+- C1: Mandatory if Controller supports transmitting packets, otherwise optional.
+
+ - [ ] Vol 2, Part E, 7.8.6 - LE Read Advertising Channel Tx Power Command (0x08|0x0007)
+ - [ ] Vol 2, Part E, 7.8.29 - LE Transmitter Test Command (0x08|0x001E)
+ - [ ] Vol 2, Part E, 7.8.9 - LE Set Advertise Enable Command (0x08|0x000A)
+ - [ ] Vol 2, Part E, 7.8.7 - LE Set Advertising Data Command (0x08|0x0008)
+ - [ ] Vol 2, Part E, 7.8.5 - LE Set Advertising Parameters Command (0x08|0x0006)
+ - [ ] Vol 2, Part E, 7.8.4 - LE Set Random Address Command (0x08|0x0005)
+
+
+- C2: Mandatory if Controller supports receiving packets, otherwise optional.
+
+ - [ ] Vol 2, Part E, 7.7.65.2 - LE Advertising Report Event (0x3E)
+ - [ ] Vol 2, Part E, 7.8.28 - LE Receiver Test Command (0x08|0x001D)
+ - [ ] Vol 2, Part E, 7.8.11 - LE Set Scan Enable Command (0x08|0x000C)
+ - [ ] Vol 2, Part E, 7.8.10 - LE Set Scan Parameters Command (0x08|0x000B)
+
+
+- C3: Mandatory if Controller supports transmitting and receiving packets, otherwise optional.
+
+ - [ ] Vol 2, Part E, 7.1.6 - Disconnect Command (0x01|0x0006)
+ - [ ] Vol 2, Part E, 7.7.5 - Disconnection Complete Event (0x05)
+ - [ ] Vol 2, Part E, 7.7.65.1 - LE Connection Complete Event (0x3E)
+ - [ ] Vol 2, Part E, 7.8.18 - LE Connection Update Command (0x08|0x0013)
+ - [ ] Vol 2, Part E, 7.7.65.3 - LE Connection Update Complete Event (0x0E)
+ - [ ] Vol 2, Part E, 7.8.12 - LE Create Connection Command (0x08|0x000D)
+ - [ ] Vol 2, Part E, 7.8.13 - LE Create Connection Cancel Command (0x08|0x000E)
+ - [ ] Vol 2, Part E, 7.8.20 - LE Read Channel Map Command (0x08|0x0015)
+ - [ ] Vol 2, Part E, 7.8.21 - LE Read Remote Used Features Command (0x08|0x0016)
+ - [ ] Vol 2, Part E, 7.7.65.4 - LE Read Remote Used Features Complete Event (0x3E)
+ - [ ] Vol 2, Part E, 7.8.19 - LE Set Host Channel Classification Command (0x08|0x0014)
+ - [ ] Vol 2, Part E, 7.8.8 - LE Set Scan Response Data Command (0x08|0x0009)
+ - [ ] Vol 2, Part E, 7.3.40 - Host Number Of Completed Packets Command (0x03|0x0035)
+ - [ ] Vol 2, Part E, 7.3.35 - Read Transmit Power Level Command (0x03|0x002D)
+ - [ ] Vol 2, Part E, 7.1.23 - Read Remote Version Information Command (0x01|0x001D)
+ - [ ] Vol 2, Part E, 7.7.12 - Read Remote Version Information Complete Event (0x0C)
+ - [ ] Vol 2, Part E, 7.5.4 - Read RSSI Command (0x05|0x0005)
+
+
+- C4: Mandatory if LE Feature (LL Encryption) is supported otherwise optional.
+
+ - [ ] Vol 2, Part E, 7.7.8 - Encryption Change Event (0x08)
+ - [ ] Vol 2, Part E, 7.7.39 - Encryption Key Refresh Complete Event (0x30)
+ - [ ] Vol 2, Part E, 7.8.22 - LE Encrypt Command (0x08|0x0017)
+ - [ ] Vol 2, Part E, 7.7.65.5 - LE Long Term Key Request Event (0x3E)
+ - [ ] Vol 2, Part E, 7.8.25 - LE Long Term Key Request Reply Command (0x08|0x001A)
+ - [ ] Vol 2, Part E, 7.8.26 - LE Long Term Key Request Negative Reply Command (0x08|0x001B)
+ - [ ] Vol 2, Part E, 7.8.23 - LE Rand Command (0x08|0x0018)
+ - [ ] Vol 2, Part E, 7.8.24 - LE Start Encryption Command (0x08|0x0019)
+
+
+- C5: Mandatory if BR/EDR is supported otherwise optional. [Won't supported]
+
+ - [ ] Vol 2, Part E, 7.4.5 - Read Buffer Size Command
+ - [ ] Vol 2, Part E, 7.3.78 - Read LE Host Support
+ - [ ] Vol 2, Part E, 7.3.79 - Write LE Host Support Command (0x03|0x006D)
+
+
+- C6: Mandatory if LE Feature (Connection Parameters Request procedure) is supported, otherwise optional.
+
+ - [ ] Vol 2, Part E, 7.8.31 - LE Remote Connection Parameter Request Reply Command (0x08|0x0020)
+ - [ ] Vol 2, Part E, 7.8.32 - LE Remote Connection Parameter Request Negative Reply Command (0x08|0x0021)
+ - [ ] Vol 2, Part E, 7.7.65.6 - LE Remote Connection Parameter Request Event (0x3E)
+
+
+- C7: Mandatory if LE Ping is supported otherwise excluded
+
+ - [ ] Vol 2, Part E, 7.3.94 - Write Authenticated Payload Timeout Command (0x01|0x007C)
+ - [ ] Vol 2, Part E, 7.3.93 - Read Authenticated Payload Timeout Command (0x03|0x007B)
+ - [ ] Vol 2, Part E, 7.7.75 - Authenticated Payload Timeout Expired Event (0x57)
+ - [ ] Vol 2, Part E, 7.3.69 - Set Event Mask Page 2 Command (0x03|0x0063)
+
+
+- Optional support
+
+ - [ ] Vol 2, Part E, 7.7.26 - Data Buffer Overflow Event (0x1A)
+ - [ ] Vol 2, Part E, 7.7.16 - Hardware Error Event (0x10)
+ - [ ] Vol 2, Part E, 7.3.39 - Host Buffer Size Command (0x03|0x0033)
+ - [ ] Vol 2, Part E, 7.7.19 - Number Of Completed Packets Event (0x13)
+ - [ ] Vol 2, Part E, 7.3.38 - Set Controller To Host Flow Control Command
+
+ ## Vol 3, Part A, 4 L2CAP Signaling mandatory for LE-U
+
+ - [ ] Vol 3, Part A, 4.1 - Command Reject (0x01)
+ - [ ] Vol 3, Part A, 4.6 - Disconnect Request (0x06)
+ - [ ] Vol 3, Part A, 4.7 - Disconnect Response (0x07)
+ - [ ] Vol 3, Part A, 4.20 - Connection Parameter Update Request (0x12)
+ - [ ] Vol 3, Part A, 4.21 - Connection Parameter Update Response (0x13)
+ - [ ] Vol 3, Part A, 4.22 - LE Credit Based Connection Request (0x14)
+ - [ ] Vol 3, Part A, 4.23 - LE Credit Based Connection Response (0x15)
+ - [ ] Vol 3, Part A, 4.24 - LE Flow Control Credit (0x16)
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/adv.go b/vendor/github.com/currantlabs/ble/linux/hci/adv.go
new file mode 100644
index 0000000..eb0f7f7
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/adv.go
@@ -0,0 +1,135 @@
+package hci
+
+import (
+ "net"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/linux/adv"
+ "github.com/currantlabs/ble/linux/hci/evt"
+)
+
+// RandomAddress is a Random Device Address.
+type RandomAddress struct {
+ ble.Addr
+}
+
+// [Vol 6, Part B, 4.4.2] [Vol 3, Part C, 11]
+const (
+ evtTypAdvInd = 0x00 // Connectable undirected advertising (ADV_IND).
+ evtTypAdvDirectInd = 0x01 // Connectable directed advertising (ADV_DIRECT_IND).
+ evtTypAdvScanInd = 0x02 // Scannable undirected advertising (ADV_SCAN_IND).
+ evtTypAdvNonconnInd = 0x03 // Non connectable undirected advertising (ADV_NONCONN_IND).
+ evtTypScanRsp = 0x04 // Scan Response (SCAN_RSP).
+)
+
+func newAdvertisement(e evt.LEAdvertisingReport, i int) *Advertisement {
+ return &Advertisement{e: e, i: i}
+}
+
+// Advertisement implements ble.Advertisement and other functions that are only
+// available on Linux.
+type Advertisement struct {
+ e evt.LEAdvertisingReport
+ i int
+ sr *Advertisement
+
+ // cached packets.
+ p *adv.Packet
+}
+
+// setScanResponse ssociate sca response to the existing advertisement.
+func (a *Advertisement) setScanResponse(sr *Advertisement) {
+ a.sr = sr
+ a.p = nil // clear the cached.
+}
+
+// packets returns the combined advertising packet and scan response (if presents)
+func (a *Advertisement) packets() *adv.Packet {
+ if a.p != nil {
+ return a.p
+ }
+ return adv.NewRawPacket(a.Data(), a.ScanResponse())
+}
+
+// LocalName returns the LocalName of the remote peripheral.
+func (a *Advertisement) LocalName() string {
+ return a.packets().LocalName()
+}
+
+// ManufacturerData returns the ManufacturerData of the advertisement.
+func (a *Advertisement) ManufacturerData() []byte {
+ return a.packets().ManufacturerData()
+}
+
+// ServiceData returns the service data of the advertisement.
+func (a *Advertisement) ServiceData() []ble.ServiceData {
+ return a.packets().ServiceData()
+}
+
+// Services returns the service UUIDs of the advertisement.
+func (a *Advertisement) Services() []ble.UUID {
+ return a.packets().UUIDs()
+}
+
+// OverflowService returns the UUIDs of overflowed service.
+func (a *Advertisement) OverflowService() []ble.UUID {
+ return a.packets().UUIDs()
+}
+
+// TxPowerLevel returns the tx power level of the remote peripheral.
+func (a *Advertisement) TxPowerLevel() int {
+ pwr, _ := a.packets().TxPower()
+ return pwr
+}
+
+// SolicitedService returns UUIDs of solicited services.
+func (a *Advertisement) SolicitedService() []ble.UUID {
+ return a.packets().ServiceSol()
+}
+
+// Connectable indicates weather the remote peripheral is connectable.
+func (a *Advertisement) Connectable() bool {
+ return a.EventType() == evtTypAdvDirectInd || a.EventType() == evtTypAdvInd
+}
+
+// RSSI returns RSSI signal strength.
+func (a *Advertisement) RSSI() int {
+ return int(a.e.RSSI(a.i))
+}
+
+// Address returns the address of the remote peripheral.
+func (a *Advertisement) Address() ble.Addr {
+ b := a.e.Address(a.i)
+ addr := net.HardwareAddr([]byte{b[5], b[4], b[3], b[2], b[1], b[0]})
+ if a.e.AddressType(a.i) == 1 {
+ return RandomAddress{addr}
+ }
+ return addr
+}
+
+// EventType returns the event type of Advertisement.
+// This is linux sepcific.
+func (a *Advertisement) EventType() uint8 {
+ return a.e.EventType(a.i)
+}
+
+// AddressType returns the address type of the Advertisement.
+// This is linux sepcific.
+func (a *Advertisement) AddressType() uint8 {
+ return a.e.AddressType(a.i)
+}
+
+// Data returns the advertising data of the packet.
+// This is linux sepcific.
+func (a *Advertisement) Data() []byte {
+ return a.e.Data(a.i)
+}
+
+// ScanResponse returns the scan response of the packet, if it presents.
+// This is linux sepcific.
+func (a *Advertisement) ScanResponse() []byte {
+ if a.sr == nil {
+ return nil
+ }
+ return a.sr.Data()
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/buffer.go b/vendor/github.com/currantlabs/ble/linux/hci/buffer.go
new file mode 100644
index 0000000..93162a7
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/buffer.go
@@ -0,0 +1,74 @@
+package hci
+
+import (
+ "bytes"
+ "sync"
+)
+
+// Pool ...
+type Pool struct {
+ sync.Mutex
+
+ sz int
+ cnt int
+ ch chan *bytes.Buffer
+}
+
+// NewPool ...
+func NewPool(sz int, cnt int) *Pool {
+ ch := make(chan *bytes.Buffer, cnt)
+ for len(ch) < cnt {
+ ch <- bytes.NewBuffer(make([]byte, sz))
+ }
+ return &Pool{sz: sz, cnt: cnt, ch: ch}
+}
+
+// Client ...
+type Client struct {
+ p *Pool
+ sent chan *bytes.Buffer
+}
+
+// NewClient ...
+func NewClient(p *Pool) *Client {
+ return &Client{p: p, sent: make(chan *bytes.Buffer, p.cnt)}
+}
+
+// LockPool ...
+func (c *Client) LockPool() {
+ c.p.Lock()
+}
+
+// UnlockPool ...
+func (c *Client) UnlockPool() {
+ c.p.Unlock()
+}
+
+// Get returns a buffer from the shared buffer pool.
+func (c *Client) Get() *bytes.Buffer {
+ b := <-c.p.ch
+ b.Reset()
+ c.sent <- b
+ return b
+}
+
+// Put puts the oldest sent buffer back to the shared pool.
+func (c *Client) Put() {
+ select {
+ case b := <-c.sent:
+ c.p.ch <- b
+ default:
+ }
+}
+
+// PutAll puts all the sent buffers back to the shared pool.
+func (c *Client) PutAll() {
+ for {
+ select {
+ case b := <-c.sent:
+ c.p.ch <- b
+ default:
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/cmd/cmd.go b/vendor/github.com/currantlabs/ble/linux/hci/cmd/cmd.go
new file mode 100644
index 0000000..7c5c639
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/cmd/cmd.go
@@ -0,0 +1,31 @@
+package cmd
+
+import (
+ "bytes"
+ "encoding/binary"
+ "io"
+)
+
+type command interface {
+ OpCode() int
+ Len() int
+ Marshal([]byte) error
+}
+
+type commandRP interface {
+ Unmarshal(b []byte) error
+}
+
+func marshal(c command, b []byte) error {
+ buf := bytes.NewBuffer(b)
+ buf.Reset()
+ if buf.Cap() < c.Len() {
+ return io.ErrShortBuffer
+ }
+ return binary.Write(buf, binary.LittleEndian, c)
+}
+
+func unmarshal(c commandRP, b []byte) error {
+ buf := bytes.NewBuffer(b)
+ return binary.Read(buf, binary.LittleEndian, c)
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/cmd/cmd_gen.go b/vendor/github.com/currantlabs/ble/linux/hci/cmd/cmd_gen.go
new file mode 100644
index 0000000..8b10a15
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/cmd/cmd_gen.go
@@ -0,0 +1,1562 @@
+package cmd
+
+// Disconnect implements Disconnect (0x01|0x0006) [Vol 2, Part E, 7.1.6]
+type Disconnect struct {
+ ConnectionHandle uint16
+ Reason uint8
+}
+
+func (c *Disconnect) String() string {
+ return "Disconnect (0x01|0x0006)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *Disconnect) OpCode() int { return 0x01<<10 | 0x0006 }
+
+// Len returns the length of the command.
+func (c *Disconnect) Len() int { return 3 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *Disconnect) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadRemoteVersionInformation implements Read Remote Version Information (0x01|0x001D) [Vol 2, Part E, 7.1.23]
+type ReadRemoteVersionInformation struct {
+ ConnectionHandle uint16
+}
+
+func (c *ReadRemoteVersionInformation) String() string {
+ return "Read Remote Version Information (0x01|0x001D)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadRemoteVersionInformation) OpCode() int { return 0x01<<10 | 0x001D }
+
+// Len returns the length of the command.
+func (c *ReadRemoteVersionInformation) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadRemoteVersionInformation) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// WriteDefaultLinkPolicySettings implements Write Default Link Policy Settings (0x02|0x000D) [Vol 2, Part E, 7.2.12]
+type WriteDefaultLinkPolicySettings struct {
+ DefaultLinkPolicySettings uint16
+}
+
+func (c *WriteDefaultLinkPolicySettings) String() string {
+ return "Write Default Link Policy Settings (0x02|0x000D)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *WriteDefaultLinkPolicySettings) OpCode() int { return 0x02<<10 | 0x000D }
+
+// Len returns the length of the command.
+func (c *WriteDefaultLinkPolicySettings) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *WriteDefaultLinkPolicySettings) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// WriteDefaultLinkPolicySettingsRP returns the return parameter of Write Default Link Policy Settings
+type WriteDefaultLinkPolicySettingsRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *WriteDefaultLinkPolicySettingsRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// SetEventMask implements Set Event Mask (0x03|0x0001) [Vol 2, Part E, 7.3.1]
+type SetEventMask struct {
+ EventMask uint64
+}
+
+func (c *SetEventMask) String() string {
+ return "Set Event Mask (0x03|0x0001)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *SetEventMask) OpCode() int { return 0x03<<10 | 0x0001 }
+
+// Len returns the length of the command.
+func (c *SetEventMask) Len() int { return 8 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *SetEventMask) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// SetEventMaskRP returns the return parameter of Set Event Mask
+type SetEventMaskRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *SetEventMaskRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// Reset implements Reset (0x03|0x003) [Vol 2, Part E, 7.3.2]
+type Reset struct {
+}
+
+func (c *Reset) String() string {
+ return "Reset (0x03|0x003)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *Reset) OpCode() int { return 0x03<<10 | 0x003 }
+
+// Len returns the length of the command.
+func (c *Reset) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *Reset) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ResetRP returns the return parameter of Reset
+type ResetRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ResetRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// WritePageTimeout implements Write Page Timeout (0x03|0x0018) [Vol 2, Part E, 7.3.16]
+type WritePageTimeout struct {
+ PageTimeout uint16
+}
+
+func (c *WritePageTimeout) String() string {
+ return "Write Page Timeout (0x03|0x0018)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *WritePageTimeout) OpCode() int { return 0x03<<10 | 0x0018 }
+
+// Len returns the length of the command.
+func (c *WritePageTimeout) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *WritePageTimeout) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// WritePageTimeoutRP returns the return parameter of Write Page Timeout
+type WritePageTimeoutRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *WritePageTimeoutRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// WriteClassOfDevice implements Write Class Of Device (0x03|0x0024) [Vol 2, Part E, 7.3.26]
+type WriteClassOfDevice struct {
+ ClassOfDevice [3]byte
+}
+
+func (c *WriteClassOfDevice) String() string {
+ return "Write Class Of Device (0x03|0x0024)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *WriteClassOfDevice) OpCode() int { return 0x03<<10 | 0x0024 }
+
+// Len returns the length of the command.
+func (c *WriteClassOfDevice) Len() int { return 3 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *WriteClassOfDevice) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// WriteClassOfDeviceRP returns the return parameter of Write Class Of Device
+type WriteClassOfDeviceRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *WriteClassOfDeviceRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// ReadTransmitPowerLevel implements Read Transmit Power Level (0x03|0x002D) [Vol 2, Part E, 7.3.35]
+type ReadTransmitPowerLevel struct {
+ ConnectionHandle uint16
+ Type uint8
+}
+
+func (c *ReadTransmitPowerLevel) String() string {
+ return "Read Transmit Power Level (0x03|0x002D)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadTransmitPowerLevel) OpCode() int { return 0x03<<10 | 0x002D }
+
+// Len returns the length of the command.
+func (c *ReadTransmitPowerLevel) Len() int { return 3 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadTransmitPowerLevel) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadTransmitPowerLevelRP returns the return parameter of Read Transmit Power Level
+type ReadTransmitPowerLevelRP struct {
+ Status uint8
+ ConnectionHandle uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ReadTransmitPowerLevelRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// HostBufferSize implements Host Buffer Size (0x03|0x0033) [Vol 2, Part E, 7.3.39]
+type HostBufferSize struct {
+ HostACLDataPacketLength uint16
+ HostSynchronousDataPacketLength uint8
+ HostTotalNumACLDataPackets uint16
+ HostTotalNumSynchronousDataPackets uint16
+}
+
+func (c *HostBufferSize) String() string {
+ return "Host Buffer Size (0x03|0x0033)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *HostBufferSize) OpCode() int { return 0x03<<10 | 0x0033 }
+
+// Len returns the length of the command.
+func (c *HostBufferSize) Len() int { return 7 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *HostBufferSize) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// HostBufferSizeRP returns the return parameter of Host Buffer Size
+type HostBufferSizeRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *HostBufferSizeRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// HostNumberOfCompletedPackets implements Host Number Of Completed Packets (0x03|0x0035) [Vol 2, Part E, 7.3.40]
+type HostNumberOfCompletedPackets struct {
+ NumberOfHandles uint8
+ ConnectionHandle []uint16
+ HostNumOfCompletedPackets []uint16
+}
+
+func (c *HostNumberOfCompletedPackets) String() string {
+ return "Host Number Of Completed Packets (0x03|0x0035)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *HostNumberOfCompletedPackets) OpCode() int { return 0x03<<10 | 0x0035 }
+
+// Len returns the length of the command.
+func (c *HostNumberOfCompletedPackets) Len() int { return -1 }
+
+// SetEventMaskPage2 implements Set Event Mask Page 2 (0x03|0x0063) [Vol 2, Part E, 7.3.69]
+type SetEventMaskPage2 struct {
+ EventMaskPage2 uint64
+}
+
+func (c *SetEventMaskPage2) String() string {
+ return "Set Event Mask Page 2 (0x03|0x0063)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *SetEventMaskPage2) OpCode() int { return 0x03<<10 | 0x0063 }
+
+// Len returns the length of the command.
+func (c *SetEventMaskPage2) Len() int { return 8 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *SetEventMaskPage2) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// SetEventMaskPage2RP returns the return parameter of Set Event Mask Page 2
+type SetEventMaskPage2RP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *SetEventMaskPage2RP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// WriteLEHostSupport implements Write LE Host Support (0x03|0x006D) [Vol 2, Part E, 7.3.79]
+type WriteLEHostSupport struct {
+ LESupportedHost uint8
+ SimultaneousLEHost uint8
+}
+
+func (c *WriteLEHostSupport) String() string {
+ return "Write LE Host Support (0x03|0x006D)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *WriteLEHostSupport) OpCode() int { return 0x03<<10 | 0x006D }
+
+// Len returns the length of the command.
+func (c *WriteLEHostSupport) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *WriteLEHostSupport) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// WriteLEHostSupportRP returns the return parameter of Write LE Host Support
+type WriteLEHostSupportRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *WriteLEHostSupportRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// ReadAuthenticatedPayloadTimeout implements Read Authenticated Payload Timeout (0x03|0x007B) [Vol 2, Part E, 7.3.93]
+type ReadAuthenticatedPayloadTimeout struct {
+ ConnectionHandle uint16
+}
+
+func (c *ReadAuthenticatedPayloadTimeout) String() string {
+ return "Read Authenticated Payload Timeout (0x03|0x007B)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadAuthenticatedPayloadTimeout) OpCode() int { return 0x03<<10 | 0x007B }
+
+// Len returns the length of the command.
+func (c *ReadAuthenticatedPayloadTimeout) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadAuthenticatedPayloadTimeout) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadAuthenticatedPayloadTimeoutRP returns the return parameter of Read Authenticated Payload Timeout
+type ReadAuthenticatedPayloadTimeoutRP struct {
+ Status uint8
+ ConnectionHandle uint16
+ AuthenticatedPayloadTimeout uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ReadAuthenticatedPayloadTimeoutRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// WriteAuthenticatedPayloadTimeout implements Write Authenticated Payload Timeout (0x01|0x007C) [Vol 2, Part E, 7.3.94]
+type WriteAuthenticatedPayloadTimeout struct {
+ ConnectionHandle uint16
+ AuthenticatedPayloadTimeout uint16
+}
+
+func (c *WriteAuthenticatedPayloadTimeout) String() string {
+ return "Write Authenticated Payload Timeout (0x01|0x007C)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *WriteAuthenticatedPayloadTimeout) OpCode() int { return 0x01<<10 | 0x007C }
+
+// Len returns the length of the command.
+func (c *WriteAuthenticatedPayloadTimeout) Len() int { return 4 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *WriteAuthenticatedPayloadTimeout) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// WriteAuthenticatedPayloadTimeoutRP returns the return parameter of Write Authenticated Payload Timeout
+type WriteAuthenticatedPayloadTimeoutRP struct {
+ Status uint8
+ ConnectionHandle uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *WriteAuthenticatedPayloadTimeoutRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// ReadLocalVersionInformation implements Read Local Version Information (0x04|0x0001) [Vol 2, Part E, 7.4.1]
+type ReadLocalVersionInformation struct {
+}
+
+func (c *ReadLocalVersionInformation) String() string {
+ return "Read Local Version Information (0x04|0x0001)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadLocalVersionInformation) OpCode() int { return 0x04<<10 | 0x0001 }
+
+// Len returns the length of the command.
+func (c *ReadLocalVersionInformation) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadLocalVersionInformation) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadLocalVersionInformationRP returns the return parameter of Read Local Version Information
+type ReadLocalVersionInformationRP struct {
+ Status uint8
+ HCIVersion uint8
+ HCIRevision uint16
+ LMPPAMVersion uint8
+ ManufacturerName uint16
+ LMPPAMSubversion uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ReadLocalVersionInformationRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// ReadLocalSupportedCommands implements Read Local Supported Commands (0x04|0x0002) [Vol 2, Part E, 7.4.2]
+type ReadLocalSupportedCommands struct {
+}
+
+func (c *ReadLocalSupportedCommands) String() string {
+ return "Read Local Supported Commands (0x04|0x0002)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadLocalSupportedCommands) OpCode() int { return 0x04<<10 | 0x0002 }
+
+// Len returns the length of the command.
+func (c *ReadLocalSupportedCommands) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadLocalSupportedCommands) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadLocalSupportedCommandsRP returns the return parameter of Read Local Supported Commands
+type ReadLocalSupportedCommandsRP struct {
+ Status uint8
+ Supporteds uint64
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ReadLocalSupportedCommandsRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// ReadLocalSupportedFeatures implements Read Local Supported Features (0x04|0x0003) [Vol 2, Part E, 7.4.3]
+type ReadLocalSupportedFeatures struct {
+}
+
+func (c *ReadLocalSupportedFeatures) String() string {
+ return "Read Local Supported Features (0x04|0x0003)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadLocalSupportedFeatures) OpCode() int { return 0x04<<10 | 0x0003 }
+
+// Len returns the length of the command.
+func (c *ReadLocalSupportedFeatures) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadLocalSupportedFeatures) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadLocalSupportedFeaturesRP returns the return parameter of Read Local Supported Features
+type ReadLocalSupportedFeaturesRP struct {
+ Status uint8
+ LMPFeatures uint64
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ReadLocalSupportedFeaturesRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// ReadBufferSize implements Read Buffer Size (0x04|0x0005) [Vol 2, Part E, 7.4.5]
+type ReadBufferSize struct {
+}
+
+func (c *ReadBufferSize) String() string {
+ return "Read Buffer Size (0x04|0x0005)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadBufferSize) OpCode() int { return 0x04<<10 | 0x0005 }
+
+// Len returns the length of the command.
+func (c *ReadBufferSize) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadBufferSize) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadBufferSizeRP returns the return parameter of Read Buffer Size
+type ReadBufferSizeRP struct {
+ Status uint8
+ HCACLDataPacketLength uint16
+ HCSynchronousDataPacketLength uint8
+ HCTotalNumACLDataPackets uint16
+ HCTotalNumSynchronousDataPackets uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ReadBufferSizeRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// ReadBDADDR implements Read BD_ADDR (0x04|0x0009) [Vol 2, Part E, 7.4.6]
+type ReadBDADDR struct {
+}
+
+func (c *ReadBDADDR) String() string {
+ return "Read BD_ADDR (0x04|0x0009)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadBDADDR) OpCode() int { return 0x04<<10 | 0x0009 }
+
+// Len returns the length of the command.
+func (c *ReadBDADDR) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadBDADDR) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadBDADDRRP returns the return parameter of Read BD_ADDR
+type ReadBDADDRRP struct {
+ Status uint8
+ BDADDR [6]byte
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ReadBDADDRRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// ReadRSSI implements Read RSSI (0x05|0x0005) [Vol 2, Part E, 7.5.4]
+type ReadRSSI struct {
+ Handle uint16
+}
+
+func (c *ReadRSSI) String() string {
+ return "Read RSSI (0x05|0x0005)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *ReadRSSI) OpCode() int { return 0x05<<10 | 0x0005 }
+
+// Len returns the length of the command.
+func (c *ReadRSSI) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *ReadRSSI) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// ReadRSSIRP returns the return parameter of Read RSSI
+type ReadRSSIRP struct {
+ Status uint8
+ ConnectionHandle uint16
+ RSSI int8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *ReadRSSIRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LESetEventMask implements LE Set Event Mask (0x08|0x0001) [Vol 2, Part E, 7.8.1]
+type LESetEventMask struct {
+ LEEventMask uint64
+}
+
+func (c *LESetEventMask) String() string {
+ return "LE Set Event Mask (0x08|0x0001)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetEventMask) OpCode() int { return 0x08<<10 | 0x0001 }
+
+// Len returns the length of the command.
+func (c *LESetEventMask) Len() int { return 8 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetEventMask) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetEventMaskRP returns the return parameter of LE Set Event Mask
+type LESetEventMaskRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetEventMaskRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEReadBufferSize implements LE Read Buffer Size (0x08|0x0002) [Vol 2, Part E, 7.8.2]
+type LEReadBufferSize struct {
+}
+
+func (c *LEReadBufferSize) String() string {
+ return "LE Read Buffer Size (0x08|0x0002)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEReadBufferSize) OpCode() int { return 0x08<<10 | 0x0002 }
+
+// Len returns the length of the command.
+func (c *LEReadBufferSize) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEReadBufferSize) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEReadBufferSizeRP returns the return parameter of LE Read Buffer Size
+type LEReadBufferSizeRP struct {
+ Status uint8
+ HCLEDataPacketLength uint16
+ HCTotalNumLEDataPackets uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEReadBufferSizeRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEReadLocalSupportedFeatures implements LE Read Local Supported Features (0x08|0x0003) [Vol 2, Part E, 7.8.3]
+type LEReadLocalSupportedFeatures struct {
+}
+
+func (c *LEReadLocalSupportedFeatures) String() string {
+ return "LE Read Local Supported Features (0x08|0x0003)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEReadLocalSupportedFeatures) OpCode() int { return 0x08<<10 | 0x0003 }
+
+// Len returns the length of the command.
+func (c *LEReadLocalSupportedFeatures) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEReadLocalSupportedFeatures) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEReadLocalSupportedFeaturesRP returns the return parameter of LE Read Local Supported Features
+type LEReadLocalSupportedFeaturesRP struct {
+ Status uint8
+ LEFeatures uint64
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEReadLocalSupportedFeaturesRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LESetRandomAddress implements LE Set Random Address (0x08|0x0005) [Vol 2, Part E, 7.8.4]
+type LESetRandomAddress struct {
+ RandomAddress [6]byte
+}
+
+func (c *LESetRandomAddress) String() string {
+ return "LE Set Random Address (0x08|0x0005)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetRandomAddress) OpCode() int { return 0x08<<10 | 0x0005 }
+
+// Len returns the length of the command.
+func (c *LESetRandomAddress) Len() int { return 6 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetRandomAddress) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetRandomAddressRP returns the return parameter of LE Set Random Address
+type LESetRandomAddressRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetRandomAddressRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LESetAdvertisingParameters implements LE Set Advertising Parameters (0x08|0x0006) [Vol 2, Part E, 7.8.5]
+type LESetAdvertisingParameters struct {
+ AdvertisingIntervalMin uint16
+ AdvertisingIntervalMax uint16
+ AdvertisingType uint8
+ OwnAddressType uint8
+ DirectAddressType uint8
+ DirectAddress [6]byte
+ AdvertisingChannelMap uint8
+ AdvertisingFilterPolicy uint8
+}
+
+func (c *LESetAdvertisingParameters) String() string {
+ return "LE Set Advertising Parameters (0x08|0x0006)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetAdvertisingParameters) OpCode() int { return 0x08<<10 | 0x0006 }
+
+// Len returns the length of the command.
+func (c *LESetAdvertisingParameters) Len() int { return 15 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetAdvertisingParameters) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetAdvertisingParametersRP returns the return parameter of LE Set Advertising Parameters
+type LESetAdvertisingParametersRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetAdvertisingParametersRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEReadAdvertisingChannelTxPower implements LE Read Advertising Channel Tx Power (0x08|0x0007) [Vol 2, Part E, 7.8.6]
+type LEReadAdvertisingChannelTxPower struct {
+}
+
+func (c *LEReadAdvertisingChannelTxPower) String() string {
+ return "LE Read Advertising Channel Tx Power (0x08|0x0007)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEReadAdvertisingChannelTxPower) OpCode() int { return 0x08<<10 | 0x0007 }
+
+// Len returns the length of the command.
+func (c *LEReadAdvertisingChannelTxPower) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEReadAdvertisingChannelTxPower) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEReadAdvertisingChannelTxPowerRP returns the return parameter of LE Read Advertising Channel Tx Power
+type LEReadAdvertisingChannelTxPowerRP struct {
+ Status uint8
+ TransmitPowerLevel uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEReadAdvertisingChannelTxPowerRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LESetAdvertisingData implements LE Set Advertising Data (0x08|0x0008) [Vol 2, Part E, 7.8.7]
+type LESetAdvertisingData struct {
+ AdvertisingDataLength uint8
+ AdvertisingData [31]byte
+}
+
+func (c *LESetAdvertisingData) String() string {
+ return "LE Set Advertising Data (0x08|0x0008)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetAdvertisingData) OpCode() int { return 0x08<<10 | 0x0008 }
+
+// Len returns the length of the command.
+func (c *LESetAdvertisingData) Len() int { return 32 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetAdvertisingData) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetAdvertisingDataRP returns the return parameter of LE Set Advertising Data
+type LESetAdvertisingDataRP struct {
+ Status uint8
+ HCLEDataPacketLength uint16
+ HCTotalNumLEDataPackets uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetAdvertisingDataRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LESetScanResponseData implements LE Set Scan Response Data (0x08|0x0009) [Vol 2, Part E, 7.8.8]
+type LESetScanResponseData struct {
+ ScanResponseDataLength uint8
+ ScanResponseData [31]byte
+}
+
+func (c *LESetScanResponseData) String() string {
+ return "LE Set Scan Response Data (0x08|0x0009)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetScanResponseData) OpCode() int { return 0x08<<10 | 0x0009 }
+
+// Len returns the length of the command.
+func (c *LESetScanResponseData) Len() int { return 32 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetScanResponseData) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetScanResponseDataRP returns the return parameter of LE Set Scan Response Data
+type LESetScanResponseDataRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetScanResponseDataRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LESetAdvertiseEnable implements LE Set Advertise Enable (0x08|0x000A) [Vol 2, Part E, 7.8.9]
+type LESetAdvertiseEnable struct {
+ AdvertisingEnable uint8
+}
+
+func (c *LESetAdvertiseEnable) String() string {
+ return "LE Set Advertise Enable (0x08|0x000A)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetAdvertiseEnable) OpCode() int { return 0x08<<10 | 0x000A }
+
+// Len returns the length of the command.
+func (c *LESetAdvertiseEnable) Len() int { return 1 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetAdvertiseEnable) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetAdvertiseEnableRP returns the return parameter of LE Set Advertise Enable
+type LESetAdvertiseEnableRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetAdvertiseEnableRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LESetScanParameters implements LE Set Scan Parameters (0x08|0x000B) [Vol 2, Part E, 7.8.10]
+type LESetScanParameters struct {
+ LEScanType uint8
+ LEScanInterval uint16
+ LEScanWindow uint16
+ OwnAddressType uint8
+ ScanningFilterPolicy uint8
+}
+
+func (c *LESetScanParameters) String() string {
+ return "LE Set Scan Parameters (0x08|0x000B)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetScanParameters) OpCode() int { return 0x08<<10 | 0x000B }
+
+// Len returns the length of the command.
+func (c *LESetScanParameters) Len() int { return 7 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetScanParameters) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetScanParametersRP returns the return parameter of LE Set Scan Parameters
+type LESetScanParametersRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetScanParametersRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LESetScanEnable implements LE Set Scan Enable (0x08|0x000C) [Vol 2, Part E, 7.8.11]
+type LESetScanEnable struct {
+ LEScanEnable uint8
+ FilterDuplicates uint8
+}
+
+func (c *LESetScanEnable) String() string {
+ return "LE Set Scan Enable (0x08|0x000C)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetScanEnable) OpCode() int { return 0x08<<10 | 0x000C }
+
+// Len returns the length of the command.
+func (c *LESetScanEnable) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetScanEnable) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetScanEnableRP returns the return parameter of LE Set Scan Enable
+type LESetScanEnableRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetScanEnableRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LECreateConnection implements LE Create Connection (0x08|0x000D) [Vol 2, Part E, 7.8.12]
+type LECreateConnection struct {
+ LEScanInterval uint16
+ LEScanWindow uint16
+ InitiatorFilterPolicy uint8
+ PeerAddressType uint8
+ PeerAddress [6]byte
+ OwnAddressType uint8
+ ConnIntervalMin uint16
+ ConnIntervalMax uint16
+ ConnLatency uint16
+ SupervisionTimeout uint16
+ MinimumCELength uint16
+ MaximumCELength uint16
+}
+
+func (c *LECreateConnection) String() string {
+ return "LE Create Connection (0x08|0x000D)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LECreateConnection) OpCode() int { return 0x08<<10 | 0x000D }
+
+// Len returns the length of the command.
+func (c *LECreateConnection) Len() int { return 25 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LECreateConnection) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LECreateConnectionCancel implements LE Create Connection Cancel (0x08|0x000E) [Vol 2, Part E, 7.8.13]
+type LECreateConnectionCancel struct {
+}
+
+func (c *LECreateConnectionCancel) String() string {
+ return "LE Create Connection Cancel (0x08|0x000E)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LECreateConnectionCancel) OpCode() int { return 0x08<<10 | 0x000E }
+
+// Len returns the length of the command.
+func (c *LECreateConnectionCancel) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LECreateConnectionCancel) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LECreateConnectionCancelRP returns the return parameter of LE Create Connection Cancel
+type LECreateConnectionCancelRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LECreateConnectionCancelRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEReadWhiteListSize implements LE Read White List Size (0x08|0x000F) [Vol 2, Part E, 7.8.14]
+type LEReadWhiteListSize struct {
+}
+
+func (c *LEReadWhiteListSize) String() string {
+ return "LE Read White List Size (0x08|0x000F)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEReadWhiteListSize) OpCode() int { return 0x08<<10 | 0x000F }
+
+// Len returns the length of the command.
+func (c *LEReadWhiteListSize) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEReadWhiteListSize) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEReadWhiteListSizeRP returns the return parameter of LE Read White List Size
+type LEReadWhiteListSizeRP struct {
+ Status uint8
+ WhiteListSize uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEReadWhiteListSizeRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEClearWhiteList implements LE Clear White List (0x08|0x0010) [Vol 2, Part E, 7.8.15]
+type LEClearWhiteList struct {
+}
+
+func (c *LEClearWhiteList) String() string {
+ return "LE Clear White List (0x08|0x0010)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEClearWhiteList) OpCode() int { return 0x08<<10 | 0x0010 }
+
+// Len returns the length of the command.
+func (c *LEClearWhiteList) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEClearWhiteList) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEClearWhiteListRP returns the return parameter of LE Clear White List
+type LEClearWhiteListRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEClearWhiteListRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEAddDeviceToWhiteList implements LE Add Device To White List (0x08|0x0011) [Vol 2, Part E, 7.8.16]
+type LEAddDeviceToWhiteList struct {
+ AddressType uint8
+ Address [6]byte
+}
+
+func (c *LEAddDeviceToWhiteList) String() string {
+ return "LE Add Device To White List (0x08|0x0011)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEAddDeviceToWhiteList) OpCode() int { return 0x08<<10 | 0x0011 }
+
+// Len returns the length of the command.
+func (c *LEAddDeviceToWhiteList) Len() int { return 7 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEAddDeviceToWhiteList) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEAddDeviceToWhiteListRP returns the return parameter of LE Add Device To White List
+type LEAddDeviceToWhiteListRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEAddDeviceToWhiteListRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LERemoveDeviceFromWhiteList implements LE Remove Device From White List (0x08|0x0012) [Vol 2, Part E, 7.8.17]
+type LERemoveDeviceFromWhiteList struct {
+ AddressType uint8
+ Address [6]byte
+}
+
+func (c *LERemoveDeviceFromWhiteList) String() string {
+ return "LE Remove Device From White List (0x08|0x0012)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LERemoveDeviceFromWhiteList) OpCode() int { return 0x08<<10 | 0x0012 }
+
+// Len returns the length of the command.
+func (c *LERemoveDeviceFromWhiteList) Len() int { return 7 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LERemoveDeviceFromWhiteList) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LERemoveDeviceFromWhiteListRP returns the return parameter of LE Remove Device From White List
+type LERemoveDeviceFromWhiteListRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LERemoveDeviceFromWhiteListRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEConnectionUpdate implements LE Connection Update (0x08|0x0013) [Vol 2, Part E, 7.8.18]
+type LEConnectionUpdate struct {
+ ConnectionHandle uint16
+ ConnIntervalMin uint16
+ ConnIntervalMax uint16
+ ConnLatency uint16
+ SupervisionTimeout uint16
+ MinimumCELength uint16
+ MaximumCELength uint16
+}
+
+func (c *LEConnectionUpdate) String() string {
+ return "LE Connection Update (0x08|0x0013)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEConnectionUpdate) OpCode() int { return 0x08<<10 | 0x0013 }
+
+// Len returns the length of the command.
+func (c *LEConnectionUpdate) Len() int { return 14 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEConnectionUpdate) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetHostChannelClassification implements LE Set Host Channel Classification (0x08|0x0014) [Vol 2, Part E, 7.8.19]
+type LESetHostChannelClassification struct {
+ ChannelMap [5]byte
+}
+
+func (c *LESetHostChannelClassification) String() string {
+ return "LE Set Host Channel Classification (0x08|0x0014)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LESetHostChannelClassification) OpCode() int { return 0x08<<10 | 0x0014 }
+
+// Len returns the length of the command.
+func (c *LESetHostChannelClassification) Len() int { return 5 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LESetHostChannelClassification) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LESetHostChannelClassificationRP returns the return parameter of LE Set Host Channel Classification
+type LESetHostChannelClassificationRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LESetHostChannelClassificationRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEReadChannelMap implements LE Read Channel Map (0x08|0x0015) [Vol 2, Part E, 7.8.20]
+type LEReadChannelMap struct {
+ ConnectionHandle uint16
+}
+
+func (c *LEReadChannelMap) String() string {
+ return "LE Read Channel Map (0x08|0x0015)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEReadChannelMap) OpCode() int { return 0x08<<10 | 0x0015 }
+
+// Len returns the length of the command.
+func (c *LEReadChannelMap) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEReadChannelMap) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEReadChannelMapRP returns the return parameter of LE Read Channel Map
+type LEReadChannelMapRP struct {
+ Status uint8
+ ConnectionHandle uint16
+ ChannelMap [5]byte
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEReadChannelMapRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEReadRemoteUsedFeatures implements LE Read Remote Used Features (0x08|0x0016) [Vol 2, Part E, 7.8.21]
+type LEReadRemoteUsedFeatures struct {
+ ConnectionHandle uint16
+}
+
+func (c *LEReadRemoteUsedFeatures) String() string {
+ return "LE Read Remote Used Features (0x08|0x0016)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEReadRemoteUsedFeatures) OpCode() int { return 0x08<<10 | 0x0016 }
+
+// Len returns the length of the command.
+func (c *LEReadRemoteUsedFeatures) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEReadRemoteUsedFeatures) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEEncrypt implements LE Encrypt (0x08|0x0017) [Vol 2, Part E, 7.8.22]
+type LEEncrypt struct {
+ Key [16]byte
+ PlaintextData [16]byte
+}
+
+func (c *LEEncrypt) String() string {
+ return "LE Encrypt (0x08|0x0017)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEEncrypt) OpCode() int { return 0x08<<10 | 0x0017 }
+
+// Len returns the length of the command.
+func (c *LEEncrypt) Len() int { return 32 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEEncrypt) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEEncryptRP returns the return parameter of LE Encrypt
+type LEEncryptRP struct {
+ Status uint8
+ EncryptedData [16]byte
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEEncryptRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LERand implements LE Rand (0x08|0x0018) [Vol 2, Part E, 7.8.23]
+type LERand struct {
+}
+
+func (c *LERand) String() string {
+ return "LE Rand (0x08|0x0018)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LERand) OpCode() int { return 0x08<<10 | 0x0018 }
+
+// Len returns the length of the command.
+func (c *LERand) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LERand) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LERandRP returns the return parameter of LE Rand
+type LERandRP struct {
+ Status uint8
+ RandomNumber uint64
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LERandRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEStartEncryption implements LE Start Encryption (0x08|0x0019) [Vol 2, Part E, 7.8.24]
+type LEStartEncryption struct {
+ ConnectionHandle uint16
+ RandomNumber uint64
+ EncryptedDiversifier uint16
+ LongTermKey [16]byte
+}
+
+func (c *LEStartEncryption) String() string {
+ return "LE Start Encryption (0x08|0x0019)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEStartEncryption) OpCode() int { return 0x08<<10 | 0x0019 }
+
+// Len returns the length of the command.
+func (c *LEStartEncryption) Len() int { return 28 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEStartEncryption) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LELongTermKeyRequestReply implements LE Long Term Key Request Reply (0x08|0x001A) [Vol 2, Part E, 7.8.25]
+type LELongTermKeyRequestReply struct {
+ ConnectionHandle uint16
+ LongTermKey [16]byte
+}
+
+func (c *LELongTermKeyRequestReply) String() string {
+ return "LE Long Term Key Request Reply (0x08|0x001A)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LELongTermKeyRequestReply) OpCode() int { return 0x08<<10 | 0x001A }
+
+// Len returns the length of the command.
+func (c *LELongTermKeyRequestReply) Len() int { return 18 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LELongTermKeyRequestReply) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LELongTermKeyRequestReplyRP returns the return parameter of LE Long Term Key Request Reply
+type LELongTermKeyRequestReplyRP struct {
+ Status uint8
+ ConnectionHandle uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LELongTermKeyRequestReplyRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LELongTermKeyRequestNegativeReply implements LE Long Term Key Request Negative Reply (0x08|0x001B) [Vol 2, Part E, 7.8.26]
+type LELongTermKeyRequestNegativeReply struct {
+ ConnectionHandle uint16
+}
+
+func (c *LELongTermKeyRequestNegativeReply) String() string {
+ return "LE Long Term Key Request Negative Reply (0x08|0x001B)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LELongTermKeyRequestNegativeReply) OpCode() int { return 0x08<<10 | 0x001B }
+
+// Len returns the length of the command.
+func (c *LELongTermKeyRequestNegativeReply) Len() int { return 2 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LELongTermKeyRequestNegativeReply) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LELongTermKeyRequestNegativeReplyRP returns the return parameter of LE Long Term Key Request Negative Reply
+type LELongTermKeyRequestNegativeReplyRP struct {
+ Status uint8
+ ConnectionHandle uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LELongTermKeyRequestNegativeReplyRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEReadSupportedStates implements LE Read Supported States (0x08|0x001C) [Vol 2, Part E, 7.8.27]
+type LEReadSupportedStates struct {
+}
+
+func (c *LEReadSupportedStates) String() string {
+ return "LE Read Supported States (0x08|0x001C)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEReadSupportedStates) OpCode() int { return 0x08<<10 | 0x001C }
+
+// Len returns the length of the command.
+func (c *LEReadSupportedStates) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEReadSupportedStates) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEReadSupportedStatesRP returns the return parameter of LE Read Supported States
+type LEReadSupportedStatesRP struct {
+ Status uint8
+ LEStates uint64
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEReadSupportedStatesRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LEReceiverTest implements LE Receiver Test (0x08|0x001D) [Vol 2, Part E, 7.8.28]
+type LEReceiverTest struct {
+ RXChannel uint8
+}
+
+func (c *LEReceiverTest) String() string {
+ return "LE Receiver Test (0x08|0x001D)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LEReceiverTest) OpCode() int { return 0x08<<10 | 0x001D }
+
+// Len returns the length of the command.
+func (c *LEReceiverTest) Len() int { return 1 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LEReceiverTest) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LEReceiverTestRP returns the return parameter of LE Receiver Test
+type LEReceiverTestRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LEReceiverTestRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LETransmitterTest implements LE Transmitter Test (0x08|0x001E) [Vol 2, Part E, 7.8.29]
+type LETransmitterTest struct {
+ TXChannel uint8
+ LengthOfTestData uint8
+ PacketPayload uint8
+}
+
+func (c *LETransmitterTest) String() string {
+ return "LE Transmitter Test (0x08|0x001E)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LETransmitterTest) OpCode() int { return 0x08<<10 | 0x001E }
+
+// Len returns the length of the command.
+func (c *LETransmitterTest) Len() int { return 3 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LETransmitterTest) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LETransmitterTestRP returns the return parameter of LE Transmitter Test
+type LETransmitterTestRP struct {
+ Status uint8
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LETransmitterTestRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LETestEnd implements LE Test End (0x08|0x001F) [Vol 2, Part E, 7.8.30]
+type LETestEnd struct {
+}
+
+func (c *LETestEnd) String() string {
+ return "LE Test End (0x08|0x001F)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LETestEnd) OpCode() int { return 0x08<<10 | 0x001F }
+
+// Len returns the length of the command.
+func (c *LETestEnd) Len() int { return 0 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LETestEnd) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LETestEndRP returns the return parameter of LE Test End
+type LETestEndRP struct {
+ Status uint8
+ NumberOfPackats uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LETestEndRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LERemoteConnectionParameterRequestReply implements LE Remote Connection Parameter Request Reply (0x08|0x0020) [Vol 2, Part E, 7.8.31]
+type LERemoteConnectionParameterRequestReply struct {
+ ConnectionHandle uint16
+ IntervalMin uint16
+ IntervalMax uint16
+ Latency uint16
+ Timeout uint16
+ MinimumCELength uint16
+ MaximumCELength uint16
+}
+
+func (c *LERemoteConnectionParameterRequestReply) String() string {
+ return "LE Remote Connection Parameter Request Reply (0x08|0x0020)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LERemoteConnectionParameterRequestReply) OpCode() int { return 0x08<<10 | 0x0020 }
+
+// Len returns the length of the command.
+func (c *LERemoteConnectionParameterRequestReply) Len() int { return 14 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LERemoteConnectionParameterRequestReply) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LERemoteConnectionParameterRequestReplyRP returns the return parameter of LE Remote Connection Parameter Request Reply
+type LERemoteConnectionParameterRequestReplyRP struct {
+ Status uint8
+ ConnectionHandle uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LERemoteConnectionParameterRequestReplyRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
+
+// LERemoteConnectionParameterRequestNegativeReply implements LE Remote Connection Parameter Request Negative Reply (0x08|0x0021) [Vol 2, Part E, 7.8.32]
+type LERemoteConnectionParameterRequestNegativeReply struct {
+ ConnectionHandle uint16
+ Reason uint8
+}
+
+func (c *LERemoteConnectionParameterRequestNegativeReply) String() string {
+ return "LE Remote Connection Parameter Request Negative Reply (0x08|0x0021)"
+}
+
+// OpCode returns the opcode of the command.
+func (c *LERemoteConnectionParameterRequestNegativeReply) OpCode() int { return 0x08<<10 | 0x0021 }
+
+// Len returns the length of the command.
+func (c *LERemoteConnectionParameterRequestNegativeReply) Len() int { return 3 }
+
+// Marshal serializes the command parameters into binary form.
+func (c *LERemoteConnectionParameterRequestNegativeReply) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+
+// LERemoteConnectionParameterRequestNegativeReplyRP returns the return parameter of LE Remote Connection Parameter Request Negative Reply
+type LERemoteConnectionParameterRequestNegativeReplyRP struct {
+ Status uint8
+ ConnectionHandle uint16
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *LERemoteConnectionParameterRequestNegativeReplyRP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/conn.go b/vendor/github.com/currantlabs/ble/linux/hci/conn.go
new file mode 100644
index 0000000..af7d652
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/conn.go
@@ -0,0 +1,338 @@
+package hci
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/linux/hci/cmd"
+ "github.com/currantlabs/ble/linux/hci/evt"
+ "github.com/pkg/errors"
+)
+
+// Conn ...
+type Conn struct {
+ hci *HCI
+ ctx context.Context
+
+ param evt.LEConnectionComplete
+
+ // While MTU is the maximum size of payload data that the upper layer (ATT)
+ // can accept, the MPS is the maximum PDU payload size this L2CAP implementation
+ // supports. When segmantation is not used, the MPS should be made to the same
+ // values of MTUs [Vol 3, Part A, 1.4].
+ //
+ // For LE-U logical transport, the L2CAP implementations should support
+ // a minimum of 23 bytes, which are also the default values before the
+ // upper layer (ATT) optionally reconfigures them [Vol 3, Part A, 3.2.8].
+ rxMTU int
+ txMTU int
+ rxMPS int
+
+ // leFrame is set to be true when the LE Credit based flow control is used.
+ leFrame bool
+
+ // Signaling MTUs are The maximum size of command information that the
+ // L2CAP layer entity is capable of accepting.
+ // A L2CAP implementations supporting LE-U should support at least 23 bytes.
+ // Currently, we support 512 bytes, which should be more than sufficient.
+ // The sigTxMTU is discovered via when we sent a signaling pkt that is
+ // larger thean the remote device can handle, and get a response of "Command
+ // Reject" indicating "Signaling MTU exceeded" along with the actual
+ // signaling MTU [Vol 3, Part A, 4.1].
+ sigRxMTU int
+ sigTxMTU int
+
+ // sigID is used to match responses with signaling requests.
+ // The requesting device sets this field and the responding device uses the
+ // same value in its response. Within each signalling channel a different
+ // Identifier shall be used for each successive command. [Vol 3, Part A, 4]
+ sigID uint8
+
+ sigSent chan []byte
+ smpSent chan []byte
+
+ chInPkt chan packet
+ chInPDU chan pdu
+
+ // Host to Controller Data Flow Control pkt-based Data flow control for LE-U [Vol 2, Part E, 4.1.1]
+ // chSentBufs tracks the HCI buffer occupied by this connection.
+ txBuffer *Client
+
+ chDone chan struct{}
+}
+
+func newConn(h *HCI, param evt.LEConnectionComplete) *Conn {
+ c := &Conn{
+ hci: h,
+ ctx: context.Background(),
+ param: param,
+
+ rxMTU: ble.DefaultMTU,
+ txMTU: ble.DefaultMTU,
+
+ rxMPS: ble.DefaultMTU,
+
+ sigRxMTU: ble.MaxMTU,
+ sigTxMTU: ble.DefaultMTU,
+
+ chInPkt: make(chan packet, 16),
+ chInPDU: make(chan pdu, 16),
+
+ txBuffer: NewClient(h.pool),
+
+ chDone: make(chan struct{}),
+ }
+
+ go func() {
+ for {
+ if err := c.recombine(); err != nil {
+ if err != io.EOF {
+ // TODO: wrap and pass the error up.
+ // err := errors.Wrap(err, "recombine failed")
+ logger.Error("recombine failed: ", "err", err)
+ }
+ close(c.chInPDU)
+ return
+ }
+ }
+ }()
+ return c
+}
+
+// Context returns the context that is used by this Conn.
+func (c *Conn) Context() context.Context {
+ return c.ctx
+}
+
+// SetContext sets the context that is used by this Conn.
+func (c *Conn) SetContext(ctx context.Context) {
+ c.ctx = ctx
+}
+
+// Read copies re-assembled L2CAP PDUs into sdu.
+func (c *Conn) Read(sdu []byte) (n int, err error) {
+ p, ok := <-c.chInPDU
+ if !ok {
+ return 0, errors.Wrap(io.ErrClosedPipe, "input channel closed")
+ }
+ if len(p) == 0 {
+ return 0, errors.Wrap(io.ErrUnexpectedEOF, "recieved empty packet")
+ }
+
+ // Assume it's a B-Frame.
+ slen := p.dlen()
+ data := p.payload()
+ if c.leFrame {
+ // LE-Frame.
+ slen = leFrameHdr(p).slen()
+ data = leFrameHdr(p).payload()
+ }
+ if cap(sdu) < slen {
+ return 0, errors.Wrapf(io.ErrShortBuffer, "payload recieved exceeds sdu buffer")
+ }
+ buf := bytes.NewBuffer(sdu)
+ buf.Reset()
+ buf.Write(data)
+ for buf.Len() < slen {
+ p := <-c.chInPDU
+ buf.Write(pdu(p).payload())
+ }
+ return slen, nil
+}
+
+// Write breaks down a L2CAP SDU into segmants [Vol 3, Part A, 7.3.1]
+func (c *Conn) Write(sdu []byte) (int, error) {
+ if len(sdu) > c.txMTU {
+ return 0, errors.Wrap(io.ErrShortWrite, "payload exceeds mtu")
+ }
+
+ plen := len(sdu)
+ if plen > c.txMTU {
+ plen = c.txMTU
+ }
+ b := make([]byte, 4+plen)
+ binary.LittleEndian.PutUint16(b[0:2], uint16(len(sdu)))
+ binary.LittleEndian.PutUint16(b[2:4], cidLEAtt)
+ if c.leFrame {
+ binary.LittleEndian.PutUint16(b[4:6], uint16(len(sdu)))
+ copy(b[6:], sdu)
+ } else {
+ copy(b[4:], sdu)
+ }
+ sent, err := c.writePDU(b)
+ if err != nil {
+ return sent, err
+ }
+ sdu = sdu[plen:]
+
+ for len(sdu) > 0 {
+ plen := len(sdu)
+ if plen > c.txMTU {
+ plen = c.txMTU
+ }
+ n, err := c.writePDU(sdu[:plen])
+ sent += n
+ if err != nil {
+ return sent, err
+ }
+ sdu = sdu[plen:]
+ }
+ return sent, nil
+}
+
+// writePDU breaks down a L2CAP PDU into fragments if it's larger than the HCI buffer size. [Vol 3, Part A, 7.2.1]
+func (c *Conn) writePDU(pdu []byte) (int, error) {
+ sent := 0
+ flags := uint16(pbfHostToControllerStart << 4) // ACL boundary flags
+
+ // All L2CAP fragments associated with an L2CAP PDU shall be processed for
+ // transmission by the Controller before any other L2CAP PDU for the same
+ // logical transport shall be processed.
+ c.txBuffer.LockPool()
+ defer c.txBuffer.UnlockPool()
+
+ for len(pdu) > 0 {
+ // Get a buffer from our pre-allocated and flow-controlled pool.
+ pkt := c.txBuffer.Get() // ACL pkt
+ flen := len(pdu) // fragment length
+ if flen > pkt.Cap()-1-4 {
+ flen = pkt.Cap() - 1 - 4
+ }
+
+ // Prepare the Headers
+ binary.Write(pkt, binary.LittleEndian, uint8(pktTypeACLData)) // HCI Header: pkt Type
+ binary.Write(pkt, binary.LittleEndian, uint16(c.param.ConnectionHandle()|(flags<<8))) // ACL Header: handle and flags
+ binary.Write(pkt, binary.LittleEndian, uint16(flen)) // ACL Header: data len
+ binary.Write(pkt, binary.LittleEndian, pdu[:flen]) // Append payload
+
+ // Flush the pkt to HCI
+ select {
+ case <-c.chDone:
+ return 0, io.ErrClosedPipe
+ default:
+ }
+
+ if _, err := c.hci.skt.Write(pkt.Bytes()); err != nil {
+ return sent, err
+ }
+ sent += flen
+
+ flags = (pbfContinuing << 4) // Set "continuing" in the boundary flags for the rest of fragments, if any.
+ pdu = pdu[flen:] // Advence the point
+ }
+ return sent, nil
+}
+
+// Recombines fragments into a L2CAP PDU. [Vol 3, Part A, 7.2.2]
+func (c *Conn) recombine() error {
+ pkt, ok := <-c.chInPkt
+ if !ok {
+ return io.EOF
+ }
+
+ p := pdu(pkt.data())
+
+ // Currently, check for LE-U only. For channels that we don't recognizes,
+ // re-combine them anyway, and discard them later when we dispatch the PDU
+ // according to CID.
+ if p.cid() == cidLEAtt && p.dlen() > c.rxMPS {
+ return fmt.Errorf("fragment size (%d) larger than rxMPS (%d)", p.dlen(), c.rxMPS)
+ }
+
+ // If this pkt is not a complete PDU, and we'll be receiving more
+ // fragments, re-allocate the whole PDU (including Header).
+ if len(p.payload()) < p.dlen() {
+ p = make([]byte, 0, 4+p.dlen())
+ p = append(p, pdu(pkt.data())...)
+ }
+ for len(p) < 4+p.dlen() {
+ if pkt, ok = <-c.chInPkt; !ok || (pkt.pbf()&pbfContinuing) == 0 {
+ return io.ErrUnexpectedEOF
+ }
+ p = append(p, pdu(pkt.data())...)
+ }
+
+ // TODO: support dynamic or assigned channels for LE-Frames.
+ switch p.cid() {
+ case cidLEAtt:
+ c.chInPDU <- p
+ case cidLESignal:
+ c.handleSignal(p)
+ case cidSMP:
+ c.handleSMP(p)
+ default:
+ logger.Info("recombine()", "unrecognized CID", fmt.Sprintf("%04X, [%X]", p.cid(), p))
+ }
+ return nil
+}
+
+// Disconnected returns a receiving channel, which is closed when the connection disconnects.
+func (c *Conn) Disconnected() <-chan struct{} {
+ return c.chDone
+}
+
+// Close disconnects the connection by sending hci disconnect command to the device.
+func (c *Conn) Close() error {
+ select {
+ case <-c.chDone:
+ // Return if it's already closed.
+ return nil
+ default:
+ c.hci.Send(&cmd.Disconnect{
+ ConnectionHandle: c.param.ConnectionHandle(),
+ Reason: 0x13,
+ }, nil)
+ return nil
+ }
+}
+
+// LocalAddr returns local device's MAC address.
+func (c *Conn) LocalAddr() ble.Addr { return c.hci.Addr() }
+
+// RemoteAddr returns remote device's MAC address.
+func (c *Conn) RemoteAddr() ble.Addr {
+ a := c.param.PeerAddress()
+ return net.HardwareAddr([]byte{a[5], a[4], a[3], a[2], a[1], a[0]})
+}
+
+// RxMTU returns the MTU which the upper layer is capable of accepting.
+func (c *Conn) RxMTU() int { return c.rxMTU }
+
+// SetRxMTU sets the MTU which the upper layer is capable of accepting.
+func (c *Conn) SetRxMTU(mtu int) { c.rxMTU, c.rxMPS = mtu, mtu }
+
+// TxMTU returns the MTU which the remote device is capable of accepting.
+func (c *Conn) TxMTU() int { return c.txMTU }
+
+// SetTxMTU sets the MTU which the remote device is capable of accepting.
+func (c *Conn) SetTxMTU(mtu int) { c.txMTU = mtu }
+
+// pkt implements HCI ACL Data Packet [Vol 2, Part E, 5.4.2]
+// Packet boundary flags , bit[5:6] of handle field's MSB
+// Broadcast flags. bit[7:8] of handle field's MSB
+// Not used in LE-U. Leave it as 0x00 (Point-to-Point).
+// Broadcasting in LE uses ADVB logical transport.
+type packet []byte
+
+func (a packet) handle() uint16 { return uint16(a[0]) | (uint16(a[1]&0x0f) << 8) }
+func (a packet) pbf() int { return (int(a[1]) >> 4) & 0x3 }
+func (a packet) bcf() int { return (int(a[1]) >> 6) & 0x3 }
+func (a packet) dlen() int { return int(a[2]) | (int(a[3]) << 8) }
+func (a packet) data() []byte { return a[4:] }
+
+type pdu []byte
+
+func (p pdu) dlen() int { return int(binary.LittleEndian.Uint16(p[0:2])) }
+func (p pdu) cid() uint16 { return binary.LittleEndian.Uint16(p[2:4]) }
+func (p pdu) payload() []byte { return p[4:] }
+
+type leFrameHdr pdu
+
+func (f leFrameHdr) slen() int { return int(binary.LittleEndian.Uint16(f[4:6])) }
+func (f leFrameHdr) payload() []byte { return f[6:] }
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/const.go b/vendor/github.com/currantlabs/ble/linux/hci/const.go
new file mode 100644
index 0000000..bf1781b
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/const.go
@@ -0,0 +1,30 @@
+package hci
+
+// HCI Packet types
+const (
+ pktTypeCommand uint8 = 0x01
+ pktTypeACLData uint8 = 0x02
+ pktTypeSCOData uint8 = 0x03
+ pktTypeEvent uint8 = 0x04
+ pktTypeVendor uint8 = 0xFF
+)
+
+// Packet boundary flags of HCI ACL Data Packet [Vol 2, Part E, 5.4.2].
+const (
+ pbfHostToControllerStart = 0x00 // Start of a non-automatically-flushable from host to controller.
+ pbfContinuing = 0x01 // Continuing fragment.
+ pbfControllerToHostStart = 0x02 // Start of a non-automatically-flushable from controller to host.
+ pbfCompleteL2CAPPDU = 0x03 // A automatically flushable complete PDU. (Not used in LE-U).
+)
+
+// L2CAP Channel Identifier namespace for LE-U logical link [Vol 3, Part A, 2.1].
+const (
+ cidLEAtt uint16 = 0x04 // Attribute Protocol [Vol 3, Part F].
+ cidLESignal uint16 = 0x05 // Low Energy L2CAP Signaling channel [Vol 3, Part A, 4].
+ cidSMP uint16 = 0x06 // SecurityManager Protocol [Vol 3, Part H].
+)
+
+const (
+ roleMaster = 0x00
+ roleSlave = 0x01
+)
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/error.go b/vendor/github.com/currantlabs/ble/linux/hci/error.go
new file mode 100644
index 0000000..0a23b6a
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/error.go
@@ -0,0 +1,161 @@
+package hci
+
+import "errors"
+
+// errors
+var (
+ ErrBusyScanning = errors.New("busy scanning")
+ ErrBusyAdvertising = errors.New("busy advertising")
+ ErrBusyDialing = errors.New("busy dialing")
+ ErrBusyListening = errors.New("busy listening")
+ ErrInvalidAddr = errors.New("invalid address")
+)
+
+// HCI Command Errors [Vol2, Part D, 1.3 ]
+// FIXME: Terrible shorthand. Name them properly.
+const (
+ ErrUnknownCommand ErrCommand = 0x01 // Unknown HCI Command
+ ErrConnID ErrCommand = 0x02 // Unknown Connection Identifier
+ ErrHardware ErrCommand = 0x03 // Hardware Failure
+ ErrPageTimeout ErrCommand = 0x04 // Page Timeout
+ ErrAuth ErrCommand = 0x05 // Authentication Failure
+ ErrPINMissing ErrCommand = 0x06 // PIN or Key Missing
+ ErrMemoryCapacity ErrCommand = 0x07 // Memory Capacity Exceeded
+ ErrConnTimeout ErrCommand = 0x08 // Connection Timeout
+ ErrConnLimit ErrCommand = 0x09 // Connection Limit Exceeded
+ ErrSCOConnLimit ErrCommand = 0x0A // Synchronous Connection Limit To A Device Exceeded
+ ErrACLConnExists ErrCommand = 0x0B // ACL Connection Already Exists
+ ErrDisallowed ErrCommand = 0x0C // Command Disallowed
+ ErrLimitedResource ErrCommand = 0x0D // Connection Rejected due to Limited Resources
+ ErrSecurity ErrCommand = 0x0E // Connection Rejected Due To Security Reasons
+ ErrBDADDR ErrCommand = 0x0F // Connection Rejected due to Unacceptable BD_ADDR
+ ErrConnAcceptTimeout ErrCommand = 0x10 // Connection Accept Timeout Exceeded
+ ErrUnsupportedParams ErrCommand = 0x11 // Unsupported Feature or Parameter Value
+ ErrInvalidParams ErrCommand = 0x12 // Invalid HCI Command Parameters
+ ErrRemoteUser ErrCommand = 0x13 // Remote User Terminated Connection
+ ErrRemoteLowResources ErrCommand = 0x14 // Remote Device Terminated Connection due to Low Resources
+ ErrRemotePowerOff ErrCommand = 0x15 // Remote Device Terminated Connection due to Power Off
+ ErrLocalHost ErrCommand = 0x16 // Connection Terminated By Local Host
+ ErrRepeatedAttempts ErrCommand = 0x17 // Repeated Attempts
+ ErrPairingNotAllowed ErrCommand = 0x18 // Pairing Not Allowed
+ ErrUnknownLMP ErrCommand = 0x19 // Unknown LMP PDU
+ ErrUnsupportedLMP ErrCommand = 0x1A // Unsupported Remote Feature / Unsupported LMP Feature
+ ErrSCOOffset ErrCommand = 0x1B // SCO Offset Rejected
+ ErrSCOInterval ErrCommand = 0x1C // SCO Interval Rejected
+ ErrSCOAirMode ErrCommand = 0x1D // SCO Air Mode Rejected
+ ErrInvalidLLParams ErrCommand = 0x1E // Invalid LMP Parameters / Invalid LL Parameters
+ ErrUnspecified ErrCommand = 0x1F // Unspecified Error
+ ErrUnsupportedLLParams ErrCommand = 0x20 // Unsupported LMP Parameter Value / Unsupported LL Parameter Value
+ ErrRoleChangeNotAllowed ErrCommand = 0x21 // Role Change Not Allowed
+ ErrLLResponseTimeout ErrCommand = 0x22 // LMP Response Timeout / LL Response Timeout
+ ErrLMPTransColl ErrCommand = 0x23 // LMP Error Transaction Collision
+ ErrLMPPDU ErrCommand = 0x24 // LMP PDU Not Allowed
+ ErrEncNotAccepted ErrCommand = 0x25 // Encryption Mode Not Acceptable
+ ErrLinkKey ErrCommand = 0x26 // Link Key cannot be Changed
+ ErrQoSNotSupported ErrCommand = 0x27 // Requested QoS Not Supported
+ ErrInstantPassed ErrCommand = 0x28 // Instant Passed
+ ErrUnitKeyNotSupported ErrCommand = 0x29 // Pairing With Unit Key Not Supported
+ ErrDifferentTransColl ErrCommand = 0x2A // Different Transaction Collision
+ ErrQOSParameter ErrCommand = 0x2C // QoS Unacceptable Parameter
+ ErrQOSReject ErrCommand = 0x2D // QoS Rejected
+ ErrChannelClass ErrCommand = 0x2E // Channel Classification Not Supported
+ ErrInsufficientSecurity ErrCommand = 0x2F // Insufficient Security
+ ErrOutOfRange ErrCommand = 0x30 // Parameter Out Of Mandatory Range
+ ErrRoleSwitchPending ErrCommand = 0x32 // Role Switch Pending
+ ErrReservedSlot ErrCommand = 0x34 // Reserved Slot Violation
+ ErrRoleSwitch ErrCommand = 0x35 // Role Switch Failed
+ ErrEIRTooLarge ErrCommand = 0x36 // Extended Inquiry Response Too Large
+ ErrSecureSimplePairing ErrCommand = 0x37 // Secure Simple Pairing Not Supported By Host
+ ErrHostBusy ErrCommand = 0x38 // Host Busy - Pairing
+ ErrNoChannel ErrCommand = 0x39 // Connection Rejected due to No Suitable Channel Found
+ ErrControllerBusy ErrCommand = 0x3A // Controller Busy
+ ErrConnParams ErrCommand = 0x3B // Unacceptable Connection Parameters
+ ErrDirAdvTimeout ErrCommand = 0x3C // Directed Advertising Timeout
+ ErrMIC ErrCommand = 0x3D // Connection Terminated due to MIC Failure
+ ErrEstablished ErrCommand = 0x3E // Connection Failed to be Established
+ ErrMACConn ErrCommand = 0x3F // MAC Connection Failed
+ ErrCoarseClock ErrCommand = 0x40 // Coarse Clock Adjustment Rejected but Will Try to Adjust Using Clock Dragging
+ // 0x2B // Reserved
+ // 0x31 // Reserved
+ // 0x33 // Reserved
+)
+
+// ErrCommand [Vol2, Part D, 1.3 ]
+type ErrCommand byte
+
+func (e ErrCommand) Error() string {
+ if s, ok := errCmd[e]; ok {
+ return s
+ }
+ // A Host shall consider any error code that it does not explicitly
+ // understand equivalent to the “Unspecified Error (0x1F).”
+ return errCmd[0x1F]
+}
+
+var errCmd = map[ErrCommand]string{
+ 0x00: "Success",
+ 0x01: "Unknown HCI Command",
+ 0x02: "Unknown Connection Identifier",
+ 0x03: "Hardware Failure",
+ 0x04: "Page Timeou",
+ 0x05: "Authentication Failure",
+ 0x06: "PIN or Key Missing",
+ 0x07: "Memory Capacity Exceeded",
+ 0x08: "Connection Timeout",
+ 0x09: "Connection Limit Exceeded",
+ 0x0A: "Synchronous Connection Limit To A Device Exceeded",
+ 0x0B: "ACL Connection Already Exists",
+ 0x0C: "Command Disallowed",
+ 0x0D: "Connection Rejected due to Limited Resources",
+ 0x0E: "Connection Rejected Due To Security Reasons",
+ 0x0F: "Connection Rejected due to Unacceptable BD_ADDR",
+ 0x10: "Connection Accept Timeout Exceeded",
+ 0x11: "Unsupported Feature or Parameter Value",
+ 0x12: "Invalid HCI Command Parameters",
+ 0x13: "Remote User Terminated Connection",
+ 0x14: "Remote Device Terminated Connection due to Low Resources",
+ 0x15: "Remote Device Terminated Connection due to Power Off",
+ 0x16: "Connection Terminated By Local Host",
+ 0x17: "Repeated Attempts",
+ 0x18: "Pairing Not Allowed",
+ 0x19: "Unknown LMP PDU",
+ 0x1A: "Unsupported Remote Feature / Unsupported LMP Feature",
+ 0x1B: "SCO Offset Rejected",
+ 0x1C: "SCO Interval Rejected",
+ 0x1D: "SCO Air Mode Rejected",
+ 0x1E: "Invalid LMP Parameters / Invalid LL Parameters",
+ 0x1F: "Unspecified Error",
+ 0x20: "Unsupported LMP Parameter Value / Unsupported LL Parameter Value",
+ 0x21: "Role Change Not Allowed",
+ 0x22: "LMP Response Timeout / LL Response Timeout",
+ 0x23: "LMP Error Transaction Collision",
+ 0x24: "LMP PDU Not Allowed",
+ 0x25: "Encryption Mode Not Acceptable",
+ 0x26: "Link Key cannot be Changed",
+ 0x27: "Requested QoS Not Supported",
+ 0x28: "Instant Passed",
+ 0x29: "Pairing With Unit Key Not Supported",
+ 0x2A: "Different Transaction Collision",
+ 0x2B: "Reserved",
+ 0x2C: "QoS Unacceptable Parameter",
+ 0x2D: "QoS Rejected",
+ 0x2E: "Channel Classification Not Supported",
+ 0x2F: "Insufficient Security",
+ 0x30: "Parameter Out Of Mandatory Range",
+ 0x31: "Reserved",
+ 0x32: "Role Switch Pending",
+ 0x33: "Reserved",
+ 0x34: "Reserved Slot Violation",
+ 0x35: "Role Switch Failed",
+ 0x36: "Extended Inquiry Response Too Large",
+ 0x37: "Secure Simple Pairing Not Supported By Host",
+ 0x38: "Host Busy - Pairing",
+ 0x39: "Connection Rejected due to No Suitable Channel Found",
+ 0x3A: "Controller Busy",
+ 0x3B: "Unacceptable Connection Parameters",
+ 0x3C: "Directed Advertising Timeout",
+ 0x3D: "Connection Terminated due to MIC Failure",
+ 0x3E: "Connection Failed to be Established",
+ 0x3F: "MAC Connection Failed",
+ 0x40: "Coarse Clock Adjustment Rejected but Will Try to Adjust Using Clock Dragging",
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/evt/evt.go b/vendor/github.com/currantlabs/ble/linux/hci/evt/evt.go
new file mode 100644
index 0000000..4ce88d5
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/evt/evt.go
@@ -0,0 +1,55 @@
+package evt
+
+import "encoding/binary"
+
+func (e CommandComplete) NumHCICommandPackets() uint8 { return e[0] }
+func (e CommandComplete) CommandOpcode() uint16 { return binary.LittleEndian.Uint16(e[1:]) }
+func (e CommandComplete) ReturnParameters() []byte { return e[3:] }
+
+// Per-spec [Vol 2, Part E, 7.7.19], the packet structure should be:
+//
+// NumOfHandle, HandleA, HandleB, CompPktNumA, CompPktNumB
+//
+// But we got the actual packet from BCM20702A1 with the following structure instead.
+//
+// NumOfHandle, HandleA, CompPktNumA, HandleB, CompPktNumB
+// 02, 40 00, 01 00, 41 00, 01 00
+
+func (e NumberOfCompletedPackets) NumberOfHandles() uint8 { return e[0] }
+func (e NumberOfCompletedPackets) ConnectionHandle(i int) uint16 {
+ // return binary.LittleEndian.Uint16(e[1+i*2:])
+ return binary.LittleEndian.Uint16(e[1+i*4:])
+}
+func (e NumberOfCompletedPackets) HCNumOfCompletedPackets(i int) uint16 {
+ // return binary.LittleEndian.Uint16(e[1+int(e.NumberOfHandles())*2:])
+ return binary.LittleEndian.Uint16(e[1+i*4+2:])
+}
+func (e LEAdvertisingReport) SubeventCode() uint8 { return e[0] }
+func (e LEAdvertisingReport) NumReports() uint8 { return e[1] }
+func (e LEAdvertisingReport) EventType(i int) uint8 { return e[2+i] }
+func (e LEAdvertisingReport) AddressType(i int) uint8 { return e[2+int(e.NumReports())*1+i] }
+func (e LEAdvertisingReport) Address(i int) [6]byte {
+ e = e[2+int(e.NumReports())*2:]
+ b := [6]byte{}
+ copy(b[:], e[6*i:])
+ return b
+}
+
+func (e LEAdvertisingReport) LengthData(i int) uint8 { return e[2+int(e.NumReports())*8+i] }
+
+func (e LEAdvertisingReport) Data(i int) []byte {
+ l := 0
+ for j := 0; j < i; j++ {
+ l += int(e.LengthData(j))
+ }
+ b := e[2+int(e.NumReports())*9+l:]
+ return b[:e.LengthData(i)]
+}
+
+func (e LEAdvertisingReport) RSSI(i int) int8 {
+ l := 0
+ for j := 0; j < int(e.NumReports()); j++ {
+ l += int(e.LengthData(j))
+ }
+ return int8(e[2+int(e.NumReports())*9+l+i])
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/evt/evt_gen.go b/vendor/github.com/currantlabs/ble/linux/hci/evt/evt_gen.go
new file mode 100644
index 0000000..7582d7f
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/evt/evt_gen.go
@@ -0,0 +1,227 @@
+package evt
+
+import "encoding/binary"
+
+const DisconnectionCompleteCode = 0x05
+
+// DisconnectionComplete implements Disconnection Complete (0x05) [Vol 2, Part E, 7.7.5].
+type DisconnectionComplete []byte
+
+func (r DisconnectionComplete) Status() uint8 { return r[0] }
+
+func (r DisconnectionComplete) ConnectionHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+func (r DisconnectionComplete) Reason() uint8 { return r[3] }
+
+const EncryptionChangeCode = 0x08
+
+// EncryptionChange implements Encryption Change (0x08) [Vol 2, Part E, 7.7.8].
+type EncryptionChange []byte
+
+func (r EncryptionChange) Status() uint8 { return r[0] }
+
+func (r EncryptionChange) ConnectionHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+func (r EncryptionChange) EncryptionEnabled() uint8 { return r[3] }
+
+const ReadRemoteVersionInformationCompleteCode = 0x0C
+
+// ReadRemoteVersionInformationComplete implements Read Remote Version Information Complete (0x0C) [Vol 2, Part E, 7.7.12].
+type ReadRemoteVersionInformationComplete []byte
+
+func (r ReadRemoteVersionInformationComplete) Status() uint8 { return r[0] }
+
+func (r ReadRemoteVersionInformationComplete) ConnectionHandle() uint16 {
+ return binary.LittleEndian.Uint16(r[1:])
+}
+
+func (r ReadRemoteVersionInformationComplete) Version() uint8 { return r[3] }
+
+func (r ReadRemoteVersionInformationComplete) ManufacturerName() uint16 {
+ return binary.LittleEndian.Uint16(r[4:])
+}
+
+func (r ReadRemoteVersionInformationComplete) Subversion() uint16 {
+ return binary.LittleEndian.Uint16(r[6:])
+}
+
+const CommandCompleteCode = 0x0E
+
+// CommandComplete implements Command Complete (0x0E) [Vol 2, Part E, 7.7.14].
+type CommandComplete []byte
+
+const CommandStatusCode = 0x0F
+
+// CommandStatus implements Command Status (0x0F) [Vol 2, Part E, 7.7.15].
+type CommandStatus []byte
+
+func (r CommandStatus) Status() uint8 { return r[0] }
+
+func (r CommandStatus) NumHCICommandPackets() uint8 { return r[1] }
+
+func (r CommandStatus) CommandOpcode() uint16 { return binary.LittleEndian.Uint16(r[2:]) }
+
+const HardwareErrorCode = 0x10
+
+// HardwareError implements Hardware Error (0x10) [Vol 2, Part E, 7.7.16].
+type HardwareError []byte
+
+func (r HardwareError) HardwareCode() uint8 { return r[0] }
+
+const NumberOfCompletedPacketsCode = 0x13
+
+// NumberOfCompletedPackets implements Number Of Completed Packets (0x13) [Vol 2, Part E, 7.7.19].
+type NumberOfCompletedPackets []byte
+
+const DataBufferOverflowCode = 0x1A
+
+// DataBufferOverflow implements Data Buffer Overflow (0x1A) [Vol 2, Part E, 7.7.26].
+type DataBufferOverflow []byte
+
+func (r DataBufferOverflow) LinkType() uint8 { return r[0] }
+
+const EncryptionKeyRefreshCompleteCode = 0x30
+
+// EncryptionKeyRefreshComplete implements Encryption Key Refresh Complete (0x30) [Vol 2, Part E, 7.7.39].
+type EncryptionKeyRefreshComplete []byte
+
+func (r EncryptionKeyRefreshComplete) Status() uint8 { return r[0] }
+
+func (r EncryptionKeyRefreshComplete) ConnectionHandle() uint16 {
+ return binary.LittleEndian.Uint16(r[1:])
+}
+
+const LEConnectionCompleteCode = 0x3E
+
+const LEConnectionCompleteSubCode = 0x01
+
+// LEConnectionComplete implements LE Connection Complete (0x3E:0x01) [Vol 2, Part E, 7.7.65.1].
+type LEConnectionComplete []byte
+
+func (r LEConnectionComplete) SubeventCode() uint8 { return r[0] }
+
+func (r LEConnectionComplete) Status() uint8 { return r[1] }
+
+func (r LEConnectionComplete) ConnectionHandle() uint16 { return binary.LittleEndian.Uint16(r[2:]) }
+
+func (r LEConnectionComplete) Role() uint8 { return r[4] }
+
+func (r LEConnectionComplete) PeerAddressType() uint8 { return r[5] }
+
+func (r LEConnectionComplete) PeerAddress() [6]byte {
+ b := [6]byte{}
+ copy(b[:], r[6:])
+ return b
+}
+
+func (r LEConnectionComplete) ConnInterval() uint16 { return binary.LittleEndian.Uint16(r[12:]) }
+
+func (r LEConnectionComplete) ConnLatency() uint16 { return binary.LittleEndian.Uint16(r[14:]) }
+
+func (r LEConnectionComplete) SupervisionTimeout() uint16 { return binary.LittleEndian.Uint16(r[16:]) }
+
+func (r LEConnectionComplete) MasterClockAccuracy() uint8 { return r[18] }
+
+const LEAdvertisingReportCode = 0x3E
+
+const LEAdvertisingReportSubCode = 0x02
+
+// LEAdvertisingReport implements LE Advertising Report (0x3E:0x02) [Vol 2, Part E, 7.7.65.2].
+type LEAdvertisingReport []byte
+
+const LEConnectionUpdateCompleteCode = 0x0E
+
+const LEConnectionUpdateCompleteSubCode = 0x03
+
+// LEConnectionUpdateComplete implements LE Connection Update Complete (0x0E:0x03) [Vol 2, Part E, 7.7.65.3].
+type LEConnectionUpdateComplete []byte
+
+func (r LEConnectionUpdateComplete) SubeventCode() uint8 { return r[0] }
+
+func (r LEConnectionUpdateComplete) Status() uint8 { return r[1] }
+
+func (r LEConnectionUpdateComplete) ConnectionHandle() uint16 {
+ return binary.LittleEndian.Uint16(r[2:])
+}
+
+func (r LEConnectionUpdateComplete) ConnInterval() uint16 { return binary.LittleEndian.Uint16(r[4:]) }
+
+func (r LEConnectionUpdateComplete) ConnLatency() uint16 { return binary.LittleEndian.Uint16(r[6:]) }
+
+func (r LEConnectionUpdateComplete) SupervisionTimeout() uint16 {
+ return binary.LittleEndian.Uint16(r[8:])
+}
+
+const LEReadRemoteUsedFeaturesCompleteCode = 0x3E
+
+const LEReadRemoteUsedFeaturesCompleteSubCode = 0x04
+
+// LEReadRemoteUsedFeaturesComplete implements LE Read Remote Used Features Complete (0x3E:0x04) [Vol 2, Part E, 7.7.65.4].
+type LEReadRemoteUsedFeaturesComplete []byte
+
+func (r LEReadRemoteUsedFeaturesComplete) SubeventCode() uint8 { return r[0] }
+
+func (r LEReadRemoteUsedFeaturesComplete) Status() uint8 { return r[1] }
+
+func (r LEReadRemoteUsedFeaturesComplete) ConnectionHandle() uint16 {
+ return binary.LittleEndian.Uint16(r[2:])
+}
+
+func (r LEReadRemoteUsedFeaturesComplete) LEFeatures() uint64 {
+ return binary.LittleEndian.Uint64(r[4:])
+}
+
+const LELongTermKeyRequestCode = 0x3E
+
+const LELongTermKeyRequestSubCode = 0x05
+
+// LELongTermKeyRequest implements LE Long Term Key Request (0x3E:0x05) [Vol 2, Part E, 7.7.65.5].
+type LELongTermKeyRequest []byte
+
+func (r LELongTermKeyRequest) SubeventCode() uint8 { return r[0] }
+
+func (r LELongTermKeyRequest) ConnectionHandle() uint16 { return binary.LittleEndian.Uint16(r[1:]) }
+
+func (r LELongTermKeyRequest) RandomNumber() uint64 { return binary.LittleEndian.Uint64(r[3:]) }
+
+func (r LELongTermKeyRequest) EncryptionDiversifier() uint16 {
+ return binary.LittleEndian.Uint16(r[11:])
+}
+
+const LERemoteConnectionParameterRequestCode = 0x3E
+
+const LERemoteConnectionParameterRequestSubCode = 0x06
+
+// LERemoteConnectionParameterRequest implements LE Remote Connection Parameter Request (0x3E:0x06) [Vol 2, Part E, 7.7.65.6].
+type LERemoteConnectionParameterRequest []byte
+
+func (r LERemoteConnectionParameterRequest) SubeventCode() uint8 { return r[0] }
+
+func (r LERemoteConnectionParameterRequest) ConnectionHandle() uint16 {
+ return binary.LittleEndian.Uint16(r[1:])
+}
+
+func (r LERemoteConnectionParameterRequest) IntervalMin() uint16 {
+ return binary.LittleEndian.Uint16(r[3:])
+}
+
+func (r LERemoteConnectionParameterRequest) IntervalMax() uint16 {
+ return binary.LittleEndian.Uint16(r[5:])
+}
+
+func (r LERemoteConnectionParameterRequest) Latency() uint16 {
+ return binary.LittleEndian.Uint16(r[7:])
+}
+
+func (r LERemoteConnectionParameterRequest) Timeout() uint16 {
+ return binary.LittleEndian.Uint16(r[9:])
+}
+
+const AuthenticatedPayloadTimeoutExpiredCode = 0x57
+
+// AuthenticatedPayloadTimeoutExpired implements Authenticated Payload Timeout Expired (0x57) [Vol 2, Part E, 7.7.75].
+type AuthenticatedPayloadTimeoutExpired []byte
+
+func (r AuthenticatedPayloadTimeoutExpired) ConnectionHandle() uint16 {
+ return binary.LittleEndian.Uint16(r[0:])
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/gap.go b/vendor/github.com/currantlabs/ble/linux/hci/gap.go
new file mode 100644
index 0000000..0a0e0f0
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/gap.go
@@ -0,0 +1,214 @@
+package hci
+
+import (
+ "fmt"
+ "net"
+ "time"
+
+ "golang.org/x/net/context"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/linux/adv"
+ "github.com/currantlabs/ble/linux/gatt"
+ "github.com/pkg/errors"
+)
+
+// Addr ...
+func (h *HCI) Addr() ble.Addr { return h.addr }
+
+// SetAdvHandler ...
+func (h *HCI) SetAdvHandler(ah ble.AdvHandler) error {
+ h.advHandler = ah
+ return nil
+}
+
+// Scan starts scanning.
+func (h *HCI) Scan(allowDup bool) error {
+ h.params.scanEnable.FilterDuplicates = 1
+ if allowDup {
+ h.params.scanEnable.FilterDuplicates = 0
+ }
+ h.params.scanEnable.LEScanEnable = 1
+ h.adHist = make([]*Advertisement, 128)
+ h.adLast = 0
+ return h.Send(&h.params.scanEnable, nil)
+}
+
+// StopScanning stops scanning.
+func (h *HCI) StopScanning() error {
+ h.params.scanEnable.LEScanEnable = 0
+ return h.Send(&h.params.scanEnable, nil)
+}
+
+// AdvertiseNameAndServices advertises device name, and specified service UUIDs.
+// It tries to fit the UUIDs in the advertising data as much as possible.
+// If name doesn't fit in the advertising data, it will be put in scan response.
+func (h *HCI) AdvertiseNameAndServices(name string, uuids ...ble.UUID) error {
+ ad, err := adv.NewPacket(adv.Flags(adv.FlagGeneralDiscoverable | adv.FlagLEOnly))
+ if err != nil {
+ return err
+ }
+ f := adv.AllUUID
+
+ // Current length of ad packet plus two bytes of length and tag.
+ l := ad.Len() + 1 + 1
+ for _, u := range uuids {
+ l += u.Len()
+ }
+ if l > adv.MaxEIRPacketLength {
+ f = adv.SomeUUID
+ }
+ for _, u := range uuids {
+ if err := ad.Append(f(u)); err != nil {
+ if err == adv.ErrNotFit {
+ break
+ }
+ return err
+ }
+ }
+ sr, _ := adv.NewPacket()
+ switch {
+ case ad.Append(adv.CompleteName(name)) == nil:
+ case sr.Append(adv.CompleteName(name)) == nil:
+ case sr.Append(adv.ShortName(name)) == nil:
+ }
+ if err := h.SetAdvertisement(ad.Bytes(), sr.Bytes()); err != nil {
+ return nil
+ }
+ return h.Advertise()
+}
+
+// AdvertiseMfgData avertises the given manufacturer data.
+func (h *HCI) AdvertiseMfgData(id uint16, md []byte) error {
+ ad, err := adv.NewPacket(adv.ManufacturerData(id, md))
+ if err != nil {
+ return err
+ }
+ if err := h.SetAdvertisement(ad.Bytes(), nil); err != nil {
+ return nil
+ }
+ return h.Advertise()
+}
+
+// AdvertiseServiceData16 advertises data associated with a 16bit service uuid
+func (h *HCI) AdvertiseServiceData16(id uint16, b []byte) error {
+ ad, err := adv.NewPacket(adv.ServiceData16(id, b))
+ if err != nil {
+ return err
+ }
+ if err := h.SetAdvertisement(ad.Bytes(), nil); err != nil {
+ return nil
+ }
+ return h.Advertise()
+}
+
+// AdvertiseIBeaconData advertise iBeacon with given manufacturer data.
+func (h *HCI) AdvertiseIBeaconData(md []byte) error {
+ ad, err := adv.NewPacket(adv.IBeaconData(md))
+ if err != nil {
+ return err
+ }
+ if err := h.SetAdvertisement(ad.Bytes(), nil); err != nil {
+ return nil
+ }
+ return h.Advertise()
+}
+
+// AdvertiseIBeacon advertises iBeacon with specified parameters.
+func (h *HCI) AdvertiseIBeacon(u ble.UUID, major, minor uint16, pwr int8) error {
+ ad, err := adv.NewPacket(adv.IBeacon(u, major, minor, pwr))
+ if err != nil {
+ return err
+ }
+ if err := h.SetAdvertisement(ad.Bytes(), nil); err != nil {
+ return nil
+ }
+ return h.Advertise()
+}
+
+// StopAdvertising stops advertising.
+func (h *HCI) StopAdvertising() error {
+ h.params.advEnable.AdvertisingEnable = 0
+ return h.Send(&h.params.advEnable, nil)
+}
+
+// Accept starts advertising and accepts connection.
+func (h *HCI) Accept() (ble.Conn, error) {
+ var tmo <-chan time.Time
+ if h.listenerTmo != time.Duration(0) {
+ tmo = time.After(h.listenerTmo)
+ }
+ select {
+ case <-h.done:
+ return nil, h.err
+ case c := <-h.chSlaveConn:
+ return c, nil
+ case <-tmo:
+ return nil, fmt.Errorf("listner timed out")
+ }
+}
+
+// Dial ...
+func (h *HCI) Dial(ctx context.Context, a ble.Addr) (ble.Client, error) {
+ b, err := net.ParseMAC(a.String())
+ if err != nil {
+ return nil, ErrInvalidAddr
+ }
+ h.params.connParams.PeerAddress = [6]byte{b[5], b[4], b[3], b[2], b[1], b[0]}
+ if _, ok := a.(RandomAddress); ok {
+ h.params.connParams.PeerAddressType = 1
+ }
+ if err = h.Send(&h.params.connParams, nil); err != nil {
+ return nil, err
+ }
+ var tmo <-chan time.Time
+ if h.dialerTmo != time.Duration(0) {
+ tmo = time.After(h.dialerTmo)
+ }
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-h.done:
+ return nil, h.err
+ case c := <-h.chMasterConn:
+ return gatt.NewClient(c)
+ case <-tmo:
+ err := h.Send(&h.params.connCancel, nil)
+ if err == nil {
+ // The pending connection was canceled successfully.
+ return nil, fmt.Errorf("connection timed out")
+ }
+ // The connection has been established, the cancel command
+ // failed with ErrDisallowed.
+ if err == ErrDisallowed {
+ return gatt.NewClient(<-h.chMasterConn)
+ }
+ return nil, errors.Wrap(err, "cancel connection failed")
+ }
+}
+
+// Advertise starts advertising.
+func (h *HCI) Advertise() error {
+ h.params.advEnable.AdvertisingEnable = 1
+ return h.Send(&h.params.advEnable, nil)
+}
+
+// SetAdvertisement sets advertising data and scanResp.
+func (h *HCI) SetAdvertisement(ad []byte, sr []byte) error {
+ if len(ad) > adv.MaxEIRPacketLength || len(sr) > adv.MaxEIRPacketLength {
+ return ble.ErrEIRPacketTooLong
+ }
+
+ h.params.advData.AdvertisingDataLength = uint8(len(ad))
+ copy(h.params.advData.AdvertisingData[:], ad)
+ if err := h.Send(&h.params.advData, nil); err != nil {
+ return err
+ }
+
+ h.params.scanResp.ScanResponseDataLength = uint8(len(sr))
+ copy(h.params.scanResp.ScanResponseData[:], sr)
+ if err := h.Send(&h.params.scanResp, nil); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/hci.go b/vendor/github.com/currantlabs/ble/linux/hci/hci.go
new file mode 100644
index 0000000..42240cd
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/hci.go
@@ -0,0 +1,522 @@
+package hci
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/currantlabs/ble"
+ "github.com/currantlabs/ble/linux/hci/cmd"
+ "github.com/currantlabs/ble/linux/hci/evt"
+ "github.com/currantlabs/ble/linux/hci/socket"
+ "github.com/pkg/errors"
+)
+
+// Command ...
+type Command interface {
+ OpCode() int
+ Len() int
+ Marshal([]byte) error
+}
+
+// CommandRP ...
+type CommandRP interface {
+ Unmarshal(b []byte) error
+}
+
+type handlerFn func(b []byte) error
+
+type pkt struct {
+ cmd Command
+ done chan []byte
+}
+
+// NewHCI returns a hci device.
+func NewHCI(opts ...Option) (*HCI, error) {
+ h := &HCI{
+ id: -1,
+
+ chCmdPkt: make(chan *pkt),
+ chCmdBufs: make(chan []byte, 8),
+ sent: make(map[int]*pkt),
+
+ evth: map[int]handlerFn{},
+ subh: map[int]handlerFn{},
+
+ muConns: &sync.Mutex{},
+ conns: make(map[uint16]*Conn),
+ chMasterConn: make(chan *Conn),
+ chSlaveConn: make(chan *Conn),
+
+ done: make(chan bool),
+ }
+ h.params.init()
+ if err := h.Option(opts...); err != nil {
+ return nil, errors.Wrap(err, "can't set options")
+ }
+
+ return h, nil
+}
+
+// HCI ...
+type HCI struct {
+ sync.Mutex
+
+ params params
+
+ skt io.ReadWriteCloser
+ id int
+
+ // Host to Controller command flow control [Vol 2, Part E, 4.4]
+ chCmdPkt chan *pkt
+ chCmdBufs chan []byte
+ sent map[int]*pkt
+
+ // evtHub
+ evth map[int]handlerFn
+ subh map[int]handlerFn
+
+ // aclHandler
+ bufSize int
+ bufCnt int
+
+ // Device information or status.
+ addr net.HardwareAddr
+ txPwrLv int
+
+ // adHist and adLast track the history of past scannable advertising packets.
+ // Controller delivers AD(Advertising Data) and SR(Scan Response) separately
+ // through HCI. Upon recieving an AD, no matter it's scannable or not, we
+ // pass a Advertisment (AD only) to advHandler immediately.
+ // Upon recieving a SR, we search the AD history for the AD from the same
+ // device, and pass the Advertisiement (AD+SR) to advHandler.
+ // The adHist and adLast are allocated in the Scan().
+ advHandler ble.AdvHandler
+ adHist []*Advertisement
+ adLast int
+
+ // Host to Controller Data Flow Control Packet-based Data flow control for LE-U [Vol 2, Part E, 4.1.1]
+ // Minimum 27 bytes. 4 bytes of L2CAP Header, and 23 bytes Payload from upper layer (ATT)
+ pool *Pool
+
+ // L2CAP connections
+ muConns *sync.Mutex
+ conns map[uint16]*Conn
+ chMasterConn chan *Conn // Dial returns master connections.
+ chSlaveConn chan *Conn // Peripheral accept slave connections.
+
+ dialerTmo time.Duration
+ listenerTmo time.Duration
+
+ err error
+ done chan bool
+}
+
+// Init ...
+func (h *HCI) Init() error {
+ h.evth[0x3E] = h.handleLEMeta
+ h.evth[evt.CommandCompleteCode] = h.handleCommandComplete
+ h.evth[evt.CommandStatusCode] = h.handleCommandStatus
+ h.evth[evt.DisconnectionCompleteCode] = h.handleDisconnectionComplete
+ h.evth[evt.NumberOfCompletedPacketsCode] = h.handleNumberOfCompletedPackets
+
+ h.subh[evt.LEAdvertisingReportSubCode] = h.handleLEAdvertisingReport
+ h.subh[evt.LEConnectionCompleteSubCode] = h.handleLEConnectionComplete
+ h.subh[evt.LEConnectionUpdateCompleteSubCode] = h.handleLEConnectionUpdateComplete
+ h.subh[evt.LELongTermKeyRequestSubCode] = h.handleLELongTermKeyRequest
+ // evt.EncryptionChangeCode: todo),
+ // evt.ReadRemoteVersionInformationCompleteCode: todo),
+ // evt.HardwareErrorCode: todo),
+ // evt.DataBufferOverflowCode: todo),
+ // evt.EncryptionKeyRefreshCompleteCode: todo),
+ // evt.AuthenticatedPayloadTimeoutExpiredCode: todo),
+ // evt.LEReadRemoteUsedFeaturesCompleteSubCode: todo),
+ // evt.LERemoteConnectionParameterRequestSubCode: todo),
+
+ skt, err := socket.NewSocket(h.id)
+ if err != nil {
+ return err
+ }
+ h.skt = skt
+
+ h.chCmdBufs <- make([]byte, 64)
+
+ go h.sktLoop()
+ h.init()
+
+ // Pre-allocate buffers with additional head room for lower layer headers.
+ // HCI header (1 Byte) + ACL Data Header (4 bytes) + L2CAP PDU (or fragment)
+ h.pool = NewPool(1+4+h.bufSize, h.bufCnt-1)
+
+ h.Send(&h.params.advParams, nil)
+ h.Send(&h.params.scanParams, nil)
+ return nil
+}
+
+// Close ...
+func (h *HCI) Close() error {
+ return h.close(nil)
+}
+
+// Error ...
+func (h *HCI) Error() error {
+ return h.err
+}
+
+// Option sets the options specified.
+func (h *HCI) Option(opts ...Option) error {
+ var err error
+ for _, opt := range opts {
+ err = opt(h)
+ }
+ return err
+}
+
+func (h *HCI) init() error {
+ h.Send(&cmd.Reset{}, nil)
+
+ ReadBDADDRRP := cmd.ReadBDADDRRP{}
+ h.Send(&cmd.ReadBDADDR{}, &ReadBDADDRRP)
+
+ a := ReadBDADDRRP.BDADDR
+ h.addr = net.HardwareAddr([]byte{a[5], a[4], a[3], a[2], a[1], a[0]})
+
+ ReadBufferSizeRP := cmd.ReadBufferSizeRP{}
+ h.Send(&cmd.ReadBufferSize{}, &ReadBufferSizeRP)
+
+ // Assume the buffers are shared between ACL-U and LE-U.
+ h.bufCnt = int(ReadBufferSizeRP.HCTotalNumACLDataPackets)
+ h.bufSize = int(ReadBufferSizeRP.HCACLDataPacketLength)
+
+ LEReadBufferSizeRP := cmd.LEReadBufferSizeRP{}
+ h.Send(&cmd.LEReadBufferSize{}, &LEReadBufferSizeRP)
+
+ if LEReadBufferSizeRP.HCTotalNumLEDataPackets != 0 {
+ // Okay, LE-U do have their own buffers.
+ h.bufCnt = int(LEReadBufferSizeRP.HCTotalNumLEDataPackets)
+ h.bufSize = int(LEReadBufferSizeRP.HCLEDataPacketLength)
+ }
+
+ LEReadAdvertisingChannelTxPowerRP := cmd.LEReadAdvertisingChannelTxPowerRP{}
+ h.Send(&cmd.LEReadAdvertisingChannelTxPower{}, &LEReadAdvertisingChannelTxPowerRP)
+
+ h.txPwrLv = int(LEReadAdvertisingChannelTxPowerRP.TransmitPowerLevel)
+
+ LESetEventMaskRP := cmd.LESetEventMaskRP{}
+ h.Send(&cmd.LESetEventMask{LEEventMask: 0x000000000000001F}, &LESetEventMaskRP)
+
+ SetEventMaskRP := cmd.SetEventMaskRP{}
+ h.Send(&cmd.SetEventMask{EventMask: 0x3dbff807fffbffff}, &SetEventMaskRP)
+
+ WriteLEHostSupportRP := cmd.WriteLEHostSupportRP{}
+ h.Send(&cmd.WriteLEHostSupport{LESupportedHost: 1, SimultaneousLEHost: 0}, &WriteLEHostSupportRP)
+
+ return h.err
+}
+
+// Send ...
+func (h *HCI) Send(c Command, r CommandRP) error {
+ b, err := h.send(c)
+ if err != nil {
+ return err
+ }
+ if len(b) > 0 && b[0] != 0x00 {
+ return ErrCommand(b[0])
+ }
+ if r != nil {
+ return r.Unmarshal(b)
+ }
+ return nil
+}
+
+func (h *HCI) send(c Command) ([]byte, error) {
+ if h.err != nil {
+ return nil, h.err
+ }
+ p := &pkt{c, make(chan []byte)}
+ b := <-h.chCmdBufs
+ b[0] = byte(pktTypeCommand) // HCI header
+ b[1] = byte(c.OpCode())
+ b[2] = byte(c.OpCode() >> 8)
+ b[3] = byte(c.Len())
+ if err := c.Marshal(b[4:]); err != nil {
+ h.close(fmt.Errorf("hci: failed to marshal cmd"))
+ }
+
+ h.sent[c.OpCode()] = p // TODO: lock
+ if n, err := h.skt.Write(b[:4+c.Len()]); err != nil {
+ h.close(fmt.Errorf("hci: failed to send cmd"))
+ } else if n != 4+c.Len() {
+ h.close(fmt.Errorf("hci: failed to send whole cmd pkt to hci socket"))
+ }
+
+ select {
+ case <-h.done:
+ return nil, h.err
+ case b := <-p.done:
+ return b, nil
+ }
+}
+
+func (h *HCI) sktLoop() {
+ b := make([]byte, 4096)
+ defer close(h.done)
+ for {
+ n, err := h.skt.Read(b)
+ if n == 0 || err != nil {
+ h.err = fmt.Errorf("skt: %s", err)
+ return
+ }
+ p := make([]byte, n)
+ copy(p, b)
+ if err := h.handlePkt(p); err != nil {
+ h.err = fmt.Errorf("skt: %s", err)
+ return
+ }
+ }
+}
+
+func (h *HCI) close(err error) error {
+ h.err = err
+ return h.skt.Close()
+}
+
+func (h *HCI) handlePkt(b []byte) error {
+ // Strip the 1-byte HCI header and pass down the rest of the packet.
+ t, b := b[0], b[1:]
+ switch t {
+ case pktTypeCommand:
+ return fmt.Errorf("unmanaged cmd: % X", b)
+ case pktTypeACLData:
+ return h.handleACL(b)
+ case pktTypeSCOData:
+ return fmt.Errorf("unsupported sco packet: % X", b)
+ case pktTypeEvent:
+ return h.handleEvt(b)
+ case pktTypeVendor:
+ return fmt.Errorf("unsupported vendor packet: % X", b)
+ default:
+ return fmt.Errorf("invalid packet: 0x%02X % X", t, b)
+ }
+}
+
+func (h *HCI) handleACL(b []byte) error {
+ handle := packet(b).handle()
+ h.muConns.Lock()
+ c, ok := h.conns[handle]
+ h.muConns.Unlock()
+ if !ok {
+ logger.Warn("invalid connection handle on ACL packet", "handle", handle)
+ return nil
+ }
+ c.chInPkt <- b
+ return nil
+}
+
+func (h *HCI) handleEvt(b []byte) error {
+ code, plen := int(b[0]), int(b[1])
+ if plen != len(b[2:]) {
+ return fmt.Errorf("invalid event packet: % X", b)
+ }
+ if code == evt.CommandCompleteCode || code == evt.CommandStatusCode {
+ if f := h.evth[code]; f != nil {
+ return f(b[2:])
+ }
+ }
+ if plen != len(b[2:]) {
+ h.err = fmt.Errorf("invalid event packet: % X", b)
+ }
+ if f := h.evth[code]; f != nil {
+ h.err = f(b[2:])
+ return nil
+ }
+ if code == 0xff { // Ignore vendor events
+ return nil
+ }
+ return fmt.Errorf("unsupported event packet: % X", b)
+}
+
+func (h *HCI) handleLEMeta(b []byte) error {
+ subcode := int(b[0])
+ if f := h.subh[subcode]; f != nil {
+ return f(b)
+ }
+ return fmt.Errorf("unsupported LE event: % X", b)
+}
+
+func (h *HCI) handleLEAdvertisingReport(b []byte) error {
+ if h.advHandler == nil {
+ return nil
+ }
+
+ e := evt.LEAdvertisingReport(b)
+ for i := 0; i < int(e.NumReports()); i++ {
+ var a *Advertisement
+ switch e.EventType(i) {
+ case evtTypAdvInd:
+ fallthrough
+ case evtTypAdvScanInd:
+ a = newAdvertisement(e, i)
+ h.adHist[h.adLast] = a
+ h.adLast++
+ if h.adLast == len(h.adHist) {
+ h.adLast = 0
+ }
+ case evtTypScanRsp:
+ sr := newAdvertisement(e, i)
+ for idx := h.adLast - 1; idx != h.adLast; idx-- {
+ if idx == -1 {
+ idx = len(h.adHist) - 1
+ }
+ if h.adHist[idx] == nil {
+ break
+ }
+ if h.adHist[idx].Address().String() == sr.Address().String() {
+ h.adHist[idx].setScanResponse(sr)
+ a = h.adHist[idx]
+ break
+ }
+ }
+ // Got a SR without having recieved an associated AD before?
+ if a == nil {
+ return fmt.Errorf("recieved scan response %s with no associated Advertising Data packet", sr.Address())
+ }
+ default:
+ a = newAdvertisement(e, i)
+ }
+ go h.advHandler(a)
+ }
+
+ return nil
+}
+
+func (h *HCI) handleCommandComplete(b []byte) error {
+ e := evt.CommandComplete(b)
+ for i := 0; i < int(e.NumHCICommandPackets()); i++ {
+ h.chCmdBufs <- make([]byte, 64)
+ }
+
+ // NOP command, used for flow control purpose [Vol 2, Part E, 4.4]
+ if e.CommandOpcode() == 0x0000 {
+ h.chCmdBufs = make(chan []byte, 8)
+ return nil
+ }
+ p, found := h.sent[int(e.CommandOpcode())]
+ if !found {
+ return fmt.Errorf("can't find the cmd for CommandCompleteEP: % X", e)
+ }
+ p.done <- e.ReturnParameters()
+ return nil
+}
+
+func (h *HCI) handleCommandStatus(b []byte) error {
+ e := evt.CommandStatus(b)
+ for i := 0; i < int(e.NumHCICommandPackets()); i++ {
+ h.chCmdBufs <- make([]byte, 64)
+ }
+
+ p, found := h.sent[int(e.CommandOpcode())]
+ if !found {
+ return fmt.Errorf("can't find the cmd for CommandStatusEP: % X", e)
+ }
+ p.done <- []byte{e.Status()}
+ return nil
+}
+
+func (h *HCI) handleLEConnectionComplete(b []byte) error {
+ e := evt.LEConnectionComplete(b)
+ c := newConn(h, e)
+ h.muConns.Lock()
+ h.conns[e.ConnectionHandle()] = c
+ h.muConns.Unlock()
+ if e.Role() == roleMaster {
+ if e.Status() == 0x00 {
+ h.chMasterConn <- c
+ return nil
+ }
+ if ErrCommand(e.Status()) == ErrConnID {
+ // The connection was canceled successfully.
+ return nil
+ }
+ return nil
+ }
+ if e.Status() == 0x00 {
+ h.chSlaveConn <- c
+ // When a controller accepts a connection, it moves from advertising
+ // state to idle/ready state. Host needs to explicitly ask the
+ // controller to re-enable advertising. Note that the host was most
+ // likely in advertising state. Otherwise it couldn't accept the
+ // connection in the first place. The only exception is that user
+ // asked the host to stop advertising during this tiny window.
+ // The re-enabling might failed or ignored by the controller, if
+ // it had reached the maximum number of concurrent connections.
+ // So we also re-enable the advertising when a connection disconnected
+ h.params.RLock()
+ if h.params.advEnable.AdvertisingEnable == 1 {
+ go h.Send(&h.params.advEnable, nil)
+ }
+ h.params.RUnlock()
+ }
+ return nil
+}
+
+func (h *HCI) handleLEConnectionUpdateComplete(b []byte) error {
+ return nil
+}
+
+func (h *HCI) handleDisconnectionComplete(b []byte) error {
+ e := evt.DisconnectionComplete(b)
+ h.muConns.Lock()
+ c, found := h.conns[e.ConnectionHandle()]
+ delete(h.conns, e.ConnectionHandle())
+ h.muConns.Unlock()
+ if !found {
+ return fmt.Errorf("disconnecting an invalid handle %04X", e.ConnectionHandle())
+ }
+ close(c.chInPkt)
+ if c.param.Role() == roleSlave {
+ // Re-enable advertising, if it was advertising. Refer to the
+ // handleLEConnectionComplete() for details.
+ // This may failed with ErrCommandDisallowed, if the controller
+ // was actually in advertising state. It does no harm though.
+ h.params.RLock()
+ if h.params.advEnable.AdvertisingEnable == 1 {
+ go h.Send(&h.params.advEnable, nil)
+ }
+ h.params.RUnlock()
+ } else {
+ // remote peripheral disconnected
+ close(c.chDone)
+ }
+ // When a connection disconnects, all the sent packets and weren't acked yet
+ // will be recycled. [Vol2, Part E 4.1.1]
+ c.txBuffer.PutAll()
+ return nil
+}
+
+func (h *HCI) handleNumberOfCompletedPackets(b []byte) error {
+ e := evt.NumberOfCompletedPackets(b)
+ h.muConns.Lock()
+ defer h.muConns.Unlock()
+ for i := 0; i < int(e.NumberOfHandles()); i++ {
+ c, found := h.conns[e.ConnectionHandle(i)]
+ if !found {
+ continue
+ }
+
+ // Put the delivered buffers back to the pool.
+ for j := 0; j < int(e.HCNumOfCompletedPackets(i)); j++ {
+ c.txBuffer.Put()
+ }
+ }
+ return nil
+}
+
+func (h *HCI) handleLELongTermKeyRequest(b []byte) error {
+ e := evt.LELongTermKeyRequest(b)
+ return h.Send(&cmd.LELongTermKeyRequestNegativeReply{
+ ConnectionHandle: e.ConnectionHandle(),
+ }, nil)
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/log.go b/vendor/github.com/currantlabs/ble/linux/hci/log.go
new file mode 100644
index 0000000..412ef65
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/log.go
@@ -0,0 +1,7 @@
+package hci
+
+import (
+ "github.com/mgutz/logxi/v1"
+)
+
+var logger = log.New("hci")
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/option.go b/vendor/github.com/currantlabs/ble/linux/hci/option.go
new file mode 100644
index 0000000..5d1ca26
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/option.go
@@ -0,0 +1,42 @@
+package hci
+
+import (
+ "time"
+
+ "github.com/currantlabs/ble/linux/hci/cmd"
+)
+
+// An Option is a configuration function, which configures the device.
+type Option func(*HCI) error
+
+// OptDeviceID sets HCI device ID.
+func OptDeviceID(id int) Option {
+ return func(h *HCI) error {
+ h.id = id
+ return nil
+ }
+}
+
+// OptDialerTimeout sets dialing timeout for Dialer.
+func OptDialerTimeout(d time.Duration) Option {
+ return func(h *HCI) error {
+ h.dialerTmo = d
+ return nil
+ }
+}
+
+// OptListenerTimeout sets dialing timeout for Listener.
+func OptListenerTimeout(d time.Duration) Option {
+ return func(h *HCI) error {
+ h.listenerTmo = d
+ return nil
+ }
+}
+
+// OptConnParams overrides default connection parameters.
+func OptConnParams(param cmd.LECreateConnection) Option {
+ return func(h *HCI) error {
+ h.params.connParams = param
+ return nil
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/params.go b/vendor/github.com/currantlabs/ble/linux/hci/params.go
new file mode 100644
index 0000000..bbf36f6
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/params.go
@@ -0,0 +1,55 @@
+package hci
+
+import (
+ "sync"
+
+ "github.com/currantlabs/ble/linux/hci/cmd"
+)
+
+type params struct {
+ sync.RWMutex
+
+ advEnable cmd.LESetAdvertiseEnable
+ scanEnable cmd.LESetScanEnable
+ connCancel cmd.LECreateConnectionCancel
+
+ advData cmd.LESetAdvertisingData
+ scanResp cmd.LESetScanResponseData
+ advParams cmd.LESetAdvertisingParameters
+ scanParams cmd.LESetScanParameters
+ connParams cmd.LECreateConnection
+}
+
+func (p *params) init() {
+ p.scanParams = cmd.LESetScanParameters{
+ LEScanType: 0x01, // 0x00: passive, 0x01: active
+ LEScanInterval: 0x0004, // 0x0004 - 0x4000; N * 0.625msec
+ LEScanWindow: 0x0004, // 0x0004 - 0x4000; N * 0.625msec
+ OwnAddressType: 0x00, // 0x00: public, 0x01: random
+ ScanningFilterPolicy: 0x00, // 0x00: accept all, 0x01: ignore non-white-listed.
+ }
+ p.advParams = cmd.LESetAdvertisingParameters{
+ AdvertisingIntervalMin: 0x0020, // 0x0020 - 0x4000; N * 0.625 msec
+ AdvertisingIntervalMax: 0x0020, // 0x0020 - 0x4000; N * 0.625 msec
+ AdvertisingType: 0x00, // 00: ADV_IND, 0x01: DIRECT(HIGH), 0x02: SCAN, 0x03: NONCONN, 0x04: DIRECT(LOW)
+ OwnAddressType: 0x00, // 0x00: public, 0x01: random
+ DirectAddressType: 0x00, // 0x00: public, 0x01: random
+ DirectAddress: [6]byte{}, // Public or Random Address of the Device to be connected
+ AdvertisingChannelMap: 0x7, // 0x07 0x01: ch37, 0x2: ch38, 0x4: ch39
+ AdvertisingFilterPolicy: 0x00,
+ }
+ p.connParams = cmd.LECreateConnection{
+ LEScanInterval: 0x0004, // 0x0004 - 0x4000; N * 0.625 msec
+ LEScanWindow: 0x0004, // 0x0004 - 0x4000; N * 0.625 msec
+ InitiatorFilterPolicy: 0x00, // White list is not used
+ PeerAddressType: 0x00, // Public Device Address
+ PeerAddress: [6]byte{}, //
+ OwnAddressType: 0x00, // Public Device Address
+ ConnIntervalMin: 0x0006, // 0x0006 - 0x0C80; N * 1.25 msec
+ ConnIntervalMax: 0x0006, // 0x0006 - 0x0C80; N * 1.25 msec
+ ConnLatency: 0x0000, // 0x0000 - 0x01F3; N * 1.25 msec
+ SupervisionTimeout: 0x0048, // 0x000A - 0x0C80; N * 10 msec
+ MinimumCELength: 0x0000, // 0x0000 - 0xFFFF; N * 0.625 msec
+ MaximumCELength: 0x0000, // 0x0000 - 0xFFFF; N * 0.625 msec
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/signal.go b/vendor/github.com/currantlabs/ble/linux/hci/signal.go
new file mode 100644
index 0000000..81d4998
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/signal.go
@@ -0,0 +1,223 @@
+package hci
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/currantlabs/ble/linux/hci/cmd"
+)
+
+// Signal ...
+type Signal interface {
+ Code() int
+ Marshal() []byte
+ Unmarshal([]byte) error
+}
+
+type sigCmd []byte
+
+func (s sigCmd) code() int { return int(s[0]) }
+func (s sigCmd) id() uint8 { return s[1] }
+func (s sigCmd) len() int { return int(binary.LittleEndian.Uint16(s[2:4])) }
+func (s sigCmd) data() []byte { return s[4 : 4+s.len()] }
+
+// Signal ...
+func (c *Conn) Signal(req Signal, rsp Signal) error {
+ data := req.Marshal()
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, uint16(4+len(data)))
+ binary.Write(buf, binary.LittleEndian, uint16(cidLESignal))
+
+ binary.Write(buf, binary.LittleEndian, uint8(req.Code()))
+ binary.Write(buf, binary.LittleEndian, uint8(c.sigID))
+ binary.Write(buf, binary.LittleEndian, uint16(len(data)))
+ binary.Write(buf, binary.LittleEndian, data)
+
+ c.sigSent = make(chan []byte)
+ defer close(c.sigSent)
+ if _, err := c.writePDU(buf.Bytes()); err != nil {
+ return err
+ }
+ var s sigCmd
+ select {
+ case s = <-c.sigSent:
+ case <-time.After(time.Second):
+ // TODO: Find the proper timed out defined in spec, if any.
+ return errors.New("signaling request timed out")
+ }
+
+ if s.code() != req.Code() {
+ return errors.New("mismatched signaling response")
+ }
+ if s.id() != c.sigID {
+ return errors.New("mismatched signaling id")
+ }
+ c.sigID++
+ if rsp == nil {
+ return nil
+ }
+ return rsp.Unmarshal(s.data())
+}
+
+func (c *Conn) sendResponse(code uint8, id uint8, r Signal) (int, error) {
+ data := r.Marshal()
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, uint16(4+len(data)))
+ binary.Write(buf, binary.LittleEndian, uint16(cidLESignal))
+ binary.Write(buf, binary.LittleEndian, uint8(code))
+ binary.Write(buf, binary.LittleEndian, uint8(id))
+ binary.Write(buf, binary.LittleEndian, uint16(len(data)))
+ if err := binary.Write(buf, binary.LittleEndian, data); err != nil {
+ return 0, err
+ }
+ logger.Debug("sig", "send", fmt.Sprintf("[%X]", buf.Bytes()))
+ return c.writePDU(buf.Bytes())
+}
+
+func (c *Conn) handleSignal(p pdu) error {
+ logger.Debug("sig", "recv", fmt.Sprintf("[%X]", p))
+ // When multiple commands are included in an L2CAP packet and the packet
+ // exceeds the signaling MTU (MTUsig) of the receiver, a single Command Reject
+ // packet shall be sent in response. The identifier shall match the first Request
+ // command in the L2CAP packet. If only Responses are recognized, the packet
+ // shall be silently discarded. [Vol3, Part A, 4.1]
+ if p.dlen() > c.sigRxMTU {
+ c.sendResponse(
+ SignalCommandReject,
+ sigCmd(p.payload()).id(),
+ &CommandReject{
+ Reason: 0x0001, // Signaling MTU exceeded.
+ Data: []byte{uint8(c.sigRxMTU), uint8(c.sigRxMTU >> 8)}, // Actual MTUsig.
+ })
+ return nil
+ }
+
+ s := sigCmd(p.payload())
+ for len(s) > 0 {
+ // Check if it's a supported request.
+ switch s.code() {
+ case SignalDisconnectRequest:
+ c.handleDisconnectRequest(s)
+ case SignalConnectionParameterUpdateRequest:
+ c.handleConnectionParameterUpdateRequest(s)
+ case SignalLECreditBasedConnectionRequest:
+ c.LECreditBasedConnectionRequest(s)
+ case SignalLEFlowControlCredit:
+ c.LEFlowControlCredit(s)
+ default:
+ // Check if it's a response to a sent command.
+ select {
+ case c.sigSent <- s:
+ continue
+ default:
+ }
+
+ c.sendResponse(
+ SignalCommandReject,
+ s.id(),
+ &CommandReject{
+ Reason: 0x0000, // Command not understood.
+ })
+ }
+ s = s[4+s.len():] // advance to next the packet.
+
+ }
+ return nil
+}
+
+// DisconnectRequest implements Disconnect Request (0x06) [Vol 3, Part A, 4.6].
+func (c *Conn) handleDisconnectRequest(s sigCmd) {
+ var req DisconnectRequest
+ if err := req.Unmarshal(s.data()); err != nil {
+ return
+ }
+
+ // Send Command Reject when the DCID is unrecognized.
+ if req.DestinationCID != cidLEAtt {
+ endpoints := make([]byte, 4)
+ binary.LittleEndian.PutUint16(endpoints, req.SourceCID)
+ binary.LittleEndian.PutUint16(endpoints, req.DestinationCID)
+ c.sendResponse(
+ SignalCommandReject,
+ s.id(),
+ &CommandReject{
+ Reason: 0x0002, // Invalid CID in request
+ Data: endpoints,
+ })
+ return
+ }
+
+ // Silently discard the request if SCID failed to find the same match.
+ if req.SourceCID != cidLEAtt {
+ return
+ }
+
+ c.sendResponse(
+ SignalDisconnectResponse,
+ s.id(),
+ &DisconnectResponse{
+ DestinationCID: req.DestinationCID,
+ SourceCID: req.SourceCID,
+ })
+}
+
+// ConnectionParameterUpdateRequest implements Connection Parameter Update Request (0x12) [Vol 3, Part A, 4.20].
+func (c *Conn) handleConnectionParameterUpdateRequest(s sigCmd) {
+ // This command shall only be sent from the LE slave device to the LE master
+ // device and only if one or more of the LE slave Controller, the LE master
+ // Controller, the LE slave Host and the LE master Host do not support the
+ // Connection Parameters Request Link Layer Control Procedure ([Vol. 6] Part B,
+ // Section 5.1.7). If an LE slave Host receives a Connection Parameter Update
+ // Request packet it shall respond with a Command Reject packet with reason
+ // 0x0000 (Command not understood).
+ if c.param.Role() != roleMaster {
+ c.sendResponse(
+ SignalCommandReject,
+ s.id(),
+ &CommandReject{
+ Reason: 0x0000, // Command not understood.
+ })
+
+ return
+ }
+ var req ConnectionParameterUpdateRequest
+ if err := req.Unmarshal(s.data()); err != nil {
+ return
+ }
+
+ // LE Connection Update (0x08|0x0013) [Vol 2, Part E, 7.8.18]
+ c.hci.Send(&cmd.LEConnectionUpdate{
+ ConnectionHandle: c.param.ConnectionHandle(),
+ ConnIntervalMin: req.IntervalMin,
+ ConnIntervalMax: req.IntervalMax,
+ ConnLatency: req.SlaveLatency,
+ SupervisionTimeout: req.TimeoutMultiplier,
+ MinimumCELength: 0, // Informational, and spec doesn't specify the use.
+ MaximumCELength: 0, // Informational, and spec doesn't specify the use.
+ }, nil)
+
+ // Currently, we (as a slave host) accept all the parameters and forward
+ // it to the controller. The controller might update all, partial or even
+ // none (ignore) of the parameters. The slave(remote) host will be indicated
+ // by its controller if the update actually happens.
+ // TODO: allow users to implement what parameters to accept.
+ c.sendResponse(
+ SignalConnectionParameterUpdateResponse,
+ s.id(),
+ &ConnectionParameterUpdateResponse{
+ Result: 0, // Accept.
+ })
+}
+
+// LECreditBasedConnectionRequest ...
+func (c *Conn) LECreditBasedConnectionRequest(s sigCmd) {
+ // TODO:
+}
+
+// LEFlowControlCredit ...
+func (c *Conn) LEFlowControlCredit(s sigCmd) {
+ // TODO:
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/signal_gen.go b/vendor/github.com/currantlabs/ble/linux/hci/signal_gen.go
new file mode 100644
index 0000000..6f2d988
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/signal_gen.go
@@ -0,0 +1,205 @@
+package hci
+
+import (
+ "bytes"
+ "encoding/binary"
+)
+
+// SignalCommandReject is the code of Command Reject signaling packet.
+const SignalCommandReject = 0x01
+
+// CommandReject implements Command Reject (0x01) [Vol 3, Part A, 4.1].
+type CommandReject struct {
+ Reason uint16
+ Data []byte
+}
+
+// Code returns the event code of the command.
+func (s CommandReject) Code() int { return 0x01 }
+
+// Marshal serializes the command parameters into binary form.
+func (s *CommandReject) Marshal() []byte {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *CommandReject) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
+
+// SignalDisconnectRequest is the code of Disconnect Request signaling packet.
+const SignalDisconnectRequest = 0x06
+
+// DisconnectRequest implements Disconnect Request (0x06) [Vol 3, Part A, 4.6].
+type DisconnectRequest struct {
+ DestinationCID uint16
+ SourceCID uint16
+}
+
+// Code returns the event code of the command.
+func (s DisconnectRequest) Code() int { return 0x06 }
+
+// Marshal serializes the command parameters into binary form.
+func (s *DisconnectRequest) Marshal() []byte {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *DisconnectRequest) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
+
+// SignalDisconnectResponse is the code of Disconnect Response signaling packet.
+const SignalDisconnectResponse = 0x07
+
+// DisconnectResponse implements Disconnect Response (0x07) [Vol 3, Part A, 4.7].
+type DisconnectResponse struct {
+ DestinationCID uint16
+ SourceCID uint16
+}
+
+// Code returns the event code of the command.
+func (s DisconnectResponse) Code() int { return 0x07 }
+
+// Marshal serializes the command parameters into binary form.
+func (s *DisconnectResponse) Marshal() []byte {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *DisconnectResponse) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
+
+// SignalConnectionParameterUpdateRequest is the code of Connection Parameter Update Request signaling packet.
+const SignalConnectionParameterUpdateRequest = 0x12
+
+// ConnectionParameterUpdateRequest implements Connection Parameter Update Request (0x12) [Vol 3, Part A, 4.20].
+type ConnectionParameterUpdateRequest struct {
+ IntervalMin uint16
+ IntervalMax uint16
+ SlaveLatency uint16
+ TimeoutMultiplier uint16
+}
+
+// Code returns the event code of the command.
+func (s ConnectionParameterUpdateRequest) Code() int { return 0x12 }
+
+// Marshal serializes the command parameters into binary form.
+func (s *ConnectionParameterUpdateRequest) Marshal() []byte {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *ConnectionParameterUpdateRequest) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
+
+// SignalConnectionParameterUpdateResponse is the code of Connection Parameter Update Response signaling packet.
+const SignalConnectionParameterUpdateResponse = 0x13
+
+// ConnectionParameterUpdateResponse implements Connection Parameter Update Response (0x13) [Vol 3, Part A, 4.21].
+type ConnectionParameterUpdateResponse struct {
+ Result uint16
+}
+
+// Code returns the event code of the command.
+func (s ConnectionParameterUpdateResponse) Code() int { return 0x13 }
+
+// Marshal serializes the command parameters into binary form.
+func (s *ConnectionParameterUpdateResponse) Marshal() []byte {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *ConnectionParameterUpdateResponse) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
+
+// SignalLECreditBasedConnectionRequest is the code of LE Credit Based Connection Request signaling packet.
+const SignalLECreditBasedConnectionRequest = 0x14
+
+// LECreditBasedConnectionRequest implements LE Credit Based Connection Request (0x14) [Vol 3, Part A, 4.22].
+type LECreditBasedConnectionRequest struct {
+ LEPSM uint16
+ SourceCID uint16
+ MTU uint16
+ MPS uint16
+ InitialCredits uint16
+}
+
+// Code returns the event code of the command.
+func (s LECreditBasedConnectionRequest) Code() int { return 0x14 }
+
+// Marshal serializes the command parameters into binary form.
+func (s *LECreditBasedConnectionRequest) Marshal() []byte {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *LECreditBasedConnectionRequest) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
+
+// SignalLECreditBasedConnectionResponse is the code of LE Credit Based Connection Response signaling packet.
+const SignalLECreditBasedConnectionResponse = 0x15
+
+// LECreditBasedConnectionResponse implements LE Credit Based Connection Response (0x15) [Vol 3, Part A, 4.23].
+type LECreditBasedConnectionResponse struct {
+ DestinationCID uint16
+ MTU uint16
+ MPS uint16
+ InitialCreditsCID uint16
+ Result uint16
+}
+
+// Code returns the event code of the command.
+func (s LECreditBasedConnectionResponse) Code() int { return 0x15 }
+
+// Marshal serializes the command parameters into binary form.
+func (s *LECreditBasedConnectionResponse) Marshal() []byte {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *LECreditBasedConnectionResponse) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
+
+// SignalLEFlowControlCredit is the code of LE Flow Control Credit signaling packet.
+const SignalLEFlowControlCredit = 0x16
+
+// LEFlowControlCredit implements LE Flow Control Credit (0x16) [Vol 3, Part A, 4.24].
+type LEFlowControlCredit struct {
+ CID uint16
+ Credits uint16
+}
+
+// Code returns the event code of the command.
+func (s LEFlowControlCredit) Code() int { return 0x16 }
+
+// Marshal serializes the command parameters into binary form.
+func (s *LEFlowControlCredit) Marshal() []byte {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *LEFlowControlCredit) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/smp.go b/vendor/github.com/currantlabs/ble/linux/hci/smp.go
new file mode 100644
index 0000000..87a43f8
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/smp.go
@@ -0,0 +1,61 @@
+package hci
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+)
+
+const (
+ pairingRequest = 0x01 // Pairing Request LE-U, ACL-U
+ pairingResponse = 0x02 // Pairing Response LE-U, ACL-U
+ pairingConfirm = 0x03 // Pairing Confirm LE-U
+ pairingRandom = 0x04 // Pairing Random LE-U
+ pairingFailed = 0x05 // Pairing Failed LE-U, ACL-U
+ encryptionInformation = 0x06 // Encryption Information LE-U
+ masterIdentification = 0x07 // Master Identification LE-U
+ identiInformation = 0x08 // Identity Information LE-U, ACL-U
+ identityAddreInformation = 0x09 // Identity Address Information LE-U, ACL-U
+ signingInformation = 0x0A // Signing Information LE-U, ACL-U
+ securityRequest = 0x0B // Security Request LE-U
+ pairingPublicKey = 0x0C // Pairing Public Key LE-U
+ pairingDHKeyCheck = 0x0D // Pairing DHKey Check LE-U
+ pairingKeypress = 0x0E // Pairing Keypress Notification LE-U
+)
+
+func (c *Conn) sendSMP(p pdu) error {
+ buf := bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, uint16(4+len(p)))
+ binary.Write(buf, binary.LittleEndian, cidSMP)
+ binary.Write(buf, binary.LittleEndian, p)
+ _, err := c.writePDU(buf.Bytes())
+ logger.Debug("smp", "send", fmt.Sprintf("[%X]", buf.Bytes()))
+ return err
+}
+
+func (c *Conn) handleSMP(p pdu) error {
+ logger.Debug("smp", "recv", fmt.Sprintf("[%X]", p))
+ code := p[0]
+ switch code {
+ case pairingRequest:
+ case pairingResponse:
+ case pairingConfirm:
+ case pairingRandom:
+ case pairingFailed:
+ case encryptionInformation:
+ case masterIdentification:
+ case identiInformation:
+ case identityAddreInformation:
+ case signingInformation:
+ case securityRequest:
+ case pairingPublicKey:
+ case pairingDHKeyCheck:
+ case pairingKeypress:
+ default:
+ // If a packet is received with a reserved Code it shall be ignored. [Vol 3, Part H, 3.3]
+ return nil
+ }
+ // FIXME: work aound to the lack of SMP implementation - always return non-supported.
+ // C.5.1 Pairing Not Supported by Slave
+ return c.sendSMP([]byte{pairingFailed, 0x05})
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/socket/dummy.go b/vendor/github.com/currantlabs/ble/linux/hci/socket/dummy.go
new file mode 100644
index 0000000..6062008
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/socket/dummy.go
@@ -0,0 +1,10 @@
+// +build !linux
+
+package socket
+
+import "io"
+
+// NewSocket is a dummy function for non-Linux platform.
+func NewSocket(id int) (io.ReadWriteCloser, error) {
+ return nil, nil
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/hci/socket/socket.go b/vendor/github.com/currantlabs/ble/linux/hci/socket/socket.go
new file mode 100644
index 0000000..312f600
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/hci/socket/socket.go
@@ -0,0 +1,146 @@
+// +build linux
+
+package socket
+
+import (
+ "fmt"
+ "io"
+ "sync"
+ "unsafe"
+
+ "github.com/pkg/errors"
+ "golang.org/x/sys/unix"
+)
+
+func ioR(t, nr, size uintptr) uintptr {
+ return (2 << 30) | (t << 8) | nr | (size << 16)
+}
+
+func ioW(t, nr, size uintptr) uintptr {
+ return (1 << 30) | (t << 8) | nr | (size << 16)
+}
+
+func ioctl(fd, op, arg uintptr) error {
+ if _, _, ep := unix.Syscall(unix.SYS_IOCTL, fd, op, arg); ep != 0 {
+ return ep
+ }
+ return nil
+}
+
+const (
+ ioctlSize = 4
+ hciMaxDevices = 16
+ typHCI = 72 // 'H'
+)
+
+var (
+ hciUpDevice = ioW(typHCI, 201, ioctlSize) // HCIDEVUP
+ hciDownDevice = ioW(typHCI, 202, ioctlSize) // HCIDEVDOWN
+ hciResetDevice = ioW(typHCI, 203, ioctlSize) // HCIDEVRESET
+ hciGetDeviceList = ioR(typHCI, 210, ioctlSize) // HCIGETDEVLIST
+ hciGetDeviceInfo = ioR(typHCI, 211, ioctlSize) // HCIGETDEVINFO
+)
+
+type devListRequest struct {
+ devNum uint16
+ devRequest [hciMaxDevices]struct {
+ id uint16
+ opt uint32
+ }
+}
+
+// Socket implements a HCI User Channel as ReadWriteCloser.
+type Socket struct {
+ fd int
+ closed chan struct{}
+ rmu sync.Mutex
+ wmu sync.Mutex
+}
+
+// NewSocket returns a HCI User Channel of specified device id.
+// If id is -1, the first available HCI device is returned.
+func NewSocket(id int) (*Socket, error) {
+ var err error
+ // Create RAW HCI Socket.
+ fd, err := unix.Socket(unix.AF_BLUETOOTH, unix.SOCK_RAW, unix.BTPROTO_HCI)
+ if err != nil {
+ return nil, errors.Wrap(err, "can't create socket")
+ }
+
+ if id != -1 {
+ return open(fd, id)
+ }
+
+ req := devListRequest{devNum: hciMaxDevices}
+ if err = ioctl(uintptr(fd), hciGetDeviceList, uintptr(unsafe.Pointer(&req))); err != nil {
+ return nil, errors.Wrap(err, "can't get device list")
+ }
+ var msg string
+ for id := 0; id < int(req.devNum); id++ {
+ s, err := open(fd, id)
+ if err == nil {
+ return s, nil
+ }
+ msg = msg + fmt.Sprintf("(hci%d: %s)", id, err)
+ }
+ return nil, errors.Errorf("no devices available: %s", msg)
+}
+
+func open(fd, id int) (*Socket, error) {
+ // Reset the device in case previous session didn't cleanup properly.
+ if err := ioctl(uintptr(fd), hciDownDevice, uintptr(id)); err != nil {
+ return nil, errors.Wrap(err, "can't down device")
+ }
+ if err := ioctl(uintptr(fd), hciUpDevice, uintptr(id)); err != nil {
+ return nil, errors.Wrap(err, "can't up device")
+ }
+
+ // HCI User Channel requires exclusive access to the device.
+ // The device has to be down at the time of binding.
+ if err := ioctl(uintptr(fd), hciDownDevice, uintptr(id)); err != nil {
+ return nil, errors.Wrap(err, "can't down device")
+ }
+
+ // Bind the RAW socket to HCI User Channel
+ sa := unix.SockaddrHCI{Dev: uint16(id), Channel: unix.HCI_CHANNEL_USER}
+ if err := unix.Bind(fd, &sa); err != nil {
+ return nil, errors.Wrap(err, "can't bind socket to hci user channel")
+ }
+
+ // poll for 20ms to see if any data becomes available, then clear it
+ pfds := []unix.PollFd{unix.PollFd{Fd: int32(fd), Events: unix.POLLIN}}
+ unix.Poll(pfds, 20)
+ if pfds[0].Revents&unix.POLLIN > 0 {
+ b := make([]byte, 100)
+ unix.Read(fd, b)
+ }
+
+ return &Socket{fd: fd, closed: make(chan struct{})}, nil
+}
+
+func (s *Socket) Read(p []byte) (int, error) {
+ select {
+ case <-s.closed:
+ return 0, io.EOF
+ default:
+ }
+ s.rmu.Lock()
+ defer s.rmu.Unlock()
+ n, err := unix.Read(s.fd, p)
+ return n, errors.Wrap(err, "can't read hci socket")
+}
+
+func (s *Socket) Write(p []byte) (int, error) {
+ s.wmu.Lock()
+ defer s.wmu.Unlock()
+ n, err := unix.Write(s.fd, p)
+ return n, errors.Wrap(err, "can't write hci socket")
+}
+
+func (s *Socket) Close() error {
+ close(s.closed)
+ s.Write([]byte{0x01, 0x09, 0x10, 0x00})
+ s.rmu.Lock()
+ defer s.rmu.Unlock()
+ return errors.Wrap(unix.Close(s.fd), "can't close hci socket")
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/Makefile b/vendor/github.com/currantlabs/ble/linux/tools/codegen/Makefile
new file mode 100644
index 0000000..72412d3
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/Makefile
@@ -0,0 +1,12 @@
+signal_out="../../l2cap/signal_gen.go"
+cmd_out="../../hci/cmd/cmd_gen.go"
+evt_out="../../hci/evt/evt_gen.go"
+att_out="../../att/att_gen.go"
+
+targets := signal cmd evt att
+
+all: ${targets}
+
+${targets}:
+ go run codegen.go -tmpl $@ -out ${$@_out} && goimports -w ${$@_out}
+
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/att.json b/vendor/github.com/currantlabs/ble/linux/tools/codegen/att.json
new file mode 100644
index 0000000..62cbb01
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/att.json
@@ -0,0 +1,431 @@
+{
+ "Atts": [
+ {
+ "Name": "Error Response",
+ "Spec": "Vol 3, Part E, 3.4.1.1",
+ "Code": "0x01",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Request Opcode In Error": "uint8"
+ },
+ {
+ "Attribute In Error": "uint16"
+ },
+ {
+ "Error Code": "uint8"
+ }
+ ]
+ },
+ {
+ "Name": "Exchange MTU Request",
+ "Spec": "Vol 3, Part E, 3.4.2.1",
+ "Code": "0x02",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Client Rx MTU": "uint16"
+ }
+ ]
+ },
+ {
+ "Name": "Exchange MTU Response",
+ "Spec": "Vol 3, Part E, 3.4.2.2",
+ "Code": "0x03",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Server Rx MTU": "uint16"
+ }
+ ]
+ },
+ {
+ "Name": "Find Information Request",
+ "Spec": "Vol 3, Part E, 3.4.3.1",
+ "Code": "0x04",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Starting Handle": "uint16"
+ },
+ {
+ "Ending Handle": "uint16"
+ }
+ ]
+ },
+ {
+ "Name": "Find Information Response",
+ "Spec": "Vol 3, Part E, 3.4.3.2",
+ "Code": "0x05",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Format": "uint8"
+ },
+ {
+ "Information Data": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Find By Type Value Request",
+ "Spec": "Vol 3, Part E, 3.4.3.3",
+ "Code": "0x06",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Starting Handle": "uint16"
+ },
+ {
+ "Ending Handle": "uint16"
+ },
+ {
+ "Attribute Type": "uint16"
+ },
+ {
+ "Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Find By Type Value Response",
+ "Spec": "Vol 3, Part E, 3.4.3.4",
+ "Code": "0x07",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Handle Information List": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Read By Type Request",
+ "Spec": "Vol 3, Part E, 3.4.4.1",
+ "Code": "0x08",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Starting Handle": "uint16"
+ },
+ {
+ "Ending Handle": "uint16"
+ },
+ {
+ "Attribute Type": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Read By Type Response",
+ "Spec": "Vol 3, Part E, 3.4.4.2",
+ "Code": "0x09",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Length": "uint8"
+ },
+ {
+ "Attribute Data List": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Read Request",
+ "Spec": "Vol 3, Part E, 3.4.4.3",
+ "Code": "0x0A",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ }
+ ]
+ },
+ {
+ "Name": "Read Response",
+ "Spec": "Vol 3, Part E, 3.4.4.4",
+ "Code": "0x0B",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Read Blob Request",
+ "Spec": "Vol 3, Part E, 3.4.4.5",
+ "Code": "0x0C",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ },
+ {
+ "Value Offset": "uint16"
+ }
+ ]
+ },
+ {
+ "Name": "Read Blob Response",
+ "Spec": "Vol 3, Part E, 3.4.4.6",
+ "Code": "0x0D",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Part Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Read Multiple Request",
+ "Spec": "Vol 3, Part E, 3.4.4.7",
+ "Code": "0x0E",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Set Of Handles": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Read Multiple Response",
+ "Spec": "Vol 3, Part E, 3.4.4.8",
+ "Code": "0x0F",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Set Of Values": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Read By Group Type Request",
+ "Spec": "Vol 3, Part E, 3.4.4.9",
+ "Code": "0x10",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Starting Handle": "uint16"
+ },
+ {
+ "Ending Handle": "uint16"
+ },
+ {
+ "Attribute Group Type": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Read By Group Type Response",
+ "Spec": "Vol 3, Part E, 3.4.4.10",
+ "Code": "0x11",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Length": "uint8"
+ },
+ {
+ "Attribute Data List": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Write Request",
+ "Spec": "Vol 3, Part E, 3.4.5.1",
+ "Code": "0x12",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ },
+ {
+ "Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Write Response",
+ "Spec": "Vol 3, Part E, 3.4.5.2",
+ "Code": "0x13",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ }
+ ]
+ },
+ {
+ "Name": "Write Command",
+ "Spec": "Vol 3, Part E, 3.4.5.3",
+ "Code": "0x52",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ },
+ {
+ "Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Signed Write Command",
+ "Spec": "Vol 3, Part E, 3.4.5.4",
+ "Code": "0xD2",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ },
+ {
+ "Attribute Value": "[]byte"
+ },
+ {
+ "Authentication Signature": "[12]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Prepare Write Request",
+ "Spec": "Vol 3, Part E, 3.4.6.1",
+ "Code": "0x16",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ },
+ {
+ "Value Offset": "uint16"
+ },
+ {
+ "Part Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Prepare Write Response",
+ "Spec": "Vol 3, Part E, 3.4.6.2",
+ "Code": "0x17",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ },
+ {
+ "Value Offset": "uint16"
+ },
+ {
+ "Part Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Execute Write Request",
+ "Spec": "Vol 3, Part E, 3.4.6.3",
+ "Code": "0x18",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Flags": "uint8"
+ }
+ ]
+ },
+ {
+ "Name": "Execute Write Response",
+ "Spec": "Vol 3, Part E, 3.4.6.4",
+ "Code": "0x19",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ }
+ ]
+ },
+ {
+ "Name": "Handle Value Notification",
+ "Spec": "Vol 3, Part E, 3.4.7.1",
+ "Code": "0x1B",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ },
+ {
+ "Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Handle Value Indication",
+ "Spec": "Vol 3, Part E, 3.4.7.2",
+ "Code": "0x1D",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ },
+ {
+ "Attribute Handle": "uint16"
+ },
+ {
+ "Attribute Value": "[]byte"
+ }
+ ]
+ },
+ {
+ "Name": "Handle Value Confirmation",
+ "Spec": "Vol 3, Part E, 3.4.7.3",
+ "Code": "0x1E",
+ "Param": [
+ {
+ "Attribute Opcode": "uint8"
+ }
+ ]
+ }
+ ]
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/att.tmpl b/vendor/github.com/currantlabs/ble/linux/tools/codegen/att.tmpl
new file mode 100644
index 0000000..1929144
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/att.tmpl
@@ -0,0 +1,7 @@
+{{reset}}{{$n := (esc .Name)}}
+// {{$n}}Code ...
+const {{$n}}Code = {{.Code}}
+{{$c := .Code}}// {{$n}} implements {{.Name}} ({{.Code}}) [{{.Spec}}].
+type {{$n}} []byte
+{{range .Param}} {{range $k, $v := .}} {{roy $n $c (esc $k) $v}} {{end}}
+{{end}}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/cmd.json b/vendor/github.com/currantlabs/ble/linux/tools/codegen/cmd.json
new file mode 100644
index 0000000..73e4cce
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/cmd.json
@@ -0,0 +1,1246 @@
+{
+ "LinkControl": [
+ {
+ "Name": "Disconnect",
+ "Spec": "Vol 2, Part E, 7.1.6",
+ "OGF": "0x01",
+ "OCF": "0x0006",
+ "Len": 3,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Reason": "uint8"
+ }
+ ],
+ "Return": [],
+ "Events": [
+ "Command Status"
+ ]
+ },
+ {
+ "Name": "Read Remote Version Information",
+ "Spec": "Vol 2, Part E, 7.1.23",
+ "OGF": "0x01",
+ "OCF": "0x001D",
+ "Len": 2,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Return": [],
+ "Events": [
+ "Command Status",
+ "Read Remote Version Information Complete"
+ ]
+ }
+ ],
+ "LinkPolicy": [
+ {
+ "Name": "Write Default Link Policy Settings",
+ "Spec": "Vol 2, Part E, 7.2.12",
+ "OGF": "0x02",
+ "OCF": "0x000D",
+ "Len": 2,
+ "Param": [
+ {
+ "Default Link Policy Settings": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ }
+ ],
+ "HostControl": [
+ {
+ "Name": "Set Event Mask",
+ "Spec": "Vol 2, Part E, 7.3.1",
+ "OGF": "0x03",
+ "OCF": "0x0001",
+ "Len": 8,
+ "Param": [
+ {
+ "Event Mask": "uint64"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Reset",
+ "Spec": "Vol 2, Part E, 7.3.2",
+ "OGF": "0x03",
+ "OCF": "0x003",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Write Page Timeout",
+ "Spec": "Vol 2, Part E, 7.3.16",
+ "OGF": "0x03",
+ "OCF": "0x0018",
+ "Len": 2,
+ "Param": [
+ {
+ "Page Timeout": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Write Class Of Device",
+ "Spec": "Vol 2, Part E, 7.3.26",
+ "OGF": "0x03",
+ "OCF": "0x0024",
+ "Len": 3,
+ "Param": [
+ {
+ "ClassOfDevice": "[3]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Read Transmit Power Level",
+ "Spec": "Vol 2, Part E, 7.3.35",
+ "OGF": "0x03",
+ "OCF": "0x002D",
+ "Len": 3,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Type": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Host Buffer Size",
+ "Spec": "Vol 2, Part E, 7.3.39",
+ "OGF": "0x03",
+ "OCF": "0x0033",
+ "Len": 7,
+ "Param": [
+ {
+ "Host ACL Data Packet Length": "uint16"
+ },
+ {
+ "Host Synchronous Data Packet Length": "uint8"
+ },
+ {
+ "Host Total Num ACL Data Packets": "uint16"
+ },
+ {
+ "Host Total Num Synchronous Data Packets": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Host Number Of Completed Packets",
+ "Spec": "Vol 2, Part E, 7.3.40",
+ "OGF": "0x03",
+ "OCF": "0x0035",
+ "Len": -1,
+ "Param": [
+ {
+ "Number Of Handles": "uint8"
+ },
+ {
+ "Connection Handle": "[]uint16"
+ },
+ {
+ "Host Num Of Completed Packets": "[]uint16"
+ }
+ ],
+ "Return": [],
+ "Events": []
+ },
+ {
+ "Name": "Set Event Mask Page 2",
+ "Spec": "Vol 2, Part E, 7.3.69",
+ "OGF": "0x03",
+ "OCF": "0x0063",
+ "Len": 8,
+ "Param": [
+ {
+ "Event Mask Page2": "uint64"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Write LE Host Support",
+ "Spec": "Vol 2, Part E, 7.3.79",
+ "OGF": "0x03",
+ "OCF": "0x006D",
+ "Len": 2,
+ "Param": [
+ {
+ "LE Supported Host": "uint8"
+ },
+ {
+ "Simultaneous LE Host": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Read Authenticated Payload Timeout",
+ "Spec": "Vol 2, Part E, 7.3.93",
+ "OGF": "0x03",
+ "OCF": "0x007B",
+ "Len": 2,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Authenticated Payload Timeout": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Write Authenticated Payload Timeout",
+ "Spec": "Vol 2, Part E, 7.3.94",
+ "OGF": "0x01",
+ "OCF": "0x007C",
+ "Len": 4,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Authenticated Payload Timeout": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Status",
+ "Read Remote Version Information Complete"
+ ]
+ }
+ ],
+ "InfoParam": [
+ {
+ "Name": "Read Local Version Information",
+ "Spec": "Vol 2, Part E, 7.4.1",
+ "OGF": "0x04",
+ "OCF": "0x0001",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "HCI Version": "uint8"
+ },
+ {
+ "HCI Revision": "uint16"
+ },
+ {
+ "LMP/PAM Version": "uint8"
+ },
+ {
+ "Manufacturer Name": "uint16"
+ },
+ {
+ "LMP/PAM Subversion": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Read Local Supported Commands",
+ "Spec": "Vol 2, Part E, 7.4.2",
+ "OGF": "0x04",
+ "OCF": "0x0002",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Supporteds": "uint64"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Read Local Supported Features",
+ "Spec": "Vol 2, Part E, 7.4.3",
+ "OGF": "0x04",
+ "OCF": "0x0003",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "LMP Features": "uint64"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Read Buffer Size",
+ "Spec": "Vol 2, Part E, 7.4.5",
+ "OGF": "0x04",
+ "OCF": "0x0005",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "HC ACL Data Packet Length": "uint16"
+ },
+ {
+ "HC Synchronous Data Packet Length": "uint8"
+ },
+ {
+ "HC Total Num ACL Data Packets": "uint16"
+ },
+ {
+ "HC Total Num Synchronous Data Packets": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "Read BD_ADDR",
+ "Spec": "Vol 2, Part E, 7.4.6",
+ "OGF": "0x04",
+ "OCF": "0x0009",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "BDADDR": "[6]byte"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ }
+ ],
+ "StatusParam": [
+ {
+ "Name": "Read RSSI",
+ "Spec": "Vol 2, Part E, 7.5.4",
+ "OGF": "0x05",
+ "OCF": "0x0005",
+ "Len": 2,
+ "Param": [
+ {
+ "Handle": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "RSSI": "int8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ }
+ ],
+ "LEControl": [
+ {
+ "Name": "LE Set Event Mask",
+ "Spec": "Vol 2, Part E, 7.8.1",
+ "OGF": "0x08",
+ "OCF": "0x0001",
+ "Len": 8,
+ "Param": [
+ {
+ "LE Event Mask": "uint64"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Read Buffer Size",
+ "Spec": "Vol 2, Part E, 7.8.2",
+ "OGF": "0x08",
+ "OCF": "0x0002",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "HC LE Data Packet Length": "uint16"
+ },
+ {
+ "HC Total Num LE Data Packets": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Read Local Supported Features",
+ "Spec": "Vol 2, Part E, 7.8.3",
+ "OGF": "0x08",
+ "OCF": "0x0003",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "LE Features": "uint64"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Set Random Address",
+ "Spec": "Vol 2, Part E, 7.8.4",
+ "OGF": "0x08",
+ "OCF": "0x0005",
+ "Len": 6,
+ "Param": [
+ {
+ "RandomAddress": "[6]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Set Advertising Parameters",
+ "Spec": "Vol 2, Part E, 7.8.5",
+ "OGF": "0x08",
+ "OCF": "0x0006",
+ "Len": 15,
+ "Param": [
+ {
+ "Advertising Interval Min": "uint16"
+ },
+ {
+ "Advertising Interval Max": "uint16"
+ },
+ {
+ "Advertising Type": "uint8"
+ },
+ {
+ "Own Address Type": "uint8"
+ },
+ {
+ "Direct Address Type": "uint8"
+ },
+ {
+ "Direct Address": "[6]byte"
+ },
+ {
+ "Advertising Channel Map": "uint8"
+ },
+ {
+ "Advertising Filter Policy": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Read Advertising Channel Tx Power",
+ "Spec": "Vol 2, Part E, 7.8.6",
+ "OGF": "0x08",
+ "OCF": "0x0007",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Transmit Power Level": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Set Advertising Data",
+ "Spec": "Vol 2, Part E, 7.8.7",
+ "OGF": "0x08",
+ "OCF": "0x0008",
+ "Len": 32,
+ "Param": [
+ {
+ "Advertising Data Length": "uint8"
+ },
+ {
+ "Advertising Data": "[31]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "HC LE Data Packet Length": "uint16"
+ },
+ {
+ "HC Total Num LE Data Packets": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Set Scan Response Data",
+ "Spec": "Vol 2, Part E, 7.8.8",
+ "OGF": "0x08",
+ "OCF": "0x0009",
+ "Len": 32,
+ "Param": [
+ {
+ "Scan Response Data Length": "uint8"
+ },
+ {
+ "Scan Response Data": "[31]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Set Advertise Enable",
+ "Spec": "Vol 2, Part E, 7.8.9",
+ "OGF": "0x08",
+ "OCF": "0x000A",
+ "Len": 1,
+ "Param": [
+ {
+ "Advertising Enable": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Set Scan Parameters",
+ "Spec": "Vol 2, Part E, 7.8.10",
+ "OGF": "0x08",
+ "OCF": "0x000B",
+ "Len": 7,
+ "Param": [
+ {
+ "LE Scan Type": "uint8"
+ },
+ {
+ "LE Scan Interval": "uint16"
+ },
+ {
+ "LE Scan Window": "uint16"
+ },
+ {
+ "Own Address Type": "uint8"
+ },
+ {
+ "Scanning Filter Policy": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Set Scan Enable",
+ "Spec": "Vol 2, Part E, 7.8.11",
+ "OGF": "0x08",
+ "OCF": "0x000C",
+ "Len": 2,
+ "Param": [
+ {
+ "LE Scan Enable": "uint8"
+ },
+ {
+ "Filter Duplicates": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete",
+ "LE Advertising Report"
+ ]
+ },
+ {
+ "Name": "LE Create Connection",
+ "Spec": "Vol 2, Part E, 7.8.12",
+ "OGF": "0x08",
+ "OCF": "0x000D",
+ "Len": 25,
+ "Param": [
+ {
+ "LE Scan Interval": "uint16"
+ },
+ {
+ "LE Scan Window": "uint16"
+ },
+ {
+ "Initiator Filter Policy": "uint8"
+ },
+ {
+ "Peer Address Type": "uint8"
+ },
+ {
+ "Peer Address": "[6]byte"
+ },
+ {
+ "Own Address Type": "uint8"
+ },
+ {
+ "Conn Interval Min": "uint16"
+ },
+ {
+ "Conn Interval Max": "uint16"
+ },
+ {
+ "Conn Latency": "uint16"
+ },
+ {
+ "Supervision Timeout": "uint16"
+ },
+ {
+ "Minimum CE Length": "uint16"
+ },
+ {
+ "Maximum CE Length": "uint16"
+ }
+ ],
+ "Return": [],
+ "Events": [
+ "Command Status",
+ "LE Connection Complete"
+ ]
+ },
+ {
+ "Name": "LE Create Connection Cancel",
+ "Spec": "Vol 2, Part E, 7.8.13",
+ "OGF": "0x08",
+ "OCF": "0x000E",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete",
+ "LE Connection Complete"
+ ]
+ },
+ {
+ "Name": "LE Read White List Size",
+ "Spec": "Vol 2, Part E, 7.8.14",
+ "OGF": "0x08",
+ "OCF": "0x000F",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "White List Size": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Clear White List",
+ "Spec": "Vol 2, Part E, 7.8.15",
+ "OGF": "0x08",
+ "OCF": "0x0010",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Add Device To White List",
+ "Spec": "Vol 2, Part E, 7.8.16",
+ "OGF": "0x08",
+ "OCF": "0x0011",
+ "Len": 7,
+ "Param": [
+ {
+ "Address Type": "uint8"
+ },
+ {
+ "Address": "[6]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Remove Device From White List",
+ "Spec": "Vol 2, Part E, 7.8.17",
+ "OGF": "0x08",
+ "OCF": "0x0012",
+ "Len": 7,
+ "Param": [
+ {
+ "Address Type": "uint8"
+ },
+ {
+ "Address": "[6]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Connection Update",
+ "Spec": "Vol 2, Part E, 7.8.18",
+ "OGF": "0x08",
+ "OCF": "0x0013",
+ "Len": 14,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Conn Interval Min": "uint16"
+ },
+ {
+ "Conn Interval Max": "uint16"
+ },
+ {
+ "Conn Latency": "uint16"
+ },
+ {
+ "Supervision Timeout": "uint16"
+ },
+ {
+ "Minimum CE Length": "uint16"
+ },
+ {
+ "Maximum CE Length": "uint16"
+ }
+ ],
+ "Return": [],
+ "Events": [
+ "Command Status",
+ "LE Connection Update Complete"
+ ]
+ },
+ {
+ "Name": "LE Set Host Channel Classification",
+ "Spec": "Vol 2, Part E, 7.8.19",
+ "OGF": "0x08",
+ "OCF": "0x0014",
+ "Len": 5,
+ "Param": [
+ {
+ "Channel Map": "[5]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Read Channel Map",
+ "Spec": "Vol 2, Part E, 7.8.20",
+ "OGF": "0x08",
+ "OCF": "0x0015",
+ "Len": 2,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Channel Map": "[5]byte"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Read Remote Used Features",
+ "Spec": "Vol 2, Part E, 7.8.21",
+ "OGF": "0x08",
+ "OCF": "0x0016",
+ "Len": 2,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Return": [],
+ "Events": [
+ "Command Status",
+ "LE Read Remote Used Features Complete"
+ ]
+ },
+ {
+ "Name": "LE Encrypt",
+ "Spec": "Vol 2, Part E, 7.8.22",
+ "OGF": "0x08",
+ "OCF": "0x0017",
+ "Len": 32,
+ "Param": [
+ {
+ "Key": "[16]byte"
+ },
+ {
+ "Plaintext Data": "[16]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Encrypted Data": "[16]byte"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Rand",
+ "Spec": "Vol 2, Part E, 7.8.23",
+ "OGF": "0x08",
+ "OCF": "0x0018",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Random Number": "uint64"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Start Encryption",
+ "Spec": "Vol 2, Part E, 7.8.24",
+ "OGF": "0x08",
+ "OCF": "0x0019",
+ "Len": 28,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Random Number": "uint64"
+ },
+ {
+ "Encrypted Diversifier": "uint16"
+ },
+ {
+ "Long Term Key": "[16]byte"
+ }
+ ],
+ "Return": [],
+ "Events": [
+ "Command Status",
+ "Encryption Change",
+ "Encryption Key Refresh Complete"
+ ]
+ },
+ {
+ "Name": "LE Long Term Key Request Reply",
+ "Spec": "Vol 2, Part E, 7.8.25",
+ "OGF": "0x08",
+ "OCF": "0x001A",
+ "Len": 18,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Long Term Key": "[16]byte"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Long Term Key Request Negative Reply",
+ "Spec": "Vol 2, Part E, 7.8.26",
+ "OGF": "0x08",
+ "OCF": "0x001B",
+ "Len": 2,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Read Supported States",
+ "Spec": "Vol 2, Part E, 7.8.27",
+ "OGF": "0x08",
+ "OCF": "0x001C",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "LE States": "uint64"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Receiver Test",
+ "Spec": "Vol 2, Part E, 7.8.28",
+ "OGF": "0x08",
+ "OCF": "0x001D",
+ "Len": 1,
+ "Param": [
+ {
+ "RX Channel": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Transmitter Test",
+ "Spec": "Vol 2, Part E, 7.8.29",
+ "OGF": "0x08",
+ "OCF": "0x001E",
+ "Len": 3,
+ "Param": [
+ {
+ "TX Channel": "uint8"
+ },
+ {
+ "Length Of Test Data": "uint8"
+ },
+ {
+ "Packet Payload": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Test End",
+ "Spec": "Vol 2, Part E, 7.8.30",
+ "OGF": "0x08",
+ "OCF": "0x001F",
+ "Len": 0,
+ "Param": [],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Number Of Packats": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Remote Connection Parameter Request Reply",
+ "Spec": "Vol 2, Part E, 7.8.31",
+ "OGF": "0x08",
+ "OCF": "0x0020",
+ "Len": 14,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Interval Min": "uint16"
+ },
+ {
+ "Interval Max": "uint16"
+ },
+ {
+ "Latency": "uint16"
+ },
+ {
+ "Timeout": "uint16"
+ },
+ {
+ "Minimum CE Length": "uint16"
+ },
+ {
+ "Maximum CE Length": "uint16"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ },
+ {
+ "Name": "LE Remote Connection Parameter Request Negative Reply",
+ "Spec": "Vol 2, Part E, 7.8.32",
+ "OGF": "0x08",
+ "OCF": "0x0021",
+ "Len": 3,
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Reason": "uint8"
+ }
+ ],
+ "Return": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "Events": [
+ "Command Complete"
+ ]
+ }
+ ]
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/cmd.tmpl b/vendor/github.com/currantlabs/ble/linux/tools/codegen/cmd.tmpl
new file mode 100644
index 0000000..02359a8
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/cmd.tmpl
@@ -0,0 +1,29 @@
+// {{esc .Name}} {{printf "implements %s (%s|%s) [%s]" .Name .OGF .OCF .Spec}}
+type {{esc .Name}} struct {
+{{range .Param}}{{range $k, $v := .}}{{printf "\t%s\t%s\n" (esc $k) $v}}{{end}}{{end}}
+}
+
+func (c *{{esc .Name}}) String() string {
+ return {{printf "\"%s (%s|%s)\"" .Name .OGF .OCF}}
+}
+
+// OpCode returns the opcode of the command.
+func (c *{{esc .Name}}) OpCode() int { return {{printf "%s<<10 | %s" .OGF .OCF}} }
+
+// Len returns the length of the command.
+func (c *{{esc .Name}}) Len() int { return {{.Len}} }
+{{if ge .Len 0}}
+// Marshal serializes the command parameters into binary form.
+func (c *{{esc .Name}}) Marshal(b []byte) error {
+ return marshal(c, b)
+}
+{{end}}
+{{if .Return}}
+// {{esc .Name}}RP returns the return parameter of {{.Name}}
+type {{esc .Name}}RP struct {
+{{range .Return}}{{range $k, $v := .}}{{printf "\t%s\t%s\n" (esc $k) $v}}{{end}}{{end}}
+}
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (c *{{esc .Name}}RP) Unmarshal(b []byte) error {
+ return unmarshal(c, b)
+}{{end}}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/codegen.go b/vendor/github.com/currantlabs/ble/linux/tools/codegen/codegen.go
new file mode 100644
index 0000000..1ea1ecd
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/codegen.go
@@ -0,0 +1,301 @@
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+ "text/template"
+)
+
+var (
+ out = flag.String("out", "", "help message for flagname")
+ tmpl = flag.String("tmpl", "", "help message for flagname")
+)
+
+var cnt = 0
+
+var funcMap = template.FuncMap{
+ "esc": func(s string) string {
+ s = strings.Replace(s, " ", "", -1)
+ s = strings.Replace(s, "/", "", -1)
+ s = strings.Replace(s, "_", "", -1)
+ return s
+ },
+ "reset": func() string {
+ cnt = 0
+ return ""
+ },
+ "roy": func(n, c, k, v string) string {
+ var s string
+ switch v {
+ case "uint8":
+ s += fmt.Sprintf("// %s ...\n", k)
+ s += fmt.Sprintf("func (r %s) %s () %s { return r[%d]}\n", n, k, v, cnt)
+ s += fmt.Sprintf("// Set%s ...\n", k)
+ if k == "AttributeOpcode" {
+ s += fmt.Sprintf("func (r %s) Set%s () { r[%d] = %s}", n, k, cnt, c)
+ } else {
+ s += fmt.Sprintf("func (r %s) Set%s (v %s) { r[%d] = v}", n, k, v, cnt)
+ }
+ cnt++
+ case "uint16":
+ s += fmt.Sprintf("// %s ...\n", k)
+ s += fmt.Sprintf("func (r %s) %s () %s { return binary.LittleEndian.Uint16(r[%d:])}\n", n, k, v, cnt)
+ s += fmt.Sprintf("// Set%s ...\n", k)
+ s += fmt.Sprintf("func (r %s) Set%s (v %s) { binary.LittleEndian.PutUint16(r[%d:], v)}", n, k, v, cnt)
+ cnt += 2
+ case "uint64":
+ s += fmt.Sprintf("// %s ...\n", k)
+ s += fmt.Sprintf("func (r %s) %s () %s { return binary.LittleEndian.Uint64(r[%d:])}\n", n, k, v, cnt)
+ s += fmt.Sprintf("// Set%s ...\n", k)
+ s += fmt.Sprintf("func (r %s) Set%s (v %s) { binary.LittleEndian.PutUint64(r[%d:], v)}", n, k, v, cnt)
+ cnt += 8
+ case "[]byte":
+ s += fmt.Sprintf("// %s ...\n", k)
+ s += fmt.Sprintf("func (r %s) %s () %s { return r[%d:]}\n", n, k, v, cnt)
+ s += fmt.Sprintf("// Set%s ...\n", k)
+ s += fmt.Sprintf("func (r %s) Set%s (v %s) { copy(r[%d:], v)}", n, k, v, cnt)
+ case "[6]byte":
+ s += fmt.Sprintf("// %s ...\n", k)
+ s += fmt.Sprintf(`func (r %s) %s () %s {
+ b:=[6]byte{}
+ copy(b[:], r[%d:])
+ return b
+ }
+ `, n, k, v, cnt)
+ s += fmt.Sprintf("// Set%s ...\n", k)
+ s += fmt.Sprintf(`func (r %s) Set%s (v %s) { copy(r[%d:%d+6], v[:]) }`, n, k, v, cnt, cnt)
+ cnt += 6
+ case "[12]byte":
+ s += fmt.Sprintf("// %s ...\n", k)
+ s += fmt.Sprintf(`func (r %s) %s () %s {
+ b:=[12]byte{}
+ copy(b[:], r[%d:])
+ return b
+ }
+ `, n, k, v, cnt)
+ s += fmt.Sprintf("// Set%s ...\n", k)
+ s += fmt.Sprintf(`func (r %s) Set%s (v %s) { copy(r[%d:%d+12], v[:]) }`, n, k, v, cnt, cnt)
+ cnt += 12
+ default:
+ s += fmt.Sprintf("XXX: %s, %s, %s", n, k, v)
+ }
+ return s
+ },
+ "getter": func(n, c, k, v string) string {
+ var s string
+ switch v {
+ case "uint8":
+ s = fmt.Sprintf("func (r %s) %s () %s { return r[%d]}\n", n, k, v, cnt)
+ cnt++
+ case "uint16":
+ s = fmt.Sprintf("func (r %s) %s () %s { return binary.LittleEndian.Uint16(r[%d:])}\n", n, k, v, cnt)
+ cnt += 2
+ case "uint64":
+ s = fmt.Sprintf("func (r %s) %s () %s { return binary.LittleEndian.Uint64(r[%d:])}\n", n, k, v, cnt)
+ cnt += 8
+ case "[]byte":
+ s = fmt.Sprintf("func (r %s) %s () %s { return r[%d:]}\n", n, k, v, cnt)
+ case "[6]byte":
+ s = fmt.Sprintf(`func (r %s) %s () %s {
+ b:=[6]byte{}
+ copy(b[:], r[%d:])
+ return b
+ }
+ `, n, k, v, cnt)
+ cnt += 6
+ case "[12]byte":
+ s = fmt.Sprintf(`func (r %s) %s () %s {
+ b:=[12]byte{}
+ copy(b[:], r[%d:])
+ return b
+ }
+ `, n, k, v, cnt)
+ cnt += 12
+ default:
+ s += fmt.Sprintf("XXX: %s, %s, %s", n, k, v)
+ }
+ return s
+ },
+}
+
+func input(s string) []byte {
+ fi, err := os.Open(s)
+ if err != nil {
+ panic(err)
+ }
+ b, err := ioutil.ReadAll(fi)
+ if err != nil {
+ panic(err)
+ }
+ return b
+}
+
+func output(s string) io.WriteCloser {
+ w, err := os.Create(s)
+ if err != nil {
+ panic(err)
+ }
+ return w
+}
+
+type field map[string]string
+
+type cmd struct {
+ Name string // Command Name
+ Spec string // Specification
+ OGF string // OoCode Group Field
+ OCF string // OpCode Command Firld
+ Len int // Parameter Total Length
+ Param []field // Command Parameters
+ Return []field // Return Parameters
+ Events []string // Relevant events
+}
+
+type commands struct {
+ LinkControl []cmd
+ LinkPolicy []cmd
+ HostControl []cmd
+ InfoParam []cmd
+ StatusParam []cmd
+ LEControl []cmd
+}
+
+func genCmd(b []byte, w io.Writer, t *template.Template) {
+ var cmds commands
+ if err := json.Unmarshal(b, &cmds); err != nil {
+ log.Printf("failed to read spec.json, err: %s", err)
+ }
+
+ gen := func(t *template.Template, w io.Writer, cmds []cmd) {
+ for _, c := range cmds {
+ if err := t.Execute(w, c); err != nil {
+ log.Fatalf("execution: %s", err)
+ }
+ }
+ }
+
+ gen(t, w, cmds.LinkControl)
+ gen(t, w, cmds.LinkPolicy)
+ gen(t, w, cmds.HostControl)
+ gen(t, w, cmds.InfoParam)
+ gen(t, w, cmds.StatusParam)
+ gen(t, w, cmds.LEControl)
+}
+
+type evt struct {
+ Name string
+ Spec string
+ Code string
+ SubCode string
+ Param []field
+ DefaultUnmarshaller bool
+}
+
+type events struct {
+ Events []evt
+}
+
+func genEvt(b []byte, w io.Writer, t *template.Template) {
+ var evts events
+ if err := json.Unmarshal(b, &evts); err != nil {
+ log.Printf("failed to read spec.json, err: %s", err)
+ }
+ for _, e := range evts.Events {
+ if err := t.Execute(w, e); err != nil {
+ log.Fatalf("execution: %s", err)
+ }
+ }
+}
+
+// Signal Packet format
+type signal struct {
+ Name string
+ Spec string
+ Code string
+ Fields []field
+ Type string
+}
+
+type signals struct {
+ Signals []signal
+}
+
+func genSignal(b []byte, w io.Writer, t *template.Template) {
+ var signals signals
+ if err := json.Unmarshal(b, &signals); err != nil {
+ log.Printf("failed to read spec.json, err: %s", err)
+ }
+ for _, p := range signals.Signals {
+ if err := t.Execute(w, p); err != nil {
+ log.Fatalf("execution: %s", err)
+ }
+ }
+}
+
+type att struct {
+ Name string
+ Spec string
+ Code string
+ Param []field
+ Type string
+}
+
+type atts struct {
+ Atts []att
+}
+
+func genAtt(b []byte, w io.Writer, t *template.Template) {
+ var atts atts
+ if err := json.Unmarshal(b, &atts); err != nil {
+ log.Printf("failed to read spec.json, err: %s", err)
+ }
+ for _, p := range atts.Atts {
+ if err := t.Execute(w, p); err != nil {
+ log.Fatalf("execution: %s", err)
+ }
+ }
+}
+
+func main() {
+ flag.Parse()
+
+ b := input(*tmpl + ".json")
+ w := output(*out)
+
+ switch *tmpl {
+ case "cmd":
+ fmt.Fprintf(w, "package %s\n", *tmpl)
+ t, err := template.New(*tmpl).Funcs(funcMap).Parse(string(input("cmd.tmpl")))
+ if err != nil {
+ log.Fatalf("parsing: %s", err)
+ }
+ genCmd(b, w, t)
+ case "evt":
+ fmt.Fprintf(w, "package %s\n", *tmpl)
+ t, err := template.New(*tmpl).Funcs(funcMap).Parse(string(input("evt.tmpl")))
+ if err != nil {
+ log.Fatalf("parsing: %s", err)
+ }
+ genEvt(b, w, t)
+ case "signal":
+ fmt.Fprintf(w, "package l2cap\n")
+ t, err := template.New(*tmpl).Funcs(funcMap).Parse(string(input("signal.tmpl")))
+ if err != nil {
+ log.Fatalf("parsing: %s", err)
+ }
+ genSignal(b, w, t)
+ case "att":
+ fmt.Fprintf(w, "package att\n")
+ t, err := template.New(*tmpl).Funcs(funcMap).Parse(string(input("att.tmpl")))
+ if err != nil {
+ log.Fatalf("parsing: %s", err)
+ }
+ genAtt(b, w, t)
+ }
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/evt.json b/vendor/github.com/currantlabs/ble/linux/tools/codegen/evt.json
new file mode 100644
index 0000000..5d842e1
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/evt.json
@@ -0,0 +1,327 @@
+{
+ "Events": [
+ {
+ "Name": "Disconnection Complete",
+ "Spec": "Vol 2, Part E, 7.7.5",
+ "Code": "0x05",
+ "Param": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Reason": "uint8"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "Encryption Change",
+ "Spec": "Vol 2, Part E, 7.7.8",
+ "Code": "0x08",
+ "Param": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Encryption Enabled": "uint8"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "Read Remote Version Information Complete",
+ "Spec": "Vol 2, Part E, 7.7.12",
+ "Code": "0x0C",
+ "Param": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Version": "uint8"
+ },
+ {
+ "Manufacturer Name": "uint16"
+ },
+ {
+ "Subversion": "uint16"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "Command Complete",
+ "Spec": "Vol 2, Part E, 7.7.14",
+ "Code": "0x0E",
+ "Param": [
+ {
+ "Num HCI Command Packets": "uint8"
+ },
+ {
+ "Command Opcode": "uint16"
+ },
+ {
+ "Return Parameters": "[]byte"
+ }
+ ],
+ "DefaultUnmarshaller": false
+ },
+ {
+ "Name": "Command Status",
+ "Spec": "Vol 2, Part E, 7.7.15",
+ "Code": "0x0F",
+ "Param": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Num HCI Command Packets": "uint8"
+ },
+ {
+ "Command Opcode": "uint16"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "Hardware Error",
+ "Spec": "Vol 2, Part E, 7.7.16",
+ "Code": "0x10",
+ "Param": [
+ {
+ "Hardware Code": "uint8"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "Number Of Completed Packets",
+ "Spec": "Vol 2, Part E, 7.7.19",
+ "Code": "0x13",
+ "Param": [
+ {
+ "Number Of Handles": "uint8"
+ },
+ {
+ "Connection Handle": "[]uint16"
+ },
+ {
+ "HC Num Of Completed Packets": "[]uint16"
+ }
+ ],
+ "DefaultUnmarshaller": false
+ },
+ {
+ "Name": "Data Buffer Overflow",
+ "Spec": "Vol 2, Part E, 7.7.26",
+ "Code": "0x1A",
+ "Param": [
+ {
+ "Link Type": "uint8"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "Encryption Key Refresh Complete",
+ "Spec": "Vol 2, Part E, 7.7.39",
+ "Code": "0x30",
+ "Param": [
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "LE Connection Complete",
+ "Spec": "Vol 2, Part E, 7.7.65.1",
+ "Code": "0x3E",
+ "SubCode": "0x01",
+ "Param": [
+ {
+ "Subevent Code": "uint8"
+ },
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Role": "uint8"
+ },
+ {
+ "Peer Address Type": "uint8"
+ },
+ {
+ "Peer Address": "[6]byte"
+ },
+ {
+ "Conn Interval": "uint16"
+ },
+ {
+ "Conn Latency": "uint16"
+ },
+ {
+ "Supervision Timeout": "uint16"
+ },
+ {
+ "Master Clock Accuracy": "uint8"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "LE Advertising Report",
+ "Spec": "Vol 2, Part E, 7.7.65.2",
+ "Code": "0x3E",
+ "SubCode": "0x02",
+ "Param": [
+ {
+ "Subevent Code": "uint8"
+ },
+ {
+ "Num Reports": "uint8"
+ },
+ {
+ "Event Type": "[]uint8"
+ },
+ {
+ "Address Type": "[]uint8"
+ },
+ {
+ "Address": "[][6]byte"
+ },
+ {
+ "Length": "[]uint8"
+ },
+ {
+ "Data": "[][]byte"
+ },
+ {
+ "RSSI": "[]int8"
+ }
+ ],
+ "DefaultUnmarshaller": false
+ },
+ {
+ "Name": "LE Connection Update Complete",
+ "Spec": "Vol 2, Part E, 7.7.65.3",
+ "Code": "0x0E",
+ "SubCode": "0x03",
+ "Param": [
+ {
+ "Subevent Code": "uint8"
+ },
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Conn Interval": "uint16"
+ },
+ {
+ "Conn Latency": "uint16"
+ },
+ {
+ "Supervision Timeout": "uint16"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "LE Read Remote Used Features Complete",
+ "Spec": "Vol 2, Part E, 7.7.65.4",
+ "Code": "0x3E",
+ "SubCode": "0x04",
+ "Param": [
+ {
+ "Subevent Code": "uint8"
+ },
+ {
+ "Status": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "LE Features": "uint64"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "LE Long Term Key Request",
+ "Spec": "Vol 2, Part E, 7.7.65.5",
+ "Code": "0x3E",
+ "SubCode": "0x05",
+ "Param": [
+ {
+ "Subevent Code": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Random Number": "uint64"
+ },
+ {
+ "Encryption Diversifier": "uint16"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "LE Remote Connection Parameter Request",
+ "Spec": "Vol 2, Part E, 7.7.65.6",
+ "Code": "0x3E",
+ "SubCode": "0x06",
+ "Param": [
+ {
+ "Subevent Code": "uint8"
+ },
+ {
+ "Connection Handle": "uint16"
+ },
+ {
+ "Interval Min": "uint16"
+ },
+ {
+ "Interval Max": "uint16"
+ },
+ {
+ "Latency": "uint16"
+ },
+ {
+ "Timeout": "uint16"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ },
+ {
+ "Name": "Authenticated Payload Timeout Expired",
+ "Spec": "Vol 2, Part E, 7.7.75",
+ "Code": "0x57",
+ "Param": [
+ {
+ "Connection Handle": "uint16"
+ }
+ ],
+ "DefaultUnmarshaller": true
+ }
+ ]
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/evt.tmpl b/vendor/github.com/currantlabs/ble/linux/tools/codegen/evt.tmpl
new file mode 100644
index 0000000..dd5ecec
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/evt.tmpl
@@ -0,0 +1,10 @@
+{{reset}}{{$n := (esc .Name)}}const {{$n}}Code = {{.Code}}
+{{if .SubCode}}
+const {{esc .Name}}SubCode = {{.SubCode}}
+{{end}}
+// {{esc .Name}} implements {{.Name}} ({{.Code}}{{if .SubCode}}:{{.SubCode}}{{end}}) [{{.Spec}}]. {{$c := .Code}}
+type {{$n}} []byte
+{{if .DefaultUnmarshaller}}
+{{range .Param}} {{range $k, $v := .}} {{getter $n $c (esc $k) $v}} {{end}}
+{{end}}
+{{end}}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/signal.json b/vendor/github.com/currantlabs/ble/linux/tools/codegen/signal.json
new file mode 100644
index 0000000..8284315
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/signal.json
@@ -0,0 +1,137 @@
+{
+ "Signals": [
+ {
+ "Name": "Command Reject",
+ "Spec": "Vol 3, Part A, 4.1",
+ "Code": "0x01",
+ "Fields": [
+ {
+ "Reason": "uint16"
+ },
+ {
+ "Data": "[]byte"
+ }
+ ],
+ "Type": "Request"
+ },
+ {
+ "Name": "Disconnect Request",
+ "Spec": "Vol 3, Part A, 4.6",
+ "Code": "0x06",
+ "Fields": [
+ {
+ "Destination CID": "uint16"
+ },
+ {
+ "Source CID": "uint16"
+ }
+ ],
+ "Type": "Request"
+ },
+ {
+ "Name": "Disconnect Response",
+ "Spec": "Vol 3, Part A, 4.7",
+ "Code": "0x07",
+ "Fields": [
+ {
+ "Destination CID": "uint16"
+ },
+ {
+ "Source CID": "uint16"
+ }
+ ],
+ "Type": "Response"
+ },
+ {
+ "Name": "Connection Parameter Update Request",
+ "Spec": "Vol 3, Part A, 4.20",
+ "Code": "0x12",
+ "Fields": [
+ {
+ "Interval Min": "uint16"
+ },
+ {
+ "Interval Max": "uint16"
+ },
+ {
+ "Slave Latency": "uint16"
+ },
+ {
+ "Timeout Multiplier": "uint16"
+ }
+ ],
+ "Type": "Request"
+ },
+ {
+ "Name": "Connection Parameter Update Response",
+ "Spec": "Vol 3, Part A, 4.21",
+ "Code": "0x13",
+ "Fields": [
+ {
+ "Result": "uint16"
+ }
+ ],
+ "Type": "Response"
+ },
+ {
+ "Name": "LE Credit Based Connection Request",
+ "Spec": "Vol 3, Part A, 4.22",
+ "Code": "0x14",
+ "Fields": [
+ {
+ "LE_PSM": "uint16"
+ },
+ {
+ "Source CID": "uint16"
+ },
+ {
+ "MTU": "uint16"
+ },
+ {
+ "MPS": "uint16"
+ },
+ {
+ "Initial Credits": "uint16"
+ }
+ ],
+ "Type": "Request"
+ },
+ {
+ "Name": "LE Credit Based Connection Response",
+ "Spec": "Vol 3, Part A, 4.23",
+ "Code": "0x15",
+ "Fields": [
+ {
+ "Destination CID": "uint16"
+ },
+ {
+ "MTU": "uint16"
+ },
+ {
+ "MPS": "uint16"
+ },
+ {
+ "Initial Credits CID": "uint16"
+ },
+ {
+ "Result": "uint16"
+ }
+ ],
+ "Type": "Response"
+ },
+ {
+ "Name": "LE Flow Control Credit",
+ "Spec": "Vol 3, Part A, 4.24",
+ "Code": "0x16",
+ "Fields": [
+ {
+ "CID": "uint16"
+ },
+ {
+ "Credits": "uint16"
+ }
+ ],
+ "Type": "Request"
+ }
+ ]
+}
diff --git a/vendor/github.com/currantlabs/ble/linux/tools/codegen/signal.tmpl b/vendor/github.com/currantlabs/ble/linux/tools/codegen/signal.tmpl
new file mode 100644
index 0000000..a17dd24
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/linux/tools/codegen/signal.tmpl
@@ -0,0 +1,19 @@
+// Signal{{esc .Name}} is the code of {{.Name}} signaling packet.
+const Signal{{esc .Name}} = {{.Code}}
+// {{esc .Name}} implements {{.Name}} ({{.Code}}) [{{.Spec}}].
+type {{esc .Name}} struct {
+{{range .Fields}}{{range $k, $v := .}}{{printf "\t%s\t%s\n" (esc $k) $v}}{{end}}{{end}}}
+// Code returns the event code of the command.
+func (s {{esc .Name}}) Code() int { return {{.Code}} }
+
+// Marshal serializes the command parameters into binary form.
+func (s *{{esc .Name}}) Marshal() []byte {
+ buf:= bytes.NewBuffer(make([]byte, 0))
+ binary.Write(buf, binary.LittleEndian, s)
+ return buf.Bytes()
+}
+
+// Unmarshal de-serializes the binary data and stores the result in the receiver.
+func (s *{{esc .Name}}) Unmarshal(b []byte) error {
+ return binary.Read(bytes.NewBuffer(b), binary.LittleEndian, s)
+}
diff --git a/vendor/github.com/currantlabs/ble/profile.go b/vendor/github.com/currantlabs/ble/profile.go
new file mode 100644
index 0000000..a59772d
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/profile.go
@@ -0,0 +1,221 @@
+package ble
+
+// NewService creates and initialize a new Service using u as it's UUID.
+func NewService(u UUID) *Service {
+ return &Service{UUID: u}
+}
+
+// NewDescriptor creates and returns a Descriptor.
+func NewDescriptor(u UUID) *Descriptor {
+ return &Descriptor{UUID: u}
+}
+
+// NewCharacteristic creates and returns a Characteristic.
+func NewCharacteristic(u UUID) *Characteristic {
+ return &Characteristic{UUID: u}
+}
+
+// Property ...
+type Property int
+
+// Characteristic property flags (spec 3.3.3.1)
+const (
+ CharBroadcast Property = 0x01 // may be brocasted
+ CharRead Property = 0x02 // may be read
+ CharWriteNR Property = 0x04 // may be written to, with no reply
+ CharWrite Property = 0x08 // may be written to, with a reply
+ CharNotify Property = 0x10 // supports notifications
+ CharIndicate Property = 0x20 // supports Indications
+ CharSignedWrite Property = 0x40 // supports signed write
+ CharExtended Property = 0x80 // supports extended properties
+)
+
+// A Profile is composed of one or more services necessary to fulfill a use case.
+type Profile struct {
+ Services []*Service
+}
+
+// Find searches discovered profile for the specified target's type and UUID.
+// The target must has the type of *Service, *Characteristic, or *Descriptor.
+func (p *Profile) Find(target interface{}) interface{} {
+ switch target.(type) {
+ case *Service:
+ case *Characteristic:
+ case *Descriptor:
+ default:
+ return nil
+ }
+ for _, s := range p.Services {
+ ts, ok := target.(*Service)
+ if ok && s.UUID.Equal(ts.UUID) {
+ target = s
+ return s
+ }
+ for _, c := range s.Characteristics {
+ tc, ok := target.(*Characteristic)
+ if ok && c.UUID.Equal(tc.UUID) {
+ return c
+ }
+ for _, d := range c.Descriptors {
+ td, ok := target.(*Descriptor)
+ if ok && d.UUID.Equal(td.UUID) {
+ return d
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// A Service is a BLE service.
+type Service struct {
+ UUID UUID
+ Characteristics []*Characteristic
+
+ Handle uint16
+ EndHandle uint16
+}
+
+// AddCharacteristic adds a characteristic to a service.
+// AddCharacteristic panics if the service already contains another characteristic with the same UUID.
+func (s *Service) AddCharacteristic(c *Characteristic) *Characteristic {
+ for _, x := range s.Characteristics {
+ if x.UUID.Equal(c.UUID) {
+ panic("service already contains a characteristic with UUID " + c.UUID.String())
+ }
+ }
+ s.Characteristics = append(s.Characteristics, c)
+ return c
+}
+
+// NewCharacteristic adds a characteristic to a service.
+// NewCharacteristic panics if the service already contains another characteristic with the same UUID.
+func (s *Service) NewCharacteristic(u UUID) *Characteristic {
+ return s.AddCharacteristic(&Characteristic{UUID: u})
+}
+
+// A Characteristic is a BLE characteristic.
+type Characteristic struct {
+ UUID UUID
+ Property Property
+ Secure Property // FIXME
+ Descriptors []*Descriptor
+ CCCD *Descriptor
+
+ Value []byte
+
+ ReadHandler ReadHandler
+ WriteHandler WriteHandler
+ NotifyHandler NotifyHandler
+ IndicateHandler NotifyHandler
+
+ Handle uint16
+ ValueHandle uint16
+ EndHandle uint16
+}
+
+// AddDescriptor adds a descriptor to a characteristic.
+// AddDescriptor panics if the characteristic already contains another descriptor with the same UUID.
+func (c *Characteristic) AddDescriptor(d *Descriptor) *Descriptor {
+ for _, x := range c.Descriptors {
+ if x.UUID.Equal(d.UUID) {
+ panic("service already contains a characteristic with UUID " + d.UUID.String())
+ }
+ }
+ c.Descriptors = append(c.Descriptors, d)
+ return d
+}
+
+// NewDescriptor adds a descriptor to a characteristic.
+// NewDescriptor panics if the characteristic already contains another descriptor with the same UUID.
+func (c *Characteristic) NewDescriptor(u UUID) *Descriptor {
+ return c.AddDescriptor(&Descriptor{UUID: u})
+}
+
+// SetValue makes the characteristic support read requests, and returns a static value.
+// SetValue must be called before the containing service is added to a server.
+// SetValue panics if the characteristic has been configured with a ReadHandler.
+func (c *Characteristic) SetValue(b []byte) {
+ if c.ReadHandler != nil {
+ panic("charactristic has been configured with a read handler")
+ }
+ c.Property |= CharRead
+ c.Value = make([]byte, len(b))
+ copy(c.Value, b)
+}
+
+// HandleRead makes the characteristic support read requests, and routes read requests to h.
+// HandleRead must be called before the containing service is added to a server.
+// HandleRead panics if the characteristic has been configured with a static value.
+func (c *Characteristic) HandleRead(h ReadHandler) {
+ if c.Value != nil {
+ panic("charactristic has been configured with a static value")
+ }
+ c.Property |= CharRead
+ c.ReadHandler = h
+}
+
+// HandleWrite makes the characteristic support write and write-no-response requests, and routes write requests to h.
+// The WriteHandler does not differentiate between write and write-no-response requests; it is handled automatically.
+// HandleWrite must be called before the containing service is added to a server.
+func (c *Characteristic) HandleWrite(h WriteHandler) {
+ c.Property |= CharWrite | CharWriteNR
+ c.WriteHandler = h
+}
+
+// HandleNotify makes the characteristic support notify requests, and routes notification requests to h.
+// HandleNotify must be called before the containing service is added to a server.
+func (c *Characteristic) HandleNotify(h NotifyHandler) {
+ c.Property |= CharNotify
+ c.NotifyHandler = h
+}
+
+// HandleIndicate makes the characteristic support indicate requests, and routes notification requests to h.
+// HandleIndicate must be called before the containing service is added to a server.
+func (c *Characteristic) HandleIndicate(h NotifyHandler) {
+ c.Property |= CharIndicate
+ c.IndicateHandler = h
+}
+
+// Descriptor is a BLE descriptor
+type Descriptor struct {
+ UUID UUID
+ Property Property
+
+ Handle uint16
+ Value []byte
+
+ ReadHandler ReadHandler
+ WriteHandler WriteHandler
+}
+
+// SetValue makes the descriptor support read requests, and returns a static value.
+// SetValue must be called before the containing service is added to a server.
+// SetValue panics if the descriptor has already configured with a ReadHandler.
+func (d *Descriptor) SetValue(b []byte) {
+ if d.ReadHandler != nil {
+ panic("descriptor has been configured with a read handler")
+ }
+ d.Property |= CharRead
+ d.Value = make([]byte, len(b))
+ copy(d.Value, b)
+}
+
+// HandleRead makes the descriptor support read requests, and routes read requests to h.
+// HandleRead must be called before the containing service is added to a server.
+// HandleRead panics if the descriptor has been configured with a static value.
+func (d *Descriptor) HandleRead(h ReadHandler) {
+ if d.Value != nil {
+ panic("descriptor has been configured with a static value")
+ }
+ d.Property |= CharRead
+ d.ReadHandler = h
+}
+
+// HandleWrite makes the descriptor support write and write-no-response requests, and routes write requests to h.
+// The WriteHandler does not differentiate between write and write-no-response requests; it is handled automatically.
+// HandleWrite must be called before the containing service is added to a server.
+func (d *Descriptor) HandleWrite(h WriteHandler) {
+ d.Property |= CharWrite | CharWriteNR
+ d.WriteHandler = h
+}
diff --git a/vendor/github.com/currantlabs/ble/uuid.go b/vendor/github.com/currantlabs/ble/uuid.go
new file mode 100644
index 0000000..79ebf33
--- /dev/null
+++ b/vendor/github.com/currantlabs/ble/uuid.go
@@ -0,0 +1,217 @@
+package ble
+
+import (
+ "bytes"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "strings"
+)
+
+// A UUID is a BLE UUID.
+type UUID []byte
+
+// UUID16 converts a uint16 (such as 0x1800) to a UUID.
+func UUID16(i uint16) UUID {
+ b := make([]byte, 2)
+ binary.LittleEndian.PutUint16(b, i)
+ return UUID(b)
+}
+
+// Parse parses a standard-format UUID string, such
+// as "1800" or "34DA3AD1-7110-41A1-B1EF-4430F509CDE7".
+func Parse(s string) (UUID, error) {
+ s = strings.Replace(s, "-", "", -1)
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ return nil, err
+ }
+ if err := lenErr(len(b)); err != nil {
+ return nil, err
+ }
+ return UUID(Reverse(b)), nil
+}
+
+// MustParse parses a standard-format UUID string,
+// like Parse, but panics in case of error.
+func MustParse(s string) UUID {
+ u, err := Parse(s)
+ if err != nil {
+ panic(err)
+ }
+ return u
+}
+
+// lenErr returns an error if n is an invalid UUID length.
+func lenErr(n int) error {
+ switch n {
+ case 2, 16:
+ return nil
+ }
+ return fmt.Errorf("UUIDs must have length 2 or 16, got %d", n)
+}
+
+// Len returns the length of the UUID, in bytes.
+// BLE UUIDs are either 2 or 16 bytes.
+func (u UUID) Len() int {
+ return len(u)
+}
+
+// String hex-encodes a UUID.
+func (u UUID) String() string {
+ return fmt.Sprintf("%x", Reverse(u))
+}
+
+// Equal returns a boolean reporting whether v represent the same UUID as u.
+func (u UUID) Equal(v UUID) bool {
+ return bytes.Equal(u, v)
+}
+
+// Contains returns a boolean reporting whether u is in the slice s.
+func Contains(s []UUID, u UUID) bool {
+ if s == nil {
+ return true
+ }
+
+ for _, a := range s {
+ if a.Equal(u) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Reverse returns a reversed copy of u.
+func Reverse(u []byte) []byte {
+ // Special-case 16 bit UUIDS for speed.
+ l := len(u)
+ if l == 2 {
+ return []byte{u[1], u[0]}
+ }
+ b := make([]byte, l)
+ for i := 0; i < l/2+1; i++ {
+ b[i], b[l-i-1] = u[l-i-1], u[i]
+ }
+ return b
+}
+
+// Name returns name of know services, characteristics, or descriptors.
+func Name(u UUID) string {
+ return knownUUID[u.String()].Name
+}
+
+// A dictionary of known service names and type (keyed by service uuid)
+var knownUUID = map[string]struct{ Name, Type string }{
+ "1800": {Name: "Generic Access", Type: "org.bluetooth.service.generic_access"},
+ "1801": {Name: "Generic Attribute", Type: "org.bluetooth.service.generic_attribute"},
+ "1802": {Name: "Immediate Alert", Type: "org.bluetooth.service.immediate_alert"},
+ "1803": {Name: "Link Loss", Type: "org.bluetooth.service.link_loss"},
+ "1804": {Name: "Tx Power", Type: "org.bluetooth.service.tx_power"},
+ "1805": {Name: "Current Time Service", Type: "org.bluetooth.service.current_time"},
+ "1806": {Name: "Reference Time Update Service", Type: "org.bluetooth.service.reference_time_update"},
+ "1807": {Name: "Next DST Change Service", Type: "org.bluetooth.service.next_dst_change"},
+ "1808": {Name: "Glucose", Type: "org.bluetooth.service.glucose"},
+ "1809": {Name: "Health Thermometer", Type: "org.bluetooth.service.health_thermometer"},
+ "180a": {Name: "Device Information", Type: "org.bluetooth.service.device_information"},
+ "180d": {Name: "Heart Rate", Type: "org.bluetooth.service.heart_rate"},
+ "180e": {Name: "Phone Alert Status Service", Type: "org.bluetooth.service.phone_alert_service"},
+ "180f": {Name: "Battery Service", Type: "org.bluetooth.service.battery_service"},
+ "1810": {Name: "Blood Pressure", Type: "org.bluetooth.service.blood_pressuer"},
+ "1811": {Name: "Alert Notification Service", Type: "org.bluetooth.service.alert_notification"},
+ "1812": {Name: "Human Interface Device", Type: "org.bluetooth.service.human_interface_device"},
+ "1813": {Name: "Scan Parameters", Type: "org.bluetooth.service.scan_parameters"},
+ "1814": {Name: "Running Speed and Cadence", Type: "org.bluetooth.service.running_speed_and_cadence"},
+ "1815": {Name: "Cycling Speed and Cadence", Type: "org.bluetooth.service.cycling_speed_and_cadence"},
+
+ // A dictionary of known descriptor names and type (keyed by attribute uuid)
+ "2800": {Name: "Primary Service", Type: "org.bluetooth.attribute.gatt.primary_service_declaration"},
+ "2801": {Name: "Secondary Service", Type: "org.bluetooth.attribute.gatt.secondary_service_declaration"},
+ "2802": {Name: "Include", Type: "org.bluetooth.attribute.gatt.include_declaration"},
+ "2803": {Name: "Characteristic", Type: "org.bluetooth.attribute.gatt.characteristic_declaration"},
+
+ // A dictionary of known descriptor names and type (keyed by descriptor uuid)
+ "2900": {Name: "Characteristic Extended Properties", Type: "org.bluetooth.descriptor.gatt.characteristic_extended_properties"},
+ "2901": {Name: "Characteristic User Description", Type: "org.bluetooth.descriptor.gatt.characteristic_user_description"},
+ "2902": {Name: "Client Characteristic Configuration", Type: "org.bluetooth.descriptor.gatt.client_characteristic_configuration"},
+ "2903": {Name: "Server Characteristic Configuration", Type: "org.bluetooth.descriptor.gatt.server_characteristic_configuration"},
+ "2904": {Name: "Characteristic Presentation Format", Type: "org.bluetooth.descriptor.gatt.characteristic_presentation_format"},
+ "2905": {Name: "Characteristic Aggregate Format", Type: "org.bluetooth.descriptor.gatt.characteristic_aggregate_format"},
+ "2906": {Name: "Valid Range", Type: "org.bluetooth.descriptor.valid_range"},
+ "2907": {Name: "External Report Reference", Type: "org.bluetooth.descriptor.external_report_reference"},
+ "2908": {Name: "Report Reference", Type: "org.bluetooth.descriptor.report_reference"},
+
+ // A dictionary of known characteristic names and type (keyed by characteristic uuid)
+ "2a00": {Name: "Device Name", Type: "org.bluetooth.characteristic.ble.device_name"},
+ "2a01": {Name: "Appearance", Type: "org.bluetooth.characteristic.ble.appearance"},
+ "2a02": {Name: "Peripheral Privacy Flag", Type: "org.bluetooth.characteristic.ble.peripheral_privacy_flag"},
+ "2a03": {Name: "Reconnection Address", Type: "org.bluetooth.characteristic.ble.reconnection_address"},
+ "2a04": {Name: "Peripheral Preferred Connection Parameters", Type: "org.bluetooth.characteristic.ble.peripheral_preferred_connection_parameters"},
+ "2a05": {Name: "Service Changed", Type: "org.bluetooth.characteristic.gatt.service_changed"},
+ "2a06": {Name: "Alert Level", Type: "org.bluetooth.characteristic.alert_level"},
+ "2a07": {Name: "Tx Power Level", Type: "org.bluetooth.characteristic.tx_power_level"},
+ "2a08": {Name: "Date Time", Type: "org.bluetooth.characteristic.date_time"},
+ "2a09": {Name: "Day of Week", Type: "org.bluetooth.characteristic.day_of_week"},
+ "2a0a": {Name: "Day Date Time", Type: "org.bluetooth.characteristic.day_date_time"},
+ "2a0c": {Name: "Exact Time 256", Type: "org.bluetooth.characteristic.exact_time_256"},
+ "2a0d": {Name: "DST Offset", Type: "org.bluetooth.characteristic.dst_offset"},
+ "2a0e": {Name: "Time Zone", Type: "org.bluetooth.characteristic.time_zone"},
+ "2a0f": {Name: "Local Time Information", Type: "org.bluetooth.characteristic.local_time_information"},
+ "2a11": {Name: "Time with DST", Type: "org.bluetooth.characteristic.time_with_dst"},
+ "2a12": {Name: "Time Accuracy", Type: "org.bluetooth.characteristic.time_accuracy"},
+ "2a13": {Name: "Time Source", Type: "org.bluetooth.characteristic.time_source"},
+ "2a14": {Name: "Reference Time Information", Type: "org.bluetooth.characteristic.reference_time_information"},
+ "2a16": {Name: "Time Update Control Point", Type: "org.bluetooth.characteristic.time_update_control_point"},
+ "2a17": {Name: "Time Update State", Type: "org.bluetooth.characteristic.time_update_state"},
+ "2a18": {Name: "Glucose Measurement", Type: "org.bluetooth.characteristic.glucose_measurement"},
+ "2a19": {Name: "Battery Level", Type: "org.bluetooth.characteristic.battery_level"},
+ "2a1c": {Name: "Temperature Measurement", Type: "org.bluetooth.characteristic.temperature_measurement"},
+ "2a1d": {Name: "Temperature Type", Type: "org.bluetooth.characteristic.temperature_type"},
+ "2a1e": {Name: "Intermediate Temperature", Type: "org.bluetooth.characteristic.intermediate_temperature"},
+ "2a21": {Name: "Measurement Interval", Type: "org.bluetooth.characteristic.measurement_interval"},
+ "2a22": {Name: "Boot Keyboard Input Report", Type: "org.bluetooth.characteristic.boot_keyboard_input_report"},
+ "2a23": {Name: "System ID", Type: "org.bluetooth.characteristic.system_id"},
+ "2a24": {Name: "Model Number String", Type: "org.bluetooth.characteristic.model_number_string"},
+ "2a25": {Name: "Serial Number String", Type: "org.bluetooth.characteristic.serial_number_string"},
+ "2a26": {Name: "Firmware Revision String", Type: "org.bluetooth.characteristic.firmware_revision_string"},
+ "2a27": {Name: "Hardware Revision String", Type: "org.bluetooth.characteristic.hardware_revision_string"},
+ "2a28": {Name: "Software Revision String", Type: "org.bluetooth.characteristic.software_revision_string"},
+ "2a29": {Name: "Manufacturer Name String", Type: "org.bluetooth.characteristic.manufacturer_name_string"},
+ "2a2a": {Name: "IEEE 11073-20601 Regulatory Certification Data List", Type: "org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list"},
+ "2a2b": {Name: "Current Time", Type: "org.bluetooth.characteristic.current_time"},
+ "2a31": {Name: "Scan Refresh", Type: "org.bluetooth.characteristic.scan_refresh"},
+ "2a32": {Name: "Boot Keyboard Output Report", Type: "org.bluetooth.characteristic.boot_keyboard_output_report"},
+ "2a33": {Name: "Boot Mouse Input Report", Type: "org.bluetooth.characteristic.boot_mouse_input_report"},
+ "2a34": {Name: "Glucose Measurement Context", Type: "org.bluetooth.characteristic.glucose_measurement_context"},
+ "2a35": {Name: "Blood Pressure Measurement", Type: "org.bluetooth.characteristic.blood_pressure_measurement"},
+ "2a36": {Name: "Intermediate Cuff Pressure", Type: "org.bluetooth.characteristic.intermediate_blood_pressure"},
+ "2a37": {Name: "Heart Rate Measurement", Type: "org.bluetooth.characteristic.heart_rate_measurement"},
+ "2a38": {Name: "Body Sensor Location", Type: "org.bluetooth.characteristic.body_sensor_location"},
+ "2a39": {Name: "Heart Rate Control Point", Type: "org.bluetooth.characteristic.heart_rate_control_point"},
+ "2a3f": {Name: "Alert Status", Type: "org.bluetooth.characteristic.alert_status"},
+ "2a40": {Name: "Ringer Control Point", Type: "org.bluetooth.characteristic.ringer_control_point"},
+ "2a41": {Name: "Ringer Setting", Type: "org.bluetooth.characteristic.ringer_setting"},
+ "2a42": {Name: "Alert Category ID Bit Mask", Type: "org.bluetooth.characteristic.alert_category_id_bit_mask"},
+ "2a43": {Name: "Alert Category ID", Type: "org.bluetooth.characteristic.alert_category_id"},
+ "2a44": {Name: "Alert Notification Control Point", Type: "org.bluetooth.characteristic.alert_notification_control_point"},
+ "2a45": {Name: "Unread Alert Status", Type: "org.bluetooth.characteristic.unread_alert_status"},
+ "2a46": {Name: "New Alert", Type: "org.bluetooth.characteristic.new_alert"},
+ "2a47": {Name: "Supported New Alert Category", Type: "org.bluetooth.characteristic.supported_new_alert_category"},
+ "2a48": {Name: "Supported Unread Alert Category", Type: "org.bluetooth.characteristic.supported_unread_alert_category"},
+ "2a49": {Name: "Blood Pressure Feature", Type: "org.bluetooth.characteristic.blood_pressure_feature"},
+ "2a4a": {Name: "HID Information", Type: "org.bluetooth.characteristic.hid_information"},
+ "2a4b": {Name: "Report Map", Type: "org.bluetooth.characteristic.report_map"},
+ "2a4c": {Name: "HID Control Point", Type: "org.bluetooth.characteristic.hid_control_point"},
+ "2a4d": {Name: "Report", Type: "org.bluetooth.characteristic.report"},
+ "2a4e": {Name: "Protocol Mode", Type: "org.bluetooth.characteristic.protocol_mode"},
+ "2a4f": {Name: "Scan Interval Window", Type: "org.bluetooth.characteristic.scan_interval_window"},
+ "2a50": {Name: "PnP ID", Type: "org.bluetooth.characteristic.pnp_id"},
+ "2a51": {Name: "Glucose Feature", Type: "org.bluetooth.characteristic.glucose_feature"},
+ "2a52": {Name: "Record Access Control Point", Type: "org.bluetooth.characteristic.record_access_control_point"},
+ "2a53": {Name: "RSC Measurement", Type: "org.bluetooth.characteristic.rsc_measurement"},
+ "2a54": {Name: "RSC Feature", Type: "org.bluetooth.characteristic.rsc_feature"},
+ "2a55": {Name: "SC Control Point", Type: "org.bluetooth.characteristic.sc_control_point"},
+ "2a5b": {Name: "CSC Measurement", Type: "org.bluetooth.characteristic.csc_measurement"},
+ "2a5c": {Name: "CSC Feature", Type: "org.bluetooth.characteristic.csc_feature"},
+ "2a5d": {Name: "Sensor Location", Type: "org.bluetooth.characteristic.sensor_location"},
+}
diff --git a/vendor/github.com/raff/goble/.gitignore b/vendor/github.com/raff/goble/.gitignore
new file mode 100644
index 0000000..8365624
--- /dev/null
+++ b/vendor/github.com/raff/goble/.gitignore
@@ -0,0 +1,23 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
diff --git a/vendor/github.com/raff/goble/LICENSE b/vendor/github.com/raff/goble/LICENSE
new file mode 100644
index 0000000..a4f7f21
--- /dev/null
+++ b/vendor/github.com/raff/goble/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Raffaele Sena
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/raff/goble/README.md b/vendor/github.com/raff/goble/README.md
new file mode 100644
index 0000000..2ec14c0
--- /dev/null
+++ b/vendor/github.com/raff/goble/README.md
@@ -0,0 +1,20 @@
+goble
+=====
+
+Go implementation of Bluetooth LE support for OSX (derived from noble/bleno)
+
+This is a port of nodejs [noble](https://github.com/sandeepmistry/noble)/[bleno](https://github.com/sandeepmistry/bleno) for OSX only.
+
+Once I have something working it can maybe integrated with [github.com/paypal/gatt](https://github.com/paypal/gatt), that right now is Linux only.
+
+## Installation
+
+ $ go get github.com/raff/goble
+
+## Documentation
+http://godoc.org/github.com/raff/goble
+
+## Examples
+* examples/main.go : an example of how to use most of the APIs
+* examples/discoverer.go : a port of nodejs noble "advertisement-discovery.js" example
+* examples/explorer.go : a port of nodejs noble "peripheral-explorer.js" example
diff --git a/vendor/github.com/raff/goble/characteristics.go b/vendor/github.com/raff/goble/characteristics.go
new file mode 100644
index 0000000..94590ee
--- /dev/null
+++ b/vendor/github.com/raff/goble/characteristics.go
@@ -0,0 +1,77 @@
+package goble
+
+// A dictionary of known characteristic names and type (keyed by characteristic uuid)
+var knownCharacteristics = map[string]struct{ Name, Type string }{
+ "2a00": {Name: "Device Name", Type: "org.bluetooth.characteristic.gap.device_name"},
+ "2a01": {Name: "Appearance", Type: "org.bluetooth.characteristic.gap.appearance"},
+ "2a02": {Name: "Peripheral Privacy Flag", Type: "org.bluetooth.characteristic.gap.peripheral_privacy_flag"},
+ "2a03": {Name: "Reconnection Address", Type: "org.bluetooth.characteristic.gap.reconnection_address"},
+ "2a04": {Name: "Peripheral Preferred Connection Parameters", Type: "org.bluetooth.characteristic.gap.peripheral_preferred_connection_parameters"},
+ "2a05": {Name: "Service Changed", Type: "org.bluetooth.characteristic.gatt.service_changed"},
+ "2a06": {Name: "Alert Level", Type: "org.bluetooth.characteristic.alert_level"},
+ "2a07": {Name: "Tx Power Level", Type: "org.bluetooth.characteristic.tx_power_level"},
+ "2a08": {Name: "Date Time", Type: "org.bluetooth.characteristic.date_time"},
+ "2a09": {Name: "Day of Week", Type: "org.bluetooth.characteristic.day_of_week"},
+ "2a0a": {Name: "Day Date Time", Type: "org.bluetooth.characteristic.day_date_time"},
+ "2a0c": {Name: "Exact Time 256", Type: "org.bluetooth.characteristic.exact_time_256"},
+ "2a0d": {Name: "DST Offset", Type: "org.bluetooth.characteristic.dst_offset"},
+ "2a0e": {Name: "Time Zone", Type: "org.bluetooth.characteristic.time_zone"},
+ "2a0f": {Name: "Local Time Information", Type: "org.bluetooth.characteristic.local_time_information"},
+ "2a11": {Name: "Time with DST", Type: "org.bluetooth.characteristic.time_with_dst"},
+ "2a12": {Name: "Time Accuracy", Type: "org.bluetooth.characteristic.time_accuracy"},
+ "2a13": {Name: "Time Source", Type: "org.bluetooth.characteristic.time_source"},
+ "2a14": {Name: "Reference Time Information", Type: "org.bluetooth.characteristic.reference_time_information"},
+ "2a16": {Name: "Time Update Control Point", Type: "org.bluetooth.characteristic.time_update_control_point"},
+ "2a17": {Name: "Time Update State", Type: "org.bluetooth.characteristic.time_update_state"},
+ "2a18": {Name: "Glucose Measurement", Type: "org.bluetooth.characteristic.glucose_measurement"},
+ "2a19": {Name: "Battery Level", Type: "org.bluetooth.characteristic.battery_level"},
+ "2a1c": {Name: "Temperature Measurement", Type: "org.bluetooth.characteristic.temperature_measurement"},
+ "2a1d": {Name: "Temperature Type", Type: "org.bluetooth.characteristic.temperature_type"},
+ "2a1e": {Name: "Intermediate Temperature", Type: "org.bluetooth.characteristic.intermediate_temperature"},
+ "2a21": {Name: "Measurement Interval", Type: "org.bluetooth.characteristic.measurement_interval"},
+ "2a22": {Name: "Boot Keyboard Input Report", Type: "org.bluetooth.characteristic.boot_keyboard_input_report"},
+ "2a23": {Name: "System ID", Type: "org.bluetooth.characteristic.system_id"},
+ "2a24": {Name: "Model Number String", Type: "org.bluetooth.characteristic.model_number_string"},
+ "2a25": {Name: "Serial Number String", Type: "org.bluetooth.characteristic.serial_number_string"},
+ "2a26": {Name: "Firmware Revision String", Type: "org.bluetooth.characteristic.firmware_revision_string"},
+ "2a27": {Name: "Hardware Revision String", Type: "org.bluetooth.characteristic.hardware_revision_string"},
+ "2a28": {Name: "Software Revision String", Type: "org.bluetooth.characteristic.software_revision_string"},
+ "2a29": {Name: "Manufacturer Name String", Type: "org.bluetooth.characteristic.manufacturer_name_string"},
+ "2a2a": {Name: "IEEE 11073-20601 Regulatory Certification Data List", Type: "org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list"},
+ "2a2b": {Name: "Current Time", Type: "org.bluetooth.characteristic.current_time"},
+ "2a31": {Name: "Scan Refresh", Type: "org.bluetooth.characteristic.scan_refresh"},
+ "2a32": {Name: "Boot Keyboard Output Report", Type: "org.bluetooth.characteristic.boot_keyboard_output_report"},
+ "2a33": {Name: "Boot Mouse Input Report", Type: "org.bluetooth.characteristic.boot_mouse_input_report"},
+ "2a34": {Name: "Glucose Measurement Context", Type: "org.bluetooth.characteristic.glucose_measurement_context"},
+ "2a35": {Name: "Blood Pressure Measurement", Type: "org.bluetooth.characteristic.blood_pressure_measurement"},
+ "2a36": {Name: "Intermediate Cuff Pressure", Type: "org.bluetooth.characteristic.intermediate_blood_pressure"},
+ "2a37": {Name: "Heart Rate Measurement", Type: "org.bluetooth.characteristic.heart_rate_measurement"},
+ "2a38": {Name: "Body Sensor Location", Type: "org.bluetooth.characteristic.body_sensor_location"},
+ "2a39": {Name: "Heart Rate Control Point", Type: "org.bluetooth.characteristic.heart_rate_control_point"},
+ "2a3f": {Name: "Alert Status", Type: "org.bluetooth.characteristic.alert_status"},
+ "2a40": {Name: "Ringer Control Point", Type: "org.bluetooth.characteristic.ringer_control_point"},
+ "2a41": {Name: "Ringer Setting", Type: "org.bluetooth.characteristic.ringer_setting"},
+ "2a42": {Name: "Alert Category ID Bit Mask", Type: "org.bluetooth.characteristic.alert_category_id_bit_mask"},
+ "2a43": {Name: "Alert Category ID", Type: "org.bluetooth.characteristic.alert_category_id"},
+ "2a44": {Name: "Alert Notification Control Point", Type: "org.bluetooth.characteristic.alert_notification_control_point"},
+ "2a45": {Name: "Unread Alert Status", Type: "org.bluetooth.characteristic.unread_alert_status"},
+ "2a46": {Name: "New Alert", Type: "org.bluetooth.characteristic.new_alert"},
+ "2a47": {Name: "Supported New Alert Category", Type: "org.bluetooth.characteristic.supported_new_alert_category"},
+ "2a48": {Name: "Supported Unread Alert Category", Type: "org.bluetooth.characteristic.supported_unread_alert_category"},
+ "2a49": {Name: "Blood Pressure Feature", Type: "org.bluetooth.characteristic.blood_pressure_feature"},
+ "2a4a": {Name: "HID Information", Type: "org.bluetooth.characteristic.hid_information"},
+ "2a4b": {Name: "Report Map", Type: "org.bluetooth.characteristic.report_map"},
+ "2a4c": {Name: "HID Control Point", Type: "org.bluetooth.characteristic.hid_control_point"},
+ "2a4d": {Name: "Report", Type: "org.bluetooth.characteristic.report"},
+ "2a4e": {Name: "Protocol Mode", Type: "org.bluetooth.characteristic.protocol_mode"},
+ "2a4f": {Name: "Scan Interval Window", Type: "org.bluetooth.characteristic.scan_interval_window"},
+ "2a50": {Name: "PnP ID", Type: "org.bluetooth.characteristic.pnp_id"},
+ "2a51": {Name: "Glucose Feature", Type: "org.bluetooth.characteristic.glucose_feature"},
+ "2a52": {Name: "Record Access Control Point", Type: "org.bluetooth.characteristic.record_access_control_point"},
+ "2a53": {Name: "RSC Measurement", Type: "org.bluetooth.characteristic.rsc_measurement"},
+ "2a54": {Name: "RSC Feature", Type: "org.bluetooth.characteristic.rsc_feature"},
+ "2a55": {Name: "SC Control Point", Type: "org.bluetooth.characteristic.sc_control_point"},
+ "2a5b": {Name: "CSC Measurement", Type: "org.bluetooth.characteristic.csc_measurement"},
+ "2a5c": {Name: "CSC Feature", Type: "org.bluetooth.characteristic.csc_feature"},
+ "2a5d": {Name: "Sensor Location", Type: "org.bluetooth.characteristic.sensor_location"},
+}
diff --git a/vendor/github.com/raff/goble/descriptors.go b/vendor/github.com/raff/goble/descriptors.go
new file mode 100644
index 0000000..e8e647f
--- /dev/null
+++ b/vendor/github.com/raff/goble/descriptors.go
@@ -0,0 +1,14 @@
+package goble
+
+// A dictionary of known descriptor names and type (keyed by descriptor uuid)
+var knownDescriptors = map[string]struct{ Name, Type string }{
+ "2900": {Name: "Characteristic Extended Properties", Type: "org.bluetooth.descriptor.gatt.characteristic_extended_properties"},
+ "2901": {Name: "Characteristic User Description", Type: "org.bluetooth.descriptor.gatt.characteristic_user_description"},
+ "2902": {Name: "Client Characteristic Configuration", Type: "org.bluetooth.descriptor.gatt.client_characteristic_configuration"},
+ "2903": {Name: "Server Characteristic Configuration", Type: "org.bluetooth.descriptor.gatt.server_characteristic_configuration"},
+ "2904": {Name: "Characteristic Presentation Format", Type: "org.bluetooth.descriptor.gatt.characteristic_presentation_format"},
+ "2905": {Name: "Characteristic Aggregate Format", Type: "org.bluetooth.descriptor.gatt.characteristic_aggregate_format"},
+ "2906": {Name: "Valid Range", Type: "org.bluetooth.descriptor.valid_range"},
+ "2907": {Name: "External Report Reference", Type: "org.bluetooth.descriptor.external_report_reference"},
+ "2908": {Name: "Report Reference", Type: "org.bluetooth.descriptor.report_reference"},
+}
diff --git a/vendor/github.com/raff/goble/emitter.go b/vendor/github.com/raff/goble/emitter.go
new file mode 100644
index 0000000..e06b9c1
--- /dev/null
+++ b/vendor/github.com/raff/goble/emitter.go
@@ -0,0 +1,82 @@
+package goble
+
+import (
+ "log"
+
+ "github.com/raff/goble/xpc"
+)
+
+const (
+ ALL = "__allEvents__"
+)
+
+// Event generated by blued, with associated data
+type Event struct {
+ Name string
+ State string
+ DeviceUUID xpc.UUID
+ ServiceUuid string
+ CharacteristicUuid string
+ Peripheral Peripheral
+ Data []byte
+ Mtu int
+ IsNotification bool
+}
+
+// The event handler function.
+// Return true to terminate
+type EventHandlerFunc func(Event) bool
+
+// Emitter is an object to emit and handle Event(s)
+type Emitter struct {
+ handlers map[string]EventHandlerFunc
+ event chan Event
+ verbose bool
+}
+
+// Init initialize the emitter and start a goroutine to execute the event handlers
+func (e *Emitter) Init() {
+ e.handlers = make(map[string]EventHandlerFunc)
+ e.event = make(chan Event)
+
+ // event handler
+ go func() {
+ for {
+ ev := <-e.event
+
+ if fn, ok := e.handlers[ev.Name]; ok {
+ if fn(ev) {
+ break
+ }
+ } else if fn, ok := e.handlers[ALL]; ok {
+ if fn(ev) {
+ break
+ }
+ } else {
+ if e.verbose {
+ log.Println("unhandled Emit", ev)
+ }
+ }
+ }
+
+ close(e.event) // TOFIX: this causes new "emits" to panic.
+ }()
+}
+
+func (e *Emitter) SetVerbose(v bool) {
+ e.verbose = v
+}
+
+// Emit sends the event on the 'event' channel
+func (e *Emitter) Emit(ev Event) {
+ e.event <- ev
+}
+
+// On(event, cb) registers an handler for the specified event
+func (e *Emitter) On(event string, fn EventHandlerFunc) {
+ if fn == nil {
+ delete(e.handlers, event)
+ } else {
+ e.handlers[event] = fn
+ }
+}
diff --git a/vendor/github.com/raff/goble/examples/beacon.go b/vendor/github.com/raff/goble/examples/beacon.go
new file mode 100644
index 0000000..90adaa5
--- /dev/null
+++ b/vendor/github.com/raff/goble/examples/beacon.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+ "flag"
+ "log"
+ "time"
+
+ "github.com/raff/goble"
+ "github.com/raff/goble/xpc"
+)
+
+func main() {
+ uuid := flag.String("uuid", "1BEAC099-BEAC-BEAC-BEAC-BEAC09BEAC09", "iBeacon UUID")
+ major := flag.Int("major", 0, "iBeacon major value (uint16)")
+ minor := flag.Int("minor", 0, "iBeacon minor value (uint16)")
+ power := flag.Int("power", -57, "iBeacon measured power (int8)")
+ d := flag.Duration("duration", 1*time.Minute, "advertising duration")
+ verbose := flag.Bool("verbose", false, "dump all events")
+ flag.Parse()
+
+ ble := goble.New()
+ ble.SetVerbose(*verbose)
+ ble.Init()
+
+ var utsname xpc.Utsname
+ xpc.Uname(&utsname)
+ log.Println(utsname.Release)
+
+ time.Sleep(1 * time.Second)
+
+ log.Println("Start Advertising", *uuid, *major, *minor, *power)
+ ble.StartAdvertisingIBeacon(xpc.MustUUID(*uuid), uint16(*major), uint16(*minor), int8(*power))
+
+ time.Sleep(*d)
+
+ log.Println("Stop Advertising")
+ ble.StopAdvertising()
+}
diff --git a/vendor/github.com/raff/goble/examples/discoverer.go b/vendor/github.com/raff/goble/examples/discoverer.go
new file mode 100644
index 0000000..206bc1f
--- /dev/null
+++ b/vendor/github.com/raff/goble/examples/discoverer.go
@@ -0,0 +1,74 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+
+ "github.com/raff/goble"
+)
+
+func main() {
+ verbose := flag.Bool("verbose", false, "dump all events")
+ dups := flag.Bool("allow-duplicates", false, "allow duplicates when scanning")
+ flag.Parse()
+
+ ble := goble.New()
+ ble.SetVerbose(*verbose)
+
+ if *verbose {
+ ble.On(goble.ALL, func(ev goble.Event) (done bool) {
+ log.Println("Event", ev)
+ return
+ })
+ }
+
+ ble.On("stateChange", func(ev goble.Event) (done bool) {
+ if ev.State == "poweredOn" {
+ ble.StartScanning(nil, *dups)
+ } else {
+ ble.StopScanning()
+ done = true
+ }
+
+ return
+ })
+
+ ble.On("discover", func(ev goble.Event) (done bool) {
+ fmt.Println()
+ fmt.Println("peripheral discovered (", ev.DeviceUUID, "):")
+ fmt.Println("\thello my local name is:")
+ fmt.Println("\t\t", ev.Peripheral.Advertisement.LocalName)
+ fmt.Println("\tcan I interest you in any of the following advertised services:")
+ fmt.Println("\t\t", ev.Peripheral.Advertisement.ServiceUuids)
+
+ serviceData := ev.Peripheral.Advertisement.ServiceData
+ if len(serviceData) > 0 {
+ fmt.Println("\there is my service data:")
+ for _, d := range serviceData {
+ fmt.Println("\t\t", d.Uuid, ":", d.Data)
+ }
+ }
+
+ if len(ev.Peripheral.Advertisement.ManufacturerData) > 0 {
+ fmt.Println("\there is my manufacturer data:")
+ fmt.Println("\t\t", ev.Peripheral.Advertisement.ManufacturerData)
+ }
+
+ if ev.Peripheral.Advertisement.TxPowerLevel != 0 {
+ fmt.Println("\tmy TX power level is:")
+ fmt.Println("\t\t", ev.Peripheral.Advertisement.TxPowerLevel)
+ }
+
+ return
+ })
+
+ if *verbose {
+ log.Println("Init...")
+ }
+
+ ble.Init()
+
+ var done chan bool
+ <-done
+}
diff --git a/vendor/github.com/raff/goble/examples/explorer.go b/vendor/github.com/raff/goble/examples/explorer.go
new file mode 100644
index 0000000..a587337
--- /dev/null
+++ b/vendor/github.com/raff/goble/examples/explorer.go
@@ -0,0 +1,284 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/raff/goble"
+)
+
+var (
+ debug = flag.Bool("debug", false, "log debug messages")
+ verbose = flag.Bool("verbose", false, "dump all events")
+ dups = flag.Bool("allow-duplicates", false, "allow duplicates when scanning")
+)
+
+func DebugPrint(params ...interface{}) {
+ if *debug {
+ log.Println(params...)
+ }
+}
+
+type Result struct {
+ count int
+ data string
+}
+
+func explore(ble *goble.BLE, peripheral *goble.Peripheral) {
+ results := map[string]Result{}
+
+ // connect
+ ble.On("connect", func(ev goble.Event) (done bool) {
+ DebugPrint("connected", ev)
+ ble.DiscoverServices(ev.DeviceUUID, nil)
+
+ go func() {
+ time.Sleep(2 * time.Minute)
+ ble.Disconnect(ev.DeviceUUID)
+ }()
+
+ return
+ })
+
+ // discover services
+ ble.On("servicesDiscover", func(ev goble.Event) (done bool) {
+ DebugPrint("serviceDiscovered", ev)
+ for sid, service := range ev.Peripheral.Services {
+ // this is a map that contains services UUIDs (string) and service startHandle (int)
+ // for now we only process the "strings"
+ if _, ok := sid.(string); ok {
+ serviceInfo := service.Uuid
+
+ if len(service.Name) > 0 {
+ serviceInfo += " (" + service.Name + ")"
+ }
+
+ results[service.Uuid] = Result{data: serviceInfo}
+ ble.DiscoverCharacterstics(ev.DeviceUUID, service.Uuid, nil)
+ }
+ }
+
+ return
+ })
+
+ // discover characteristics
+ ble.On("characteristicsDiscover", func(ev goble.Event) (done bool) {
+ DebugPrint("characteristicsDiscovered", ev)
+ serviceUuid := ev.ServiceUuid
+ serviceResult := results[serviceUuid]
+
+ for cid, characteristic := range ev.Peripheral.Services[serviceUuid].Characteristics {
+ // this is a map that contains services UUIDs (string) and service startHandle (int)
+ // for now we only process the "strings"
+ if _, ok := cid.(string); ok {
+ characteristicInfo := " " + characteristic.Uuid
+
+ if len(characteristic.Name) > 0 {
+ characteristicInfo += " (" + characteristic.Name + ")"
+ }
+
+ characteristicInfo += "\n properties " + characteristic.Properties.String()
+ serviceResult.data += characteristicInfo
+
+ if characteristic.Properties.Readable() {
+ serviceResult.count += 1
+ ble.Read(ev.DeviceUUID, serviceUuid, characteristic.Uuid)
+ }
+
+ //ble.DiscoverDescriptors(ev.DeviceUUID, serviceUuid, characteristic.Uuid)
+ results[serviceUuid] = serviceResult
+
+ if *verbose {
+ log.Println(results[serviceUuid])
+ }
+ }
+ }
+
+ return
+ })
+
+ // discover descriptors
+ ble.On("descriptorsDiscover", func(ev goble.Event) (done bool) {
+ DebugPrint("descriptorsDiscovered", ev)
+ fmt.Println(" descriptors ", ev.Peripheral.Services[ev.ServiceUuid].Characteristics[ev.CharacteristicUuid].Descriptors)
+ return
+ })
+
+ // read
+ ble.On("read", func(ev goble.Event) (done bool) {
+ DebugPrint("read", ev)
+ serviceUuid := ev.ServiceUuid
+ serviceResult := results[serviceUuid]
+ serviceResult.data += fmt.Sprintf(" value %x | %q\n", ev.Data, ev.Data)
+ serviceResult.count -= 1
+
+ if serviceResult.count <= 0 {
+ fmt.Println(serviceResult.data)
+ return true
+ } else {
+ results[serviceUuid] = serviceResult
+ }
+
+ return
+ })
+
+ // disconnect
+ ble.On("disconnect", func(ev goble.Event) (done bool) {
+ DebugPrint("disconnected", ev)
+ os.Exit(0)
+ return true
+ })
+
+ fmt.Println("services and characteristics:")
+ ble.Connect(peripheral.Uuid)
+
+ /*
+ async.series([
+ function(callback) {
+ characteristic.discoverDescriptors(function(error, descriptors) {
+ async.detect(
+ descriptors,
+ function(descriptor, callback) {
+ return callback(descriptor.uuid === '2901")
+ },
+ function(userDescriptionDescriptor){
+ if (userDescriptionDescriptor) {
+ userDescriptionDescriptor.readValue(function(error, data) {
+ characteristicInfo += ' (' + data.toString() + ')';
+ callback();
+ });
+ } else {
+ callback();
+ }
+ }
+ );
+ });
+ },
+ function(callback) {
+ characteristicInfo += '\n properties ' + characteristic.properties.join(', ")
+
+ if (characteristic.properties.indexOf('read') !== -1) {
+ characteristic.read(function(error, data) {
+ if (data) {
+ var string = data.toString('ascii")
+
+ characteristicInfo += '\n value ' + data.toString('hex') + ' | \'' + string + '\'';
+ }
+ callback();
+ });
+ } else {
+ callback();
+ }
+ },
+ function() {
+ console.log(characteristicInfo);
+ characteristicIndex++;
+ callback();
+ }
+ ]);
+ },
+ function(error) {
+ serviceIndex++;
+ callback();
+ }
+ );
+ });
+ },
+ */
+}
+
+func main() {
+ flag.Parse()
+
+ if len(flag.Args()) != 1 {
+ fmt.Println("usage:", os.Args[0], "[options] peripheral-uuid")
+ os.Exit(1)
+ }
+
+ peripheralUuid := flag.Args()[0]
+
+ var done chan bool
+
+ ble := goble.New()
+ ble.SetVerbose(*verbose)
+
+ if *verbose {
+ ble.On(goble.ALL, func(ev goble.Event) (done bool) {
+ log.Println("Event", ev)
+ return
+ })
+ }
+
+ ble.On("stateChange", func(ev goble.Event) (done bool) {
+ DebugPrint("stateChanged", ev)
+ if ev.State == "poweredOn" {
+ ble.StartScanning(nil, *dups)
+ } else {
+ ble.StopScanning()
+ done = true
+ }
+
+ return
+ })
+
+ ble.On("discover", func(ev goble.Event) (done bool) {
+ DebugPrint("discovered", ev)
+ if peripheralUuid == ev.DeviceUUID.String() {
+ ble.StopScanning()
+
+ fmt.Println()
+ fmt.Println("peripheral with UUID", ev.DeviceUUID, "found")
+
+ advertisement := ev.Peripheral.Advertisement
+
+ DebugPrint("advertised", advertisement)
+
+ localName := advertisement.LocalName
+ txPowerLevel := advertisement.TxPowerLevel
+ manufacturerData := advertisement.ManufacturerData
+ serviceData := advertisement.ServiceData
+ //serviceUuids := advertisement.ServiceUuids
+
+ if ev.Peripheral.Connectable {
+ fmt.Println(" Connectable")
+ }
+ if len(localName) > 0 {
+ fmt.Println(" Local Name =", localName)
+ }
+
+ if txPowerLevel != 0 {
+ fmt.Println(" TX Power Level =", txPowerLevel)
+ }
+
+ if len(manufacturerData) > 0 {
+ fmt.Println(" Manufacturer Data =", manufacturerData)
+ }
+
+ if len(serviceData) > 0 {
+ fmt.Println(" Service Data =", serviceData)
+ }
+
+ fmt.Println()
+
+ DebugPrint("explore", ev.Peripheral)
+ explore(ble, &ev.Peripheral)
+ }
+
+ return
+ })
+
+ if *verbose {
+ log.Println("Init...")
+ }
+
+ ble.Init()
+
+ fmt.Println("waiting...")
+ <-done
+
+ fmt.Println("goodbye!")
+ os.Exit(0)
+}
diff --git a/vendor/github.com/raff/goble/examples/main.go b/vendor/github.com/raff/goble/examples/main.go
new file mode 100644
index 0000000..0809e5e
--- /dev/null
+++ b/vendor/github.com/raff/goble/examples/main.go
@@ -0,0 +1,129 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "strings"
+ "time"
+
+ "github.com/raff/goble"
+ "github.com/raff/goble/xpc"
+)
+
+func main() {
+ verbose := flag.Bool("verbose", false, "dump all events")
+ advertise := flag.Duration("advertise", 0, "Duration of advertising - 0: does not advertise")
+ dups := flag.Bool("allow-duplicates", false, "allow duplicates when scanning")
+ ibeacon := flag.Duration("ibeacon", 0, "Duration of IBeacon advertising - 0: does not advertise")
+ scan := flag.Duration("scan", 10, "Duration of scanning - 0: does not scan")
+ uuid := flag.String("uuid", "", "device uuid (for ibeacon uuid,major,minor,power)")
+ connect := flag.Bool("connect", false, "connect to device")
+ disconnect := flag.Bool("disconnect", false, "disconnect from device")
+ rssi := flag.Bool("rssi", false, "update rssi for device")
+ remove := flag.Bool("remove", false, "Remove all services")
+ discover := flag.Bool("discover", false, "Discover services")
+
+ flag.Parse()
+
+ ble := goble.New()
+
+ ble.SetVerbose(*verbose)
+
+ log.Println("Init...")
+ ble.Init()
+
+ if *advertise > 0 {
+ uuids := []xpc.UUID{}
+
+ if len(*uuid) > 0 {
+ uuids = append(uuids, xpc.MakeUUID(*uuid))
+ }
+
+ time.Sleep(1 * time.Second)
+ log.Println("Start Advertising...")
+ ble.StartAdvertising("gobble", uuids)
+
+ time.Sleep(*advertise)
+ log.Println("Stop Advertising...")
+ ble.StopAdvertising()
+ }
+
+ if *ibeacon > 0 {
+ parts := strings.Split(*uuid, ",")
+ id := parts[0]
+
+ var major, minor uint16
+ var measuredPower int8
+
+ if len(parts) > 1 {
+ fmt.Sscanf(parts[1], "%d", &major)
+ }
+ if len(parts) > 2 {
+ fmt.Sscanf(parts[2], "%d", &minor)
+ }
+ if len(parts) > 2 {
+ fmt.Sscanf(parts[3], "%d", &measuredPower)
+ }
+
+ time.Sleep(1 * time.Second)
+ log.Println("Start Advertising IBeacon...")
+ ble.StartAdvertisingIBeacon(xpc.MakeUUID(id), major, minor, measuredPower)
+
+ time.Sleep(*ibeacon)
+ log.Println("Stop Advertising...")
+ ble.StopAdvertising()
+ }
+
+ if *scan > 0 {
+ time.Sleep(1 * time.Second)
+ log.Println("Start Scanning...")
+ ble.StartScanning(nil, *dups)
+
+ time.Sleep(*scan)
+ log.Println("Stop Scanning...")
+ ble.StopScanning()
+ }
+
+ if *connect {
+ time.Sleep(1 * time.Second)
+ uuid := xpc.MakeUUID(*uuid)
+ log.Println("Connect", uuid)
+ ble.Connect(uuid)
+ time.Sleep(5 * time.Second)
+ }
+
+ if *rssi {
+ time.Sleep(1 * time.Second)
+ uuid := xpc.MakeUUID(*uuid)
+ log.Println("UpdateRssi", uuid)
+ ble.UpdateRssi(uuid)
+ time.Sleep(5 * time.Second)
+ }
+
+ if *discover {
+ time.Sleep(1 * time.Second)
+ uuid := xpc.MakeUUID(*uuid)
+ log.Println("DiscoverServices", uuid)
+ ble.DiscoverServices(uuid, nil)
+ time.Sleep(5 * time.Second)
+ }
+
+ if *disconnect {
+ time.Sleep(1 * time.Second)
+ uuid := xpc.MakeUUID(*uuid)
+ log.Println("Disconnect", uuid)
+ ble.Disconnect(uuid)
+ time.Sleep(5 * time.Second)
+ }
+
+ if *remove {
+ time.Sleep(1 * time.Second)
+ log.Println("Remove all services")
+ ble.RemoveServices()
+ time.Sleep(5 * time.Second)
+ }
+
+ time.Sleep(5 * time.Second)
+ log.Println("Goodbye!")
+}
diff --git a/vendor/github.com/raff/goble/goble.go b/vendor/github.com/raff/goble/goble.go
new file mode 100644
index 0000000..e664811
--- /dev/null
+++ b/vendor/github.com/raff/goble/goble.go
@@ -0,0 +1,717 @@
+package goble
+
+import "C"
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/raff/goble/xpc"
+)
+
+// "github.com/raff/goble/xpc"
+//
+// BLE support
+//
+
+var STATES = []string{"unknown", "resetting", "unsupported", "unauthorized", "poweredOff", "poweredOn"}
+
+type Property int
+
+const (
+ Broadcast Property = 1 << iota
+ Read = 1 << iota
+ WriteWithoutResponse = 1 << iota
+ Write = 1 << iota
+ Notify = 1 << iota
+ Indicate = 1 << iota
+ AuthenticatedSignedWrites = 1 << iota
+ ExtendedProperties = 1 << iota
+)
+
+func (p Property) Readable() bool {
+ return (p & Read) != 0
+}
+
+func (p Property) String() (result string) {
+ if (p & Broadcast) != 0 {
+ result += "broadcast "
+ }
+ if (p & Read) != 0 {
+ result += "read "
+ }
+ if (p & WriteWithoutResponse) != 0 {
+ result += "writeWithoutResponse "
+ }
+ if (p & Write) != 0 {
+ result += "write "
+ }
+ if (p & Notify) != 0 {
+ result += "notify "
+ }
+ if (p & Indicate) != 0 {
+ result += "indicate "
+ }
+ if (p & AuthenticatedSignedWrites) != 0 {
+ result += "authenticateSignedWrites "
+ }
+ if (p & ExtendedProperties) != 0 {
+ result += "extendedProperties "
+ }
+
+ return
+}
+
+type ServiceData struct {
+ Uuid string
+ Data []byte
+}
+
+type CharacteristicDescriptor struct {
+ Uuid string
+ Handle int
+}
+
+type ServiceCharacteristic struct {
+ Uuid string
+ Name string
+ Type string
+ Properties Property
+ Descriptors map[interface{}]*CharacteristicDescriptor
+ Handle int
+ ValueHandle int
+}
+
+type ServiceHandle struct {
+ Uuid string
+ Name string
+ Type string
+ Characteristics map[interface{}]*ServiceCharacteristic
+ startHandle int
+ endHandle int
+}
+
+type Advertisement struct {
+ LocalName string
+ TxPowerLevel int
+ ManufacturerData []byte
+ ServiceData []ServiceData
+ ServiceUuids []string
+}
+
+type Peripheral struct {
+ Uuid xpc.UUID
+ Address string
+ AddressType string
+ Connectable bool
+ Advertisement Advertisement
+ Rssi int
+ Services map[interface{}]*ServiceHandle
+}
+
+// GATT Descriptor
+type Descriptor struct {
+ uuid xpc.UUID
+ value []byte
+}
+
+// GATT Characteristic
+type Characteristic struct {
+ uuid xpc.UUID
+ properties Property
+ secure Property
+ descriptors []Descriptor
+ value []byte
+}
+
+// GATT Service
+type Service struct {
+ uuid xpc.UUID
+ characteristics []Characteristic
+}
+
+type BLE struct {
+ Emitter
+ conn xpc.XPC
+ verbose bool
+
+ peripherals map[string]*Peripheral
+ attributes xpc.Array
+ lastServiceAttributeId int
+ allowDuplicates bool
+}
+
+func New() *BLE {
+ ble := &BLE{peripherals: map[string]*Peripheral{}, Emitter: Emitter{}}
+ ble.Emitter.Init()
+ ble.conn = xpc.XpcConnect("com.apple.blued", ble)
+ return ble
+}
+
+func (ble *BLE) SetVerbose(v bool) {
+ ble.verbose = v
+ ble.Emitter.SetVerbose(v)
+}
+
+// process BLE events and asynchronous errors
+// (implements XpcEventHandler)
+func (ble *BLE) HandleXpcEvent(event xpc.Dict, err error) {
+ if err != nil {
+ log.Println("error:", err)
+ if event == nil {
+ return
+ }
+ }
+
+ id := event.MustGetInt("kCBMsgId")
+ args := event.MustGetDict("kCBMsgArgs")
+
+ if ble.verbose {
+ log.Printf("event: %v %#v\n", id, args)
+ defer log.Printf("done event: %v", id)
+ }
+
+ switch id {
+ case 6: // state change
+ state := args.MustGetInt("kCBMsgArgState")
+ ble.Emit(Event{Name: "stateChange", State: STATES[state]})
+
+ case 16: // advertising start
+ result := args.MustGetInt("kCBMsgArgResult")
+ if result != 0 {
+ log.Printf("event: error in advertisingStart %v\n", result)
+ } else {
+ ble.Emit(Event{Name: "advertisingStart"})
+ }
+
+ case 17: // advertising stop
+ result := args.MustGetInt("kCBMsgArgResult")
+ if result != 0 {
+ log.Printf("event: error in advertisingStop %v\n", result)
+ } else {
+ ble.Emit(Event{Name: "advertisingStop"})
+ }
+
+ case 37: // discover
+ advdata := args.MustGetDict("kCBMsgArgAdvertisementData")
+ if len(advdata) == 0 {
+ //log.Println("event: discover with no advertisment data")
+ break
+ }
+
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+
+ advertisement := Advertisement{
+ LocalName: advdata.GetString("kCBAdvDataLocalName", args.GetString("kCBMsgArgName", "")),
+ TxPowerLevel: advdata.GetInt("kCBAdvDataTxPowerLevel", 0),
+ ManufacturerData: advdata.GetBytes("kCBAdvDataManufacturerData", nil),
+ ServiceData: []ServiceData{},
+ ServiceUuids: []string{},
+ }
+
+ connectable := advdata.GetInt("kCBAdvDataIsConnectable", 0) > 0
+ rssi := args.GetInt("kCBMsgArgRssi", 0)
+
+ if uuids, ok := advdata["kCBAdvDataServiceUUIDs"]; ok {
+ for _, uuid := range uuids.(xpc.Array) {
+ advertisement.ServiceUuids = append(advertisement.ServiceUuids, fmt.Sprintf("%x", uuid))
+ }
+ }
+
+ if data, ok := advdata["kCBAdvDataServiceData"]; ok {
+ sdata := data.(xpc.Array)
+
+ for i := 0; i < len(sdata); i += 2 {
+ sd := ServiceData{
+ Uuid: fmt.Sprintf("%x", sdata[i+0].([]byte)),
+ Data: sdata[i+1].([]byte),
+ }
+
+ advertisement.ServiceData = append(advertisement.ServiceData, sd)
+ }
+ }
+
+ pid := deviceUuid.String()
+ p := ble.peripherals[pid]
+ emit := ble.allowDuplicates || p == nil
+
+ if p == nil {
+ // add new peripheral
+ p = &Peripheral{
+ Uuid: deviceUuid,
+ Connectable: connectable,
+ Advertisement: advertisement,
+ Rssi: rssi,
+ Services: map[interface{}]*ServiceHandle{},
+ }
+
+ ble.peripherals[pid] = p
+ } else {
+ // update peripheral
+ p.Advertisement = advertisement
+ p.Rssi = rssi
+ }
+
+ if emit {
+ ble.Emit(Event{Name: "discover", DeviceUUID: deviceUuid, Peripheral: *p})
+ }
+
+ case 38: // connect
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+ ble.Emit(Event{Name: "connect", DeviceUUID: deviceUuid})
+
+ case 40: // disconnect
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+ ble.Emit(Event{Name: "disconnect", DeviceUUID: deviceUuid})
+
+ case 53: // mtuChange
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+ mtu := args.MustGetInt("kCBMsgArgATTMTU")
+
+ // bleno here converts the deviceUuid to an address
+ if p, ok := ble.peripherals[deviceUuid.String()]; ok {
+ ble.Emit(Event{Name: "mtuChange", DeviceUUID: deviceUuid, Peripheral: *p, Mtu: mtu})
+ }
+
+ case 54: // serviceDiscover
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+ servicesUuids := []string{}
+ servicesHandles := map[interface{}]*ServiceHandle{}
+
+ if dservices, ok := args["kCBMsgArgServices"]; ok {
+ for _, s := range dservices.(xpc.Array) {
+ service := s.(xpc.Dict)
+ serviceHandle := ServiceHandle{
+ Uuid: service.MustGetHexBytes("kCBMsgArgUUID"),
+ startHandle: service.MustGetInt("kCBMsgArgServiceStartHandle"),
+ endHandle: service.MustGetInt("kCBMsgArgServiceEndHandle"),
+ Characteristics: map[interface{}]*ServiceCharacteristic{}}
+
+ if nameType, ok := knownServices[serviceHandle.Uuid]; ok {
+ serviceHandle.Name = nameType.Name
+ serviceHandle.Type = nameType.Type
+ }
+
+ servicesHandles[serviceHandle.Uuid] = &serviceHandle
+ servicesHandles[serviceHandle.startHandle] = &serviceHandle
+
+ servicesUuids = append(servicesUuids, serviceHandle.Uuid)
+ }
+ }
+
+ if p, ok := ble.peripherals[deviceUuid.String()]; ok {
+ p.Services = servicesHandles
+ ble.Emit(Event{Name: "servicesDiscover", DeviceUUID: deviceUuid, Peripheral: *p})
+ }
+
+ case 55: // rssiUpdate
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+ rssi := args.MustGetInt("kCBMsgArgData")
+
+ if p, ok := ble.peripherals[deviceUuid.String()]; ok {
+ p.Rssi = rssi
+ ble.Emit(Event{Name: "rssiUpdate", DeviceUUID: deviceUuid, Peripheral: *p})
+ }
+
+ case 63: // characteristicsDiscover
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+ serviceStartHandle := args.MustGetInt("kCBMsgArgServiceStartHandle")
+
+ if p, ok := ble.peripherals[deviceUuid.String()]; ok {
+ service := p.Services[serviceStartHandle]
+
+ //result := args.MustGetInt("kCBMsgArgResult")
+
+ for _, c := range args.MustGetArray("kCBMsgArgCharacteristics") {
+ cDict := c.(xpc.Dict)
+
+ characteristic := ServiceCharacteristic{
+ Uuid: cDict.MustGetHexBytes("kCBMsgArgUUID"),
+ Handle: cDict.MustGetInt("kCBMsgArgCharacteristicHandle"),
+ ValueHandle: cDict.MustGetInt("kCBMsgArgCharacteristicValueHandle"),
+ Descriptors: map[interface{}]*CharacteristicDescriptor{},
+ }
+
+ if nameType, ok := knownCharacteristics[characteristic.Uuid]; ok {
+ characteristic.Name = nameType.Name
+ characteristic.Type = nameType.Type
+ }
+
+ properties := cDict.MustGetInt("kCBMsgArgCharacteristicProperties")
+
+ if (properties & 0x01) != 0 {
+ characteristic.Properties |= Broadcast
+ }
+
+ if (properties & 0x02) != 0 {
+ characteristic.Properties |= Read
+ }
+
+ if (properties & 0x04) != 0 {
+ characteristic.Properties |= WriteWithoutResponse
+ }
+
+ if (properties & 0x08) != 0 {
+ characteristic.Properties |= Write
+ }
+
+ if (properties & 0x10) != 0 {
+ characteristic.Properties |= Notify
+ }
+
+ if (properties & 0x20) != 0 {
+ characteristic.Properties |= Indicate
+ }
+
+ if (properties & 0x40) != 0 {
+ characteristic.Properties |= AuthenticatedSignedWrites
+ }
+
+ if (properties & 0x80) != 0 {
+ characteristic.Properties |= ExtendedProperties
+ }
+
+ if service != nil {
+ service.Characteristics[characteristic.Uuid] = &characteristic
+ service.Characteristics[characteristic.Handle] = &characteristic
+ service.Characteristics[characteristic.ValueHandle] = &characteristic
+ }
+ }
+
+ if service != nil {
+ ble.Emit(Event{Name: "characteristicsDiscover", DeviceUUID: deviceUuid, ServiceUuid: service.Uuid, Peripheral: *p})
+ } else {
+ log.Println("no service", serviceStartHandle)
+ }
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+
+ case 75: // descriptorsDiscover
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+ characteristicsHandle := args.MustGetInt("kCBMsgArgCharacteristicHandle")
+ //result := args.MustGetInt("kCBMsgArgResult")
+
+ if p, ok := ble.peripherals[deviceUuid.String()]; ok {
+ for _, s := range p.Services {
+ if c, ok := s.Characteristics[characteristicsHandle]; ok {
+ for _, d := range args.MustGetArray("kCBMsgArgDescriptors") {
+ dDict := d.(xpc.Dict)
+ descriptor := CharacteristicDescriptor{
+ Uuid: dDict.MustGetHexBytes("kCBMsgArgUUID"),
+ Handle: dDict.MustGetInt("kCBMsgArgDescriptorHandle"),
+ }
+
+ c.Descriptors[descriptor.Uuid] = &descriptor
+ c.Descriptors[descriptor.Handle] = &descriptor
+ }
+
+ ble.Emit(Event{Name: "descriptorsDiscover", DeviceUUID: deviceUuid, ServiceUuid: s.Uuid, CharacteristicUuid: c.Uuid, Peripheral: *p})
+ break
+ }
+ }
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+
+ case 70: // read
+ deviceUuid := args.MustGetUUID("kCBMsgArgDeviceUUID")
+ characteristicsHandle := args.MustGetInt("kCBMsgArgCharacteristicHandle")
+ //result := args.MustGetInt("kCBMsgArgResult")
+ isNotification := args.GetInt("kCBMsgArgIsNotification", 0) != 0
+ data := args.MustGetBytes("kCBMsgArgData")
+
+ if p, ok := ble.peripherals[deviceUuid.String()]; ok {
+ for _, s := range p.Services {
+ if c, ok := s.Characteristics[characteristicsHandle]; ok {
+ ble.Emit(Event{Name: "read", DeviceUUID: deviceUuid, ServiceUuid: s.Uuid, CharacteristicUuid: c.Uuid, Peripheral: *p, Data: data, IsNotification: isNotification})
+ break
+ }
+ }
+ }
+ }
+}
+
+// send a message to Blued
+func (ble *BLE) sendCBMsg(id int, args xpc.Dict) {
+ message := xpc.Dict{"kCBMsgId": id, "kCBMsgArgs": args}
+ if ble.verbose {
+ log.Printf("sendCBMsg %#v\n", message)
+ }
+
+ ble.conn.Send(xpc.Dict{"kCBMsgId": id, "kCBMsgArgs": args}, ble.verbose)
+}
+
+// initialize BLE
+func (ble *BLE) Init() {
+ ble.sendCBMsg(1, xpc.Dict{"kCBMsgArgName": fmt.Sprintf("goble-%v", time.Now().Unix()),
+ "kCBMsgArgOptions": xpc.Dict{"kCBInitOptionShowPowerAlert": 0}, "kCBMsgArgType": 0})
+}
+
+// start advertising
+func (ble *BLE) StartAdvertising(name string, serviceUuids []xpc.UUID) {
+ uuids := make([][]byte, len(serviceUuids))
+ for i, uuid := range serviceUuids {
+ uuids[i] = []byte(uuid[:])
+ }
+ ble.sendCBMsg(8, xpc.Dict{"kCBAdvDataLocalName": name, "kCBAdvDataServiceUUIDs": uuids})
+}
+
+// start advertising as IBeacon (raw data)
+func (ble *BLE) StartAdvertisingIBeaconData(data []byte) {
+ var utsname xpc.Utsname
+ xpc.Uname(&utsname)
+
+ if utsname.Release >= "14." {
+ l := len(data)
+ buf := bytes.NewBuffer([]byte{byte(l + 5), 0xFF, 0x4C, 0x00, 0x02, byte(l)})
+ buf.Write(data)
+ ble.sendCBMsg(8, xpc.Dict{"kCBAdvDataAppleMfgData": buf.Bytes()})
+ } else {
+ ble.sendCBMsg(8, xpc.Dict{"kCBAdvDataAppleBeaconKey": data})
+ }
+}
+
+// start advertising as IBeacon
+func (ble *BLE) StartAdvertisingIBeacon(uuid xpc.UUID, major, minor uint16, measuredPower int8) {
+ var buf bytes.Buffer
+ binary.Write(&buf, binary.BigEndian, uuid[:])
+ binary.Write(&buf, binary.BigEndian, major)
+ binary.Write(&buf, binary.BigEndian, minor)
+ binary.Write(&buf, binary.BigEndian, measuredPower)
+
+ ble.StartAdvertisingIBeaconData(buf.Bytes())
+}
+
+// stop advertising
+func (ble *BLE) StopAdvertising() {
+ ble.sendCBMsg(9, nil)
+}
+
+// start scanning
+func (ble *BLE) StartScanning(serviceUuids []xpc.UUID, allowDuplicates bool) {
+ uuids := []string{}
+
+ for _, uuid := range serviceUuids {
+ uuids = append(uuids, uuid.String())
+ }
+
+ args := xpc.Dict{"kCBMsgArgUUIDs": uuids}
+ if allowDuplicates {
+ args["kCBMsgArgOptions"] = xpc.Dict{"kCBScanOptionAllowDuplicates": 1}
+ } else {
+ args["kCBMsgArgOptions"] = xpc.Dict{}
+ }
+
+ ble.allowDuplicates = allowDuplicates
+ ble.sendCBMsg(29, args)
+}
+
+// stop scanning
+func (ble *BLE) StopScanning() {
+ ble.sendCBMsg(30, nil)
+}
+
+// connect
+func (ble *BLE) Connect(deviceUuid xpc.UUID) {
+ uuid := deviceUuid.String()
+ if p, ok := ble.peripherals[uuid]; ok {
+ ble.sendCBMsg(31, xpc.Dict{"kCBMsgArgOptions": xpc.Dict{"kCBConnectOptionNotifyOnDisconnection": 1}, "kCBMsgArgDeviceUUID": p.Uuid})
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+}
+
+// disconnect
+func (ble *BLE) Disconnect(deviceUuid xpc.UUID) {
+ uuid := deviceUuid.String()
+ if p, ok := ble.peripherals[uuid]; ok {
+ ble.sendCBMsg(32, xpc.Dict{"kCBMsgArgDeviceUUID": p.Uuid})
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+}
+
+// update rssi
+func (ble *BLE) UpdateRssi(deviceUuid xpc.UUID) {
+ uuid := deviceUuid.String()
+ if p, ok := ble.peripherals[uuid]; ok {
+ ble.sendCBMsg(43, xpc.Dict{"kCBMsgArgDeviceUUID": p.Uuid})
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+}
+
+// discover services
+func (ble *BLE) DiscoverServices(deviceUuid xpc.UUID, uuids []xpc.UUID) {
+ sUuid := deviceUuid.String()
+ if p, ok := ble.peripherals[sUuid]; ok {
+ sUuids := make([]string, len(uuids))
+ for i, uuid := range uuids {
+ sUuids[i] = uuid.String() // uuids may be a list of []byte (2 bytes)
+ }
+ ble.sendCBMsg(44, xpc.Dict{"kCBMsgArgDeviceUUID": p.Uuid, "kCBMsgArgUUIDs": sUuids})
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+}
+
+// discover characteristics
+func (ble *BLE) DiscoverCharacterstics(deviceUuid xpc.UUID, serviceUuid string, characteristicUuids []string) {
+ sUuid := deviceUuid.String()
+ if p, ok := ble.peripherals[sUuid]; ok {
+ cUuids := make([]string, len(characteristicUuids))
+ for i, cuuid := range characteristicUuids {
+ cUuids[i] = cuuid // characteristicUuids may be a list of []byte (2 bytes)
+ }
+
+ ble.sendCBMsg(61, xpc.Dict{
+ "kCBMsgArgDeviceUUID": p.Uuid,
+ "kCBMsgArgServiceStartHandle": p.Services[serviceUuid].startHandle,
+ "kCBMsgArgServiceEndHandle": p.Services[serviceUuid].endHandle,
+ "kCBMsgArgUUIDs": cUuids,
+ })
+
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+}
+
+// discover descriptors
+func (ble *BLE) DiscoverDescriptors(deviceUuid xpc.UUID, serviceUuid, characteristicUuid string) {
+ sUuid := deviceUuid.String()
+ if p, ok := ble.peripherals[sUuid]; ok {
+ s := p.Services[serviceUuid]
+ c := s.Characteristics[characteristicUuid]
+
+ ble.sendCBMsg(69, xpc.Dict{
+ "kCBMsgArgDeviceUUID": p.Uuid,
+ "kCBMsgArgCharacteristicHandle": c.Handle,
+ "kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
+ })
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+}
+
+// read
+func (ble *BLE) Read(deviceUuid xpc.UUID, serviceUuid, characteristicUuid string) {
+ sUuid := deviceUuid.String()
+ if p, ok := ble.peripherals[sUuid]; ok {
+ s := p.Services[serviceUuid]
+ c := s.Characteristics[characteristicUuid]
+
+ ble.sendCBMsg(64, xpc.Dict{
+ "kCBMsgArgDeviceUUID": p.Uuid,
+ "kCBMsgArgCharacteristicHandle": c.Handle,
+ "kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
+ })
+ } else {
+ log.Println("no peripheral", deviceUuid)
+ }
+}
+
+// remove all services
+func (ble *BLE) RemoveServices() {
+ ble.sendCBMsg(12, nil)
+}
+
+// set services
+func (ble *BLE) SetServices(services []Service) {
+ ble.sendCBMsg(12, nil) // remove all services
+ ble.attributes = xpc.Array{nil}
+
+ attributeId := 1
+
+ for _, service := range services {
+ arg := xpc.Dict{
+ "kCBMsgArgAttributeID": attributeId,
+ "kCBMsgArgAttributeIDs": []int{},
+ "kCBMsgArgCharacteristics": nil,
+ "kCBMsgArgType": 1, // 1 => primary, 0 => excluded
+ "kCBMsgArgUUID": service.uuid.String(),
+ }
+
+ ble.attributes = append(ble.attributes, service)
+ ble.lastServiceAttributeId = attributeId
+ attributeId += 1
+
+ characteristics := xpc.Array{}
+
+ for _, characteristic := range service.characteristics {
+ properties := 0
+ permissions := 0
+
+ if Read&characteristic.properties != 0 {
+ properties |= 0x02
+
+ if Read&characteristic.secure != 0 {
+ permissions |= 0x04
+ } else {
+ permissions |= 0x01
+ }
+ }
+
+ if WriteWithoutResponse&characteristic.properties != 0 {
+ properties |= 0x04
+
+ if WriteWithoutResponse&characteristic.secure != 0 {
+ permissions |= 0x08
+ } else {
+ permissions |= 0x02
+ }
+ }
+
+ if Write&characteristic.properties != 0 {
+ properties |= 0x08
+
+ if WriteWithoutResponse&characteristic.secure != 0 {
+ permissions |= 0x08
+ } else {
+ permissions |= 0x02
+ }
+ }
+
+ if Notify&characteristic.properties != 0 {
+ if Notify&characteristic.secure != 0 {
+ properties |= 0x100
+ } else {
+ properties |= 0x10
+ }
+ }
+
+ if Indicate&characteristic.properties != 0 {
+ if Indicate&characteristic.secure != 0 {
+ properties |= 0x200
+ } else {
+ properties |= 0x20
+ }
+ }
+
+ descriptors := xpc.Array{}
+ for _, descriptor := range characteristic.descriptors {
+ descriptors = append(descriptors, xpc.Dict{"kCBMsgArgData": descriptor.value, "kCBMsgArgUUID": descriptor.uuid.String()})
+ }
+
+ characteristicArg := xpc.Dict{
+ "kCBMsgArgAttributeID": attributeId,
+ "kCBMsgArgAttributePermissions": permissions,
+ "kCBMsgArgCharacteristicProperties": properties,
+ "kCBMsgArgData": characteristic.value,
+ "kCBMsgArgDescriptors": descriptors,
+ "kCBMsgArgUUID": characteristic.uuid.String(),
+ }
+
+ ble.attributes = append(ble.attributes, characteristic)
+ characteristics = append(characteristics, characteristicArg)
+
+ attributeId += 1
+ }
+
+ arg["kCBMsgArgCharacteristics"] = characteristics
+ ble.sendCBMsg(10, arg) // remove all services
+ }
+}
diff --git a/vendor/github.com/raff/goble/services.go b/vendor/github.com/raff/goble/services.go
new file mode 100644
index 0000000..23db6dd
--- /dev/null
+++ b/vendor/github.com/raff/goble/services.go
@@ -0,0 +1,25 @@
+package goble
+
+// A dictionary of known service names and type (keyed by service uuid)
+var knownServices = map[string]struct{ Name, Type string }{
+ "1800": {Name: "Generic Access", Type: "org.bluetooth.service.generic_access"},
+ "1801": {Name: "Generic Attribute", Type: "org.bluetooth.service.generic_attribute"},
+ "1802": {Name: "Immediate Alert", Type: "org.bluetooth.service.immediate_alert"},
+ "1803": {Name: "Link Loss", Type: "org.bluetooth.service.link_loss"},
+ "1804": {Name: "Tx Power", Type: "org.bluetooth.service.tx_power"},
+ "1805": {Name: "Current Time Service", Type: "org.bluetooth.service.current_time"},
+ "1806": {Name: "Reference Time Update Service", Type: "org.bluetooth.service.reference_time_update"},
+ "1807": {Name: "Next DST Change Service", Type: "org.bluetooth.service.next_dst_change"},
+ "1808": {Name: "Glucose", Type: "org.bluetooth.service.glucose"},
+ "1809": {Name: "Health Thermometer", Type: "org.bluetooth.service.health_thermometer"},
+ "180a": {Name: "Device Information", Type: "org.bluetooth.service.device_information"},
+ "180d": {Name: "Heart Rate", Type: "org.bluetooth.service.heart_rate"},
+ "180e": {Name: "Phone Alert Status Service", Type: "org.bluetooth.service.phone_alert_service"},
+ "180f": {Name: "Battery Service", Type: "org.bluetooth.service.battery_service"},
+ "1810": {Name: "Blood Pressure", Type: "org.bluetooth.service.blood_pressuer"},
+ "1811": {Name: "Alert Notification Service", Type: "org.bluetooth.service.alert_notification"},
+ "1812": {Name: "Human Interface Device", Type: "org.bluetooth.service.human_interface_device"},
+ "1813": {Name: "Scan Parameters", Type: "org.bluetooth.service.scan_parameters"},
+ "1814": {Name: "Running Speed and Cadence", Type: "org.bluetooth.service.running_speed_and_cadence"},
+ "1815": {Name: "Cycling Speed and Cadence", Type: "org.bluetooth.service.cycling_speed_and_cadence"},
+}
diff --git a/vendor/github.com/raff/goble/xpc/xpc.go b/vendor/github.com/raff/goble/xpc/xpc.go
new file mode 100644
index 0000000..fd210ac
--- /dev/null
+++ b/vendor/github.com/raff/goble/xpc/xpc.go
@@ -0,0 +1,381 @@
+package xpc
+
+/*
+#include "xpc_wrapper.h"
+*/
+import "C"
+
+import (
+ "errors"
+ "fmt"
+ "log"
+ r "reflect"
+ "strings"
+ "unsafe"
+)
+
+type XPC struct {
+ conn C.xpc_connection_t
+}
+
+func (x *XPC) Send(msg interface{}, verbose bool) {
+ // verbose == true converts the type from bool to C._Bool
+ C.XpcSendMessage(x.conn, goToXpc(msg), true, verbose == true)
+}
+
+//
+// minimal XPC support required for BLE
+//
+
+// a dictionary of things
+type Dict map[string]interface{}
+
+func (d Dict) Contains(k string) bool {
+ _, ok := d[k]
+ return ok
+}
+
+func (d Dict) MustGetDict(k string) Dict {
+ return d[k].(Dict)
+}
+
+func (d Dict) MustGetArray(k string) Array {
+ return d[k].(Array)
+}
+
+func (d Dict) MustGetBytes(k string) []byte {
+ return d[k].([]byte)
+}
+
+func (d Dict) MustGetHexBytes(k string) string {
+ return fmt.Sprintf("%x", d[k].([]byte))
+}
+
+func (d Dict) MustGetInt(k string) int {
+ return int(d[k].(int64))
+}
+
+func (d Dict) MustGetUUID(k string) UUID {
+ return d[k].(UUID)
+}
+
+func (d Dict) GetString(k, defv string) string {
+ if v := d[k]; v != nil {
+ //log.Printf("GetString %s %#v\n", k, v)
+ return v.(string)
+ } else {
+ //log.Printf("GetString %s default %#v\n", k, defv)
+ return defv
+ }
+}
+
+func (d Dict) GetBytes(k string, defv []byte) []byte {
+ if v := d[k]; v != nil {
+ //log.Printf("GetBytes %s %#v\n", k, v)
+ return v.([]byte)
+ } else {
+ //log.Printf("GetBytes %s default %#v\n", k, defv)
+ return defv
+ }
+}
+
+func (d Dict) GetInt(k string, defv int) int {
+ if v := d[k]; v != nil {
+ //log.Printf("GetString %s %#v\n", k, v)
+ return int(v.(int64))
+ } else {
+ //log.Printf("GetString %s default %#v\n", k, defv)
+ return defv
+ }
+}
+
+func (d Dict) GetUUID(k string) UUID {
+ return GetUUID(d[k])
+}
+
+// an Array of things
+type Array []interface{}
+
+func (a Array) GetUUID(k int) UUID {
+ return GetUUID(a[k])
+}
+
+// a UUID
+type UUID [16]byte
+
+func MakeUUID(s string) UUID {
+ var sl []byte
+
+ s = strings.Replace(s, "-", "", -1)
+ fmt.Sscanf(s, "%32x", &sl)
+
+ var uuid [16]byte
+ copy(uuid[:], sl)
+ return UUID(uuid)
+}
+
+func MustUUID(s string) UUID {
+ var sl []byte
+
+ s = strings.Replace(s, "-", "", -1)
+ if len(s) != 32 {
+ log.Fatal("invalid UUID")
+ }
+ if n, err := fmt.Sscanf(s, "%32x", &sl); err != nil || n != 1 {
+ log.Fatal("invalid UUID ", s, " len ", n, " error ", err)
+ }
+
+ var uuid [16]byte
+ copy(uuid[:], sl)
+ return UUID(uuid)
+}
+
+func (uuid UUID) String() string {
+ return fmt.Sprintf("%x", [16]byte(uuid))
+}
+
+func GetUUID(v interface{}) UUID {
+ if v == nil {
+ return UUID{}
+ }
+
+ if uuid, ok := v.(UUID); ok {
+ return uuid
+ }
+
+ if bytes, ok := v.([]byte); ok {
+ uuid := UUID{}
+
+ for i, b := range bytes {
+ uuid[i] = b
+ }
+
+ return uuid
+ }
+
+ if bytes, ok := v.([]uint8); ok {
+ uuid := UUID{}
+
+ for i, b := range bytes {
+ uuid[i] = b
+ }
+
+ return uuid
+ }
+
+ log.Fatalf("invalid type for UUID: %#v", v)
+ return UUID{}
+}
+
+var (
+ CONNECTION_INVALID = errors.New("connection invalid")
+ CONNECTION_INTERRUPTED = errors.New("connection interrupted")
+ CONNECTION_TERMINATED = errors.New("connection terminated")
+
+ TYPE_OF_UUID = r.TypeOf(UUID{})
+ TYPE_OF_BYTES = r.TypeOf([]byte{})
+
+ handlers = map[uintptr]XpcEventHandler{}
+)
+
+type XpcEventHandler interface {
+ HandleXpcEvent(event Dict, err error)
+}
+
+func XpcConnect(service string, eh XpcEventHandler) XPC {
+ // func XpcConnect(service string, eh XpcEventHandler) C.xpc_connection_t {
+ ctx := uintptr(unsafe.Pointer(&eh))
+ handlers[ctx] = eh
+
+ cservice := C.CString(service)
+ defer C.free(unsafe.Pointer(cservice))
+ // return C.XpcConnect(cservice, C.uintptr_t(ctx))
+ return XPC{conn: C.XpcConnect(cservice, C.uintptr_t(ctx))}
+}
+
+//export handleXpcEvent
+func handleXpcEvent(event C.xpc_object_t, p C.ulong) {
+ //log.Printf("handleXpcEvent %#v %#v\n", event, p)
+
+ t := C.xpc_get_type(event)
+
+ eh := handlers[uintptr(p)]
+ if eh == nil {
+ //log.Println("no handler for", p)
+ return
+ }
+
+ if t == C.TYPE_ERROR {
+ if event == C.ERROR_CONNECTION_INVALID {
+ // The client process on the other end of the connection has either
+ // crashed or cancelled the connection. After receiving this error,
+ // the connection is in an invalid state, and you do not need to
+ // call xpc_connection_cancel(). Just tear down any associated state
+ // here.
+ //log.Println("connection invalid")
+ eh.HandleXpcEvent(nil, CONNECTION_INVALID)
+ } else if event == C.ERROR_CONNECTION_INTERRUPTED {
+ //log.Println("connection interrupted")
+ eh.HandleXpcEvent(nil, CONNECTION_INTERRUPTED)
+ } else if event == C.ERROR_CONNECTION_TERMINATED {
+ // Handle per-connection termination cleanup.
+ //log.Println("connection terminated")
+ eh.HandleXpcEvent(nil, CONNECTION_TERMINATED)
+ } else {
+ //log.Println("got some error", event)
+ eh.HandleXpcEvent(nil, fmt.Errorf("%v", event))
+ }
+ } else {
+ eh.HandleXpcEvent(xpcToGo(event).(Dict), nil)
+ }
+}
+
+// goToXpc converts a go object to an xpc object
+func goToXpc(o interface{}) C.xpc_object_t {
+ return valueToXpc(r.ValueOf(o))
+}
+
+// valueToXpc converts a go Value to an xpc object
+//
+// note that not all the types are supported, but only the subset required for Blued
+func valueToXpc(val r.Value) C.xpc_object_t {
+ if !val.IsValid() {
+ return nil
+ }
+
+ var xv C.xpc_object_t
+
+ switch val.Kind() {
+ case r.Int, r.Int8, r.Int16, r.Int32, r.Int64:
+ xv = C.xpc_int64_create(C.int64_t(val.Int()))
+
+ case r.Uint, r.Uint8, r.Uint16, r.Uint32:
+ xv = C.xpc_int64_create(C.int64_t(val.Uint()))
+
+ case r.String:
+ xv = C.xpc_string_create(C.CString(val.String()))
+
+ case r.Map:
+ xv = C.xpc_dictionary_create(nil, nil, 0)
+ for _, k := range val.MapKeys() {
+ v := valueToXpc(val.MapIndex(k))
+ C.xpc_dictionary_set_value(xv, C.CString(k.String()), v)
+ if v != nil {
+ C.xpc_release(v)
+ }
+ }
+
+ case r.Array, r.Slice:
+ if val.Type() == TYPE_OF_UUID {
+ // Array of bytes
+ var uuid [16]byte
+ r.Copy(r.ValueOf(uuid[:]), val)
+ xv = C.xpc_uuid_create(C.ptr_to_uuid(unsafe.Pointer(&uuid[0])))
+ } else if val.Type() == TYPE_OF_BYTES {
+ // slice of bytes
+ xv = C.xpc_data_create(unsafe.Pointer(val.Pointer()), C.size_t(val.Len()))
+ } else {
+ xv = C.xpc_array_create(nil, 0)
+ l := val.Len()
+
+ for i := 0; i < l; i++ {
+ v := valueToXpc(val.Index(i))
+ C.xpc_array_append_value(xv, v)
+ if v != nil {
+ C.xpc_release(v)
+ }
+ }
+ }
+
+ case r.Interface, r.Ptr:
+ xv = valueToXpc(val.Elem())
+
+ default:
+ log.Fatalf("unsupported %#v", val.String())
+ }
+
+ return xv
+}
+
+//export arraySet
+func arraySet(u C.uintptr_t, i C.int, v C.xpc_object_t) {
+ a := *(*Array)(unsafe.Pointer(uintptr(u)))
+ a[i] = xpcToGo(v)
+}
+
+//export dictSet
+func dictSet(u C.uintptr_t, k *C.char, v C.xpc_object_t) {
+ d := *(*Dict)(unsafe.Pointer(uintptr(u)))
+ d[C.GoString(k)] = xpcToGo(v)
+}
+
+// xpcToGo converts an xpc object to a go object
+//
+// note that not all the types are supported, but only the subset required for Blued
+func xpcToGo(v C.xpc_object_t) interface{} {
+ t := C.xpc_get_type(v)
+
+ switch t {
+ case C.TYPE_ARRAY:
+ a := make(Array, C.int(C.xpc_array_get_count(v)))
+ p := uintptr(unsafe.Pointer(&a))
+ C.XpcArrayApply(C.uintptr_t(p), v)
+ return a
+
+ case C.TYPE_DATA:
+ return C.GoBytes(C.xpc_data_get_bytes_ptr(v), C.int(C.xpc_data_get_length(v)))
+
+ case C.TYPE_DICT:
+ d := make(Dict)
+ p := uintptr(unsafe.Pointer(&d))
+ C.XpcDictApply(C.uintptr_t(p), v)
+ return d
+
+ case C.TYPE_INT64:
+ return int64(C.xpc_int64_get_value(v))
+
+ case C.TYPE_STRING:
+ return C.GoString(C.xpc_string_get_string_ptr(v))
+
+ case C.TYPE_UUID:
+ a := [16]byte{}
+ C.XpcUUIDGetBytes(unsafe.Pointer(&a), v)
+ return UUID(a)
+
+ default:
+ log.Fatalf("unexpected type %#v, value %#v", t, v)
+ }
+
+ return nil
+}
+
+// xpc_release is needed by tests, since they can't use CGO
+func xpc_release(xv C.xpc_object_t) {
+ C.xpc_release(xv)
+}
+
+// this is used to check the OS version
+
+type Utsname struct {
+ Sysname string
+ Nodename string
+ Release string
+ Version string
+ Machine string
+}
+
+func Uname(utsname *Utsname) error {
+ var cstruct C.struct_utsname
+ if err := C.uname(&cstruct); err != 0 {
+ return errors.New("utsname error")
+ }
+
+ // XXX: this may crash if any value is exactly 256 characters (no 0 terminator)
+ utsname.Sysname = C.GoString(&cstruct.sysname[0])
+ utsname.Nodename = C.GoString(&cstruct.nodename[0])
+ utsname.Release = C.GoString(&cstruct.release[0])
+ utsname.Version = C.GoString(&cstruct.version[0])
+ utsname.Machine = C.GoString(&cstruct.machine[0])
+
+ return nil
+}
diff --git a/vendor/github.com/raff/goble/xpc/xpc_test.go b/vendor/github.com/raff/goble/xpc/xpc_test.go
new file mode 100644
index 0000000..e0ebe12
--- /dev/null
+++ b/vendor/github.com/raff/goble/xpc/xpc_test.go
@@ -0,0 +1,106 @@
+package xpc
+
+import (
+ "testing"
+)
+
+func CheckUUID(t *testing.T, v interface{}) UUID {
+ if uuid, ok := v.(UUID); ok {
+ return uuid
+ } else {
+ t.Errorf("not a UUID: %#v\n", v)
+ return uuid
+ }
+}
+
+func TestConvertUUID(t *testing.T) {
+ uuid := MakeUUID("00112233445566778899aabbccddeeff")
+
+ xv := goToXpc(uuid)
+ v := xpcToGo(xv)
+
+ xpc_release(xv)
+
+ uuid2 := CheckUUID(t, v)
+
+ if uuid != uuid2 {
+ t.Errorf("expected %#v got %#v\n", uuid, uuid2)
+ }
+}
+
+func TestConvertSlice(t *testing.T) {
+ arr := []string{"one", "two", "three"}
+
+ xv := goToXpc(arr)
+ v := xpcToGo(xv)
+
+ xpc_release(xv)
+
+ if arr2, ok := v.(Array); !ok {
+ t.Errorf("not an array: %#v\n", v)
+ } else if len(arr) != len(arr2) {
+ t.Errorf("expected %#v got %#v\n", arr, arr2)
+ } else {
+ for i := range arr {
+ if arr[i] != arr2[i] {
+ t.Errorf("expected array[%d]: %#v got %#v\n", i, arr[i], arr2[i])
+ }
+ }
+ }
+}
+
+func TestConvertSliceUUID(t *testing.T) {
+ arr := []UUID{MakeUUID("0000000000000000"), MakeUUID("1111111111111111"), MakeUUID("2222222222222222")}
+
+ xv := goToXpc(arr)
+ v := xpcToGo(xv)
+
+ xpc_release(xv)
+
+ if arr2, ok := v.(Array); !ok {
+ t.Errorf("not an array: %#v\n", v)
+ } else if len(arr) != len(arr2) {
+ t.Errorf("expected %#v got %#v\n", arr, arr2)
+ } else {
+ for i := range arr {
+ uuid1 := CheckUUID(t, arr[i])
+ uuid2 := CheckUUID(t, arr2[i])
+
+ if uuid1 != uuid2 {
+ t.Errorf("expected array[%d]: %#v got %#v\n", i, arr[i], arr2[i])
+ }
+ }
+ }
+}
+
+func TestConvertMap(t *testing.T) {
+ d := Dict{
+ "number": int64(42),
+ "text": "hello gopher",
+ "uuid": MakeUUID("aabbccddeeff00112233445566778899"),
+ }
+
+ xv := goToXpc(d)
+ v := xpcToGo(xv)
+
+ xpc_release(xv)
+
+ if d2, ok := v.(Dict); !ok {
+ t.Errorf("not a map: %#v", v)
+ } else if len(d) != len(d2) {
+ t.Errorf("expected %#v got %#v\n", d, d2)
+ } else {
+ fail := false
+
+ for k, v := range d {
+ if v != d2[k] {
+ t.Logf("expected map[%s]: %#v got %#v\n", k, v, d2[k])
+ fail = true
+ }
+ }
+
+ if fail {
+ t.Error("test failed")
+ }
+ }
+}
diff --git a/vendor/github.com/raff/goble/xpc/xpc_wrapper.c b/vendor/github.com/raff/goble/xpc/xpc_wrapper.c
new file mode 100644
index 0000000..130b237
--- /dev/null
+++ b/vendor/github.com/raff/goble/xpc/xpc_wrapper.c
@@ -0,0 +1,85 @@
+#include <dispatch/dispatch.h>
+#include <xpc/xpc.h>
+#include <xpc/connection.h>
+#include <Block.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "_cgo_export.h"
+
+//
+// types and errors are implemented as macros
+// create some real objects to make them accessible to Go
+//
+xpc_type_t TYPE_ERROR = XPC_TYPE_ERROR;
+
+xpc_type_t TYPE_ARRAY = XPC_TYPE_ARRAY;
+xpc_type_t TYPE_DATA = XPC_TYPE_DATA;
+xpc_type_t TYPE_DICT = XPC_TYPE_DICTIONARY;
+xpc_type_t TYPE_INT64 = XPC_TYPE_INT64;
+xpc_type_t TYPE_STRING = XPC_TYPE_STRING;
+xpc_type_t TYPE_UUID = XPC_TYPE_UUID;
+
+xpc_object_t ERROR_CONNECTION_INVALID = (xpc_object_t) XPC_ERROR_CONNECTION_INVALID;
+xpc_object_t ERROR_CONNECTION_INTERRUPTED = (xpc_object_t) XPC_ERROR_CONNECTION_INTERRUPTED;
+xpc_object_t ERROR_CONNECTION_TERMINATED = (xpc_object_t) XPC_ERROR_TERMINATION_IMMINENT;
+
+const ptr_to_uuid_t ptr_to_uuid(void *p) { return (ptr_to_uuid_t)p; }
+
+
+//
+// connect to XPC service
+//
+xpc_connection_t XpcConnect(char *service, uintptr_t ctx) {
+ dispatch_queue_t queue = dispatch_queue_create(service, 0);
+ xpc_connection_t conn = xpc_connection_create_mach_service(service, queue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
+
+ // making a local copy, that should be made "persistent" with the following Block_copy
+ //GoInterface ictx = *((GoInterface*)ctx);
+
+ xpc_connection_set_event_handler(conn,
+ Block_copy(^(xpc_object_t event) {
+ handleXpcEvent(event, ctx); //(void *)&ictx);
+ })
+ );
+
+ xpc_connection_resume(conn);
+ return conn;
+}
+
+void XpcSendMessage(xpc_connection_t conn, xpc_object_t message, bool release, bool reportDelivery) {
+ xpc_connection_send_message(conn, message);
+ xpc_connection_send_barrier(conn, ^{
+ // Block is invoked on connection's target queue
+ // when 'message' has been sent.
+ if (reportDelivery) { // maybe this could be a callback
+ puts("message delivered");
+ }
+ });
+ if (release) {
+ xpc_release(message);
+ }
+}
+
+void XpcArrayApply(uintptr_t v, xpc_object_t arr) {
+ xpc_array_apply(arr, ^bool(size_t index, xpc_object_t value) {
+ arraySet(v, index, value);
+ return true;
+ });
+}
+
+void XpcDictApply(uintptr_t v, xpc_object_t dict) {
+ xpc_dictionary_apply(dict, ^bool(const char *key, xpc_object_t value) {
+ dictSet(v, (char *)key, value);
+ return true;
+ });
+}
+
+void XpcUUIDGetBytes(void *v, xpc_object_t uuid) {
+ const uint8_t *src = xpc_uuid_get_bytes(uuid);
+ uint8_t *dest = (uint8_t *)v;
+
+ for (int i=0; i < sizeof(uuid_t); i++) {
+ dest[i] = src[i];
+ }
+}
diff --git a/vendor/github.com/raff/goble/xpc/xpc_wrapper.h b/vendor/github.com/raff/goble/xpc/xpc_wrapper.h
new file mode 100644
index 0000000..eb7528f
--- /dev/null
+++ b/vendor/github.com/raff/goble/xpc/xpc_wrapper.h
@@ -0,0 +1,33 @@
+#ifndef _XPC_WRAPPER_H_
+#define _XPC_WRAPPER_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <xpc/xpc.h>
+#include <sys/utsname.h>
+
+extern xpc_type_t TYPE_ERROR;
+
+extern xpc_type_t TYPE_ARRAY;
+extern xpc_type_t TYPE_DATA;
+extern xpc_type_t TYPE_DICT;
+extern xpc_type_t TYPE_INT64;
+extern xpc_type_t TYPE_STRING;
+extern xpc_type_t TYPE_UUID;
+
+extern xpc_object_t ERROR_CONNECTION_INVALID;
+extern xpc_object_t ERROR_CONNECTION_INTERRUPTED;
+extern xpc_object_t ERROR_CONNECTION_TERMINATED;
+
+extern xpc_connection_t XpcConnect(char *, uintptr_t);
+extern void XpcSendMessage(xpc_connection_t, xpc_object_t, bool, bool);
+extern void XpcArrayApply(uintptr_t, xpc_object_t);
+extern void XpcDictApply(uintptr_t, xpc_object_t);
+extern void XpcUUIDGetBytes(void *, xpc_object_t);
+
+// the input type for xpc_uuid_create should be uuid_t but CGO instists on unsigned char *
+// typedef uuid_t * ptr_to_uuid_t;
+typedef unsigned char * ptr_to_uuid_t;
+extern const ptr_to_uuid_t ptr_to_uuid(void *p);
+
+#endif
--
To stop receiving notification emails like this one, please contact
['"commits@mynewt.apache.org" <co...@mynewt.apache.org>'].