-
Notifications
You must be signed in to change notification settings - Fork 681
Expand file tree
/
Copy pathatt_delayed_response.c
More file actions
258 lines (223 loc) · 10 KB
/
att_delayed_response.c
File metadata and controls
258 lines (223 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
* Copyright (C) 2018 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "att_delayed_response.c"
// *****************************************************************************
/* EXAMPLE_START(att_delayed_response): LE Peripheral - Delayed Response
*
* @text If the value for a GATT Chararacteristic isn't availabl for read,
* the value ATT_READ_RESPONSE_PENDING can be returned. When the value is available,
* att_server_response_ready is then called to complete the ATT request.
*
* Similarly, the error code ATT_ERROR_WRITE_RESPONSE_PENING can be returned when
* it is unclear if a write can be performed or not. When the decision was made,
* att_server_response_ready is is then called to complete the ATT request.
*/
// *****************************************************************************
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "btstack.h"
// att_delayed_response.gatt contains the declaration of the provided GATT Services + Characteristics
// att_delayed_response.h contains the binary representation of att_delayed_response.gatt
// it is generated by the build system by calling: $BTSTACK_ROOT/tool/compile_gatt.py att_delayed_response.gatt att_delayed_response.h
// it needs to be regenerated when the GATT Database declared in att_delayed_response.gatt file is modified
#include "att_delayed_response.h"
#define ATT_VALUE_DELAY_MS 3000
#define ATT_VALUE_INVALID_MS 5000
/* @section Main Application Setup
*
* @text Listing MainConfiguration shows main application code.
* It initializes L2CAP, the Security Manager and configures the ATT Server with the pre-compiled
* ATT Database generated from $att_delayed_response.gatt$.
* Additionally, it enables the Battery Service Server with the current battery level.
* Finally, it configures the advertisements and boots the Bluetooth stack.
* In this example, the Advertisement contains the Flags attribute and the device name.
* The flag 0x06 indicates: LE General Discoverable Mode and BR/EDR not supported.
*/
/* LISTING_START(MainConfiguration): Init L2CAP SM ATT Server */
#ifdef ENABLE_ATT_DELAYED_RESPONSE
static btstack_timer_source_t att_timer;
static hci_con_handle_t con_handle;
static int value_ready;
#endif
static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size);
const uint8_t adv_data[] = {
// Flags general discoverable, BR/EDR not supported
0x02, 0x01, 0x06,
// Name
0x08, 0x09, 'D', 'e', 'l', 'a', 'y', 'e', 'd',
};
const uint8_t adv_data_len = sizeof(adv_data);
const char * test_string = "Delayed response";
static void example_setup(void){
l2cap_init();
// setup SM: Display only
sm_init();
// setup ATT server
att_server_init(profile_data, att_read_callback, att_write_callback);
// setup advertisements
uint16_t adv_int_min = 0x0030;
uint16_t adv_int_max = 0x0030;
uint8_t adv_type = 0;
bd_addr_t null_addr;
memset(null_addr, 0, 6);
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
gap_advertisements_enable(1);
}
/* LISTING_END */
/*
* @section att_invalidate_value Handler
*
* @text The att_invalidate_value handler 'invalidates' the value of the single Characteristic provided in this example
*/
#ifdef ENABLE_ATT_DELAYED_RESPONSE
static void att_invalidate_value(struct btstack_timer_source *ts){
UNUSED(ts);
printf("Value got stale\n");
value_ready = 0;
}
#endif
/*
* @section att_update_value Handler
*
* @text The att_update_value handler 'updates' the value of the single Characteristic provided in this example
*/
/* LISTING_START(att_read_delay): ATT Read Delay Handler */
#ifdef ENABLE_ATT_DELAYED_RESPONSE
static void att_update_value(struct btstack_timer_source *ts){
UNUSED(ts);
value_ready = 1;
// trigger ATT Server to try request again
int status = att_server_response_ready(con_handle);
printf("Value updated -> complete ATT request - status 0x%02x\n", status);
// simulated value becoming stale again
att_timer.process = &att_invalidate_value;
btstack_run_loop_set_timer(&att_timer, ATT_VALUE_DELAY_MS);
btstack_run_loop_add_timer(&att_timer);
}
#endif
/* LISTING_END */
/*
* @section ATT Read
*
* @text The ATT Server handles all reads to constant data. For dynamic data like the custom characteristic, the registered
* att_read_callback is called. To handle long characteristics and long reads, the att_read_callback is first called
* with buffer == NULL, to request the total value length. Then it will be called again requesting a chunk of the value.
* See Listing attRead.
*/
/* LISTING_START(attRead): ATT Read */
// ATT Client Read Callback for Dynamic Data
// - if buffer == NULL, don't copy data, just return size of value
// - if buffer != NULL, copy data and return number bytes copied
// @param offset defines start of attribute value
static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
#ifdef ENABLE_ATT_DELAYED_RESPONSE
switch (att_handle){
case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE:
if (value_ready){
return att_read_callback_handle_blob((const uint8_t *)test_string, strlen(test_string), offset, buffer, buffer_size);
} else {
printf("Read callback for handle 0x%02x, but value not ready -> report response pending\n", att_handle);
con_handle = connection_handle;
return ATT_READ_RESPONSE_PENDING;
}
break;
case ATT_READ_RESPONSE_PENDING:
// virtual handle indicating all attributes have been queried
printf("Read callback for virtual handle 0x%02x - all attributes have been queried (important for read multiple or read by type) -> start updating values\n", att_handle);
// simulated delayed response for example
att_timer.process = &att_update_value;
btstack_run_loop_set_timer(&att_timer, ATT_VALUE_DELAY_MS);
btstack_run_loop_add_timer(&att_timer);
return 0;
default:
break;
}
#else
UNUSED(connection_handle);
// useless code when ENABLE_ATT_DELAYED_RESPONSE is not defined - but avoids built errors
if (att_handle == ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE){
printf("ENABLE_ATT_DELAYED_RESPONSE not defined in btstack_config.h, responding right away");
return att_read_callback_handle_blob((const uint8_t *)test_string, (uint16_t) strlen(test_string), offset, buffer, buffer_size);
}
#endif
return 0;
}
/*
* @section ATT Write
* */
/* LISTING_START(attWrite): ATT Write */
static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
UNUSED(transaction_mode);
UNUSED(offset);
UNUSED(buffer_size);
UNUSED(connection_handle);
if (att_handle == ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE) {
printf("Write request, value: ");
printf_hexdump(buffer, buffer_size);
#ifdef ENABLE_ATT_DELAYED_RESPONSE
if (value_ready){
printf("Write callback, value ready\n");
return 0;
} else {
printf("Write callback for handle 0x%02x, but not ready -> return response pending\n", att_handle);
}
// simulated delayed response for example
att_timer.process = &att_update_value;
btstack_run_loop_set_timer(&att_timer, ATT_VALUE_DELAY_MS);
btstack_run_loop_add_timer(&att_timer);
return ATT_ERROR_WRITE_RESPONSE_PENDING;
#else
printf("ENABLE_ATT_DELAYED_RESPONSE not defined in btstack_config.h, responding right away");
return 0;
#endif
}
return 0;
}
/* LISTING_END */
int btstack_main(void);
int btstack_main(void)
{
example_setup();
// turn on!
hci_power_control(HCI_POWER_ON);
return 0;
}
/* EXAMPLE_END */