This repository has been archived by the owner on Dec 5, 2017. It is now read-only.
forked from paypal/gatt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
device.go
109 lines (99 loc) · 2.63 KB
/
device.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package linux
import (
"errors"
"log"
"sync"
"syscall"
"unsafe"
"github.com/paypal/gatt/linux/gioctl"
"github.com/paypal/gatt/linux/socket"
)
type device struct {
fd int
dev int
name string
rmu *sync.Mutex
wmu *sync.Mutex
}
func newDevice(n int, chk bool) (*device, error) {
fd, err := socket.Socket(socket.AF_BLUETOOTH, syscall.SOCK_RAW, socket.BTPROTO_HCI)
if err != nil {
return nil, err
}
if n != -1 {
return newSocket(fd, n, chk)
}
req := devListRequest{devNum: hciMaxDevices}
if err := gioctl.Ioctl(uintptr(fd), hciGetDeviceList, uintptr(unsafe.Pointer(&req))); err != nil {
return nil, err
}
for i := 0; i < int(req.devNum); i++ {
d, err := newSocket(fd, i, chk)
if err == nil {
log.Printf("dev: %s opened", d.name)
return d, err
}
}
return nil, errors.New("no supported devices available")
}
func newSocket(fd, n int, chk bool) (*device, error) {
i := hciDevInfo{id: uint16(n)}
if err := gioctl.Ioctl(uintptr(fd), hciGetDeviceInfo, uintptr(unsafe.Pointer(&i))); err != nil {
return nil, err
}
name := string(i.name[:])
// Check the feature list returned feature list.
if chk && i.features[4]&0x40 == 0 {
err := errors.New("does not support LE")
log.Printf("dev: %s %s", name, err)
return nil, err
}
log.Printf("dev: %s up", name)
if err := gioctl.Ioctl(uintptr(fd), hciUpDevice, uintptr(n)); err != nil {
if err != syscall.EALREADY {
return nil, err
}
log.Printf("dev: %s reset", name)
if err := gioctl.Ioctl(uintptr(fd), hciResetDevice, uintptr(n)); err != nil {
return nil, err
}
}
log.Printf("dev: %s down", name)
if err := gioctl.Ioctl(uintptr(fd), hciDownDevice, uintptr(n)); err != nil {
return nil, err
}
// Attempt to use the linux 3.14 feature, if this fails with EINVAL fall back to raw access
// on older kernels.
sa := socket.SockaddrHCI{Dev: n, Channel: socket.HCI_CHANNEL_USER}
if err := socket.Bind(fd, &sa); err != nil {
if err != syscall.EINVAL {
return nil, err
}
log.Printf("dev: %s can't bind to hci user channel, err: %s.", name, err)
sa := socket.SockaddrHCI{Dev: n, Channel: socket.HCI_CHANNEL_RAW}
if err := socket.Bind(fd, &sa); err != nil {
log.Printf("dev: %s can't bind to hci raw channel, err: %s.", name, err)
return nil, err
}
}
return &device{
fd: fd,
dev: n,
name: name,
rmu: &sync.Mutex{},
wmu: &sync.Mutex{},
}, nil
}
func (d device) Read(b []byte) (int, error) {
d.rmu.Lock()
defer d.rmu.Unlock()
return syscall.Read(d.fd, b)
}
func (d device) Write(b []byte) (int, error) {
d.wmu.Lock()
defer d.wmu.Unlock()
return syscall.Write(d.fd, b)
}
func (d device) Close() error {
return syscall.Close(d.fd)
}