blob: 2b8ee2212ee2f6786830215589ab4bc02255cc5a [file] [log] [blame]
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <memory>
#include "pw_bluetooth/gatt/constants.h"
#include "pw_bluetooth/gatt/error.h"
#include "pw_bluetooth/gatt/types.h"
#include "pw_bluetooth/result.h"
#include "pw_bluetooth/types.h"
#include "pw_containers/vector.h"
#include "pw_function/function.h"
#include "pw_result/result.h"
#include "pw_span/span.h"
namespace pw::bluetooth::gatt {
// Represents a GATT service on a remote GATT server.
// Clients should call `SetErrorCallback` before using in order to handle fatal
// errors.
class RemoteService {
public:
enum class RemoteServiceError {
// The service has been modified or removed.
kServiceRemoved = 0,
// The peer serving this service has disconnected.
kPeerDisconnected = 1,
};
// Wrapper around a possible truncated value received from the server.
struct ReadValue {
// Characteristic or descriptor handle.
Handle handle;
// The value of the characteristic or descriptor.
Vector<std::byte> value;
// True if `value` might be truncated (the buffer was completely filled by
// the server and the read was a short read). `ReadCharacteristic` or
// `ReadDescriptor` should be used to read the complete value.
bool maybe_truncated;
};
// A result returned by `ReadByType`.
struct ReadByTypeResult {
// Characteristic or descriptor handle.
Handle handle;
// The value of the characteristic or descriptor, if it was read
// successfully, or an error explaining why the value could not be read.
Result<Error, ReadValue> result;
};
// Represents the supported options to read a long characteristic or
// descriptor value from a server. Long values are those that may not fit in a
// single message (longer than 22 bytes).
struct LongReadOptions {
// The byte to start the read at. Must be less than the length of the
// value.
uint16_t offset = 0;
// The maximum number of bytes to read.
uint16_t max_bytes = kMaxValueLength;
};
// Represents the supported write modes for writing characteristics &
// descriptors to the server.
enum class WriteMode : uint8_t {
// Wait for a response from the server before returning but do not verify
// the echo response. Supported for both characteristics and descriptors.
kDefault = 0,
// Every value blob is verified against an echo response from the server.
// The procedure is aborted if a value blob has not been reliably delivered
// to the peer. Only supported for characteristics.
kReliable = 1,
// Delivery will not be confirmed before returning. Writing without a
// response is only supported for short characteristics with the
// `WRITE_WITHOUT_RESPONSE` property. The value must fit into a single
// message. It is guaranteed that at least 20 bytes will fit into a single
// message. If the value does not fit, a `kFailure` error will be produced.
// The value will be written at offset 0. Only supported for
// characteristics.
kWithoutResponse = 2,
};
// Represents the supported options to write a characteristic/descriptor value
// to a server.
struct WriteOptions {
// The mode of the write operation. For descriptors, only
// `WriteMode::kDefault` is supported
WriteMode mode = WriteMode::kDefault;
// Request a write starting at the byte indicated.
// Must be 0 if `mode` is `WriteMode.kWithoutResponse`.
uint16_t offset = 0;
};
using ReadByTypeCallback = Function<void(Result<Vector<ReadByTypeResult>>)>;
using ReadCallback = Function<void(Result<ReadValue>)>;
using NotificationCallback = Function<void(ReadValue)>;
// Set a callback that will be called when there is an error with this
// RemoteService, after which this RemoteService will be invalid.
void SetErrorCallback(Function<void(RemoteServiceError)> error_callback);
// Calls `characteristic_callback` with the characteristics and descriptors in
// this service.
void DiscoverCharacteristics(
Function<void(Characteristic)>&& characteristic_callback);
// Reads characteristics and descriptors with the specified type. This method
// is useful for reading values before discovery has completed, thereby
// reducing latency.
// `uuid` - The UUID of the characteristics/descriptors to read.
// `result_callback` - Results are returned via this callback. Results may be
// empty if no matching values are read. If reading a value results in a
// permission error, the handle and error will be included.
//
// This may fail with the following errors:
// kInvalidParameters: if `uuid` refers to an internally reserved descriptor
// type (e.g. the Client Characteristic Configuration descriptor).
// kTooManyResults: More results were read than can fit
// in a Vector. Consider reading characteristics/descriptors individually
// after performing discovery.
// kFailure: The server returned an error not specific to a single result.
void ReadByType(Uuid uuid, ReadByTypeCallback&& result_callback);
// Reads the value of a characteristic.
// `handle` - The handle of the characteristic to be read.
// `options` - If null, a short read will be performed, which may be truncated
// to what fits in a single message (at least 22 bytes). If long read
// options are present, performs a long read with the indicated options.
// `result_callback` - called with the result of the read and the value of the
// characteristic if successful.
//
// This may fail with the following errors:
// kInvalidHandle - if `handle` is invalid
// kInvalidParameters - if `options is invalid
// kReadNotPermitted or kInsufficient* if the server rejects the request.
// kFailure if the server returns an error not covered by the above errors.
void ReadCharacteristic(Handle handle,
std::optional<LongReadOptions> options,
ReadCallback&& result_callback);
// Writes `value` to the characteristic with `handle` using the provided
// `options`. It is not recommended to send additional writes while a write
// is already in progress (the server may receive simultaneous writes in any
// order).
//
// Parameters:
// `handle` - Handle of the characteristic to be written to
// `value` - The value to be written.
// `options` - Options that apply to the write.
//
// This may fail with the following errors:
// kInvalidHandle - if `handle` is invalid
// kInvalidParameters - if `options is invalid
// kWriteNotPermitted or kInsufficient* if the server rejects the request.
// kFailure if the server returns an error not covered by the above errors.
void WriteCharacteristic(Handle handle,
span<const std::byte> value,
WriteOptions options,
Function<void(Result<Error>)>&& result_callback);
// Reads the value of the characteristic descriptor with `handle` and
// returns it in the reply.
// `handle` - The descriptor handle to read.
// `options` - Options that apply to the read.
// `result_callback` - Returns a result containing the value of the descriptor
// on success.
//
// This may fail with the following errors:
// `kInvalidHandle` - if `handle` is invalid.
// `kInvalidParameters` - if `options` is invalid.
// `kReadNotPermitted` or `INSUFFICIENT_*` - if the server rejects the read
// request. `kFailure` - if the server returns an error.
void ReadDescriptor(Handle handle,
std::optional<LongReadOptions> options,
ReadCallback&& result_callback);
void WriteDescriptor(Handle handle,
span<const std::byte> value,
WriteOptions options,
Function<void(Result<Error>)>&& result_callback);
// Subscribe to notifications & indications from the characteristic with
// the given `handle`.
//
// Either notifications or indications will be enabled depending on
// characteristic properties. Indications will be preferred if they are
// supported. This operation fails if the characteristic does not have the
// "notify" or "indicate" property.
//
// A write request will be issued to configure the characteristic for
// notifications/indications if it contains a Client Characteristic
// Configuration descriptor. This method fails if an error occurs while
// writing to the descriptor.
//
// On success, `notification_callback` will be called when
// the peer sends a notification or indication. Indications are
// automatically confirmed.
//
// Subscriptions can be canceled with `StopNotifications`.
//
// Parameters:
// `handle` - the handle of the characteristic to subscribe to.
// `notification_callback` - will be called with the values of
// notifications/indications when received.
// `result_callback` - called with the result of enabling
// notifications/indications.
//
// This may fail with the following errors:
// `kFailure` - the characteristic does not support notifications or
// indications.
// `kInvalidHandle` - `handle` is invalid.
// `kWriteNotPermitted`or `kInsufficient*` - descriptor write error.
void RegisterNotificationCallback(
Handle handle,
NotificationCallback&& notification_callback,
Function<void(Result<Error>)> result_callback);
// Stops notifications for the characteristic with the given `handle`.
void StopNotifications(Handle handle);
};
// Represents a GATT client that interacts with services on a GATT server.
class Client {
public:
// Represents a remote GATT service.
struct RemoteServiceInfo {
// Uniquely identifies this GATT service.
Handle handle;
// Indicates whether this is a primary or secondary service.
bool primary;
// The UUID that identifies the type of this service.
// There may be multiple services with the same UUID.
Uuid type;
};
virtual ~Client() = default;
// Enumerates existing services found on the peer that this Client represents,
// and provides a stream of updates thereafter. Results can be filtered by
// specifying a list of UUIDs in `uuids`. To further interact with services,
// clients must obtain a RemoteService protocol by calling ConnectToService().
// `uuid_allowlist` - The allowlist of UUIDs to filter services with.
// `updated_callback` - Will be called with services that are
// updated/modified.
// `removed_callback` - Called with the handles of servies
// that have been removed. Note that handles may be reused.
virtual void WatchServices(
Vector<Uuid> uuid_allowlist,
Function<void(RemoteServiceInfo)>&& updated_callback,
Function<void(Handle)>&& removed_callback) = 0;
// Stops service watching if started by `WatchServices`.
virtual void StopWatchingServices();
// Connects to a RemoteService. Only 1 connection per service is allowed.
// `handle` - the handle of the service to connect to.
//
// This may fail with the following errors:
// kInvalidParameters - `handle` does not correspond to a known service.
virtual Result<Error, std::unique_ptr<RemoteService>> ConnectToService(
Handle handle) = 0;
};
} // namespace pw::bluetooth::gatt