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 := &notifier{}
+	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>'].