blob: 9c1873213287d74d4262a9a4b8ee3f4a3d9a1042 [file] [log] [blame]
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)
}