diff --git a/gap.go b/gap.go index 2c7aa51b..2d8d6b24 100644 --- a/gap.go +++ b/gap.go @@ -594,6 +594,61 @@ func (buf *rawAdvertisementPayload) addServiceUUID(uuid UUID) (ok bool) { } } +// ConnectionPriority is a coarse description of what a connection should be +// optimized for. +// +// It exists because not every platform accepts the connection parameters from +// the Bluetooth Core Specification. Some only accept a small fixed set of +// presets: on Windows, BluetoothLEPreferredConnectionParameters has no public +// constructor, just the static ThroughputOptimized, Balanced and PowerOptimized +// sets. A ConnectionPriority is the form of the request that those platforms +// can honour, and it maps onto the same three-way split that Android +// (CONNECTION_PRIORITY_HIGH, BALANCED and LOW_POWER) and CoreBluetooth +// (CBPeripheralManagerConnectionLatency low, medium and high) also use. +// +// The constants are named after the trade-off rather than after the resulting +// latency on purpose: those vendor enumerations do not agree on a direction, so +// that Apple's "low" and Android's "high" mean the same thing, and a name like +// "low" would be read backwards by half of the audience. It also keeps the term +// "connection latency" free, which the Core Specification uses for the number of +// connection events a peripheral may skip. +type ConnectionPriority uint8 + +const ( + // ConnectionPriorityUnspecified leaves the connection parameters unchanged. + // It is the zero value, matching the other ConnectionParams fields, which + // also mean "leave alone" when unset. + ConnectionPriorityUnspecified ConnectionPriority = iota + + // ConnectionPriorityThroughput optimizes for rapid communication, at the + // cost of power usage on both sides and of the number of connections the + // adapter can maintain at once. Suitable for a firmware update or another + // bulk transfer, and best used only for as long as it is needed. + ConnectionPriorityThroughput + + // ConnectionPriorityBalanced balances communication speed against power + // usage. + ConnectionPriorityBalanced + + // ConnectionPriorityPowerSaving optimizes for power usage and for the number + // of simultaneous connections, at the cost of communication speed. + ConnectionPriorityPowerSaving +) + +// String returns a lowercase name for the priority, for use in logs. +func (p ConnectionPriority) String() string { + switch p { + case ConnectionPriorityThroughput: + return "throughput" + case ConnectionPriorityBalanced: + return "balanced" + case ConnectionPriorityPowerSaving: + return "power-saving" + default: + return "unspecified" + } +} + // ConnectionParams are used when connecting to a peripherals or when changing // the parameters of an active connection. type ConnectionParams struct { @@ -612,6 +667,19 @@ type ConnectionParams struct { // communication, the connection is considered lost. If no timeout is // specified, the timeout will be unchanged. Timeout Duration + + // Priority is a coarse alternative to the fields above, for platforms that + // do not accept explicit connection parameters. + // + // The two are never mixed: a platform that can program the controller uses + // the explicit fields and ignores Priority, and a platform that only accepts + // presets uses Priority and ignores the explicit fields. Setting both is + // therefore the portable way to make a request, and is what a caller that + // runs on more than one platform should do. + // + // If Priority is unset, a platform that only accepts presets leaves the + // connection unchanged. + Priority ConnectionPriority } type PHY int diff --git a/gap_connparams_test.go b/gap_connparams_test.go new file mode 100644 index 00000000..51b8fb6e --- /dev/null +++ b/gap_connparams_test.go @@ -0,0 +1,35 @@ +package bluetooth + +import "testing" + +// The zero value has to stay ConnectionPriorityUnspecified, so that an unset +// Priority means "leave the connection alone" like every other field in +// ConnectionParams. Reordering the constants would silently change what an +// empty ConnectionParams asks for. +func TestConnectionPriorityZeroValue(t *testing.T) { + var priority ConnectionPriority + if priority != ConnectionPriorityUnspecified { + t.Errorf("zero value = %d, want ConnectionPriorityUnspecified", priority) + } + if got := (ConnectionParams{}).Priority; got != ConnectionPriorityUnspecified { + t.Errorf("ConnectionParams{}.Priority = %s, want unspecified", got) + } +} + +func TestConnectionPriorityString(t *testing.T) { + tests := []struct { + priority ConnectionPriority + want string + }{ + {ConnectionPriorityUnspecified, "unspecified"}, + {ConnectionPriorityThroughput, "throughput"}, + {ConnectionPriorityBalanced, "balanced"}, + {ConnectionPriorityPowerSaving, "power-saving"}, + {ConnectionPriority(99), "unspecified"}, + } + for _, test := range tests { + if got := test.priority.String(); got != test.want { + t.Errorf("ConnectionPriority(%d).String() = %q, want %q", test.priority, got, test.want) + } + } +}