mirror of
https://github.com/RoganDawes/P4wnP1_aloa.git
synced 2025-11-15 00:17:08 +01:00
Work on bluetooth stack
This commit is contained in:
@@ -3,335 +3,57 @@ package service
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mame82/P4wnP1_go/service/util"
|
||||
"github.com/mame82/P4wnP1_go/service/bluetooth"
|
||||
"github.com/mame82/mblue-toolz/toolz"
|
||||
"log"
|
||||
"net"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
bt_dev_name string = "hci0"
|
||||
BT_MINIMUM_BLUEZ_VERSION_MAJOR = 5
|
||||
BT_MINIMUM_BLUEZ_VERSION_MINOR = 43
|
||||
)
|
||||
|
||||
const (
|
||||
BT_AGENT_MODE_DISPLAY_YES_NO = BtAgentMode("DisplayYesNo")
|
||||
BT_AGENT_MODE_DISPLAY_ONLY = BtAgentMode("DisplayOnly")
|
||||
BT_AGENT_MODE_KEYBOARD_ONLY = BtAgentMode("KeyboardOnly")
|
||||
BT_AGENT_MODE_NO_INPUT_NO_OUTPUT = BtAgentMode("NoInputNoOutput")
|
||||
)
|
||||
|
||||
type BtAgentMode string
|
||||
|
||||
type BtService struct {
|
||||
DevName string
|
||||
ServiceAvailable bool
|
||||
Controller *bluetooth.Controller
|
||||
BrName string
|
||||
PathBtConf string
|
||||
bridgeIfDeployed bool
|
||||
|
||||
Agent *BtAgent
|
||||
Adapter *BtAdapter
|
||||
Agent *bluetooth.DefaultAgent
|
||||
}
|
||||
|
||||
type BtAdapter struct {
|
||||
/*
|
||||
--set <property> <value>
|
||||
Where `property` is one of:
|
||||
Name
|
||||
Discoverable
|
||||
DiscoverableTimeout
|
||||
Pairable
|
||||
PairableTimeout
|
||||
Powered
|
||||
|
||||
|
||||
root@raspberrypi:~# bt-adapter -i
|
||||
[hci0]
|
||||
Name: raspberrypi
|
||||
Address: B8:27:EB:8E:44:43
|
||||
Alias: raspberrypi [rw]
|
||||
Class: 0x0
|
||||
Discoverable: 0 [rw]
|
||||
DiscoverableTimeout: 180 [rw]
|
||||
Discovering: 0
|
||||
Pairable: 1 [rw]
|
||||
PairableTimeout: 0 [rw]
|
||||
Powered: 1 [rw]
|
||||
UUIDs: [00001801-0000-1000-8000-00805f9b34fb, AVRemoteControl, PnPInformation, 00001800-0000-1000-8000-00805f9b34fb, AVRemoteControlTarget]
|
||||
|
||||
*/
|
||||
|
||||
*sync.Mutex
|
||||
Address net.HardwareAddr
|
||||
DeviceName string
|
||||
Name string // Not changeable
|
||||
Alias string
|
||||
Discoverable bool
|
||||
DiscoverableTimeout uint64
|
||||
Pairable bool
|
||||
PairableTimeout uint64
|
||||
Powered bool
|
||||
}
|
||||
|
||||
var (
|
||||
reAdapterAddress = regexp.MustCompile("(?m)Address: ([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})")
|
||||
reAdapterName = regexp.MustCompile("(?m)Name: (.*)\n")
|
||||
reAdapterAlias = regexp.MustCompile("(?m)Alias: (.*) \\[")
|
||||
reAdapterDiscoverable = regexp.MustCompile("(?m)Discoverable: ([01])")
|
||||
reAdapterDiscoverableTimeout = regexp.MustCompile("(?m)DiscoverableTimeout: ([0-9]+)")
|
||||
reAdapterPairable = regexp.MustCompile("(?m)Pairable: ([01])")
|
||||
reAdapterPairableTimeout = regexp.MustCompile("(?m)PairableTimeout: ([0-9]+)")
|
||||
reAdapterPowered = regexp.MustCompile("(?m)Powered: ([01])")
|
||||
|
||||
eAdapterParseOutput = errors.New("Error parsing output of `bt-adapter -i`")
|
||||
eAdapterSetAdapter = errors.New("Error setting adapter options with `bt-adapter -s`")
|
||||
)
|
||||
|
||||
func (bAd *BtAdapter) DeploySate() (err error) {
|
||||
proc := exec.Command("/usr/bin/bt-adapter", "-s", "Alias", bAd.Alias, "-a", bAd.DeviceName)
|
||||
if proc.Run() != nil {
|
||||
return eAdapterSetAdapter
|
||||
}
|
||||
proc = exec.Command("/usr/bin/bt-adapter", "-s", "Discoverable", BoolToIntStr(bAd.Discoverable), "-a", bAd.DeviceName)
|
||||
if proc.Run() != nil {
|
||||
return eAdapterSetAdapter
|
||||
}
|
||||
proc = exec.Command("/usr/bin/bt-adapter", "-s", "DiscoverableTimeout", strconv.Itoa(int(bAd.DiscoverableTimeout)), "-a", bAd.DeviceName)
|
||||
if proc.Run() != nil {
|
||||
return eAdapterSetAdapter
|
||||
}
|
||||
proc = exec.Command("/usr/bin/bt-adapter", "-s", "Pairable", BoolToIntStr(bAd.Pairable), "-a", bAd.DeviceName)
|
||||
if proc.Run() != nil {
|
||||
return eAdapterSetAdapter
|
||||
}
|
||||
proc = exec.Command("/usr/bin/bt-adapter", "-s", "PairableTimeout", strconv.Itoa(int(bAd.PairableTimeout)), "-a", bAd.DeviceName)
|
||||
if proc.Run() != nil {
|
||||
return eAdapterSetAdapter
|
||||
}
|
||||
proc = exec.Command("/usr/bin/bt-adapter", "-s", "Powered", BoolToIntStr(bAd.Powered), "-a", bAd.DeviceName)
|
||||
if proc.Run() != nil {
|
||||
return eAdapterSetAdapter
|
||||
}
|
||||
bAd.updateMembers()
|
||||
return
|
||||
}
|
||||
|
||||
// Updates members via `bt-adapter -i`
|
||||
func (bAd *BtAdapter) updateMembers() (err error) {
|
||||
proc := exec.Command("/usr/bin/bt-adapter", "-i", "-a", bAd.DeviceName)
|
||||
res, err := proc.CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Error running `bt-adapter -i -a %s`: %s\niw output: %s", bAd.DeviceName, err, res))
|
||||
}
|
||||
output := string(res)
|
||||
|
||||
strAdapterAddress := ""
|
||||
strAdapterName := ""
|
||||
strAdapterAlias := ""
|
||||
strAdapterDiscoverable := ""
|
||||
strAdapterDiscoverableTimeout := ""
|
||||
strAdapterPairable := ""
|
||||
strAdapterPairableTimeout := ""
|
||||
strAdapterPowered := ""
|
||||
|
||||
if matches := reAdapterAddress.FindStringSubmatch(output); len(matches) > 1 {
|
||||
strAdapterAddress = matches[1]
|
||||
} else {
|
||||
return eAdapterParseOutput
|
||||
}
|
||||
|
||||
if matches := reAdapterName.FindStringSubmatch(output); len(matches) > 1 {
|
||||
strAdapterName = matches[1]
|
||||
} else {
|
||||
return eAdapterParseOutput
|
||||
}
|
||||
|
||||
if matches := reAdapterAlias.FindStringSubmatch(output); len(matches) > 1 {
|
||||
strAdapterAlias = matches[1]
|
||||
} else {
|
||||
return eAdapterParseOutput
|
||||
}
|
||||
|
||||
if matches := reAdapterDiscoverable.FindStringSubmatch(output); len(matches) > 1 {
|
||||
strAdapterDiscoverable = matches[1]
|
||||
} else {
|
||||
return eAdapterParseOutput
|
||||
}
|
||||
|
||||
if matches := reAdapterDiscoverableTimeout.FindStringSubmatch(output); len(matches) > 1 {
|
||||
strAdapterDiscoverableTimeout = matches[1]
|
||||
} else {
|
||||
return eAdapterParseOutput
|
||||
}
|
||||
|
||||
if matches := reAdapterPairable.FindStringSubmatch(output); len(matches) > 1 {
|
||||
strAdapterPairable = matches[1]
|
||||
} else {
|
||||
return eAdapterParseOutput
|
||||
}
|
||||
|
||||
if matches := reAdapterPairableTimeout.FindStringSubmatch(output); len(matches) > 1 {
|
||||
strAdapterPairableTimeout = matches[1]
|
||||
} else {
|
||||
return eAdapterParseOutput
|
||||
}
|
||||
|
||||
if matches := reAdapterPowered.FindStringSubmatch(output); len(matches) > 1 {
|
||||
strAdapterPowered = matches[1]
|
||||
} else {
|
||||
return eAdapterParseOutput
|
||||
}
|
||||
|
||||
/*
|
||||
fmt.Println("strAdapterAddress", strAdapterAddress)
|
||||
fmt.Println("strAdapterName", strAdapterName)
|
||||
fmt.Println("strAdapterAlias", strAdapterAlias)
|
||||
fmt.Println("strAdapterDiscoverable", strAdapterDiscoverable)
|
||||
fmt.Println("strAdapterDiscoverableTimeout", strAdapterDiscoverableTimeout)
|
||||
fmt.Println("strAdapterPairable", strAdapterPairable)
|
||||
fmt.Println("strAdapterPairableTimeout", strAdapterPairableTimeout)
|
||||
fmt.Println("strAdapterPowered", strAdapterPowered)
|
||||
*/
|
||||
|
||||
if bAd.Address, err = net.ParseMAC(strAdapterAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
if bAd.Discoverable, err = strconv.ParseBool(strAdapterDiscoverable); err != nil {
|
||||
return err
|
||||
}
|
||||
if bAd.DiscoverableTimeout, err = strconv.ParseUint(strAdapterDiscoverableTimeout, 10, 64); err != nil {
|
||||
return err
|
||||
}
|
||||
if bAd.Pairable, err = strconv.ParseBool(strAdapterPairable); err != nil {
|
||||
return err
|
||||
}
|
||||
if bAd.PairableTimeout, err = strconv.ParseUint(strAdapterPairableTimeout, 10, 64); err != nil {
|
||||
return err
|
||||
}
|
||||
if bAd.Powered, err = strconv.ParseBool(strAdapterPowered); err != nil {
|
||||
return err
|
||||
}
|
||||
bAd.Name = strAdapterName
|
||||
bAd.Alias = strAdapterAlias
|
||||
|
||||
/*
|
||||
log.Printf("adapter: %+v\n", bAd)
|
||||
*/
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func NewBtAdapter() (bAd *BtAdapter, err error) {
|
||||
bAd = &BtAdapter{
|
||||
Mutex: &sync.Mutex{},
|
||||
}
|
||||
err = bAd.updateMembers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type BtAgent struct {
|
||||
*exec.Cmd
|
||||
*sync.Mutex //mutex for wpa-supplicant proc
|
||||
mode BtAgentMode
|
||||
pinFilePath string
|
||||
logger *util.TeeLogger
|
||||
}
|
||||
|
||||
func (ba *BtAgent) Start(mode BtAgentMode) (err error) {
|
||||
log.Printf("Starting bt-agent with mode '%s'...\n", ba.mode)
|
||||
|
||||
ba.Lock()
|
||||
defer ba.Unlock()
|
||||
|
||||
ba.mode = mode
|
||||
|
||||
//stop if already running
|
||||
if ba.Cmd != nil {
|
||||
// avoid deadlock
|
||||
ba.Unlock()
|
||||
ba.Stop()
|
||||
ba.Lock()
|
||||
}
|
||||
|
||||
ba.Cmd = exec.Command("/usr/bin/bt-agent", "-c", string(ba.mode))
|
||||
ba.Cmd.Stdout = ba.logger.LogWriter
|
||||
ba.Cmd.Stderr = ba.logger.LogWriter
|
||||
err = ba.Cmd.Start()
|
||||
if err != nil {
|
||||
ba.Cmd.Wait()
|
||||
return errors.New(fmt.Sprintf("Error starting bt-agent '%v'", err))
|
||||
}
|
||||
log.Println("... bt-agent started")
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (ba *BtAgent) Stop() (err error) {
|
||||
log.Println("Stopping bt-agent...")
|
||||
ba.Lock()
|
||||
defer ba.Unlock()
|
||||
if ba.Cmd == nil {
|
||||
log.Println("bt-agent already stopped")
|
||||
}
|
||||
if ba.Process == nil {
|
||||
return errors.New("Couldn't access bt-agent process")
|
||||
}
|
||||
ba.Process.Kill()
|
||||
ba.Process.Wait()
|
||||
if ba.ProcessState == nil {
|
||||
return errors.New("Couldn't access bt-agent process state")
|
||||
}
|
||||
if !ba.ProcessState.Exited() {
|
||||
return errors.New("bt-agent didn't terminate after SIGKILL")
|
||||
}
|
||||
ba.Cmd = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewBtAgent() (res *BtAgent) {
|
||||
res = &BtAgent{
|
||||
Mutex: &sync.Mutex{},
|
||||
mode: BT_AGENT_MODE_NO_INPUT_NO_OUTPUT,
|
||||
pinFilePath: "",
|
||||
logger: util.NewTeeLogger(true),
|
||||
}
|
||||
res.logger.SetPrefix("bt-agent: ")
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func NewBtService() (res *BtService) {
|
||||
res = &BtService{
|
||||
Agent: NewBtAgent(),
|
||||
DevName: bt_dev_name,
|
||||
Agent: bluetooth.NewDefaultAgent("4321"),
|
||||
BrName: BT_ETHERNET_BRIDGE_NAME,
|
||||
PathBtConf: "",
|
||||
}
|
||||
if err := res.CheckExternalBinaries(); err != nil {
|
||||
panic(err)
|
||||
|
||||
// ToDo Check if bluetooth service is loaded
|
||||
if c,err := bluetooth.FindFirstAvailableController(); err == nil {
|
||||
res.ServiceAvailable = true
|
||||
res.Controller = c
|
||||
} else {
|
||||
res.ServiceAvailable = false
|
||||
return
|
||||
}
|
||||
if err := CheckBluezVersion(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// ToDo Check if bluetooth service is loaded
|
||||
if btAdp, errAd := NewBtAdapter(); errAd != nil {
|
||||
panic(errAd)
|
||||
} else {
|
||||
res.Adapter = btAdp
|
||||
fmt.Println(err)
|
||||
res.ServiceAvailable = false
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ToDo: Move all controller specific tasks to controller
|
||||
func (bt *BtService) StartNAP() (err error) {
|
||||
if !bt.ServiceAvailable { return bluetooth.ErrBtSvcNotAvailable
|
||||
}
|
||||
log.Println("Bluetooth: starting NAP...")
|
||||
// assure bnep module is loaded
|
||||
if err = CheckBnep(); err != nil {
|
||||
@@ -343,27 +65,40 @@ func (bt *BtService) StartNAP() (err error) {
|
||||
log.Println("Bridge exists already")
|
||||
}
|
||||
|
||||
// start bt-agent with "No Input, No Output" capabilities
|
||||
if err = bt.Agent.Start(BT_AGENT_MODE_NO_INPUT_NO_OUTPUT); err != nil {
|
||||
// Register custom agent bt-agent with "No Input, No Output" capabilities
|
||||
if err = bt.Agent.Start(toolz.AGENT_CAP_NO_INPUT_NO_OUTPUT); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Disable simple secure pairing to make PIN requests work
|
||||
bt.Controller.SetPowered(false)
|
||||
bt.Controller.SetSSP(false)
|
||||
bt.Controller.SetPowered(true)
|
||||
|
||||
// Configure adapter
|
||||
fmt.Println("Reconfigure adapter to be discoverable and pairable")
|
||||
bt.Adapter.Alias = "P4wnP1"
|
||||
bt.Adapter.Discoverable = true
|
||||
bt.Adapter.DiscoverableTimeout = 0
|
||||
bt.Adapter.Pairable = true
|
||||
bt.Adapter.PairableTimeout = 0
|
||||
if err = bt.Adapter.DeploySate(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
log.Printf("... reconfiguration succeeded: %+v\n", bt.Adapter)
|
||||
}
|
||||
err = bt.Controller.SetAlias("P4wnP1")
|
||||
if err != nil { return }
|
||||
err = bt.Controller.SetDiscoverableTimeout(0)
|
||||
if err != nil { return }
|
||||
err = bt.Controller.SetPairableTimeout(0)
|
||||
if err != nil { return }
|
||||
err = bt.Controller.SetDiscoverable(true)
|
||||
if err != nil { return }
|
||||
err = bt.Controller.SetPairable(true)
|
||||
|
||||
// Enable PAN networking for bridge
|
||||
nw,err := toolz.NetworkServer(bt.Controller.DBusPath)
|
||||
if err != nil { return }
|
||||
//defer nw.Close()
|
||||
err = nw.Register(toolz.UUID_NETWORK_SERVER_NAP, BT_ETHERNET_BRIDGE_NAME)
|
||||
if err != nil { return }
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (bt *BtService) StopNAP() (err error) {
|
||||
if !bt.ServiceAvailable { return bluetooth.ErrBtSvcNotAvailable }
|
||||
log.Println("Bluetooth: stopping NAP...")
|
||||
|
||||
//Stop bt-agent
|
||||
@@ -372,6 +107,18 @@ func (bt *BtService) StopNAP() (err error) {
|
||||
// Delete bridge interface
|
||||
bt.DisableBridge()
|
||||
|
||||
// Unregister pan service
|
||||
nw,err := toolz.NetworkServer(bt.Controller.DBusPath)
|
||||
//if err != nil { return }
|
||||
defer nw.Close()
|
||||
err = nw.Unregister("pan")
|
||||
//if err != nil { return }
|
||||
|
||||
err = bt.Controller.SetDiscoverable(false)
|
||||
//if err != nil { return }
|
||||
err = bt.Controller.SetPairable(false)
|
||||
//if err != nil { return }
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -437,6 +184,8 @@ func CheckBnep() error {
|
||||
ToDo: The binaries used (bluez-tools) should be replaced by custom functions interfacing with bluez D-Bus API, later on.
|
||||
Example: https://github.com/muka/go-bluetooth
|
||||
*/
|
||||
|
||||
/*
|
||||
func (bt BtService) CheckExternalBinaries() error {
|
||||
bins := []string{"modprobe", "lsmod", "bt-adapter", "bt-agent", "bt-device", "bt-network", "bluetoothd"}
|
||||
for _, bin := range bins {
|
||||
@@ -447,7 +196,10 @@ func (bt BtService) CheckExternalBinaries() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
// ToDo: Get rid of this as soon as an API function is found
|
||||
// btmgt tool is able to determine Bluez version, mgmt-api is only able to determine Management version (which should be 1.14)
|
||||
func GetBluezVersion() (major int, minor int, err error) {
|
||||
eGeneral := errors.New("Couldn't retrieve bluez version")
|
||||
proc := exec.Command("/usr/sbin/bluetoothd", "-v")
|
||||
|
||||
172
service/bluetooth/controller.go
Normal file
172
service/bluetooth/controller.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package bluetooth
|
||||
|
||||
import (
|
||||
"github.com/godbus/dbus"
|
||||
"net"
|
||||
|
||||
//"github.com/mame82/P4wnP1_go/service"
|
||||
"github.com/mame82/mblue-toolz/btmgmt"
|
||||
"github.com/mame82/mblue-toolz/dbusHelper"
|
||||
"github.com/mame82/mblue-toolz/toolz"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
/*
|
||||
This code assumes that the first bluetooth controller never gets detached,
|
||||
as it should be the case for a Pi Zero W. It doesn't account for plug&play Bluetooth controllers.
|
||||
Attaching an additional controller (f.e. USB) could lead to errors, which aren't handled by this code.
|
||||
*/
|
||||
|
||||
type Controller struct {
|
||||
DBusPath dbus.ObjectPath // The path of the controller, used by DBus (f.e. 'hci0')
|
||||
Index uint16 // The index of the controller, when "Bluetooth Management Socket" is used (mgmt-api)
|
||||
adapter *toolz.Adapter1
|
||||
}
|
||||
|
||||
func (c *Controller) SetSSP(val bool) (err error) {
|
||||
mgmt,err := btmgmt.NewBtMgmt()
|
||||
if err != nil { return ErrChgSetting }
|
||||
|
||||
s,err := mgmt.SetSecureSimplePairing(c.Index, val)
|
||||
if err != nil || s.SecureSimplePairing != val {
|
||||
return ErrChgSetting
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (c *Controller) StartDiscovery() error {
|
||||
return c.adapter.StartDiscovery()
|
||||
}
|
||||
|
||||
func (c *Controller) StopDiscovery() error {
|
||||
return c.adapter.StopDiscovery()
|
||||
}
|
||||
/* Properties */
|
||||
func (c *Controller) GetAddress() (res net.HardwareAddr, err error) {
|
||||
return c.adapter.GetAddress()
|
||||
}
|
||||
|
||||
func (c *Controller) GetAddressType() (res string, err error) {
|
||||
return c.adapter.GetAddressType()
|
||||
}
|
||||
|
||||
func (c *Controller) GetName() (res string, err error) {
|
||||
return c.adapter.GetName()
|
||||
}
|
||||
|
||||
func (c *Controller) SetAlias(val string) (err error) {
|
||||
return c.adapter.SetAlias(val)
|
||||
}
|
||||
|
||||
func (c *Controller) GetAlias() (res string, err error) {
|
||||
return c.adapter.GetAlias()
|
||||
}
|
||||
|
||||
func (c *Controller) GetClass() (res uint32, err error) {
|
||||
return c.adapter.GetClass()
|
||||
}
|
||||
|
||||
func (c *Controller) GetPowered() (res bool, err error) {
|
||||
return c.adapter.GetPowered()
|
||||
}
|
||||
|
||||
func (c *Controller) SetPowered(val bool) (err error) {
|
||||
return c.adapter.SetPowered(val)
|
||||
}
|
||||
|
||||
func (c *Controller) GetDiscoverable() (res bool, err error) {
|
||||
return c.adapter.GetDiscoverable()
|
||||
}
|
||||
|
||||
func (c *Controller) SetDiscoverable(val bool) (err error) {
|
||||
return c.adapter.SetDiscoverable(val)
|
||||
}
|
||||
|
||||
func (c *Controller) GetPairable() (res bool, err error) {
|
||||
return c.adapter.GetPairable()
|
||||
}
|
||||
|
||||
func (c *Controller) SetPairable(val bool) (err error) {
|
||||
return c.adapter.SetPairable(val)
|
||||
}
|
||||
|
||||
func (c *Controller) SetDiscoverableTimeout(val uint32) (err error) {
|
||||
return c.adapter.SetDiscoverableTimeout(val)
|
||||
}
|
||||
|
||||
func (c *Controller) GetDiscoverableTimeout() (res uint32, err error) {
|
||||
return c.adapter.GetDiscoverableTimeout()
|
||||
}
|
||||
|
||||
func (c *Controller) SetPairableTimeout(val uint32) (err error) {
|
||||
return c.adapter.SetPairableTimeout(val)
|
||||
}
|
||||
|
||||
func (c *Controller) GetPairableTimeout() (res uint32, err error) {
|
||||
return c.adapter.GetPairableTimeout()
|
||||
}
|
||||
|
||||
func (c *Controller) GetDiscovering() (res bool, err error) {
|
||||
return c.adapter.GetDiscovering()
|
||||
}
|
||||
|
||||
func (c *Controller) GetUUIDs() (res []string, err error) {
|
||||
return c.adapter.GetUUIDs()
|
||||
}
|
||||
|
||||
func (c *Controller) GetModalias() (res string, err error) {
|
||||
return c.adapter.GetModalias()
|
||||
}
|
||||
|
||||
|
||||
func FindFirstAvailableController() (ctl *Controller, err error) {
|
||||
// use btmgmt to fetch first controller index
|
||||
mgmt,err := btmgmt.NewBtMgmt()
|
||||
if err != nil { return nil, err }
|
||||
cil,err := mgmt.ReadControllerIndexList()
|
||||
if err != nil { return nil, err }
|
||||
|
||||
ctl = &Controller{}
|
||||
if len(cil.Indices) > 0 {
|
||||
ctl.Index = cil.Indices[0]
|
||||
} else {
|
||||
return nil, ErrBtSvcNotAvailable
|
||||
}
|
||||
|
||||
// retrieve additional info for the controller from mgmt-api
|
||||
ci,err := mgmt.ReadControllerInformation(ctl.Index)
|
||||
if err != nil { return nil,err }
|
||||
|
||||
// grab DBus object path of all available Adapters (=controller) from DBus API
|
||||
om,err := dbusHelper.NewObjectManager()
|
||||
defer om.Close()
|
||||
if err != nil { return nil,err }
|
||||
pathAdapters,err := om.GetAllObjectsPathOfInterface(toolz.DBusNameAdapter1Interface)
|
||||
if err != nil { return nil,err }
|
||||
|
||||
|
||||
for _,pathAdapter := range pathAdapters {
|
||||
// create adapter object
|
||||
adp,err := toolz.Adapter(pathAdapter)
|
||||
if err != nil { continue } // skip adapter
|
||||
hciAdapterAddr,err := adp.GetAddress()
|
||||
if err != nil {
|
||||
adp.Close()
|
||||
continue
|
||||
} // skip adapter
|
||||
// compare address of Controller from mmgmt-api with the adapter from adapter-api
|
||||
if compareHwAddr(hciAdapterAddr, ci.Address.Addr) {
|
||||
ctl.DBusPath = pathAdapter
|
||||
ctl.adapter = adp
|
||||
break // exit for loop
|
||||
} else {
|
||||
adp.Close()
|
||||
}
|
||||
}
|
||||
if ctl.adapter == nil {
|
||||
return nil,errors.New("Found controller via 'bluetooth management socket', but no match on DBus API")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
115
service/bluetooth/defaultagent.go
Normal file
115
service/bluetooth/defaultagent.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package bluetooth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/godbus/dbus"
|
||||
"github.com/mame82/mblue-toolz/bt_uuid"
|
||||
"github.com/mame82/mblue-toolz/dbusHelper"
|
||||
"github.com/mame82/mblue-toolz/toolz"
|
||||
)
|
||||
|
||||
type DefaultAgent struct {
|
||||
pincode string // only used if Secure Simple Pairing is disabled and currentCap = DISPLAY_ONLY
|
||||
currentCap toolz.AgentCapability
|
||||
}
|
||||
|
||||
// ------------ START OF AGENT INTERFACE IMPLEMENTATION ------------
|
||||
func (a *DefaultAgent) RegistrationPath() string {
|
||||
return toolz.AgentDefaultRegisterPath
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) Release() *dbus.Error {
|
||||
fmt.Println("DefaultAgent release called")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) RequestPinCode(device dbus.ObjectPath) (pincode string, err *dbus.Error) {
|
||||
fmt.Println("DefaultAgent request pincode called, returning string '12345'")
|
||||
// Called when SSP is off and currentCap != CAP_NO_INPUT_NO_OUTPUT, we could use a pre-generated PIN
|
||||
// and the remote device has to enter the same one
|
||||
return a.pincode, nil
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) DisplayPinCode(device dbus.ObjectPath, pincode string) *dbus.Error {
|
||||
fmt.Printf("DefaultAgent display pincode called, code: '%s'\n", pincode)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) RequestPasskey(device dbus.ObjectPath) (passkey uint32, err *dbus.Error) {
|
||||
fmt.Println("DefaultAgent request passkey called, returning integer 1337")
|
||||
// Called with SSP on and Cap == AGENT_CAP_KEYBOARD_ONLY
|
||||
// The needed passkey is random, we can't know the correct return value upfront (in contrast to a PIN
|
||||
// which has to be entered on both devices and match)
|
||||
|
||||
return 1337, nil
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, entered uint16) *dbus.Error {
|
||||
fmt.Printf("DefaultAgent display passkey called, passkey: %d\n", passkey)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) RequestConfirmation(device dbus.ObjectPath, passkey uint32) *dbus.Error {
|
||||
fmt.Printf("DefaultAgent request confirmation called for passkey: %d\n", passkey)
|
||||
// Called when SSP on and
|
||||
// currentCap == AGENT_CAP_DISPLAY_ONLY ||
|
||||
// currentCap == AGENT_CAP_KEYBOARD_DISPLAY ||
|
||||
// currentCap == AGENT_CAP_DISPLAY_YES_NO
|
||||
fmt.Println("... rejecting passkey")
|
||||
return toolz.ErrRejected
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) RequestAuthorization(device dbus.ObjectPath) *dbus.Error {
|
||||
fmt.Println("DefaultAgent request authorization called")
|
||||
fmt.Println("... rejecting")
|
||||
return toolz.ErrRejected
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) AuthorizeService(device dbus.ObjectPath, uuid string) *dbus.Error {
|
||||
devStr,_ := dbusHelper.DBusDevPathToHwAddr(device) // ignore error
|
||||
/*
|
||||
// alternate way to retrieve the device address (and call functions of the device)
|
||||
if d,e := toolz.Device(device); e == nil {
|
||||
addr,_ := d.GetAddress()
|
||||
fmt.Println(addr)
|
||||
}
|
||||
*/
|
||||
|
||||
fmt.Printf("DefaultAgent authorize service called for UUID: %s from %s\n", uuid, devStr)
|
||||
switch uuid {
|
||||
case bt_uuid.BNEP_SVC_UUID:
|
||||
fmt.Println("... granting BNEP access")
|
||||
return nil
|
||||
default:
|
||||
fmt.Println("... rejecting")
|
||||
return toolz.ErrRejected
|
||||
}
|
||||
return toolz.ErrRejected
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) Cancel() *dbus.Error {
|
||||
fmt.Println("DefaultAgent cancel called")
|
||||
return nil
|
||||
}
|
||||
// ------------ END OF AGENT INTERFACE IMPLEMENTATION ------------
|
||||
|
||||
func (a *DefaultAgent) Start(cap toolz.AgentCapability) (err error) {
|
||||
a.currentCap = cap
|
||||
return toolz.RegisterDefaultAgent(a, cap)
|
||||
}
|
||||
|
||||
func (a *DefaultAgent) Stop() (err error) {
|
||||
return toolz.UnregisterAgent(a.RegistrationPath())
|
||||
}
|
||||
|
||||
|
||||
func (a *DefaultAgent) SetPIN(pin string) {
|
||||
a.pincode = pin
|
||||
}
|
||||
|
||||
func NewDefaultAgent(pincode string) (res *DefaultAgent) {
|
||||
return &DefaultAgent{
|
||||
pincode: pincode,
|
||||
currentCap: toolz.AGENT_CAP_NO_INPUT_NO_OUTPUT,
|
||||
}
|
||||
}
|
||||
10
service/bluetooth/globals.go
Normal file
10
service/bluetooth/globals.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package bluetooth
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrBtSvcNotAvailable = errors.New("No bluetooth available")
|
||||
ErrBtNoMgmt = errors.New("Couldn't access bluetooth mgmt-api")
|
||||
ErrChgSetting = errors.New("Couldn't change controller setting to intended value")
|
||||
)
|
||||
|
||||
10
service/bluetooth/util.go
Normal file
10
service/bluetooth/util.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package bluetooth
|
||||
|
||||
import "net"
|
||||
|
||||
func compareHwAddr(a net.HardwareAddr, b net.HardwareAddr) bool {
|
||||
for idx,_ := range a {
|
||||
if a[idx] != b[idx] { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -17,6 +17,7 @@ type GlobalServiceState struct {
|
||||
StoredNetworkSettings map[string]*pb.EthernetInterfaceSettings
|
||||
// Wifi *WifiState
|
||||
WifiSvc *WiFiService
|
||||
BtSvc *BtService
|
||||
}
|
||||
|
||||
func InitGlobalServiceState() (err error) {
|
||||
@@ -44,7 +45,14 @@ func InitGlobalServiceState() (err error) {
|
||||
IpAddress4: "172.24.0.1",
|
||||
Netmask4: "255.255.255.0",
|
||||
}
|
||||
// state.Wifi = NewWifiState(GetDefaultWiFiSettings(), wifi_if_name)
|
||||
state.StoredNetworkSettings[BT_ETHERNET_BRIDGE_NAME] = &pb.EthernetInterfaceSettings{
|
||||
Name: BT_ETHERNET_BRIDGE_NAME,
|
||||
Enabled: true,
|
||||
Mode: pb.EthernetInterfaceSettings_MANUAL,
|
||||
IpAddress4: "172.26.0.1",
|
||||
Netmask4: "255.255.255.0",
|
||||
}
|
||||
|
||||
state.WifiSvc = NewWifiService()
|
||||
|
||||
|
||||
@@ -52,10 +60,14 @@ func InitGlobalServiceState() (err error) {
|
||||
state.EvMgr = NewEventManager(20)
|
||||
state.UsbGM,err = NewUSBGadgetManager()
|
||||
if err != nil { return }
|
||||
|
||||
state.BtSvc = NewBtService()
|
||||
|
||||
ledState, err := NewLed(false)
|
||||
if err != nil { return }
|
||||
state.Led = ledState
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -70,6 +82,8 @@ func (state *GlobalServiceState) GetInterfaceSettingsByInterfaceName(ifname stri
|
||||
|
||||
func (state *GlobalServiceState) StartService() {
|
||||
state.EvMgr.Start()
|
||||
// ToDo: Remove this, till the service is able to deploy startup settings
|
||||
state.BtSvc.StartNAP()
|
||||
}
|
||||
|
||||
func (state *GlobalServiceState) StopService() {
|
||||
|
||||
Reference in New Issue
Block a user