diff --git a/adapter_ninafw.go b/adapter_ninafw.go index c246b2c..5ae4eec 100644 --- a/adapter_ninafw.go +++ b/adapter_ninafw.go @@ -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{} } diff --git a/examples/discover/main.go b/examples/discover/main.go index e4142e6..1b9be0e 100644 --- a/examples/discover/main.go +++ b/examples/discover/main.go @@ -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{}) diff --git a/examples/heartrate-monitor/main.go b/examples/heartrate-monitor/main.go index 85cd3e9..210be9c 100644 --- a/examples/heartrate-monitor/main.go +++ b/examples/heartrate-monitor/main.go @@ -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{}) diff --git a/gap_darwin.go b/gap_darwin.go index b9f5dee..5b7fd5d 100644 --- a/gap_darwin.go +++ b/gap_darwin.go @@ -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{ - cm: a.cm, - prph: p, - servicesChan: make(chan error), - charsChan: make(chan error), + 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 diff --git a/gap_linux.go b/gap_linux.go index 7bd2973..c469708 100644 --- a/gap_linux.go +++ b/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 diff --git a/gap_ninafw.go b/gap_ninafw.go index 7ea9aef..51fbbf5 100644 --- a/gap_ninafw.go +++ b/gap_ninafw.go @@ -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}, }, - mtu: defaultMTU, - notificationRegistrations: make([]notificationRegistration, 0), + 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() } diff --git a/gap_nrf528xx-central.go b/gap_nrf528xx-central.go index 44fe683..148a2cb 100644 --- a/gap_nrf528xx-central.go +++ b/gap_nrf528xx-central.go @@ -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) diff --git a/gap_windows.go b/gap_windows.go index 8b66cb6..5ae0f97 100644 --- a/gap_windows.go +++ b/gap_windows.go @@ -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 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 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 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() diff --git a/gattc_darwin.go b/gattc_darwin.go index c4abf72..2d737da 100644 --- a/gattc_darwin.go +++ b/gattc_darwin.go @@ -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 diff --git a/gattc_ninafw.go b/gattc_ninafw.go index 00f1fb7..9ff0e5f 100644 --- a/gattc_ninafw.go +++ b/gattc_ninafw.go @@ -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") } diff --git a/gattc_sd.go b/gattc_sd.go index e0fe8c7..5b3029b 100644 --- a/gattc_sd.go +++ b/gattc_sd.go @@ -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 diff --git a/gattc_windows.go b/gattc_windows.go index 4735edd..3877df3 100644 --- a/gattc_windows.go +++ b/gattc_windows.go @@ -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 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.