1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#pragma once
6
7#include <string.h>
8
9#include <zircon/compiler.h>
10#include <zircon/types.h>
11
12__BEGIN_CDECLS;
13
14// BT SIG Base UUID for all 16/32 assigned UUID values.
15//
16//    "00000000-0000-1000-8000-00805F9B34FB"
17//
18// (see Core Spec v5.0, Vol 3, Part B, Section 2.5.1)
19#define BT_GATT_BASE_UUID                                                       \
20    {                                                                           \
21        0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, \
22            0x00, 0x00, 0x00, 0x00                                              \
23    }
24
25#define __BT_UUID_ASSIGNED_OFFSET 12
26
27typedef uint64_t bt_gatt_id_t;
28
29typedef struct bt_gatt_uuid {
30    uint8_t bytes[16];
31} bt_gatt_uuid_t;
32
33// ATT protocol error codes.
34enum {
35    BT_GATT_ERR_NO_ERROR = 0x00,
36    BT_GATT_ERR_INVALID_HANDLE = 0x01,
37    BT_GATT_ERR_READ_NOT_PERMITTED = 0x02,
38    BT_GATT_ERR_WRITE_NOT_PERMITTED = 0x03,
39    BT_GATT_ERR_INVALID_PDU = 0x04,
40    BT_GATT_ERR_INSUFFICIENT_AUTHENTICATION = 0x05,
41    BT_GATT_ERR_REQUEST_NOT_SUPPORTED = 0x06,
42    BT_GATT_ERR_INVALID_OFFSET = 0x07,
43    BT_GATT_ERR_INSUFFICIENT_AUTHORIZATION = 0x08,
44    BT_GATT_ERR_PREPARE_QUEUE_FULL = 0x09,
45    BT_GATT_ERR_ATTRIBUTE_NOT_FOUND = 0x0A,
46    BT_GATT_ERR_ATTRIBUTENOTLONG = 0x0B,
47    BT_GATT_ERR_INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0C,
48    BT_GATT_ERR_INVALID_ATTRIBUTE_VALUE_LENGTH = 0x0D,
49    BT_GATT_ERR_UNLIKELY_ERROR = 0x0E,
50    BT_GATT_ERR_INSUFFICIENT_ENCRYPTION = 0x0F,
51    BT_GATT_ERR_UNSUPPORTED_GROUP_TYPE = 0x10,
52    BT_GATT_ERR_INSUFFICIENT_RESOURCES = 0x11,
53};
54
55typedef uint8_t bt_gatt_err_t;
56
57// Represents the status of a GATT operation.
58typedef struct bt_gatt_status {
59    // Represents errors reported by the host (i.e. not over ATT).
60    zx_status_t status;
61
62    // ATT protocol error.
63    bt_gatt_err_t att_ecode;
64} bt_gatt_status_t;
65
66inline bool bt_gatt_status_is_success(bt_gatt_status_t* status) {
67    return (status->status == ZX_OK) &&
68           (status->att_ecode == BT_GATT_ERR_NO_ERROR);
69}
70
71// Possible values for the characteristic properties bitfield.
72enum bt_gatt_chr_prop {
73    BT_GATT_CHR_PROP_BROADCAST = 0x01,
74    BT_GATT_CHR_PROP_READ = 0x02,
75    BT_GATT_CHR_PROP_WRITE_WITHOUT_RESPONSE = 0x04,
76    BT_GATT_CHR_PROP_WRITE = 0x08,
77    BT_GATT_CHR_PROP_NOTIFY = 0x10,
78    BT_GATT_CHR_PROP_INDICATE = 0x20,
79    BT_GATT_CHR_PROP_AUTHENTICATED_SIGNED_WRITES = 0x40,
80    BT_GATT_CHR_PROP_EXTENDED_PROPERTIES = 0x80,
81};
82
83typedef uint8_t bt_gatt_chr_prop_t;
84
85enum bt_gatt_chr_ext_prop {
86    BT_GATT_CHR_EXT_PROP_RELIABLE_WRITE = 0x0100,
87    BT_GATT_CHR_EXT_PROP_WRITABLE_AUXILIARIES = 0x0200,
88};
89
90typedef uint16_t bt_gatt_chr_ext_prop_t;
91
92// Represents a GATT characteristic descriptor.
93typedef struct bt_gatt_descriptor {
94    bt_gatt_id_t id;
95    bt_gatt_uuid_t type;
96} bt_gatt_descriptor_t;
97
98// Represents a GATT characteristic.
99typedef struct bt_gatt_chr {
100    bt_gatt_id_t id;
101    bt_gatt_uuid_t type;
102
103    // The bitmask of characteristic properties. The |extended_properties| field
104    // is populated if the "Characteristic Extended Properties" descriptor is
105    // present.
106    //
107    // See enums bt_gatt_chr_prop_t and bt_gatt_chr_ext_prop_t for possible
108    // bit values.
109    uint8_t properties;
110    uint16_t extended_properties;
111
112    size_t num_descriptors;
113    bt_gatt_descriptor_t* descriptors;
114} bt_gatt_chr_t;
115
116// Generic status result callback for all functions return just a status.
117typedef void (*bt_gatt_status_cb)(void* cookie, bt_gatt_status_t status,
118                                  bt_gatt_id_t id);
119
120// Result callback of the |connect| function. |status| will contain
121// the result of the characteristic discovery procedure if it was initiated by
122// |connect|. The service will be ready to receive further requests once this
123// has been called successfully and the |status| callback has been called with success.
124typedef void (*bt_gatt_connect_cb)(void* cookie, bt_gatt_status_t status,
125                                   const bt_gatt_chr_t* characteristics,
126                                   size_t characteristic_count);
127
128// Result callback of the read related functions.
129typedef void (*bt_gatt_read_characteristic_cb)(void* cookie,
130                                               bt_gatt_status_t status,
131                                               bt_gatt_id_t id,
132                                               const uint8_t* value,
133                                               size_t len);
134
135// Value change notification callback of the |enable_notifications| function.
136typedef void (*bt_gatt_notification_value_cb)(void* cookie, bt_gatt_id_t id,
137                                              const uint8_t* value, size_t len);
138
139typedef struct bt_gatt_svc_ops {
140    // Connects to and starts characteristic discovery on the remote service.
141    zx_status_t (*connect)(void* ctx, void* cookie,
142                           bt_gatt_connect_cb connect_cb);
143
144    // Stops this service and unregisters previously registered callbacks.
145    void (*stop)(void* ctx);
146
147    // Reads the value of the characteristic with the given ID.
148    //
149    // The |read_cb| callback will be called to asynchronously report the result
150    // of this operation.
151    zx_status_t (*read_characteristic)(void* ctx, bt_gatt_id_t id, void* cookie,
152                                       bt_gatt_read_characteristic_cb read_cb);
153
154    // Reads the long value of the characteristic with the given ID.
155    //
156    // The |read_cb| callback will be called to asynchronously report the result
157    // of this operation.
158    zx_status_t (*read_long_characteristic)(
159        void* ctx, bt_gatt_id_t id, void* cookie, uint16_t offset,
160        size_t max_bytes, bt_gatt_read_characteristic_cb read_cb);
161
162    zx_status_t (*write_characteristic)(void* ctx, bt_gatt_id_t id, void* cookie,
163                                        const uint8_t* buff, size_t len,
164                                        bt_gatt_status_cb read_cb);
165
166    // Enables notifications from the characteristic with the given ID. Returns
167    // ZX_ERR_BAD_STATE if the service has not been started yet.
168    //
169    // Returns ZX_ERR_SHOULD_WAIT if this request is already in progress.
170    //
171    // The |status_cb| callback will be called to asynchronously report the result
172    // of this operation.
173    zx_status_t (*enable_notifications)(void* ctx, bt_gatt_id_t id, void* cookie,
174                                        bt_gatt_status_cb status_cb,
175                                        bt_gatt_notification_value_cb value_cb);
176} bt_gatt_svc_ops_t;
177
178typedef struct bt_gatt_svc_proto {
179    bt_gatt_svc_ops_t* ops;
180    void* ctx;
181} bt_gatt_svc_proto_t;
182
183static inline zx_status_t bt_gatt_svc_connect(bt_gatt_svc_proto_t* svc,
184                                              void* cookie,
185                                              bt_gatt_connect_cb connect_cb) {
186    return svc->ops->connect(svc->ctx, cookie, connect_cb);
187}
188
189static inline void bt_gatt_svc_stop(bt_gatt_svc_proto_t* svc) {
190    svc->ops->stop(svc->ctx);
191}
192
193static inline zx_status_t bt_gatt_svc_read_characteristic(
194    bt_gatt_svc_proto_t* svc, bt_gatt_id_t id, void* cookie,
195    bt_gatt_read_characteristic_cb read_cb) {
196    return svc->ops->read_characteristic(svc->ctx, id, cookie, read_cb);
197}
198
199static inline zx_status_t bt_gatt_svc_read_long_characteristic(
200    bt_gatt_svc_proto_t* svc, bt_gatt_id_t id, void* cookie, uint16_t offset,
201    size_t max_bytes, bt_gatt_read_characteristic_cb read_cb) {
202    return svc->ops->read_long_characteristic(svc->ctx, id, cookie, offset,
203                                              max_bytes, read_cb);
204}
205
206static inline zx_status_t bt_gatt_svc_write_characteristic(
207    bt_gatt_svc_proto_t* svc, bt_gatt_id_t id, void* cookie,
208    const uint8_t* buff, size_t len, bt_gatt_status_cb status_cb) {
209    return svc->ops->write_characteristic(svc->ctx, id, cookie, buff, len,
210                                          status_cb);
211}
212
213static inline zx_status_t bt_gatt_svc_enable_notifications(
214    bt_gatt_svc_proto_t* svc, bt_gatt_id_t id, void* cookie,
215    bt_gatt_status_cb status_cb, bt_gatt_notification_value_cb value_cb) {
216    return svc->ops->enable_notifications(svc->ctx, id, cookie, status_cb,
217                                          value_cb);
218}
219
220// Convenience function to make a UUID from a 32-bit assigned value.
221static inline bt_gatt_uuid_t bt_gatt_make_uuid32(uint32_t value) {
222    bt_gatt_uuid_t retval = {.bytes = BT_GATT_BASE_UUID};
223
224    retval.bytes[__BT_UUID_ASSIGNED_OFFSET] = (uint8_t)(value);
225    retval.bytes[__BT_UUID_ASSIGNED_OFFSET + 1] = (uint8_t)(value >> 8);
226    retval.bytes[__BT_UUID_ASSIGNED_OFFSET + 2] = (uint8_t)(value >> 16);
227    retval.bytes[__BT_UUID_ASSIGNED_OFFSET + 3] = (uint8_t)(value >> 24);
228
229    return retval;
230}
231
232// Convenience function to make a UUID from a 16-bit assigned value.
233static inline bt_gatt_uuid_t bt_gatt_make_uuid16(uint16_t value) {
234    return bt_gatt_make_uuid32((uint32_t)value);
235}
236
237// UUID comparsion.
238// Note: this method only does a binary comparsion and doesn't break out low,
239// mid, high, version, sequence, or node parts for indiviual comparison so
240// doesn't conform to standard UUID sort.
241static inline int bt_gatt_compare_uuid(const bt_gatt_uuid_t* u1,
242                                       const bt_gatt_uuid_t* u2) {
243    return memcmp(u1->bytes, u2->bytes, 16);
244}
245
246__END_CDECLS;
247