http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/hci.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/hci.go b/newtmgr/vendor/github.com/runtimeco/gatt/linux/hci.go new file mode 100644 index 0000000..967680d --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/hci.go @@ -0,0 +1,400 @@ +package linux + +import ( + "fmt" + "io" + "log" + "sync" + + "github.com/runtimeco/gatt/linux/cmd" + "github.com/runtimeco/gatt/linux/evt" +) + +type HCI struct { + AcceptMasterHandler func(pd *PlatData) + AcceptSlaveHandler func(pd *PlatData) + AdvertisementHandler func(pd *PlatData) + + d io.ReadWriteCloser + c *cmd.Cmd + e *evt.Evt + + plist map[bdaddr]*PlatData + plistmu *sync.Mutex + + bufCnt chan struct{} + bufSize int + + maxConn int + connsmu *sync.Mutex + conns map[uint16]*conn + + adv bool + advmu *sync.Mutex +} + +type bdaddr [6]byte + +type PlatData struct { + Name string + AddressType uint8 + Address [6]byte + Data []byte + Connectable bool + RSSI int8 + + Conn io.ReadWriteCloser +} + +func NewHCI(devID int, chk bool, maxConn int) (*HCI, error) { + d, err := newDevice(devID, chk) + if err != nil { + return nil, err + } + c := cmd.NewCmd(d) + e := evt.NewEvt() + + h := &HCI{ + d: d, + c: c, + e: e, + + plist: make(map[bdaddr]*PlatData), + plistmu: &sync.Mutex{}, + + bufCnt: make(chan struct{}, 15-1), + bufSize: 27, + + maxConn: maxConn, + connsmu: &sync.Mutex{}, + conns: map[uint16]*conn{}, + + advmu: &sync.Mutex{}, + } + + e.HandleEvent(evt.LEMeta, evt.HandlerFunc(h.handleLEMeta)) + e.HandleEvent(evt.DisconnectionComplete, evt.HandlerFunc(h.handleDisconnectionComplete)) + e.HandleEvent(evt.NumberOfCompletedPkts, evt.HandlerFunc(h.handleNumberOfCompletedPkts)) + e.HandleEvent(evt.CommandComplete, evt.HandlerFunc(c.HandleComplete)) + e.HandleEvent(evt.CommandStatus, evt.HandlerFunc(c.HandleStatus)) + + go h.mainLoop() + h.resetDevice() + return h, nil +} + +func (h *HCI) Close() error { + for _, c := range h.conns { + c.Close() + } + return h.d.Close() +} + +func (h *HCI) SetAdvertiseEnable(en bool) error { + h.advmu.Lock() + h.adv = en + h.advmu.Unlock() + return h.setAdvertiseEnable(en) +} + +func (h *HCI) setAdvertiseEnable(en bool) error { + h.advmu.Lock() + defer h.advmu.Unlock() + if en && h.adv && (len(h.conns) == h.maxConn) { + return nil + } + return h.c.SendAndCheckResp( + cmd.LESetAdvertiseEnable{ + AdvertisingEnable: btoi(en), + }, []byte{0x00}) +} + +func (h *HCI) SendCmdWithAdvOff(c cmd.CmdParam) error { + h.setAdvertiseEnable(false) + err := h.c.SendAndCheckResp(c, nil) + if h.adv { + h.setAdvertiseEnable(true) + } + return err +} + +func (h *HCI) SetScanEnable(en bool, dup bool) error { + return h.c.SendAndCheckResp( + cmd.LESetScanEnable{ + LEScanEnable: btoi(en), + FilterDuplicates: btoi(!dup), + }, []byte{0x00}) +} + +func (h *HCI) Connect(pd *PlatData) error { + h.c.Send( + cmd.LECreateConn{ + LEScanInterval: 0x0004, // N x 0.625ms + LEScanWindow: 0x0004, // N x 0.625ms + InitiatorFilterPolicy: 0x00, // white list not used + PeerAddressType: pd.AddressType, // public or random + PeerAddress: pd.Address, // + OwnAddressType: 0x00, // public + ConnIntervalMin: 6, // N x 0.125ms + ConnIntervalMax: 7, // N x 0.125ms + ConnLatency: 0x0000, // + SupervisionTimeout: 0x00100, // N x 10ms + MinimumCELength: 0x0000, // N x 0.625ms + MaximumCELength: 0x0000, // N x 0.625ms + }) + return nil +} + +func (h *HCI) CancelConnection(pd *PlatData) error { + return pd.Conn.Close() +} + +func (h *HCI) SendRawCommand(c cmd.CmdParam) ([]byte, error) { + return h.c.Send(c) +} + +func btoi(b bool) uint8 { + if b { + return 1 + } + return 0 +} + +func (h *HCI) mainLoop() { + b := make([]byte, 4096) + for { + n, err := h.d.Read(b) + if err != nil { + return + } + if n == 0 { + return + } + p := make([]byte, n) + copy(p, b) + h.handlePacket(p) + } +} + +func (h *HCI) handlePacket(b []byte) { + t, b := packetType(b[0]), b[1:] + var err error + switch t { + case typCommandPkt: + op := uint16(b[0]) | uint16(b[1])<<8 + log.Printf("unmanaged cmd: opcode (%04x) [ % X ]\n", op, b) + case typACLDataPkt: + err = h.handleL2CAP(b) + case typSCODataPkt: + err = fmt.Errorf("SCO packet not supported") + case typEventPkt: + go func() { + err := h.e.Dispatch(b) + if err != nil { + log.Printf("hci: %s, [ % X]", err, b) + } + }() + case typVendorPkt: + err = fmt.Errorf("Vendor packet not supported") + default: + log.Fatalf("Unknown event: 0x%02X [ % X ]\n", t, b) + } + if err != nil { + log.Printf("hci: %s, [ % X]", err, b) + } +} + +func (h *HCI) resetDevice() error { + seq := []cmd.CmdParam{ + cmd.Reset{}, + cmd.SetEventMask{EventMask: 0x3dbff807fffbffff}, + cmd.LESetEventMask{LEEventMask: 0x000000000000001F}, + cmd.WriteSimplePairingMode{SimplePairingMode: 1}, + cmd.WriteLEHostSupported{LESupportedHost: 1, SimultaneousLEHost: 0}, + cmd.WriteInquiryMode{InquiryMode: 2}, + cmd.WritePageScanType{PageScanType: 1}, + cmd.WriteInquiryScanType{ScanType: 1}, + cmd.WriteClassOfDevice{ClassOfDevice: [3]byte{0x40, 0x02, 0x04}}, + cmd.WritePageTimeout{PageTimeout: 0x2000}, + cmd.WriteDefaultLinkPolicy{DefaultLinkPolicySettings: 0x5}, + cmd.HostBufferSize{ + HostACLDataPacketLength: 0x1000, + HostSynchronousDataPacketLength: 0xff, + HostTotalNumACLDataPackets: 0x0014, + HostTotalNumSynchronousDataPackets: 0x000a}, + cmd.LESetScanParameters{ + LEScanType: 0x01, // [0x00]: passive, 0x01: active + LEScanInterval: 0x0010, // [0x10]: 0.625ms * 16 + LEScanWindow: 0x0010, // [0x10]: 0.625ms * 16 + OwnAddressType: 0x00, // [0x00]: public, 0x01: random + ScanningFilterPolicy: 0x00, // [0x00]: accept all, 0x01: ignore non-white-listed. + }, + } + for _, s := range seq { + if err := h.c.SendAndCheckResp(s, []byte{0x00}); err != nil { + return err + } + } + return nil +} + +func (h *HCI) handleAdvertisement(b []byte) { + // If no one is interested, don't bother. + if h.AdvertisementHandler == nil { + return + } + ep := &evt.LEAdvertisingReportEP{} + if err := ep.Unmarshal(b); err != nil { + return + } + for i := 0; i < int(ep.NumReports); i++ { + addr := bdaddr(ep.Address[i]) + et := ep.EventType[i] + connectable := et == advInd || et == advDirectInd + scannable := et == advInd || et == advScanInd + + if et == scanRsp { + h.plistmu.Lock() + pd, ok := h.plist[addr] + h.plistmu.Unlock() + if ok { + pd.Data = append(pd.Data, ep.Data[i]...) + h.AdvertisementHandler(pd) + } + continue + } + + pd := &PlatData{ + AddressType: ep.AddressType[i], + Address: ep.Address[i], + Data: ep.Data[i], + Connectable: connectable, + RSSI: ep.RSSI[i], + } + h.plistmu.Lock() + h.plist[addr] = pd + h.plistmu.Unlock() + if scannable { + continue + } + h.AdvertisementHandler(pd) + } +} + +func (h *HCI) handleNumberOfCompletedPkts(b []byte) error { + ep := &evt.NumberOfCompletedPktsEP{} + if err := ep.Unmarshal(b); err != nil { + return err + } + for _, r := range ep.Packets { + for i := 0; i < int(r.NumOfCompletedPkts); i++ { + <-h.bufCnt + } + } + return nil +} + +func (h *HCI) handleConnection(b []byte) { + ep := &evt.LEConnectionCompleteEP{} + if err := ep.Unmarshal(b); err != nil { + return // FIXME + } + hh := ep.ConnectionHandle + c := newConn(h, hh) + h.connsmu.Lock() + h.conns[hh] = c + h.connsmu.Unlock() + h.setAdvertiseEnable(true) + + // FIXME: sloppiness. This call should be called by the package user once we + // flesh out the support of l2cap signaling packets (CID:0x0001,0x0005) + if ep.ConnLatency != 0 || ep.ConnInterval > 0x18 { + c.updateConnection() + } + + // master connection + if ep.Role == 0x01 { + pd := &PlatData{ + Address: ep.PeerAddress, + Conn: c, + } + h.AcceptMasterHandler(pd) + return + } + h.plistmu.Lock() + pd := h.plist[ep.PeerAddress] + h.plistmu.Unlock() + pd.Conn = c + h.AcceptSlaveHandler(pd) +} + +func (h *HCI) handleDisconnectionComplete(b []byte) error { + ep := &evt.DisconnectionCompleteEP{} + if err := ep.Unmarshal(b); err != nil { + return err + } + hh := ep.ConnectionHandle + h.connsmu.Lock() + defer h.connsmu.Unlock() + c, found := h.conns[hh] + if !found { + // should not happen, just be cautious for now. + log.Printf("l2conn: disconnecting a disconnected 0x%04X connection", hh) + return nil + } + delete(h.conns, hh) + close(c.aclc) + h.setAdvertiseEnable(true) + return nil +} + +func (h *HCI) handleLEMeta(b []byte) error { + code := evt.LEEventCode(b[0]) + switch code { + case evt.LEConnectionComplete: + go h.handleConnection(b) + case evt.LEConnectionUpdateComplete: + // anything to do here? + case evt.LEAdvertisingReport: + go h.handleAdvertisement(b) + // case evt.LEReadRemoteUsedFeaturesComplete: + // case evt.LELTKRequest: + // case evt.LERemoteConnectionParameterRequest: + default: + return fmt.Errorf("Unhandled LE event: %s, [ % X ]", code, b) + } + return nil +} + +func (h *HCI) handleL2CAP(b []byte) error { + a := &aclData{} + if err := a.unmarshal(b); err != nil { + return err + } + h.connsmu.Lock() + defer h.connsmu.Unlock() + c, found := h.conns[a.attr] + + if a.flags != 0x002 { + if !found { + // should not happen, just be cautious for now. + log.Printf("l2conn: got data for disconnected handle: 0x%04x", a.attr) + return nil + if len(a.b) < 4 { + log.Printf("l2conn: l2cap packet is too short/corrupt, length is %d", len(a.b)) + return nil + } + cid := uint16(a.b[2]) | (uint16(a.b[3]) << 8) + if cid == 5 { + c.handleSignal(a) + return nil + } + } + } + c.aclc <- a + return nil +} + +func (h *HCI) trace(fmt string, v ...interface{}) { + log.Printf(fmt, v) +}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/l2cap.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/l2cap.go b/newtmgr/vendor/github.com/runtimeco/gatt/linux/l2cap.go new file mode 100644 index 0000000..daca82f --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/l2cap.go @@ -0,0 +1,174 @@ +package linux + +import ( + "fmt" + "io" + "log" + + "github.com/runtimeco/gatt/linux/cmd" +) + +type aclData struct { + attr uint16 + flags uint8 + dlen uint16 + b []byte +} + +func (a *aclData) unmarshal(b []byte) error { + if len(b) < 4 { + return fmt.Errorf("malformed acl packet") + } + attr := uint16(b[0]) | (uint16(b[1]&0x0f) << 8) + flags := b[1] >> 4 + dlen := uint16(b[2]) | (uint16(b[3]) << 8) + if len(b) != 4+int(dlen) { + return fmt.Errorf("malformed acl packet") + } + + *a = aclData{attr: attr, flags: flags, dlen: dlen, b: b[4:]} + return nil +} + +type conn struct { + hci *HCI + attr uint16 + aclc chan *aclData +} + +func newConn(hci *HCI, hh uint16) *conn { + return &conn{ + hci: hci, + attr: hh, + aclc: make(chan *aclData), + } +} + +func (c *conn) updateConnection() (int, error) { + b := []byte{ + 0x12, // Code (Connection Param Update) + 0x02, // ID + 0x08, 0x00, // DataLength + 0x08, 0x00, // IntervalMin + 0x18, 0x00, // IntervalMax + 0x00, 0x00, // SlaveLatency + 0xC8, 0x00} // TimeoutMultiplier + return c.write(0x05, b) +} + +// write writes the l2cap payload to the controller. +// It first prepend the l2cap header (4-bytes), and diassemble the payload +// if it is larger than the HCI LE buffer size that the conntroller can support. +func (c *conn) write(cid int, b []byte) (int, error) { + flag := uint8(0) // ACL data continuation flag + tlen := len(b) // Total length of the l2cap payload + + // log.Printf("W: [ % X ]", b) + w := append( + []byte{ + 0, // packet type + 0, 0, // attr + 0, 0, // dlen + uint8(tlen), uint8(tlen >> 8), // l2cap header + uint8(cid), uint8(cid >> 8), // l2cap header + }, b...) + + n := 4 + tlen // l2cap header + l2cap payload + for n > 0 { + dlen := n + if dlen > c.hci.bufSize { + dlen = c.hci.bufSize + } + w[0] = 0x02 // packetTypeACL + w[1] = uint8(c.attr) + w[2] = uint8(c.attr>>8) | flag + w[3] = uint8(dlen) + w[4] = uint8(dlen >> 8) + + // make sure we don't send more buffers than the controller can handdle + c.hci.bufCnt <- struct{}{} + + c.hci.d.Write(w[:5+dlen]) + w = w[dlen:] // advance the pointer to the next segment, if any. + flag = 0x10 // the rest of iterations attr continued segments, if any. + n -= dlen + } + + return len(b), nil +} + +func (c *conn) Read(b []byte) (int, error) { + a, ok := <-c.aclc + if !ok { + return 0, io.EOF + } + tlen := int(uint16(a.b[0]) | uint16(a.b[1])<<8) + if tlen > len(b) { + return 0, io.ErrShortBuffer + } + d := a.b[4:] // skip l2cap header + copy(b, d) + n := len(d) + + // Keep receiving and reassemble continued l2cap segments + for n != tlen { + if a, ok = <-c.aclc; !ok || (a.flags&0x1) == 0 { + return n, io.ErrUnexpectedEOF + } + copy(b[n:], a.b) + n += len(a.b) + } + // log.Printf("R: [ % X ]", b[:n]) + return n, nil +} + +func (c *conn) Write(b []byte) (int, error) { + return c.write(0x04, b) +} + +// Close disconnects the connection by sending HCI disconnect command to the device. +func (c *conn) Close() error { + h := c.hci + hh := c.attr + h.connsmu.Lock() + defer h.connsmu.Unlock() + _, found := h.conns[hh] + if !found { + // log.Printf("l2conn: 0x%04x already disconnected", hh) + return nil + } + if err, _ := h.c.Send(cmd.Disconnect{ConnectionHandle: hh, Reason: 0x13}); err != nil { + return fmt.Errorf("l2conn: failed to disconnect, %s", err) + } + return nil +} + +// Signal Packets +// 0x00 Reserved Any +// 0x01 Command reject 0x0001 and 0x0005 +// 0x02 Connection request 0x0001 +// 0x03 Connection response 0x0001 +// 0x04 Configure request 0x0001 +// 0x05 Configure response 0x0001 +// 0x06 Disconnection request 0x0001 and 0x0005 +// 0x07 Disconnection response 0x0001 and 0x0005 +// 0x08 Echo request 0x0001 +// 0x09 Echo response 0x0001 +// 0x0A Information request 0x0001 +// 0x0B Information response 0x0001 +// 0x0C Create Channel request 0x0001 +// 0x0D Create Channel response 0x0001 +// 0x0E Move Channel request 0x0001 +// 0x0F Move Channel response 0x0001 +// 0x10 Move Channel Confirmation 0x0001 +// 0x11 Move Channel Confirmation response 0x0001 +// 0x12 Connection Parameter Update request 0x0005 +// 0x13 Connection Parameter Update response 0x0005 +// 0x14 LE Credit Based Connection request 0x0005 +// 0x15 LE Credit Based Connection response 0x0005 +// 0x16 LE Flow Control Credit 0x0005 +func (c *conn) handleSignal(a *aclData) error { + log.Printf("ignore l2cap signal:[ % X ]", a.b) + // FIXME: handle LE signaling channel (CID: 5) + return nil +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/asm.s ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/asm.s b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/asm.s new file mode 100644 index 0000000..d4ca868 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/asm.s @@ -0,0 +1,8 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·use(SB),NOSPLIT,$0 + RET http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/asm_linux_386.s ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/asm_linux_386.s b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/asm_linux_386.s new file mode 100644 index 0000000..5d3ad9a --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/asm_linux_386.s @@ -0,0 +1,33 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// +// System calls for 386, Linux +// + +// Just jump to package syscall's implementation for all these functions. +// The runtime may know about them. + +TEXT ·Syscall(SB),NOSPLIT,$0-28 + JMP syscall·Syscall(SB) + +TEXT ·Syscall6(SB),NOSPLIT,$0-40 + JMP syscall·Syscall6(SB) + +TEXT ·RawSyscall(SB),NOSPLIT,$0-28 + JMP syscall·RawSyscall(SB) + +TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 + JMP syscall·RawSyscall6(SB) + +TEXT ·socketcall(SB),NOSPLIT,$0-36 + JMP syscall·socketcall(SB) + +TEXT ·rawsocketcall(SB),NOSPLIT,$0-36 + JMP syscall·rawsocketcall(SB) + +TEXT ·seek(SB),NOSPLIT,$0-28 + JMP syscall·seek(SB) http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket.go b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket.go new file mode 100644 index 0000000..ffc49a6 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket.go @@ -0,0 +1,121 @@ +// Package socket implements a minimal set of function of the HCI Socket, +// which is not yet supported by the Go standard library. Most of the code +// follow suit the existing code in the standard library. Once it gets +// supported officially, we can get rid of this package entirely. + +package socket + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// Bluetooth Protocols +const ( + BTPROTO_L2CAP = 0 + BTPROTO_HCI = 1 + BTPROTO_SCO = 2 + BTPROTO_RFCOMM = 3 + BTPROTO_BNEP = 4 + BTPROTO_CMTP = 5 + BTPROTO_HIDP = 6 + BTPROTO_AVDTP = 7 +) + +const ( + HCI_CHANNEL_RAW = 0 + HCI_CHANNEL_USER = 1 + HCI_CHANNEL_MONITOR = 2 + HCI_CHANNEL_CONTROL = 3 +) + +var ( + ErrSocketOpenFailed = errors.New("unable to open bluetooth socket to device") + ErrSocketBindTimeout = errors.New("timeout occured binding to bluetooth device") +) + +type _Socklen uint32 + +type Sockaddr interface { + sockaddr() (ptr unsafe.Pointer, len _Socklen, err error) // lowercase; only we can define Sockaddrs +} + +type rawSockaddrHCI struct { + Family uint16 + Dev uint16 + Channel uint16 +} + +type SockaddrHCI struct { + Dev int + Channel uint16 + raw rawSockaddrHCI +} + +const sizeofSockaddrHCI = unsafe.Sizeof(rawSockaddrHCI{}) + +func (sa *SockaddrHCI) sockaddr() (unsafe.Pointer, _Socklen, error) { + if sa.Dev < 0 || sa.Dev > 0xFFFF { + return nil, 0, syscall.EINVAL + } + if sa.Channel < 0 || sa.Channel > 0xFFFF { + return nil, 0, syscall.EINVAL + } + sa.raw.Family = AF_BLUETOOTH + sa.raw.Dev = uint16(sa.Dev) + sa.raw.Channel = sa.Channel + return unsafe.Pointer(&sa.raw), _Socklen(sizeofSockaddrHCI), nil +} + +func Socket(domain, typ, proto int) (int, error) { + for i := 0; i < 5; i++ { + if fd, err := syscall.Socket(domain, typ, proto); err == nil || err != syscall.EBUSY { + return fd, err + } + time.Sleep(time.Second) + } + return 0, ErrSocketOpenFailed +} + +func Bind(fd int, sa Sockaddr) (err error) { + ptr, n, err := sa.sockaddr() + if err != nil { + return err + } + for i := 0; i < 5; i++ { + if err = bind(fd, ptr, n); err == nil || err != syscall.EBUSY { + return err + } + time.Sleep(time.Second) + } + return ErrSocketBindTimeout +} + +// Socket Level +const ( + SOL_HCI = 0 + SOL_L2CAP = 6 + SOL_SCO = 17 + SOL_RFCOMM = 18 + + SOL_BLUETOOTH = 274 +) + +// HCI Socket options +const ( + HCI_DATA_DIR = 1 + HCI_FILTER = 2 + HCI_TIME_STAMP = 3 +) + +type HCIFilter struct { + TypeMask uint32 + EventMask [2]uint32 + opcode uint16 +} + +func SetsockoptFilter(fd int, f *HCIFilter) (err error) { + return setsockopt(fd, SOL_HCI, HCI_FILTER, unsafe.Pointer(f), unsafe.Sizeof(*f)) +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_common.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_common.go b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_common.go new file mode 100644 index 0000000..b01ceeb --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_common.go @@ -0,0 +1,24 @@ +// +build !386 + +package socket + +import ( + "syscall" + "unsafe" +) + +func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) { + _, _, e1 := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) + if e1 != 0 { + err = e1 + } + return +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_darwin.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_darwin.go b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_darwin.go new file mode 100644 index 0000000..abb96a5 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_darwin.go @@ -0,0 +1,6 @@ +// +build darwin + +package socket + +// For compile time compatibility +const AF_BLUETOOTH = 0 http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_linux.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_linux.go b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_linux.go new file mode 100644 index 0000000..4793915 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_linux.go @@ -0,0 +1,7 @@ +// +build linux + +package socket + +import "syscall" + +const AF_BLUETOOTH = syscall.AF_BLUETOOTH http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_linux_386.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_linux_386.go b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_linux_386.go new file mode 100644 index 0000000..05ca65c --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/socket/socket_linux_386.go @@ -0,0 +1,31 @@ +// +build linux,386 + +package socket + +import ( + "syscall" + "unsafe" +) + +const ( + BIND = 2 + SETSOCKETOPT = 14 +) + +func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, e1 := socketcall(BIND, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) { + _, e1 := socketcall(SETSOCKETOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), vallen, 0) + if e1 != 0 { + err = e1 + } + return +} + +func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err syscall.Errno) http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/linux/util/util.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/linux/util/util.go b/newtmgr/vendor/github.com/runtimeco/gatt/linux/util/util.go new file mode 100644 index 0000000..4933008 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/linux/util/util.go @@ -0,0 +1,16 @@ +package util + +import "encoding/binary" + +type order struct{ binary.ByteOrder } + +var Order = order{binary.LittleEndian} + +func (o order) Int8(b []byte) int8 { return int8(b[0]) } +func (o order) Uint8(b []byte) uint8 { return b[0] } +func (o order) MAC(b []byte) [6]byte { return [6]byte{b[5], b[4], b[3], b[2], b[1], b[0]} } + +func (o order) PutUint8(b []byte, v uint8) { b[0] = v } +func (o order) PutMAC(b []byte, m [6]byte) { + b[0], b[1], b[2], b[3], b[4], b[5] = m[5], m[4], m[3], m[2], m[1], m[0] +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/option_darwin.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/option_darwin.go b/newtmgr/vendor/github.com/runtimeco/gatt/option_darwin.go new file mode 100644 index 0000000..617db21 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/option_darwin.go @@ -0,0 +1,15 @@ +package gatt + +const ( + CentralManager = 0 // Client functions (default) + PeripheralManager = 1 // Server functions +) + +// MacDeviceRole specify the XPC connection type to connect blued. +// THis option can only be used with NewDevice on OS X implementation. +func MacDeviceRole(r int) Option { + return func(d Device) error { + d.(*device).role = r + return nil + } +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/option_linux.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/option_linux.go b/newtmgr/vendor/github.com/runtimeco/gatt/option_linux.go new file mode 100644 index 0000000..bf2360c --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/option_linux.go @@ -0,0 +1,87 @@ +package gatt + +import ( + "errors" + "io" + + "github.com/runtimeco/gatt/linux/cmd" +) + +// LnxDeviceID specifies which HCI device to use. +// If n is set to -1, all the available HCI devices will be probed. +// If chk is set to true, LnxDeviceID checks the LE support in the feature list of the HCI device. +// This is to filter devices that does not support LE. In case some LE driver that doesn't correctly +// set the LE support in its feature list, user can turn off the check. +// This option can only be used with NewDevice on Linux implementation. +func LnxDeviceID(n int, chk bool) Option { + return func(d Device) error { + d.(*device).devID = n + d.(*device).chkLE = chk + return nil + } +} + +// LnxMaxConnections is an optional parameter. +// If set, it overrides the default max connections supported. +// This option can only be used with NewDevice on Linux implementation. +func LnxMaxConnections(n int) Option { + return func(d Device) error { + d.(*device).maxConn = n + return nil + } +} + +// LnxSetAdvertisingEnable sets the advertising data to the HCI device. +// This option can be used with Option on Linux implementation. +func LnxSetAdvertisingEnable(en bool) Option { + return func(d Device) error { + dd := d.(*device) + if dd == nil { + return errors.New("device is not initialized") + } + if err := dd.update(); err != nil { + return err + } + return dd.hci.SetAdvertiseEnable(en) + } +} + +// LnxSetAdvertisingData sets the advertising data to the HCI device. +// This option can be used with NewDevice or Option on Linux implementation. +func LnxSetAdvertisingData(c *cmd.LESetAdvertisingData) Option { + return func(d Device) error { + d.(*device).advData = c + return nil + } +} + +// LnxSetScanResponseData sets the scan response data to the HXI device. +// This option can be used with NewDevice or Option on Linux implementation. +func LnxSetScanResponseData(c *cmd.LESetScanResponseData) Option { + return func(d Device) error { + d.(*device).scanResp = c + return nil + } +} + +// LnxSetAdvertisingParameters sets the advertising parameters to the HCI device. +// This option can be used with NewDevice or Option on Linux implementation. +func LnxSetAdvertisingParameters(c *cmd.LESetAdvertisingParameters) Option { + return func(d Device) error { + d.(*device).advParam = c + return nil + } +} + +// LnxSendHCIRawCommand sends a raw command to the HCI device +// This option can be used with NewDevice or Option on Linux implementation. +func LnxSendHCIRawCommand(c cmd.CmdParam, rsp io.Writer) Option { + return func(d Device) error { + b, err := d.(*device).SendHCIRawCommand(c) + if rsp == nil { + return err + } + rsp.Write(b) + return err + } +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/peripheral.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/peripheral.go b/newtmgr/vendor/github.com/runtimeco/gatt/peripheral.go new file mode 100644 index 0000000..36ad578 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/peripheral.go @@ -0,0 +1,102 @@ +package gatt + +import ( + "errors" + "sync" +) + +// Peripheral is the interface that represent a remote peripheral device. +type Peripheral interface { + // Device returns the underlying device. + Device() Device + + // ID is the platform specific unique ID of the remote peripheral, e.g. MAC for Linux, Peripheral UUID for MacOS. + ID() string + + // 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 + + // Services returnns the services of the remote peripheral which has been discovered. + Services() []*Service + + // DiscoverServices discover the specified services of the remote peripheral. + // If the specified services is set to nil, all the available services of the remote peripheral are returned. + DiscoverServices(s []UUID) ([]*Service, error) + + // DiscoverIncludedServices discovers the specified included services of a service. + // If the specified services is set to nil, all the included services of the service are returned. + DiscoverIncludedServices(ss []UUID, s *Service) ([]*Service, error) + + // DiscoverCharacteristics discovers the specified characteristics of a service. + // If the specified characterstics is set to nil, all the characteristic of the service are returned. + DiscoverCharacteristics(c []UUID, s *Service) ([]*Characteristic, error) + + // DiscoverDescriptors discovers the descriptors of a characteristic. + // If the specified descriptors is set to nil, all the descriptors of the characteristic are returned. + DiscoverDescriptors(d []UUID, c *Characteristic) ([]*Descriptor, error) + + // ReadCharacteristic retrieves the value of a specified characteristic. + ReadCharacteristic(c *Characteristic) ([]byte, error) + + // ReadLongCharacteristic retrieves the value of a specified characteristic that is longer than the + // MTU. + ReadLongCharacteristic(c *Characteristic) ([]byte, error) + + // ReadDescriptor retrieves the value of a specified characteristic descriptor. + ReadDescriptor(d *Descriptor) ([]byte, error) + + // WriteCharacteristic writes the value of a characteristic. + WriteCharacteristic(c *Characteristic, b []byte, noRsp bool) error + + // WriteDescriptor writes the value of a characteristic descriptor. + WriteDescriptor(d *Descriptor, b []byte) error + + // SetNotifyValue sets notifications for the value of a specified characteristic. + SetNotifyValue(c *Characteristic, f func(*Characteristic, []byte, error)) error + + // SetIndicateValue sets indications for the value of a specified characteristic. + SetIndicateValue(c *Characteristic, f func(*Characteristic, []byte, error)) error + + // ReadRSSI retrieves the current RSSI value for the remote peripheral. + ReadRSSI() int + + // SetMTU sets the mtu for the remote peripheral. + SetMTU(mtu uint16) error +} + +type subscriber struct { + sub map[uint16]subscribefn + mu *sync.Mutex +} + +type subscribefn func([]byte, error) + +func newSubscriber() *subscriber { + return &subscriber{ + sub: make(map[uint16]subscribefn), + mu: &sync.Mutex{}, + } +} + +func (s *subscriber) subscribe(h uint16, f subscribefn) { + s.mu.Lock() + s.sub[h] = f + s.mu.Unlock() +} + +func (s *subscriber) unsubscribe(h uint16) { + s.mu.Lock() + delete(s.sub, h) + s.mu.Unlock() +} + +func (s *subscriber) fn(h uint16) subscribefn { + s.mu.Lock() + defer s.mu.Unlock() + return s.sub[h] +} + +var ( + ErrInvalidLength = errors.New("invalid length") +) http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/peripheral_darwin.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/peripheral_darwin.go b/newtmgr/vendor/github.com/runtimeco/gatt/peripheral_darwin.go new file mode 100644 index 0000000..9174e61 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/peripheral_darwin.go @@ -0,0 +1,277 @@ +package gatt + +import ( + "errors" + "log" + + "github.com/runtimeco/gatt/xpc" +) + +type peripheral struct { + // NameChanged is called whenever the peripheral GAP Device name has changed. + NameChanged func(Peripheral) + + // ServicesModified is called when one or more service of a peripheral have changed. + // A list of invalid service is provided in the parameter. + ServicesModified func(Peripheral, []*Service) + + d *device + svcs []*Service + + sub *subscriber + + id xpc.UUID + name string + + reqc chan message + rspc chan message + quitc chan struct{} +} + +func NewPeripheral(u UUID) Peripheral { return &peripheral{id: xpc.UUID(u.b)} } + +func (p *peripheral) Device() Device { return p.d } +func (p *peripheral) ID() string { return p.id.String() } +func (p *peripheral) Name() string { return p.name } +func (p *peripheral) Services() []*Service { return p.svcs } + +func (p *peripheral) DiscoverServices(ss []UUID) ([]*Service, error) { + rsp := p.sendReq(45, xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgUUIDs": uuidSlice(ss), + }) + if res := rsp.MustGetInt("kCBMsgArgResult"); res != 0 { + return nil, attEcode(res) + } + svcs := []*Service{} + for _, xss := range rsp["kCBMsgArgServices"].(xpc.Array) { + xs := xss.(xpc.Dict) + u := MustParseUUID(xs.MustGetHexBytes("kCBMsgArgUUID")) + h := uint16(xs.MustGetInt("kCBMsgArgServiceStartHandle")) + endh := uint16(xs.MustGetInt("kCBMsgArgServiceEndHandle")) + svcs = append(svcs, &Service{uuid: u, h: h, endh: endh}) + } + p.svcs = svcs + return svcs, nil +} + +func (p *peripheral) DiscoverIncludedServices(ss []UUID, s *Service) ([]*Service, error) { + rsp := p.sendReq(60, xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgServiceStartHandle": s.h, + "kCBMsgArgServiceEndHandle": s.endh, + "kCBMsgArgUUIDs": uuidSlice(ss), + }) + if res := rsp.MustGetInt("kCBMsgArgResult"); res != 0 { + return nil, attEcode(res) + } + // TODO + return nil, notImplemented +} + +func (p *peripheral) DiscoverCharacteristics(cs []UUID, s *Service) ([]*Characteristic, error) { + rsp := p.sendReq(62, xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgServiceStartHandle": s.h, + "kCBMsgArgServiceEndHandle": s.endh, + "kCBMsgArgUUIDs": uuidSlice(cs), + }) + if res := rsp.MustGetInt("kCBMsgArgResult"); res != 0 { + return nil, attEcode(res) + } + for _, xcs := range rsp.MustGetArray("kCBMsgArgCharacteristics") { + xc := xcs.(xpc.Dict) + u := MustParseUUID(xc.MustGetHexBytes("kCBMsgArgUUID")) + ch := uint16(xc.MustGetInt("kCBMsgArgCharacteristicHandle")) + vh := uint16(xc.MustGetInt("kCBMsgArgCharacteristicValueHandle")) + props := Property(xc.MustGetInt("kCBMsgArgCharacteristicProperties")) + c := &Characteristic{uuid: u, svc: s, props: props, h: ch, vh: vh} + s.chars = append(s.chars, c) + } + return s.chars, nil +} + +func (p *peripheral) DiscoverDescriptors(ds []UUID, c *Characteristic) ([]*Descriptor, error) { + rsp := p.sendReq(70, xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgCharacteristicHandle": c.h, + "kCBMsgArgCharacteristicValueHandle": c.vh, + "kCBMsgArgUUIDs": uuidSlice(ds), + }) + for _, xds := range rsp.MustGetArray("kCBMsgArgDescriptors") { + xd := xds.(xpc.Dict) + u := MustParseUUID(xd.MustGetHexBytes("kCBMsgArgUUID")) + h := uint16(xd.MustGetInt("kCBMsgArgDescriptorHandle")) + d := &Descriptor{uuid: u, char: c, h: h} + c.descs = append(c.descs, d) + } + return c.descs, nil +} + +func (p *peripheral) ReadCharacteristic(c *Characteristic) ([]byte, error) { + rsp := p.sendReq(65, xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgCharacteristicHandle": c.h, + "kCBMsgArgCharacteristicValueHandle": c.vh, + }) + if res := rsp.MustGetInt("kCBMsgArgResult"); res != 0 { + return nil, attEcode(res) + } + b := rsp.MustGetBytes("kCBMsgArgData") + return b, nil +} + +func (p *peripheral) ReadLongCharacteristic(c *Characteristic) ([]byte, error) { + return nil, errors.New("Not implemented") +} + +func (p *peripheral) WriteCharacteristic(c *Characteristic, b []byte, noRsp bool) error { + args := xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgCharacteristicHandle": c.h, + "kCBMsgArgCharacteristicValueHandle": c.vh, + "kCBMsgArgData": b, + "kCBMsgArgType": map[bool]int{false: 0, true: 1}[noRsp], + } + if noRsp { + p.sendCmd(66, args) + return nil + } + rsp := p.sendReq(65, args) + if res := rsp.MustGetInt("kCBMsgArgResult"); res != 0 { + return attEcode(res) + } + return nil +} + +func (p *peripheral) ReadDescriptor(d *Descriptor) ([]byte, error) { + rsp := p.sendReq(77, xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgDescriptorHandle": d.h, + }) + if res := rsp.MustGetInt("kCBMsgArgResult"); res != 0 { + return nil, attEcode(res) + } + b := rsp.MustGetBytes("kCBMsgArgData") + return b, nil +} + +func (p *peripheral) WriteDescriptor(d *Descriptor, b []byte) error { + rsp := p.sendReq(78, xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgDescriptorHandle": d.h, + "kCBMsgArgData": b, + }) + if res := rsp.MustGetInt("kCBMsgArgResult"); res != 0 { + return attEcode(res) + } + return nil +} + +func (p *peripheral) SetNotifyValue(c *Characteristic, f func(*Characteristic, []byte, error)) error { + set := 1 + if f == nil { + set = 0 + } + // To avoid race condition, registeration is handled before requesting the server. + if f != nil { + // Note: when notified, core bluetooth reports characteristic handle, not value's handle. + p.sub.subscribe(c.h, func(b []byte, err error) { f(c, b, err) }) + } + rsp := p.sendReq(68, xpc.Dict{ + "kCBMsgArgDeviceUUID": p.id, + "kCBMsgArgCharacteristicHandle": c.h, + "kCBMsgArgCharacteristicValueHandle": c.vh, + "kCBMsgArgState": set, + }) + if res := rsp.MustGetInt("kCBMsgArgResult"); res != 0 { + return attEcode(res) + } + // To avoid race condition, unregisteration is handled after server responses. + if f == nil { + p.sub.unsubscribe(c.h) + } + return nil +} + +func (p *peripheral) SetIndicateValue(c *Characteristic, + f func(*Characteristic, []byte, error)) error { + // TODO: Implement set indications logic for darwin (https://github.com/runtimeco/gatt/issues/32) + return nil +} + +func (p *peripheral) ReadRSSI() int { + rsp := p.sendReq(43, xpc.Dict{"kCBMsgArgDeviceUUID": p.id}) + return rsp.MustGetInt("kCBMsgArgData") +} + +func (p *peripheral) SetMTU(mtu uint16) error { + return errors.New("Not implemented") +} + +func uuidSlice(uu []UUID) [][]byte { + us := [][]byte{} + for _, u := range uu { + us = append(us, reverse(u.b)) + } + return us +} + +type message struct { + id int + args xpc.Dict + rspc chan xpc.Dict +} + +func (p *peripheral) sendCmd(id int, args xpc.Dict) { + p.reqc <- message{id: id, args: args} +} + +func (p *peripheral) sendReq(id int, args xpc.Dict) xpc.Dict { + m := message{id: id, args: args, rspc: make(chan xpc.Dict)} + p.reqc <- m + return <-m.rspc +} + +func (p *peripheral) loop() { + rspc := make(chan message) + + go func() { + for { + select { + case req := <-p.reqc: + p.d.sendCBMsg(req.id, req.args) + if req.rspc == nil { + break + } + m := <-rspc + req.rspc <- m.args + case <-p.quitc: + return + } + } + }() + + for { + select { + case rsp := <-p.rspc: + // Notification + if rsp.id == 71 && rsp.args.GetInt("kCBMsgArgIsNotification", 0) != 0 { + // While we're notified with the value's handle, blued reports the characteristic handle. + ch := uint16(rsp.args.MustGetInt("kCBMsgArgCharacteristicHandle")) + b := rsp.args.MustGetBytes("kCBMsgArgData") + f := p.sub.fn(ch) + if f == nil { + log.Printf("notified by unsubscribed handle") + // FIXME: should terminate the connection? + } else { + go f(b, nil) + } + break + } + rspc <- rsp + case <-p.quitc: + return + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/peripheral_linux.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/peripheral_linux.go b/newtmgr/vendor/github.com/runtimeco/gatt/peripheral_linux.go new file mode 100644 index 0000000..43ec067 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/peripheral_linux.go @@ -0,0 +1,448 @@ +package gatt + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "log" + "net" + "strings" + + "github.com/runtimeco/gatt/linux" +) + +type peripheral struct { + // NameChanged is called whenever the peripheral GAP device name has changed. + NameChanged func(*peripheral) + + // ServicedModified is called when one or more service of a peripheral have changed. + // A list of invalid service is provided in the parameter. + ServicesModified func(*peripheral, []*Service) + + d *device + svcs []*Service + + sub *subscriber + + mtu uint16 + l2c io.ReadWriteCloser + + reqc chan message + quitc chan struct{} + + pd *linux.PlatData // platform specific data +} + +func (p *peripheral) Device() Device { return p.d } +func (p *peripheral) ID() string { return strings.ToUpper(net.HardwareAddr(p.pd.Address[:]).String()) } +func (p *peripheral) Name() string { return p.pd.Name } +func (p *peripheral) Services() []*Service { return p.svcs } + +func finish(op byte, h uint16, b []byte) bool { + done := b[0] == attOpError && b[1] == op && b[2] == byte(h) && b[3] == byte(h>>8) + e := attEcode(b[4]) + if e != attEcodeAttrNotFound { + // log.Printf("unexpected protocol error: %s", e) + // FIXME: terminate the connection + } + return done +} + +func (p *peripheral) DiscoverServices(s []UUID) ([]*Service, error) { + // TODO: implement the UUID filters + // p.pd.Conn.Write([]byte{0x02, 0x87, 0x00}) // MTU + done := false + start := uint16(0x0001) + for !done { + op := byte(attOpReadByGroupReq) + b := make([]byte, 7) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], start) + binary.LittleEndian.PutUint16(b[3:5], 0xFFFF) + binary.LittleEndian.PutUint16(b[5:7], 0x2800) + + b = p.sendReq(op, b) + if finish(op, start, b) { + break + } + b = b[1:] + l, b := int(b[0]), b[1:] + switch { + case l == 6 && (len(b)%6 == 0): + case l == 20 && (len(b)%20 == 0): + default: + return nil, ErrInvalidLength + } + + for len(b) != 0 { + h := binary.LittleEndian.Uint16(b[:2]) + endh := binary.LittleEndian.Uint16(b[2:4]) + s := &Service{ + uuid: UUID{b[4:l]}, + h: h, + endh: endh, + } + p.svcs = append(p.svcs, s) + b = b[l:] + done = endh == 0xFFFF + start = endh + 1 + } + } + return p.svcs, nil +} + +func (p *peripheral) DiscoverIncludedServices(ss []UUID, s *Service) ([]*Service, error) { + // TODO + return nil, nil +} + +func (p *peripheral) DiscoverCharacteristics(cs []UUID, s *Service) ([]*Characteristic, error) { + // TODO: implement the UUID filters + done := false + start := s.h + var prev *Characteristic + for !done { + op := byte(attOpReadByTypeReq) + b := make([]byte, 7) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], start) + binary.LittleEndian.PutUint16(b[3:5], s.endh) + binary.LittleEndian.PutUint16(b[5:7], 0x2803) + + b = p.sendReq(op, b) + if finish(op, start, b) { + break + } + b = b[1:] + + l, b := int(b[0]), b[1:] + switch { + case l == 7 && (len(b)%7 == 0): + case l == 21 && (len(b)%21 == 0): + default: + return nil, ErrInvalidLength + } + + for len(b) != 0 { + h := binary.LittleEndian.Uint16(b[:2]) + props := Property(b[2]) + vh := binary.LittleEndian.Uint16(b[3:5]) + u := UUID{b[5:l]} + s := searchService(p.svcs, h, vh) + if s == nil { + log.Printf("Can't find service range that contains 0x%04X - 0x%04X", h, vh) + return nil, fmt.Errorf("Can't find service range that contains 0x%04X - 0x%04X", h, vh) + } + c := &Characteristic{ + uuid: u, + svc: s, + props: props, + h: h, + vh: vh, + } + s.chars = append(s.chars, c) + b = b[l:] + done = vh == s.endh + start = vh + 1 + if prev != nil { + prev.endh = c.h - 1 + } + prev = c + } + } + if len(s.chars) > 1 { + s.chars[len(s.chars)-1].endh = s.endh + } + return s.chars, nil +} + +func (p *peripheral) DiscoverDescriptors(ds []UUID, c *Characteristic) ([]*Descriptor, error) { + // TODO: implement the UUID filters + done := false + start := c.vh + 1 + for !done { + if c.endh == 0 { + c.endh = c.svc.endh + } + op := byte(attOpFindInfoReq) + b := make([]byte, 5) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], start) + binary.LittleEndian.PutUint16(b[3:5], c.endh) + + b = p.sendReq(op, b) + if finish(attOpFindInfoReq, start, b) { + break + } + b = b[1:] + + var l int + f, b := int(b[0]), b[1:] + switch { + case f == 1 && (len(b)%4 == 0): + l = 4 + case f == 2 && (len(b)%18 == 0): + l = 18 + default: + return nil, ErrInvalidLength + } + + for len(b) != 0 { + h := binary.LittleEndian.Uint16(b[:2]) + u := UUID{b[2:l]} + d := &Descriptor{uuid: u, h: h, char: c} + c.descs = append(c.descs, d) + if u.Equal(attrClientCharacteristicConfigUUID) { + c.cccd = d + } + b = b[l:] + done = h == c.endh + start = h + 1 + } + } + return c.descs, nil +} + +func (p *peripheral) ReadCharacteristic(c *Characteristic) ([]byte, error) { + b := make([]byte, 3) + op := byte(attOpReadReq) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], c.vh) + + b = p.sendReq(op, b) + b = b[1:] + return b, nil +} + +func (p *peripheral) ReadLongCharacteristic(c *Characteristic) ([]byte, error) { + // The spec says that a read blob request should fail if the characteristic + // is smaller than mtu - 1. To simplify the API, the first read is done + // with a regular read request. If the buffer received is equal to mtu -1, + // then we read the rest of the data using read blob. + firstRead, err := p.ReadCharacteristic(c) + if err != nil { + return nil, err + } + if len(firstRead) < int(p.mtu)-1 { + return firstRead, nil + } + + var buf bytes.Buffer + buf.Write(firstRead) + off := uint16(len(firstRead)) + for { + b := make([]byte, 5) + op := byte(attOpReadBlobReq) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], c.vh) + binary.LittleEndian.PutUint16(b[3:5], off) + + b = p.sendReq(op, b) + b = b[1:] + if len(b) == 0 { + break + } + buf.Write(b) + off += uint16(len(b)) + if len(b) < int(p.mtu)-1 { + break + } + } + return buf.Bytes(), nil +} + +func (p *peripheral) WriteCharacteristic(c *Characteristic, value []byte, noRsp bool) error { + b := make([]byte, 3+len(value)) + op := byte(attOpWriteReq) + b[0] = op + if noRsp { + b[0] = attOpWriteCmd + } + binary.LittleEndian.PutUint16(b[1:3], c.vh) + copy(b[3:], value) + + if noRsp { + p.sendCmd(op, b) + return nil + } + b = p.sendReq(op, b) + // TODO: error handling + b = b[1:] + return nil +} + +func (p *peripheral) ReadDescriptor(d *Descriptor) ([]byte, error) { + b := make([]byte, 3) + op := byte(attOpReadReq) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], d.h) + + b = p.sendReq(op, b) + b = b[1:] + // TODO: error handling + return b, nil +} + +func (p *peripheral) WriteDescriptor(d *Descriptor, value []byte) error { + b := make([]byte, 3+len(value)) + op := byte(attOpWriteReq) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], d.h) + copy(b[3:], value) + + b = p.sendReq(op, b) + b = b[1:] + // TODO: error handling + return nil +} + +func (p *peripheral) setNotifyValue(c *Characteristic, flag uint16, + f func(*Characteristic, []byte, error)) error { +/* + if c.cccd == nil { + return errors.New("no cccd") // FIXME + } + ccc := uint16(0) + if f != nil { + ccc = flag +*/ + p.sub.subscribe(c.vh, func(b []byte, err error) { f(c, b, err) }) +/* + } + b := make([]byte, 5) + op := byte(attOpWriteReq) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], c.cccd.h) + binary.LittleEndian.PutUint16(b[3:5], ccc) + + b = p.sendReq(op, b) + b = b[1:] + // TODO: error handling + if f == nil { + p.sub.unsubscribe(c.vh) + } +*/ + return nil +} + +func (p *peripheral) SetNotifyValue(c *Characteristic, + f func(*Characteristic, []byte, error)) error { + return p.setNotifyValue(c, gattCCCNotifyFlag, f) +} + +func (p *peripheral) SetIndicateValue(c *Characteristic, + f func(*Characteristic, []byte, error)) error { + return p.setNotifyValue(c, gattCCCIndicateFlag, f) +} + +func (p *peripheral) ReadRSSI() int { + // TODO: implement + return -1 +} + +func searchService(ss []*Service, start, end uint16) *Service { + for _, s := range ss { + if s.h < start && s.endh >= end { + return s + } + } + return nil +} + +// TODO: unifiy the message with OS X pots and refactor +type message struct { + op byte + b []byte + rspc chan []byte +} + +func (p *peripheral) sendCmd(op byte, b []byte) { + p.reqc <- message{op: op, b: b} +} + +func (p *peripheral) sendReq(op byte, b []byte) []byte { + m := message{op: op, b: b, rspc: make(chan []byte)} + p.reqc <- m + return <-m.rspc +} + +func (p *peripheral) loop() { + // Serialize the request. + rspc := make(chan []byte) + + // Dequeue request loop + go func() { + for { + select { + case req := <-p.reqc: + p.l2c.Write(req.b) + if req.rspc == nil { + break + } + r := <-rspc + switch reqOp, rspOp := req.b[0], r[0]; { + case rspOp == attRspFor[reqOp]: + case rspOp == attOpError && r[1] == reqOp: + default: + log.Printf("Request 0x%02x got a mismatched response: 0x%02x", reqOp, rspOp) + // FIXME: terminate the connection? + } + req.rspc <- r + case <-p.quitc: + return + } + } + }() + + // L2CAP implementations shall support a minimum MTU size of 48 bytes. + // The default value is 672 bytes + buf := make([]byte, 672) + + // Handling response or notification/indication + for { + n, err := p.l2c.Read(buf) + if n == 0 || err != nil { + close(p.quitc) + return + } + + b := make([]byte, n) + copy(b, buf) + + if (b[0] != attOpHandleNotify) && (b[0] != attOpHandleInd) { + rspc <- b + continue + } + + h := binary.LittleEndian.Uint16(b[1:3]) + f := p.sub.fn(h) + if f == nil { + log.Printf("notified by unsubscribed handle") + // FIXME: terminate the connection? + } else { + go f(b[3:], nil) + } + + if b[0] == attOpHandleInd { + // write aknowledgement for indication + p.l2c.Write([]byte{attOpHandleCnf}) + } + + } +} + +func (p *peripheral) SetMTU(mtu uint16) error { + b := make([]byte, 3) + op := byte(attOpMtuReq) + b[0] = op + binary.LittleEndian.PutUint16(b[1:3], uint16(mtu)) + + b = p.sendReq(op, b) + serverMTU := binary.LittleEndian.Uint16(b[1:3]) + if serverMTU < mtu { + mtu = serverMTU + } + p.mtu = mtu + return nil +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/readme.md ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/readme.md b/newtmgr/vendor/github.com/runtimeco/gatt/readme.md new file mode 100644 index 0000000..ea3a957 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/readme.md @@ -0,0 +1,115 @@ +# Package gatt provides a Bluetooth Low Energy GATT implementation. + +Gatt (Generic Attribute Profile) is the protocol used to write BLE peripherals (servers) and centrals (clients). + +As a peripheral, you can create services, characteristics, and descriptors, +advertise, accept connections, and handle requests. + +As a central, you can scan, connect, discover services, and make requests. + +## SETUP + +### gatt supports both Linux and OS X. + +### On Linux: +To gain complete and exclusive control of the HCI device, gatt uses +HCI_CHANNEL_USER (introduced in Linux v3.14) instead of HCI_CHANNEL_RAW. +Those who must use an older kernel may patch in these relevant commits +from Marcel Holtmann: + + Bluetooth: Introduce new HCI socket channel for user operation + Bluetooth: Introduce user channel flag for HCI devices + Bluetooth: Refactor raw socket filter into more readable code + +Note that because gatt uses HCI_CHANNEL_USER, once gatt has opened the +device no other program may access it. + +Before starting a gatt program, make sure that your BLE device is down: + + sudo hciconfig + sudo hciconfig hci0 down # or whatever hci device you want to use + +If you have BlueZ 5.14+ (or aren't sure), stop the built-in +bluetooth server, which interferes with gatt, e.g.: + + sudo service bluetooth stop + +Because gatt programs administer network devices, they must +either be run as root, or be granted appropriate capabilities: + + sudo <executable> + # OR + sudo setcap 'cap_net_raw,cap_net_admin=eip' <executable> + <executable> + +## Usage +Please see [godoc.org](http://godoc.org/github.com/paypal/gatt) for documentation. + +## Examples + +### Build and run the examples on a native environment (Linux or OS X) + +Go is a compiled language, which means to run the examples you need to build them first. + + # Build the sample server. + go build examples/server.go + # Start the sample server. + sudo ./server + +Alternatively, you can use "go run" to build and run the examples in a single step: + + # Build and run the sample server. + sudo go run examples/server.go + +Discoverer and explorer demonstrates central (client) functions: + + # Discover surrounding peripherals. + sudo go run examples/discoverer.go + + # Connect to and explorer a peripheral device. + sudo go run examples/explorer.go <peripheral ID> + +### Cross-compile and deploy to a target device + + # Build and run the server example on a ARMv5 target device. + GOARCH=arm GOARM=5 GOOS=linux go build examples/server.go + cp server <target device> + # Start the server on the target device + sudo ./server + +See the server.go, discoverer.go, and explorer.go in the examples/ +directory for writing server or client programs that run on Linux +and OS X. + +Users, especially on Linux platforms, seeking finer-grained control +over the devices can see the examples/server_lnx.go for the usage +of Option, which are platform specific. + +See the rest of the docs for other options and finer-grained control. + +## Note +Note that some BLE central devices, particularly iOS, may aggressively +cache results from previous connections. If you change your services or +characteristics, you may need to reboot the other device to pick up the +changes. This is a common source of confusion and apparent bugs. For an +OS X central, see http://stackoverflow.com/questions/20553957. + +## Known Issues + +Currently OS X vesion does not support subscribing to indications. +Please check [#32](https://github.com/paypal/gatt/issues/32) for the status of this issue. + +## REFERENCES + +gatt started life as a port of bleno, to which it is indebted: +https://github.com/sandeepmistry/bleno. If you are having +problems with gatt, particularly around installation, issues +filed with bleno might also be helpful references. + +To try out your GATT server, it is useful to experiment with a +generic BLE client. LightBlue is a good choice. It is available +free for both iOS and OS X. + +gatt is similar to [bleno](https://github.com/sandeepmistry/bleno) and [noble](https://github.com/sandeepmistry/noble), which offer BLE GATT implementations for node.js. + +Gatt is released under a [BSD-style license](./LICENSE.md). http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/uuid.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/uuid.go b/newtmgr/vendor/github.com/runtimeco/gatt/uuid.go new file mode 100644 index 0000000..393e548 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/uuid.go @@ -0,0 +1,86 @@ +package gatt + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "strings" +) + +// A UUID is a BLE UUID. +type UUID struct { + // Hide the bytes, so that we can enforce that they have length 2 or 16, + // and that they are immutable. This simplifies the code and API. + b []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} +} + +// ParseUUID parses a standard-format UUID string, such +// as "1800" or "34DA3AD1-7110-41A1-B1EF-4430F509CDE7". +func ParseUUID(s string) (UUID, error) { + s = strings.Replace(s, "-", "", -1) + b, err := hex.DecodeString(s) + if err != nil { + return UUID{}, err + } + if err := lenErr(len(b)); err != nil { + return UUID{}, err + } + return UUID{reverse(b)}, nil +} + +// MustParseUUID parses a standard-format UUID string, +// like ParseUUID, but panics in case of error. +func MustParseUUID(s string) UUID { + u, err := ParseUUID(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.b) +} + +// String hex-encodes a UUID. +func (u UUID) String() string { + return fmt.Sprintf("%x", reverse(u.b)) +} + +// Equal returns a boolean reporting whether v represent the same UUID as u. +func (u UUID) Equal(v UUID) bool { + return bytes.Equal(u.b, v.b) +} + +// 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 +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/xpc/LICENSE ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/xpc/LICENSE b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/LICENSE new file mode 100644 index 0000000..766a0a5 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) {{{year}}} {{{fullname}}} + +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. http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/xpc/doc.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/xpc/doc.go b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/doc.go new file mode 100644 index 0000000..d292249 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/doc.go @@ -0,0 +1,8 @@ +// Package xpc provides minimal OS X XPC support required for gatt +// +// This is adapted from [goble], by Raffaele Sena. +// +// http://godoc.org/github.com/raff/goble +// https://github.com/raff/goble + +package xpc http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_darwin.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_darwin.go b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_darwin.go new file mode 100644 index 0000000..2a85a99 --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_darwin.go @@ -0,0 +1,350 @@ +package xpc + +/* +#include "xpc_wrapper_darwin.h" +#include "sys/utsname.h" +*/ +import "C" + +import ( + "errors" + "fmt" + "log" + r "reflect" + "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) []byte { + u := d[k].(UUID) + return u[:] +} + +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 []byte + +func MakeUUID(s string) UUID { + var sl []byte + fmt.Sscanf(s, "%32x", &sl) + + var uuid [16]byte + copy(uuid[:], sl) + return UUID(uuid[:]) +} + +func (uuid UUID) String() string { + return fmt.Sprintf("%x", []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{}) +) + +type XpcEventHandler interface { + HandleXpcEvent(event Dict, err error) +} + +func XpcConnect(service string, eh XpcEventHandler) XPC { + cservice := C.CString(service) + defer C.free(unsafe.Pointer(cservice)) + return XPC{conn: C.XpcConnect(cservice, unsafe.Pointer(&eh))} +} + +//export handleXpcEvent +func handleXpcEvent(event C.xpc_object_t, p unsafe.Pointer) { + //log.Printf("handleXpcEvent %#v %#v\n", event, p) + + t := C.xpc_get_type(event) + eh := *((*XpcEventHandler)(p)) + + 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 unsafe.Pointer, i C.int, v C.xpc_object_t) { + a := *(*Array)(u) + a[i] = xpcToGo(v) +} + +//export dictSet +func dictSet(u unsafe.Pointer, k *C.char, v C.xpc_object_t) { + d := *(*Dict)(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))) + C.XpcArrayApply(unsafe.Pointer(&a), 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) + C.XpcDictApply(unsafe.Pointer(&d), 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 +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_wrapper_darwin.c ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_wrapper_darwin.c b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_wrapper_darwin.c new file mode 100644 index 0000000..7bcb83d --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_wrapper_darwin.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, void *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, (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(void *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(void *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]; + } +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_wrapper_darwin.h ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_wrapper_darwin.h b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_wrapper_darwin.h new file mode 100644 index 0000000..e2cfb5c --- /dev/null +++ b/newtmgr/vendor/github.com/runtimeco/gatt/xpc/xpc_wrapper_darwin.h @@ -0,0 +1,32 @@ +#ifndef _XPC_WRAPPER_H_ +#define _XPC_WRAPPER_H_ + +#include <stdlib.h> +#include <stdio.h> +#include <xpc/xpc.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 *, void *); +extern void XpcSendMessage(xpc_connection_t, xpc_object_t, bool, bool); +extern void XpcArrayApply(void *, xpc_object_t); +extern void XpcDictApply(void *, 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 http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeinc/gatt/.gitignore ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeinc/gatt/.gitignore b/newtmgr/vendor/github.com/runtimeinc/gatt/.gitignore deleted file mode 100644 index 7ab0687..0000000 --- a/newtmgr/vendor/github.com/runtimeinc/gatt/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -c.out -c/*-ble -sample http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeinc/gatt/LICENSE.md ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeinc/gatt/LICENSE.md b/newtmgr/vendor/github.com/runtimeinc/gatt/LICENSE.md deleted file mode 100644 index 51c07a6..0000000 --- a/newtmgr/vendor/github.com/runtimeinc/gatt/LICENSE.md +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014 PayPal 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 PayPal 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. \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeinc/gatt/adv.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeinc/gatt/adv.go b/newtmgr/vendor/github.com/runtimeinc/gatt/adv.go deleted file mode 100644 index 3b8a747..0000000 --- a/newtmgr/vendor/github.com/runtimeinc/gatt/adv.go +++ /dev/null @@ -1,234 +0,0 @@ -package gatt - -import ( - "errors" - "log" -) - -// MaxEIRPacketLength is the maximum allowed AdvertisingPacket -// and ScanResponsePacket length. -const MaxEIRPacketLength = 31 - -// ErrEIRPacketTooLong is the error returned when an AdvertisingPacket -// or ScanResponsePacket is too long. -var ErrEIRPacketTooLong = errors.New("max packet length is 31") - -// Advertising data field types -const ( - typeFlags = 0x01 // Flags - typeSomeUUID16 = 0x02 // Incomplete List of 16-bit Service Class UUIDs - typeAllUUID16 = 0x03 // Complete List of 16-bit Service Class UUIDs - typeSomeUUID32 = 0x04 // Incomplete List of 32-bit Service Class UUIDs - typeAllUUID32 = 0x05 // Complete List of 32-bit Service Class UUIDs - typeSomeUUID128 = 0x06 // Incomplete List of 128-bit Service Class UUIDs - typeAllUUID128 = 0x07 // Complete List of 128-bit Service Class UUIDs - typeShortName = 0x08 // Shortened Local Name - typeCompleteName = 0x09 // Complete Local Name - typeTxPower = 0x0A // Tx Power Level - typeClassOfDevice = 0x0D // Class of Device - typeSimplePairingC192 = 0x0E // Simple Pairing Hash C-192 - typeSimplePairingR192 = 0x0F // Simple Pairing Randomizer R-192 - typeSecManagerTK = 0x10 // Security Manager TK Value - typeSecManagerOOB = 0x11 // Security Manager Out of Band Flags - typeSlaveConnInt = 0x12 // Slave Connection Interval Range - typeServiceSol16 = 0x14 // List of 16-bit Service Solicitation UUIDs - typeServiceSol128 = 0x15 // List of 128-bit Service Solicitation UUIDs - typeServiceData16 = 0x16 // Service Data - 16-bit UUID - typePubTargetAddr = 0x17 // Public Target Address - typeRandTargetAddr = 0x18 // Random Target Address - typeAppearance = 0x19 // Appearance - typeAdvInterval = 0x1A // Advertising Interval - typeLEDeviceAddr = 0x1B // LE Bluetooth Device Address - typeLERole = 0x1C // LE Role - typeServiceSol32 = 0x1F // List of 32-bit Service Solicitation UUIDs - typeServiceData32 = 0x20 // Service Data - 32-bit UUID - typeServiceData128 = 0x21 // Service Data - 128-bit UUID - typeLESecConfirm = 0x22 // LE Secure Connections Confirmation Value - typeLESecRandom = 0x23 // LE Secure Connections Random Value - typeManufacturerData = 0xFF // Manufacturer Specific Data -) - -// Advertising type 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). -) - -// FIXME: check the unmarshalling of this data structure. -type ServiceData struct { - UUID UUID - Data []byte -} - -// This is borrowed from core bluetooth. -// Embedded/Linux folks might be interested in more details. -type Advertisement struct { - LocalName string - ManufacturerData []byte - ServiceData []ServiceData - Services []UUID - OverflowService []UUID - TxPowerLevel int - Connectable bool - SolicitedService []UUID - AddressType uint8 - Address [6]byte -} - -// This is only used in Linux port. -func (a *Advertisement) unmarshall(b []byte) error { - - // Utility function for creating a list of uuids. - uuidList := func(u []UUID, d []byte, w int) []UUID { - for len(d) > 0 { - u = append(u, UUID{d[:w]}) - d = d[w:] - } - return u - } - - for len(b) > 0 { - if len(b) < 2 { - return errors.New("invalid advertise data") - } - l, t := b[0], b[1] - if len(b) < int(1+l) { - return errors.New("invalid advertise data") - } - d := b[2 : 1+l] - switch t { - case typeFlags: - // TODO: should we do anything about the discoverability here? - case typeSomeUUID16: - a.Services = uuidList(a.Services, d, 2) - case typeAllUUID16: - a.Services = uuidList(a.Services, d, 2) - case typeSomeUUID32: - a.Services = uuidList(a.Services, d, 4) - case typeAllUUID32: - a.Services = uuidList(a.Services, d, 4) - case typeSomeUUID128: - a.Services = uuidList(a.Services, d, 16) - case typeAllUUID128: - a.Services = uuidList(a.Services, d, 16) - case typeShortName: - a.LocalName = string(d) - case typeCompleteName: - a.LocalName = string(d) - case typeTxPower: - a.TxPowerLevel = int(d[0]) - case typeServiceSol16: - a.SolicitedService = uuidList(a.SolicitedService, d, 2) - case typeServiceSol128: - a.SolicitedService = uuidList(a.SolicitedService, d, 16) - case typeServiceSol32: - a.SolicitedService = uuidList(a.SolicitedService, d, 4) - case typeManufacturerData: - a.ManufacturerData = make([]byte, len(d)) - copy(a.ManufacturerData, d) - // case typeServiceData16, - // case typeServiceData32, - // case typeServiceData128: - default: - log.Printf("DATA: [ % X ]", d) - } - b = b[1+l:] - } - return nil -} - -// AdvPacket is an utility to help crafting advertisment or scan response data. -type AdvPacket struct { - b []byte -} - -// Bytes returns an 31-byte array, which contains up to 31 bytes of the packet. -func (a *AdvPacket) Bytes() [31]byte { - b := [31]byte{} - copy(b[:], a.b) - return b -} - -// Len returns the length of the packets with a maximum of 31. -func (a *AdvPacket) Len() int { - if len(a.b) > 31 { - return 31 - } - return len(a.b) -} - -// AppendField appends a BLE advertising packet field. -// TODO: refuse to append field if it'd make the packet too long. -func (a *AdvPacket) AppendField(typ byte, b []byte) *AdvPacket { - // A field consists of len, typ, b. - // Len is 1 byte for typ plus len(b). - if len(a.b)+2+len(b) > MaxEIRPacketLength { - b = b[:MaxEIRPacketLength-len(a.b)-2] - } - a.b = append(a.b, byte(len(b)+1)) - a.b = append(a.b, typ) - a.b = append(a.b, b...) - return a -} - -// AppendFlags appends a flag field to the packet. -func (a *AdvPacket) AppendFlags(f byte) *AdvPacket { - return a.AppendField(typeFlags, []byte{f}) -} - -// AppendFlags appends a name field to the packet. -// If the name fits in the space, it will be append as a complete name field, otherwise a short name field. -func (a *AdvPacket) AppendName(n string) *AdvPacket { - typ := byte(typeCompleteName) - if len(a.b)+2+len(n) > MaxEIRPacketLength { - typ = byte(typeShortName) - } - return a.AppendField(typ, []byte(n)) -} - -// AppendManufacturerData appends a manufacturer data field to the packet. -func (a *AdvPacket) AppendManufacturerData(id uint16, b []byte) *AdvPacket { - d := append([]byte{uint8(id), uint8(id >> 8)}, b...) - return a.AppendField(typeManufacturerData, d) -} - -// AppendUUIDFit appends a BLE advertised service UUID -// packet field if it fits in the packet, and reports whether the UUID fit. -func (a *AdvPacket) AppendUUIDFit(uu []UUID) bool { - // Iterate all UUIDs to see if they fit in the packet or not. - fit, l := true, len(a.b) - for _, u := range uu { - if u.Equal(attrGAPUUID) || u.Equal(attrGATTUUID) { - continue - } - l += 2 + u.Len() - if l > MaxEIRPacketLength { - fit = false - break - } - } - - // Append the UUIDs until they no longer fit. - for _, u := range uu { - if u.Equal(attrGAPUUID) || u.Equal(attrGATTUUID) { - continue - } - if len(a.b)+2+u.Len() > MaxEIRPacketLength { - break - } - switch l = u.Len(); { - case l == 2 && fit: - a.AppendField(typeAllUUID16, u.b) - case l == 16 && fit: - a.AppendField(typeAllUUID128, u.b) - case l == 2 && !fit: - a.AppendField(typeSomeUUID16, u.b) - case l == 16 && !fit: - a.AppendField(typeSomeUUID128, u.b) - } - } - return fit -} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/a1553084/newtmgr/vendor/github.com/runtimeinc/gatt/attr.go ---------------------------------------------------------------------- diff --git a/newtmgr/vendor/github.com/runtimeinc/gatt/attr.go b/newtmgr/vendor/github.com/runtimeinc/gatt/attr.go deleted file mode 100644 index d1ae09d..0000000 --- a/newtmgr/vendor/github.com/runtimeinc/gatt/attr.go +++ /dev/null @@ -1,160 +0,0 @@ -package gatt - -import "log" - -// attr is a BLE attribute. It is not exported; -// managing attributes is an implementation detail. -type attr struct { - h uint16 // attribute handle - typ UUID // attribute type in UUID - props Property // attripute property - secure Property // attribute secure (implementation specific usage) - value []byte // attribute value - - pvt interface{} // point to the corresponsing Serveice/Characteristic/Descriptor -} - -// A attrRange is a contiguous range of attributes. -type attrRange struct { - aa []attr - base uint16 // handle for first attr in aa -} - -const ( - tooSmall = -1 - tooLarge = -2 -) - -// idx returns the index into aa corresponding to attr a. -// If h is too small, idx returns tooSmall (-1). -// If h is too large, idx returns tooLarge (-2). -func (r *attrRange) idx(h int) int { - if h < int(r.base) { - return tooSmall - } - if int(h) >= int(r.base)+len(r.aa) { - return tooLarge - } - return h - int(r.base) -} - -// At returns attr a. -func (r *attrRange) At(h uint16) (a attr, ok bool) { - i := r.idx(int(h)) - if i < 0 { - return attr{}, false - } - return r.aa[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 *attrRange) 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.aa) - } - return r.aa[startidx:endidx] -} - -func dumpAttributes(aa []attr) { - log.Printf("Generating attribute table:") - log.Printf("handle\ttype\tprops\tsecure\tpvt\tvalue") - for _, a := range aa { - log.Printf("0x%04X\t0x%s\t0x%02X\t0x%02x\t%T\t[ % X ]", - a.h, a.typ, int(a.props), int(a.secure), a.pvt, a.value) - } -} - -func generateAttributes(ss []*Service, base uint16) *attrRange { - var aa []attr - h := base - last := len(ss) - 1 - for i, s := range ss { - var a []attr - h, a = generateServiceAttributes(s, h, i == last) - aa = append(aa, a...) - } - dumpAttributes(aa) - return &attrRange{aa: aa, base: base} -} - -func generateServiceAttributes(s *Service, h uint16, last bool) (uint16, []attr) { - s.h = h - // endh set later - a := attr{ - h: h, - typ: attrPrimaryServiceUUID, - value: s.uuid.b, - props: CharRead, - pvt: s, - } - aa := []attr{a} - h++ - - for _, c := range s.Characteristics() { - var a []attr - h, a = generateCharAttributes(c, h) - aa = append(aa, a...) - } - - s.endh = h - 1 - if last { - h = 0xFFFF - s.endh = h - } - - return h, aa -} - -func generateCharAttributes(c *Characteristic, h uint16) (uint16, []attr) { - c.h = h - c.vh = h + 1 - ca := attr{ - h: c.h, - typ: attrCharacteristicUUID, - value: append([]byte{byte(c.props), byte(c.vh), byte((c.vh) >> 8)}, c.uuid.b...), - props: c.props, - pvt: c, - } - va := attr{ - h: c.vh, - typ: c.uuid, - value: c.value, - props: c.props, - pvt: c, - } - h += 2 - - aa := []attr{ca, va} - for _, d := range c.descs { - aa = append(aa, generateDescAttributes(d, h)) - h++ - } - - return h, aa -} - -func generateDescAttributes(d *Descriptor, h uint16) attr { - d.h = h - a := attr{ - h: h, - typ: d.uuid, - value: d.value, - props: d.props, - pvt: d, - } - return a -}