Add Linux support

Very much experimental, no BLE service support yet and BLE LocalName in
advertisement packet doesn't seem to work. So rather useless at the
moment.
This commit is contained in:
Ayke van Laethem 2019-11-09 15:55:38 +01:00
parent 6e4cf6f8e0
commit e453c4d3f9
No known key found for this signature in database
GPG key ID: E97FF5335DFDFDED
7 changed files with 138 additions and 11 deletions

39
adapter_linux.go Normal file
View file

@ -0,0 +1,39 @@
// +build !baremetal
package bluetooth
import (
"github.com/muka/go-bluetooth/api"
"github.com/muka/go-bluetooth/bluez/profile/adapter"
)
type Adapter struct {
adapter *adapter.Adapter1
id string
handler func(Event)
}
// DefaultAdapter returns the default adapter on the current system. On Linux,
// it will return the first adapter available.
func DefaultAdapter() (*Adapter, error) {
adapter, err := api.GetDefaultAdapter()
if err != nil {
return nil, err
}
adapterID, err := adapter.GetAdapterID()
if err != nil {
return nil, err
}
return &Adapter{
adapter: adapter,
id: adapterID,
}, nil
}
// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
//
// The Linux implementation is a no-op.
func (a *Adapter) Enable() error {
return nil
}

View file

@ -62,9 +62,15 @@ type Adapter struct {
charWriteHandlers []charWriteHandler
}
// DefaultAdapter is an adapter to the default Bluetooth stack on a given
// defaultAdapter is an adapter to the default Bluetooth stack on a given
// target.
var DefaultAdapter = &Adapter{isDefault: true}
var defaultAdapter = Adapter{isDefault: true}
// DefaultAdapter returns the default adapter on the current system. On Nordic
// chips, it will return the SoftDevice interface.
func DefaultAdapter() (*Adapter, error) {
return &defaultAdapter, nil
}
// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
@ -108,7 +114,7 @@ func (a *Adapter) Enable() error {
}
func handleEvent() {
handler := DefaultAdapter.handler
handler := defaultAdapter.handler
if handler == nil {
return
}
@ -131,7 +137,7 @@ func handleEvent() {
writeEvent := gattsEvent.params.unionfield_write()
len := writeEvent.len - writeEvent.offset
data := (*[255]byte)(unsafe.Pointer(&writeEvent.data[0]))[:len:len]
handler := DefaultAdapter.getCharWriteHandler(writeEvent.handle)
handler := defaultAdapter.getCharWriteHandler(writeEvent.handle)
if handler != nil {
handler.callback(Connection(gattsEvent.conn_handle), int(writeEvent.offset), data)
}

View file

@ -10,7 +10,8 @@ import (
var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")
func main() {
adapter := bluetooth.DefaultAdapter
adapter, err := bluetooth.DefaultAdapter()
must("get default adapter", err)
must("enable SD", adapter.Enable())
adv := adapter.NewAdvertisement()
options := &bluetooth.AdvertiseOptions{

View file

@ -11,7 +11,8 @@ var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")
func main() {
println("starting")
adapter := bluetooth.DefaultAdapter
adapter, err := bluetooth.DefaultAdapter()
must("get default adapter", err)
adapter.SetEventHandler(handleBluetoothEvents)
must("enable SD", adapter.Enable())
adv := adapter.NewAdvertisement()

5
gap.go
View file

@ -1,10 +1,5 @@
package bluetooth
// Advertisement encapsulates a single advertisement instance.
type Advertisement struct {
handle uint8
}
// AdvertiseOptions configures everything related to BLE advertisements.
type AdvertiseOptions struct {
Interval AdvertiseInterval

80
gap_linux.go Normal file
View file

@ -0,0 +1,80 @@
// +build !baremetal
package bluetooth
import (
"errors"
"github.com/muka/go-bluetooth/api"
"github.com/muka/go-bluetooth/bluez/profile/advertising"
)
var (
ErrMalformedAdvertisement = errors.New("bluetooth: malformed advertisement packet")
)
// Advertisement encapsulates a single advertisement instance.
type Advertisement struct {
adapter *Adapter
advertisement *api.Advertisement
properties *advertising.LEAdvertisement1Properties
}
// NewAdvertisement creates a new advertisement instance but does not configure
// it.
func (a *Adapter) NewAdvertisement() *Advertisement {
return &Advertisement{
adapter: a,
}
}
// Configure this advertisement.
func (a *Advertisement) Configure(broadcastData, scanResponseData []byte, options *AdvertiseOptions) error {
if a.advertisement != nil {
panic("todo: configure advertisement a second time")
}
if scanResponseData != nil {
panic("todo: scan response data")
}
// Quick-and-dirty advertisement packet parser.
a.properties = &advertising.LEAdvertisement1Properties{
Type: advertising.AdvertisementTypeBroadcast,
Timeout: 1<<16 - 1,
}
for len(broadcastData) != 0 {
if len(broadcastData) < 2 {
return ErrMalformedAdvertisement
}
fieldLength := broadcastData[0]
fieldType := broadcastData[1]
fieldValue := broadcastData[2 : fieldLength+1]
if int(fieldLength) > len(broadcastData) {
return ErrMalformedAdvertisement
}
switch fieldType {
case 1:
// BLE advertisement flags. Ignore.
case 9:
// Complete Local Name.
a.properties.LocalName = string(fieldValue)
default:
return ErrMalformedAdvertisement
}
broadcastData = broadcastData[fieldLength+1:]
}
return nil
}
// Start advertisement. May only be called after it has been configured.
func (a *Advertisement) Start() error {
if a.advertisement != nil {
panic("todo: start advertisement a second time")
}
_, err := api.ExposeAdvertisement(a.adapter.id, a.properties, uint32(a.properties.Timeout))
if err != nil {
return err
}
return nil
}

View file

@ -11,6 +11,11 @@ package bluetooth
*/
import "C"
// Advertisement encapsulates a single advertisement instance.
type Advertisement struct {
handle uint8
}
// NewAdvertisement creates a new advertisement instance but does not configure
// it. It can be called before the SoftDevice has been initialized.
func (a *Adapter) NewAdvertisement() *Advertisement {