fermentord/vendor/github.com/JuulLabs-OSS/ble/linux/hci/gap.go
Søren Rasmussen 07a23c1845
Some checks reported errors
continuous-integration/drone/push Build encountered an error
Upgrade to go 1.20 and add vendor catalog
2023-04-22 10:37:23 +02:00

265 lines
6.6 KiB
Go

package hci
import (
"context"
"fmt"
"net"
"time"
"github.com/JuulLabs-OSS/ble"
"github.com/JuulLabs-OSS/ble/linux/adv"
"github.com/JuulLabs-OSS/ble/linux/gatt"
"github.com/pkg/errors"
)
// Addr ...
func (h *HCI) Addr() ble.Addr { return h.addr }
// SetAdvHandler ...
func (h *HCI) SetAdvHandler(ah ble.AdvHandler) error {
h.advHandler = ah
return nil
}
// Scan starts scanning.
func (h *HCI) Scan(allowDup bool) error {
h.params.scanEnable.FilterDuplicates = 1
if allowDup {
h.params.scanEnable.FilterDuplicates = 0
}
h.params.scanEnable.LEScanEnable = 1
h.adHist = make([]*Advertisement, 128)
h.adLast = 0
return h.Send(&h.params.scanEnable, nil)
}
// StopScanning stops scanning.
func (h *HCI) StopScanning() error {
h.params.scanEnable.LEScanEnable = 0
return h.Send(&h.params.scanEnable, nil)
}
// AdvertiseAdv advertises a given Advertisement
func (h *HCI) AdvertiseAdv(a ble.Advertisement) error {
ad, err := adv.NewPacket(adv.Flags(adv.FlagGeneralDiscoverable | adv.FlagLEOnly))
if err != nil {
return err
}
f := adv.AllUUID
// Current length of ad packet plus two bytes of length and tag.
l := ad.Len() + 1 + 1
for _, u := range a.Services() {
l += u.Len()
}
if l > adv.MaxEIRPacketLength {
f = adv.SomeUUID
}
for _, u := range a.Services() {
if err := ad.Append(f(u)); err != nil {
if err == adv.ErrNotFit {
break
}
return err
}
}
sr, _ := adv.NewPacket()
switch {
case ad.Append(adv.CompleteName(a.LocalName())) == nil:
case sr.Append(adv.CompleteName(a.LocalName())) == nil:
case sr.Append(adv.ShortName(a.LocalName())) == nil:
}
if a.ManufacturerData() != nil {
manufacuturerData := adv.ManufacturerData(1337, a.ManufacturerData())
switch {
case ad.Append(manufacuturerData) == nil:
case sr.Append(manufacuturerData) == nil:
}
}
if err := h.SetAdvertisement(ad.Bytes(), sr.Bytes()); err != nil {
return nil
}
return h.Advertise()
}
// AdvertiseNameAndServices advertises device name, and specified service UUIDs.
// It tries to fit the UUIDs in the advertising data as much as possible.
// If name doesn't fit in the advertising data, it will be put in scan response.
func (h *HCI) AdvertiseNameAndServices(name string, uuids ...ble.UUID) error {
ad, err := adv.NewPacket(adv.Flags(adv.FlagGeneralDiscoverable | adv.FlagLEOnly))
if err != nil {
return err
}
f := adv.AllUUID
// Current length of ad packet plus two bytes of length and tag.
l := ad.Len() + 1 + 1
for _, u := range uuids {
l += u.Len()
}
if l > adv.MaxEIRPacketLength {
f = adv.SomeUUID
}
for _, u := range uuids {
if err := ad.Append(f(u)); err != nil {
if err == adv.ErrNotFit {
break
}
return err
}
}
sr, _ := adv.NewPacket()
switch {
case ad.Append(adv.CompleteName(name)) == nil:
case sr.Append(adv.CompleteName(name)) == nil:
case sr.Append(adv.ShortName(name)) == nil:
}
if err := h.SetAdvertisement(ad.Bytes(), sr.Bytes()); err != nil {
return nil
}
return h.Advertise()
}
// AdvertiseMfgData avertises the given manufacturer data.
func (h *HCI) AdvertiseMfgData(id uint16, md []byte) error {
ad, err := adv.NewPacket(adv.ManufacturerData(id, md))
if err != nil {
return err
}
if err := h.SetAdvertisement(ad.Bytes(), nil); err != nil {
return nil
}
return h.Advertise()
}
// AdvertiseServiceData16 advertises data associated with a 16bit service uuid
func (h *HCI) AdvertiseServiceData16(id uint16, b []byte) error {
ad, err := adv.NewPacket(adv.ServiceData16(id, b))
if err != nil {
return err
}
if err := h.SetAdvertisement(ad.Bytes(), nil); err != nil {
return nil
}
return h.Advertise()
}
// AdvertiseIBeaconData advertise iBeacon with given manufacturer data.
func (h *HCI) AdvertiseIBeaconData(md []byte) error {
ad, err := adv.NewPacket(adv.IBeaconData(md))
if err != nil {
return err
}
if err := h.SetAdvertisement(ad.Bytes(), nil); err != nil {
return nil
}
return h.Advertise()
}
// AdvertiseIBeacon advertises iBeacon with specified parameters.
func (h *HCI) AdvertiseIBeacon(u ble.UUID, major, minor uint16, pwr int8) error {
ad, err := adv.NewPacket(adv.IBeacon(u, major, minor, pwr))
if err != nil {
return err
}
if err := h.SetAdvertisement(ad.Bytes(), nil); err != nil {
return nil
}
return h.Advertise()
}
// StopAdvertising stops advertising.
func (h *HCI) StopAdvertising() error {
h.params.advEnable.AdvertisingEnable = 0
return h.Send(&h.params.advEnable, nil)
}
// Accept starts advertising and accepts connection.
func (h *HCI) Accept() (ble.Conn, error) {
var tmo <-chan time.Time
if h.listenerTmo != time.Duration(0) {
tmo = time.After(h.listenerTmo)
}
select {
case <-h.done:
return nil, h.err
case c := <-h.chSlaveConn:
return c, nil
case <-tmo:
return nil, fmt.Errorf("listner timed out")
}
}
// Dial ...
func (h *HCI) Dial(ctx context.Context, a ble.Addr) (ble.Client, error) {
b, err := net.ParseMAC(a.String())
if err != nil {
return nil, ErrInvalidAddr
}
h.params.connParams.PeerAddress = [6]byte{b[5], b[4], b[3], b[2], b[1], b[0]}
if _, ok := a.(RandomAddress); ok {
h.params.connParams.PeerAddressType = 1
}
if err = h.Send(&h.params.connParams, nil); err != nil {
return nil, err
}
var tmo <-chan time.Time
if h.dialerTmo != time.Duration(0) {
tmo = time.After(h.dialerTmo)
}
select {
case <-ctx.Done():
return h.cancelDial()
case <-tmo:
return h.cancelDial()
case <-h.done:
return nil, h.err
case c := <-h.chMasterConn:
return gatt.NewClient(c)
}
}
// cancelDial cancels the Dialing
func (h *HCI) cancelDial() (ble.Client, error) {
err := h.Send(&h.params.connCancel, nil)
if err == nil {
// The pending connection was canceled successfully.
return nil, fmt.Errorf("connection canceled")
}
// The connection has been established, the cancel command
// failed with ErrDisallowed.
if err == ErrDisallowed {
return gatt.NewClient(<-h.chMasterConn)
}
return nil, errors.Wrap(err, "cancel connection failed")
}
// Advertise starts advertising.
func (h *HCI) Advertise() error {
h.params.advEnable.AdvertisingEnable = 1
return h.Send(&h.params.advEnable, nil)
}
// SetAdvertisement sets advertising data and scanResp.
func (h *HCI) SetAdvertisement(ad []byte, sr []byte) error {
if len(ad) > adv.MaxEIRPacketLength || len(sr) > adv.MaxEIRPacketLength {
return ble.ErrEIRPacketTooLong
}
h.params.advData.AdvertisingDataLength = uint8(len(ad))
copy(h.params.advData.AdvertisingData[:], ad)
if err := h.Send(&h.params.advData, nil); err != nil {
return err
}
h.params.scanResp.ScanResponseDataLength = uint8(len(sr))
copy(h.params.scanResp.ScanResponseData[:], sr)
if err := h.Send(&h.params.scanResp, nil); err != nil {
return err
}
return nil
}