/
socket.go
153 lines (132 loc) · 3.97 KB
/
socket.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// +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{{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) {
s.rmu.Lock()
n, err := unix.Read(s.fd, p)
s.rmu.Unlock()
// Close always sends a dummy command to wake up Read
// bad things happen to the HCI state machines if they receive
// a reply from that command, so make sure no data is returned
// on a closed socket.
//
// note that if Write and Close are called concurrently it's
// indeterminate which replies get through.
select {
case <-s.closed:
return 0, io.EOF
default:
}
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}) // no-op command to wake up the Read call if it's blocked
s.rmu.Lock()
defer s.rmu.Unlock()
return errors.Wrap(unix.Close(s.fd), "can't close hci socket")
}