all: make Device a value instead of a pointer
This is a refactor that is necessary to make it easier to work with connected central devices on a SoftDevice.
This commit is contained in:
parent
6e0df0ec3c
commit
c9eafaff20
12 changed files with 75 additions and 62 deletions
|
@ -21,7 +21,7 @@ type Adapter struct {
|
|||
|
||||
connectHandler func(device Address, connected bool)
|
||||
|
||||
connectedDevices []*Device
|
||||
connectedDevices []Device
|
||||
notificationsStarted bool
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ var DefaultAdapter = &Adapter{
|
|||
connectHandler: func(device Address, connected bool) {
|
||||
return
|
||||
},
|
||||
connectedDevices: make([]*Device, 0, maxConnections),
|
||||
connectedDevices: make([]Device, 0, maxConnections),
|
||||
}
|
||||
|
||||
// Enable configures the BLE stack. It must be called before any
|
||||
|
@ -185,7 +185,7 @@ func (a *Adapter) startNotifications() {
|
|||
}
|
||||
|
||||
d := a.findDevice(not.connectionHandle)
|
||||
if d == nil {
|
||||
if d.deviceInternal == nil {
|
||||
if _debug {
|
||||
println("no device found for handle", not.connectionHandle)
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ func (a *Adapter) startNotifications() {
|
|||
}()
|
||||
}
|
||||
|
||||
func (a *Adapter) findDevice(handle uint16) *Device {
|
||||
func (a *Adapter) findDevice(handle uint16) Device {
|
||||
for _, d := range a.connectedDevices {
|
||||
if d.handle == handle {
|
||||
if _debug {
|
||||
|
@ -223,5 +223,5 @@ func (a *Adapter) findDevice(handle uint16) *Device {
|
|||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return Device{}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func main() {
|
|||
}
|
||||
})
|
||||
|
||||
var device *bluetooth.Device
|
||||
var device bluetooth.Device
|
||||
select {
|
||||
case result := <-ch:
|
||||
device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{})
|
||||
|
|
|
@ -52,7 +52,7 @@ func main() {
|
|||
}
|
||||
})
|
||||
|
||||
var device *bluetooth.Device
|
||||
var device bluetooth.Device
|
||||
select {
|
||||
case result := <-ch:
|
||||
device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{})
|
||||
|
|
|
@ -85,6 +85,10 @@ func (a *Adapter) StopScan() error {
|
|||
|
||||
// Device is a connection to a remote peripheral.
|
||||
type Device struct {
|
||||
*deviceInternal
|
||||
}
|
||||
|
||||
type deviceInternal struct {
|
||||
delegate *peripheralDelegate
|
||||
|
||||
cm cbgo.CentralManager
|
||||
|
@ -97,14 +101,14 @@ type Device struct {
|
|||
}
|
||||
|
||||
// Connect starts a connection attempt to the given peripheral device address.
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, error) {
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, error) {
|
||||
uuid, err := cbgo.ParseUUID(address.UUID.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
prphs := a.cm.RetrievePeripheralsWithIdentifiers([]cbgo.UUID{uuid})
|
||||
if len(prphs) == 0 {
|
||||
return nil, fmt.Errorf("Connect failed: no peer with address: %s", address.UUID.String())
|
||||
return Device{}, fmt.Errorf("Connect failed: no peer with address: %s", address.UUID.String())
|
||||
}
|
||||
|
||||
timeout := defaultConnectionTimeout
|
||||
|
@ -129,14 +133,16 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
|
||||
// check if we have received a disconnected peripheral
|
||||
if p.State() == cbgo.PeripheralStateDisconnected {
|
||||
return nil, connectionError
|
||||
return Device{}, connectionError
|
||||
}
|
||||
|
||||
d := &Device{
|
||||
d := Device{
|
||||
&deviceInternal{
|
||||
cm: a.cm,
|
||||
prph: p,
|
||||
servicesChan: make(chan error),
|
||||
charsChan: make(chan error),
|
||||
},
|
||||
}
|
||||
|
||||
d.delegate = &peripheralDelegate{d: d}
|
||||
|
@ -162,7 +168,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
|
||||
// Disconnect from the BLE device. This method is non-blocking and does not
|
||||
// wait until the connection is fully gone.
|
||||
func (d *Device) Disconnect() error {
|
||||
func (d Device) Disconnect() error {
|
||||
d.cm.CancelConnect(d.prph)
|
||||
return nil
|
||||
}
|
||||
|
@ -172,7 +178,7 @@ func (d *Device) Disconnect() error {
|
|||
type peripheralDelegate struct {
|
||||
cbgo.PeripheralDelegateBase
|
||||
|
||||
d *Device
|
||||
d Device
|
||||
}
|
||||
|
||||
// DidDiscoverServices is called when the services for a Peripheral
|
||||
|
|
10
gap_linux.go
10
gap_linux.go
|
@ -300,9 +300,9 @@ type Device struct {
|
|||
// Connect starts a connection attempt to the given peripheral device address.
|
||||
//
|
||||
// On Linux and Windows, the IsRandom part of the address is ignored.
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, error) {
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, error) {
|
||||
devicePath := dbus.ObjectPath(string(a.adapter.Path()) + "/dev_" + strings.Replace(address.MAC.String(), ":", "_", -1))
|
||||
device := &Device{
|
||||
device := Device{
|
||||
device: a.bus.Object("org.bluez", devicePath),
|
||||
adapter: a,
|
||||
address: address,
|
||||
|
@ -321,7 +321,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
// Read whether this device is already connected.
|
||||
connected, err := device.device.GetProperty("org.bluez.Device1.Connected")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
// Connect to the device, if not already connected.
|
||||
|
@ -329,7 +329,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
// Start connecting (async).
|
||||
err := device.device.Call("org.bluez.Device1.Connect", 0).Err
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bluetooth: failed to connect: %w", err)
|
||||
return Device{}, fmt.Errorf("bluetooth: failed to connect: %w", err)
|
||||
}
|
||||
|
||||
// Wait until the device has connected.
|
||||
|
@ -360,7 +360,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
|
||||
// Disconnect from the BLE device. This method is non-blocking and does not
|
||||
// wait until the connection is fully gone.
|
||||
func (d *Device) Disconnect() error {
|
||||
func (d Device) Disconnect() error {
|
||||
// we don't call our cancel function here, instead we wait for the
|
||||
// property change in `watchForConnect` and cancel things then
|
||||
return d.device.Call("org.bluez.Device1.Disconnect", 0).Err
|
||||
|
|
|
@ -133,7 +133,7 @@ type Address struct {
|
|||
}
|
||||
|
||||
// Connect starts a connection attempt to the given peripheral device address.
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, error) {
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, error) {
|
||||
if _debug {
|
||||
println("Connect")
|
||||
}
|
||||
|
@ -145,14 +145,14 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
if err := a.hci.leCreateConn(0x0060, 0x0030, 0x00,
|
||||
random, makeNINAAddress(address.MAC),
|
||||
0x00, 0x0006, 0x000c, 0x0000, 0x00c8, 0x0004, 0x0006); err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
// are we connected?
|
||||
start := time.Now().UnixNano()
|
||||
for {
|
||||
if err := a.hci.poll(); err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
if a.hci.connectData.connected {
|
||||
|
@ -163,15 +163,18 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
random = true
|
||||
}
|
||||
|
||||
d := &Device{adapter: a,
|
||||
handle: a.hci.connectData.handle,
|
||||
d := Device{
|
||||
Address: Address{
|
||||
MACAddress{
|
||||
MAC: makeAddress(a.hci.connectData.peerBdaddr),
|
||||
isRandom: random},
|
||||
},
|
||||
deviceInternal: &deviceInternal{
|
||||
adapter: a,
|
||||
handle: a.hci.connectData.handle,
|
||||
mtu: defaultMTU,
|
||||
notificationRegistrations: make([]notificationRegistration, 0),
|
||||
},
|
||||
}
|
||||
a.connectedDevices = append(a.connectedDevices, d)
|
||||
|
||||
|
@ -189,10 +192,10 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
|
||||
// cancel connection attempt that failed
|
||||
if err := a.hci.leCancelConn(); err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
return nil, ErrConnect
|
||||
return Device{}, ErrConnect
|
||||
}
|
||||
|
||||
type notificationRegistration struct {
|
||||
|
@ -202,8 +205,12 @@ type notificationRegistration struct {
|
|||
|
||||
// Device is a connection to a remote peripheral.
|
||||
type Device struct {
|
||||
adapter *Adapter
|
||||
Address Address
|
||||
*deviceInternal
|
||||
}
|
||||
|
||||
type deviceInternal struct {
|
||||
adapter *Adapter
|
||||
handle uint16
|
||||
mtu uint16
|
||||
|
||||
|
@ -211,7 +218,7 @@ type Device struct {
|
|||
}
|
||||
|
||||
// Disconnect from the BLE device.
|
||||
func (d *Device) Disconnect() error {
|
||||
func (d Device) Disconnect() error {
|
||||
if _debug {
|
||||
println("Disconnect")
|
||||
}
|
||||
|
@ -219,11 +226,11 @@ func (d *Device) Disconnect() error {
|
|||
return err
|
||||
}
|
||||
|
||||
d.adapter.connectedDevices = []*Device{}
|
||||
d.adapter.connectedDevices = []Device{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) findNotificationRegistration(handle uint16) *notificationRegistration {
|
||||
func (d Device) findNotificationRegistration(handle uint16) *notificationRegistration {
|
||||
for _, n := range d.notificationRegistrations {
|
||||
if n.handle == handle {
|
||||
return &n
|
||||
|
@ -233,7 +240,7 @@ func (d *Device) findNotificationRegistration(handle uint16) *notificationRegist
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) addNotificationRegistration(handle uint16, callback func([]byte)) {
|
||||
func (d Device) addNotificationRegistration(handle uint16, callback func([]byte)) {
|
||||
d.notificationRegistrations = append(d.notificationRegistrations,
|
||||
notificationRegistration{
|
||||
handle: handle,
|
||||
|
@ -241,6 +248,6 @@ func (d *Device) addNotificationRegistration(handle uint16, callback func([]byte
|
|||
})
|
||||
}
|
||||
|
||||
func (d *Device) startNotifications() {
|
||||
func (d Device) startNotifications() {
|
||||
d.adapter.startNotifications()
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ var connectionAttempt struct {
|
|||
// connection attempt at once and that the address parameter must have the
|
||||
// IsRandom bit set correctly. This bit is set correctly for scan results, so
|
||||
// you can reuse that address directly.
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, error) {
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, error) {
|
||||
// Construct an address object as used in the SoftDevice.
|
||||
var addr C.ble_gap_addr_t
|
||||
addr.addr = makeSDAddress(address.MAC)
|
||||
|
@ -158,7 +158,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
// This should be safe as long as Connect is not called concurrently. And
|
||||
// even then, it should catch most such race conditions.
|
||||
if connectionAttempt.state.Get() != 0 {
|
||||
return nil, errAlreadyConnecting
|
||||
return Device{}, errAlreadyConnecting
|
||||
}
|
||||
connectionAttempt.state.Set(1)
|
||||
|
||||
|
@ -166,7 +166,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
errCode := C.sd_ble_gap_connect(&addr, &scanParams, &connectionParams, C.BLE_CONN_CFG_TAG_DEFAULT)
|
||||
if errCode != 0 {
|
||||
connectionAttempt.state.Set(0)
|
||||
return nil, Error(errCode)
|
||||
return Device{}, Error(errCode)
|
||||
}
|
||||
|
||||
// Wait until the connection is established.
|
||||
|
@ -179,13 +179,13 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
connectionAttempt.state.Set(0)
|
||||
|
||||
// Connection has been established.
|
||||
return &Device{
|
||||
return Device{
|
||||
connectionHandle: connectionHandle,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Disconnect from the BLE device.
|
||||
func (d *Device) Disconnect() error {
|
||||
func (d Device) Disconnect() error {
|
||||
errCode := C.sd_ble_gap_disconnect(d.connectionHandle, C.BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION)
|
||||
if errCode != 0 {
|
||||
return Error(errCode)
|
||||
|
|
|
@ -170,7 +170,7 @@ type Device struct {
|
|||
// Connect starts a connection attempt to the given peripheral device address.
|
||||
//
|
||||
// On Linux and Windows, the IsRandom part of the address is ignored.
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, error) {
|
||||
func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, error) {
|
||||
var winAddr uint64
|
||||
for i := range address.MAC {
|
||||
winAddr += uint64(address.MAC[i]) << (8 * i)
|
||||
|
@ -179,23 +179,23 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
// IAsyncOperation<BluetoothLEDevice>
|
||||
bleDeviceOp, err := bluetooth.FromBluetoothAddressAsync(winAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
// We need to pass the signature of the parameter returned by the async operation:
|
||||
// IAsyncOperation<BluetoothLEDevice>
|
||||
if err := awaitAsyncOperation(bleDeviceOp, bluetooth.SignatureBluetoothLEDevice); err != nil {
|
||||
return nil, fmt.Errorf("error connecting to device: %w", err)
|
||||
return Device{}, fmt.Errorf("error connecting to device: %w", err)
|
||||
}
|
||||
|
||||
res, err := bleDeviceOp.GetResults()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
// The returned BluetoothLEDevice is set to null if FromBluetoothAddressAsync can't find the device identified by bluetoothAddress
|
||||
if uintptr(res) == 0x0 {
|
||||
return nil, fmt.Errorf("device with the given address was not found")
|
||||
return Device{}, fmt.Errorf("device with the given address was not found")
|
||||
}
|
||||
|
||||
bleDevice := (*bluetooth.BluetoothLEDevice)(res)
|
||||
|
@ -204,7 +204,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
// To initiate a connection, we need to set GattSession.MaintainConnection to true.
|
||||
dID, err := bleDevice.GetBluetoothDeviceId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
// Windows does not support explicitly connecting to a device.
|
||||
|
@ -212,29 +212,29 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (*Device, er
|
|||
// by the calling program.
|
||||
gattSessionOp, err := genericattributeprofile.FromDeviceIdAsync(dID) // IAsyncOperation<GattSession>
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
if err := awaitAsyncOperation(gattSessionOp, genericattributeprofile.SignatureGattSession); err != nil {
|
||||
return nil, fmt.Errorf("error getting gatt session: %w", err)
|
||||
return Device{}, fmt.Errorf("error getting gatt session: %w", err)
|
||||
}
|
||||
|
||||
gattRes, err := gattSessionOp.GetResults()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
newSession := (*genericattributeprofile.GattSession)(gattRes)
|
||||
// This keeps the device connected until we set maintain_connection = False.
|
||||
if err := newSession.SetMaintainConnection(true); err != nil {
|
||||
return nil, err
|
||||
return Device{}, err
|
||||
}
|
||||
|
||||
return &Device{bleDevice, newSession}, nil
|
||||
return Device{bleDevice, newSession}, nil
|
||||
}
|
||||
|
||||
// Disconnect from the BLE device. This method is non-blocking and does not
|
||||
// wait until the connection is fully gone.
|
||||
func (d *Device) Disconnect() error {
|
||||
func (d Device) Disconnect() error {
|
||||
defer d.device.Release()
|
||||
defer d.session.Release()
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
//
|
||||
// Passing a nil slice of UUIDs will return a complete list of
|
||||
// services.
|
||||
func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||
func (d Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||
d.prph.DiscoverServices([]cbgo.UUID{})
|
||||
|
||||
// clear cache of services
|
||||
|
@ -69,7 +69,7 @@ type DeviceService struct {
|
|||
type deviceService struct {
|
||||
uuidWrapper
|
||||
|
||||
device *Device
|
||||
device Device
|
||||
|
||||
service cbgo.Service
|
||||
characteristics []DeviceCharacteristic
|
||||
|
|
|
@ -35,7 +35,7 @@ const (
|
|||
type DeviceService struct {
|
||||
uuid UUID
|
||||
|
||||
device *Device
|
||||
device Device
|
||||
startHandle, endHandle uint16
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ func (s DeviceService) UUID() UUID {
|
|||
//
|
||||
// Passing a nil slice of UUIDs will return a complete list of
|
||||
// services.
|
||||
func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||
func (d Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||
if _debug {
|
||||
println("DiscoverServices")
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func (s DeviceService) UUID() UUID {
|
|||
//
|
||||
// On the Nordic SoftDevice, only one service discovery procedure may be done at
|
||||
// a time.
|
||||
func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||
func (d Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||
if discoveringService.state.Get() != 0 {
|
||||
// Not concurrency safe, but should catch most concurrency misuses.
|
||||
return nil, errAlreadyDiscovering
|
||||
|
|
|
@ -30,7 +30,7 @@ var (
|
|||
//
|
||||
// Passing a nil slice of UUIDs will return a complete list of
|
||||
// services.
|
||||
func (d *Device) DiscoverServices(filterUUIDs []UUID) ([]DeviceService, error) {
|
||||
func (d Device) DiscoverServices(filterUUIDs []UUID) ([]DeviceService, error) {
|
||||
// IAsyncOperation<GattDeviceServicesResult>
|
||||
getServicesOperation, err := d.device.GetGattServicesWithCacheModeAsync(bluetooth.BluetoothCacheModeUncached)
|
||||
if err != nil {
|
||||
|
@ -133,7 +133,7 @@ type DeviceService struct {
|
|||
uuidWrapper
|
||||
|
||||
service *genericattributeprofile.GattDeviceService
|
||||
device *Device
|
||||
device Device
|
||||
}
|
||||
|
||||
// UUID returns the UUID for this DeviceService.
|
||||
|
|
Loading…
Reference in a new issue