xref: /btstack/src/ble/att_db.c (revision 591423b2a599e503fdfb75c3f1c4bbd1c0d3e506)
1*591423b2SMatthias Ringwald /*
2*591423b2SMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
3*591423b2SMatthias Ringwald  *
4*591423b2SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
5*591423b2SMatthias Ringwald  * modification, are permitted provided that the following conditions
6*591423b2SMatthias Ringwald  * are met:
7*591423b2SMatthias Ringwald  *
8*591423b2SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
9*591423b2SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
10*591423b2SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11*591423b2SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
12*591423b2SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
13*591423b2SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
14*591423b2SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
15*591423b2SMatthias Ringwald  *    from this software without specific prior written permission.
16*591423b2SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
17*591423b2SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
18*591423b2SMatthias Ringwald  *    monetary gain.
19*591423b2SMatthias Ringwald  *
20*591423b2SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21*591423b2SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*591423b2SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23*591423b2SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24*591423b2SMatthias Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25*591423b2SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26*591423b2SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27*591423b2SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28*591423b2SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29*591423b2SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30*591423b2SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*591423b2SMatthias Ringwald  * SUCH DAMAGE.
32*591423b2SMatthias Ringwald  *
33*591423b2SMatthias Ringwald  * Please inquire about commercial licensing options at
34*591423b2SMatthias Ringwald  * [email protected]
35*591423b2SMatthias Ringwald  *
36*591423b2SMatthias Ringwald  */
37*591423b2SMatthias Ringwald 
38*591423b2SMatthias Ringwald 
39*591423b2SMatthias Ringwald #include <stdio.h>
40*591423b2SMatthias Ringwald #include <string.h>
41*591423b2SMatthias Ringwald 
42*591423b2SMatthias Ringwald #include "bluetooth.h"
43*591423b2SMatthias Ringwald #include "ble/att_db.h"
44*591423b2SMatthias Ringwald #include "btstack_debug.h"
45*591423b2SMatthias Ringwald #include "btstack_util.h"
46*591423b2SMatthias Ringwald 
47*591423b2SMatthias Ringwald // Buetooth Base UUID 00000000-0000-1000-8000-00805F9B34FB in little endian
48*591423b2SMatthias Ringwald static const uint8_t bluetooth_base_uuid[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
49*591423b2SMatthias Ringwald 
50*591423b2SMatthias Ringwald 
51*591423b2SMatthias Ringwald static int is_Bluetooth_Base_UUID(uint8_t const *uuid){
52*591423b2SMatthias Ringwald     if (memcmp(&uuid[0],  &bluetooth_base_uuid[0], 12)) return 0;
53*591423b2SMatthias Ringwald     if (memcmp(&uuid[14], &bluetooth_base_uuid[14], 2)) return 0;
54*591423b2SMatthias Ringwald     return 1;
55*591423b2SMatthias Ringwald 
56*591423b2SMatthias Ringwald }
57*591423b2SMatthias Ringwald 
58*591423b2SMatthias Ringwald static uint16_t uuid16_from_uuid(uint16_t uuid_len, uint8_t * uuid){
59*591423b2SMatthias Ringwald     if (uuid_len == 2) return little_endian_read_16(uuid, 0);
60*591423b2SMatthias Ringwald     if (!is_Bluetooth_Base_UUID(uuid)) return 0;
61*591423b2SMatthias Ringwald     return little_endian_read_16(uuid, 12);
62*591423b2SMatthias Ringwald }
63*591423b2SMatthias Ringwald 
64*591423b2SMatthias Ringwald // ATT Database
65*591423b2SMatthias Ringwald static uint8_t const * att_db = NULL;
66*591423b2SMatthias Ringwald static att_read_callback_t  att_read_callback  = NULL;
67*591423b2SMatthias Ringwald static att_write_callback_t att_write_callback = NULL;
68*591423b2SMatthias Ringwald static uint8_t  att_prepare_write_error_code   = 0;
69*591423b2SMatthias Ringwald static uint16_t att_prepare_write_error_handle = 0x0000;
70*591423b2SMatthias Ringwald 
71*591423b2SMatthias Ringwald // new java-style iterator
72*591423b2SMatthias Ringwald typedef struct att_iterator {
73*591423b2SMatthias Ringwald     // private
74*591423b2SMatthias Ringwald     uint8_t const * att_ptr;
75*591423b2SMatthias Ringwald     // public
76*591423b2SMatthias Ringwald     uint16_t size;
77*591423b2SMatthias Ringwald     uint16_t flags;
78*591423b2SMatthias Ringwald     uint16_t handle;
79*591423b2SMatthias Ringwald     uint8_t  const * uuid;
80*591423b2SMatthias Ringwald     uint16_t value_len;
81*591423b2SMatthias Ringwald     uint8_t  const * value;
82*591423b2SMatthias Ringwald } att_iterator_t;
83*591423b2SMatthias Ringwald 
84*591423b2SMatthias Ringwald static void att_iterator_init(att_iterator_t *it){
85*591423b2SMatthias Ringwald     it->att_ptr = att_db;
86*591423b2SMatthias Ringwald }
87*591423b2SMatthias Ringwald 
88*591423b2SMatthias Ringwald static int att_iterator_has_next(att_iterator_t *it){
89*591423b2SMatthias Ringwald     return it->att_ptr != NULL;
90*591423b2SMatthias Ringwald }
91*591423b2SMatthias Ringwald 
92*591423b2SMatthias Ringwald static void att_iterator_fetch_next(att_iterator_t *it){
93*591423b2SMatthias Ringwald     it->size   = little_endian_read_16(it->att_ptr, 0);
94*591423b2SMatthias Ringwald     if (it->size == 0){
95*591423b2SMatthias Ringwald         it->flags = 0;
96*591423b2SMatthias Ringwald         it->handle = 0;
97*591423b2SMatthias Ringwald         it->uuid = NULL;
98*591423b2SMatthias Ringwald         it->value_len = 0;
99*591423b2SMatthias Ringwald         it->value = NULL;
100*591423b2SMatthias Ringwald         it->att_ptr = NULL;
101*591423b2SMatthias Ringwald         return;
102*591423b2SMatthias Ringwald     }
103*591423b2SMatthias Ringwald     it->flags  = little_endian_read_16(it->att_ptr, 2);
104*591423b2SMatthias Ringwald     it->handle = little_endian_read_16(it->att_ptr, 4);
105*591423b2SMatthias Ringwald     it->uuid   = &it->att_ptr[6];
106*591423b2SMatthias Ringwald     // handle 128 bit UUIDs
107*591423b2SMatthias Ringwald     if (it->flags & ATT_PROPERTY_UUID128){
108*591423b2SMatthias Ringwald         it->value_len = it->size - 22;
109*591423b2SMatthias Ringwald         it->value  = &it->att_ptr[22];
110*591423b2SMatthias Ringwald     } else {
111*591423b2SMatthias Ringwald         it->value_len = it->size - 8;
112*591423b2SMatthias Ringwald         it->value  = &it->att_ptr[8];
113*591423b2SMatthias Ringwald     }
114*591423b2SMatthias Ringwald     // advance AFTER setting values
115*591423b2SMatthias Ringwald     it->att_ptr += it->size;
116*591423b2SMatthias Ringwald }
117*591423b2SMatthias Ringwald 
118*591423b2SMatthias Ringwald static int att_iterator_match_uuid16(att_iterator_t *it, uint16_t uuid){
119*591423b2SMatthias Ringwald     if (it->handle == 0) return 0;
120*591423b2SMatthias Ringwald     if (it->flags & ATT_PROPERTY_UUID128){
121*591423b2SMatthias Ringwald         if (!is_Bluetooth_Base_UUID(it->uuid)) return 0;
122*591423b2SMatthias Ringwald         return little_endian_read_16(it->uuid, 12) == uuid;
123*591423b2SMatthias Ringwald     }
124*591423b2SMatthias Ringwald     return little_endian_read_16(it->uuid, 0)  == uuid;
125*591423b2SMatthias Ringwald }
126*591423b2SMatthias Ringwald 
127*591423b2SMatthias Ringwald static int att_iterator_match_uuid(att_iterator_t *it, uint8_t *uuid, uint16_t uuid_len){
128*591423b2SMatthias Ringwald     if (it->handle == 0) return 0;
129*591423b2SMatthias Ringwald     // input: UUID16
130*591423b2SMatthias Ringwald     if (uuid_len == 2) {
131*591423b2SMatthias Ringwald         return att_iterator_match_uuid16(it, little_endian_read_16(uuid, 0));
132*591423b2SMatthias Ringwald     }
133*591423b2SMatthias Ringwald     // input and db: UUID128
134*591423b2SMatthias Ringwald     if (it->flags & ATT_PROPERTY_UUID128){
135*591423b2SMatthias Ringwald         return memcmp(it->uuid, uuid, 16) == 0;
136*591423b2SMatthias Ringwald     }
137*591423b2SMatthias Ringwald     // input: UUID128, db: UUID16
138*591423b2SMatthias Ringwald     if (!is_Bluetooth_Base_UUID(uuid)) return 0;
139*591423b2SMatthias Ringwald     return little_endian_read_16(uuid, 12) == little_endian_read_16(it->uuid, 0);
140*591423b2SMatthias Ringwald }
141*591423b2SMatthias Ringwald 
142*591423b2SMatthias Ringwald 
143*591423b2SMatthias Ringwald static int att_find_handle(att_iterator_t *it, uint16_t handle){
144*591423b2SMatthias Ringwald     if (handle == 0) return 0;
145*591423b2SMatthias Ringwald     att_iterator_init(it);
146*591423b2SMatthias Ringwald     while (att_iterator_has_next(it)){
147*591423b2SMatthias Ringwald         att_iterator_fetch_next(it);
148*591423b2SMatthias Ringwald         if (it->handle != handle) continue;
149*591423b2SMatthias Ringwald         return 1;
150*591423b2SMatthias Ringwald     }
151*591423b2SMatthias Ringwald     return 0;
152*591423b2SMatthias Ringwald }
153*591423b2SMatthias Ringwald 
154*591423b2SMatthias Ringwald // experimental client API
155*591423b2SMatthias Ringwald uint16_t att_uuid_for_handle(uint16_t handle){
156*591423b2SMatthias Ringwald     att_iterator_t it;
157*591423b2SMatthias Ringwald     int ok = att_find_handle(&it, handle);
158*591423b2SMatthias Ringwald     if (!ok) return 0;
159*591423b2SMatthias Ringwald     if (it.flags & ATT_PROPERTY_UUID128) return 0;
160*591423b2SMatthias Ringwald     return little_endian_read_16(it.uuid, 0);
161*591423b2SMatthias Ringwald }
162*591423b2SMatthias Ringwald // end of client API
163*591423b2SMatthias Ringwald 
164*591423b2SMatthias Ringwald static void att_update_value_len(att_iterator_t *it, uint16_t con_handle){
165*591423b2SMatthias Ringwald     if ((it->flags & ATT_PROPERTY_DYNAMIC) == 0 || !att_read_callback) return;
166*591423b2SMatthias Ringwald     it->value_len = (*att_read_callback)(con_handle, it->handle, 0, NULL, 0);
167*591423b2SMatthias Ringwald     return;
168*591423b2SMatthias Ringwald }
169*591423b2SMatthias Ringwald 
170*591423b2SMatthias Ringwald // copy attribute value from offset into buffer with given size
171*591423b2SMatthias Ringwald static int att_copy_value(att_iterator_t *it, uint16_t offset, uint8_t * buffer, uint16_t buffer_size, uint16_t con_handle){
172*591423b2SMatthias Ringwald 
173*591423b2SMatthias Ringwald     // DYNAMIC
174*591423b2SMatthias Ringwald     if ((it->flags & ATT_PROPERTY_DYNAMIC) && att_read_callback) {
175*591423b2SMatthias Ringwald         return (*att_read_callback)(con_handle, it->handle, offset, buffer, buffer_size);
176*591423b2SMatthias Ringwald     }
177*591423b2SMatthias Ringwald 
178*591423b2SMatthias Ringwald     // STATIC
179*591423b2SMatthias Ringwald     uint16_t bytes_to_copy = it->value_len - offset;
180*591423b2SMatthias Ringwald     if (bytes_to_copy > buffer_size){
181*591423b2SMatthias Ringwald         bytes_to_copy = buffer_size;
182*591423b2SMatthias Ringwald     }
183*591423b2SMatthias Ringwald     memcpy(buffer, it->value, bytes_to_copy);
184*591423b2SMatthias Ringwald     return bytes_to_copy;
185*591423b2SMatthias Ringwald }
186*591423b2SMatthias Ringwald 
187*591423b2SMatthias Ringwald void att_set_db(uint8_t const * db){
188*591423b2SMatthias Ringwald     att_db = db;
189*591423b2SMatthias Ringwald }
190*591423b2SMatthias Ringwald 
191*591423b2SMatthias Ringwald void att_set_read_callback(att_read_callback_t callback){
192*591423b2SMatthias Ringwald     att_read_callback = callback;
193*591423b2SMatthias Ringwald }
194*591423b2SMatthias Ringwald 
195*591423b2SMatthias Ringwald void att_set_write_callback(att_write_callback_t callback){
196*591423b2SMatthias Ringwald     att_write_callback = callback;
197*591423b2SMatthias Ringwald }
198*591423b2SMatthias Ringwald 
199*591423b2SMatthias Ringwald void att_dump_attributes(void){
200*591423b2SMatthias Ringwald     att_iterator_t it;
201*591423b2SMatthias Ringwald     att_iterator_init(&it);
202*591423b2SMatthias Ringwald     uint8_t uuid128[16];
203*591423b2SMatthias Ringwald     while (att_iterator_has_next(&it)){
204*591423b2SMatthias Ringwald         att_iterator_fetch_next(&it);
205*591423b2SMatthias Ringwald         if (it.handle == 0) {
206*591423b2SMatthias Ringwald             log_info("Handle: END");
207*591423b2SMatthias Ringwald             return;
208*591423b2SMatthias Ringwald         }
209*591423b2SMatthias Ringwald         log_info("Handle: 0x%04x, flags: 0x%04x, uuid: ", it.handle, it.flags);
210*591423b2SMatthias Ringwald         if (it.flags & ATT_PROPERTY_UUID128){
211*591423b2SMatthias Ringwald             reverse_128(it.uuid, uuid128);
212*591423b2SMatthias Ringwald             log_info("%s", uuid128_to_str(uuid128));
213*591423b2SMatthias Ringwald         } else {
214*591423b2SMatthias Ringwald             log_info("%04x", little_endian_read_16(it.uuid, 0));
215*591423b2SMatthias Ringwald         }
216*591423b2SMatthias Ringwald         log_info(", value_len: %u, value: ", it.value_len);
217*591423b2SMatthias Ringwald         log_info_hexdump(it.value, it.value_len);
218*591423b2SMatthias Ringwald     }
219*591423b2SMatthias Ringwald }
220*591423b2SMatthias Ringwald 
221*591423b2SMatthias Ringwald static void att_prepare_write_reset(void){
222*591423b2SMatthias Ringwald     att_prepare_write_error_code = 0;
223*591423b2SMatthias Ringwald     att_prepare_write_error_handle = 0x0000;
224*591423b2SMatthias Ringwald }
225*591423b2SMatthias Ringwald 
226*591423b2SMatthias Ringwald static void att_prepare_write_update_errors(uint8_t error_code, uint16_t handle){
227*591423b2SMatthias Ringwald     // first ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH has highest priority
228*591423b2SMatthias Ringwald     if (error_code == ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH && error_code != att_prepare_write_error_code){
229*591423b2SMatthias Ringwald         att_prepare_write_error_code = error_code;
230*591423b2SMatthias Ringwald         att_prepare_write_error_handle = handle;
231*591423b2SMatthias Ringwald         return;
232*591423b2SMatthias Ringwald     }
233*591423b2SMatthias Ringwald     // first ATT_ERROR_INVALID_OFFSET is next
234*591423b2SMatthias Ringwald     if (error_code == ATT_ERROR_INVALID_OFFSET && att_prepare_write_error_code == 0){
235*591423b2SMatthias Ringwald         att_prepare_write_error_code = error_code;
236*591423b2SMatthias Ringwald         att_prepare_write_error_handle = handle;
237*591423b2SMatthias Ringwald         return;
238*591423b2SMatthias Ringwald     }
239*591423b2SMatthias Ringwald }
240*591423b2SMatthias Ringwald 
241*591423b2SMatthias Ringwald static uint16_t setup_error(uint8_t * response_buffer, uint16_t request, uint16_t handle, uint8_t error_code){
242*591423b2SMatthias Ringwald     response_buffer[0] = ATT_ERROR_RESPONSE;
243*591423b2SMatthias Ringwald     response_buffer[1] = request;
244*591423b2SMatthias Ringwald     little_endian_store_16(response_buffer, 2, handle);
245*591423b2SMatthias Ringwald     response_buffer[4] = error_code;
246*591423b2SMatthias Ringwald     return 5;
247*591423b2SMatthias Ringwald }
248*591423b2SMatthias Ringwald 
249*591423b2SMatthias Ringwald static inline uint16_t setup_error_read_not_permitted(uint8_t * response_buffer, uint16_t request, uint16_t start_handle){
250*591423b2SMatthias Ringwald     return setup_error(response_buffer, request, start_handle, ATT_ERROR_READ_NOT_PERMITTED);
251*591423b2SMatthias Ringwald }
252*591423b2SMatthias Ringwald 
253*591423b2SMatthias Ringwald static inline uint16_t setup_error_write_not_permitted(uint8_t * response_buffer, uint16_t request, uint16_t start_handle){
254*591423b2SMatthias Ringwald     return setup_error(response_buffer, request, start_handle, ATT_ERROR_WRITE_NOT_PERMITTED);
255*591423b2SMatthias Ringwald }
256*591423b2SMatthias Ringwald 
257*591423b2SMatthias Ringwald static inline uint16_t setup_error_atribute_not_found(uint8_t * response_buffer, uint16_t request, uint16_t start_handle){
258*591423b2SMatthias Ringwald     return setup_error(response_buffer, request, start_handle, ATT_ERROR_ATTRIBUTE_NOT_FOUND);
259*591423b2SMatthias Ringwald }
260*591423b2SMatthias Ringwald 
261*591423b2SMatthias Ringwald static inline uint16_t setup_error_invalid_handle(uint8_t * response_buffer, uint16_t request, uint16_t handle){
262*591423b2SMatthias Ringwald     return setup_error(response_buffer, request, handle, ATT_ERROR_INVALID_HANDLE);
263*591423b2SMatthias Ringwald }
264*591423b2SMatthias Ringwald 
265*591423b2SMatthias Ringwald static inline uint16_t setup_error_invalid_offset(uint8_t * response_buffer, uint16_t request, uint16_t handle){
266*591423b2SMatthias Ringwald     return setup_error(response_buffer, request, handle, ATT_ERROR_INVALID_OFFSET);
267*591423b2SMatthias Ringwald }
268*591423b2SMatthias Ringwald 
269*591423b2SMatthias Ringwald static uint8_t att_validate_security(att_connection_t * att_connection, att_iterator_t * it){
270*591423b2SMatthias Ringwald     int required_encryption_size = it->flags >> 12;
271*591423b2SMatthias Ringwald     if (required_encryption_size) required_encryption_size++;   // store -1 to fit into 4 bit
272*591423b2SMatthias Ringwald     log_info("att_validate_security. flags 0x%04x - req enc size %u, authorized %u, authenticated %u, encryption_key_size %u",
273*591423b2SMatthias Ringwald         it->flags, required_encryption_size, att_connection->authorized, att_connection->authenticated, att_connection->encryption_key_size);
274*591423b2SMatthias Ringwald     if ((it->flags & ATT_PROPERTY_AUTHENTICATION_REQUIRED) && att_connection->authenticated == 0) {
275*591423b2SMatthias Ringwald         return ATT_ERROR_INSUFFICIENT_AUTHENTICATION;
276*591423b2SMatthias Ringwald     }
277*591423b2SMatthias Ringwald     if ((it->flags & ATT_PROPERTY_AUTHORIZATION_REQUIRED) && att_connection->authorized == 0) {
278*591423b2SMatthias Ringwald         return ATT_ERROR_INSUFFICIENT_AUTHORIZATION;
279*591423b2SMatthias Ringwald     }
280*591423b2SMatthias Ringwald     if (required_encryption_size > 0 && att_connection->encryption_key_size == 0){
281*591423b2SMatthias Ringwald         return ATT_ERROR_INSUFFICIENT_ENCRYPTION;
282*591423b2SMatthias Ringwald     }
283*591423b2SMatthias Ringwald     if (required_encryption_size > att_connection->encryption_key_size){
284*591423b2SMatthias Ringwald         return ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE;
285*591423b2SMatthias Ringwald     }
286*591423b2SMatthias Ringwald     return 0;
287*591423b2SMatthias Ringwald }
288*591423b2SMatthias Ringwald 
289*591423b2SMatthias Ringwald //
290*591423b2SMatthias Ringwald // MARK: ATT_EXCHANGE_MTU_REQUEST
291*591423b2SMatthias Ringwald //
292*591423b2SMatthias Ringwald static uint16_t handle_exchange_mtu_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
293*591423b2SMatthias Ringwald                                          uint8_t * response_buffer){
294*591423b2SMatthias Ringwald 
295*591423b2SMatthias Ringwald     uint16_t client_rx_mtu = little_endian_read_16(request_buffer, 1);
296*591423b2SMatthias Ringwald 
297*591423b2SMatthias Ringwald     // find min(local max mtu, remote mtu) and use as mtu for this connection
298*591423b2SMatthias Ringwald     if (client_rx_mtu < att_connection->max_mtu){
299*591423b2SMatthias Ringwald         att_connection->mtu = client_rx_mtu;
300*591423b2SMatthias Ringwald     } else {
301*591423b2SMatthias Ringwald         att_connection->mtu = att_connection->max_mtu;
302*591423b2SMatthias Ringwald     }
303*591423b2SMatthias Ringwald 
304*591423b2SMatthias Ringwald     response_buffer[0] = ATT_EXCHANGE_MTU_RESPONSE;
305*591423b2SMatthias Ringwald     little_endian_store_16(response_buffer, 1, att_connection->mtu);
306*591423b2SMatthias Ringwald     return 3;
307*591423b2SMatthias Ringwald }
308*591423b2SMatthias Ringwald 
309*591423b2SMatthias Ringwald 
310*591423b2SMatthias Ringwald //
311*591423b2SMatthias Ringwald // MARK: ATT_FIND_INFORMATION_REQUEST
312*591423b2SMatthias Ringwald //
313*591423b2SMatthias Ringwald // TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID
314*591423b2SMatthias Ringwald //
315*591423b2SMatthias Ringwald static uint16_t handle_find_information_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size,
316*591423b2SMatthias Ringwald                                            uint16_t start_handle, uint16_t end_handle){
317*591423b2SMatthias Ringwald 
318*591423b2SMatthias Ringwald     log_info("ATT_FIND_INFORMATION_REQUEST: from %04X to %04X", start_handle, end_handle);
319*591423b2SMatthias Ringwald     uint8_t request_type = ATT_FIND_INFORMATION_REQUEST;
320*591423b2SMatthias Ringwald 
321*591423b2SMatthias Ringwald     if (start_handle > end_handle || start_handle == 0){
322*591423b2SMatthias Ringwald         return setup_error_invalid_handle(response_buffer, request_type, start_handle);
323*591423b2SMatthias Ringwald     }
324*591423b2SMatthias Ringwald 
325*591423b2SMatthias Ringwald     uint16_t offset   = 1;
326*591423b2SMatthias Ringwald     uint16_t uuid_len = 0;
327*591423b2SMatthias Ringwald 
328*591423b2SMatthias Ringwald     att_iterator_t it;
329*591423b2SMatthias Ringwald     att_iterator_init(&it);
330*591423b2SMatthias Ringwald     while (att_iterator_has_next(&it)){
331*591423b2SMatthias Ringwald         att_iterator_fetch_next(&it);
332*591423b2SMatthias Ringwald         if (!it.handle) break;
333*591423b2SMatthias Ringwald         if (it.handle > end_handle) break;
334*591423b2SMatthias Ringwald         if (it.handle < start_handle) continue;
335*591423b2SMatthias Ringwald 
336*591423b2SMatthias Ringwald         // log_info("Handle 0x%04x", it.handle);
337*591423b2SMatthias Ringwald 
338*591423b2SMatthias Ringwald         uint16_t this_uuid_len = (it.flags & ATT_PROPERTY_UUID128) ? 16 : 2;
339*591423b2SMatthias Ringwald 
340*591423b2SMatthias Ringwald         // check if value has same len as last one if not first result
341*591423b2SMatthias Ringwald         if (offset > 1){
342*591423b2SMatthias Ringwald             if (this_uuid_len != uuid_len) {
343*591423b2SMatthias Ringwald                 break;
344*591423b2SMatthias Ringwald             }
345*591423b2SMatthias Ringwald         }
346*591423b2SMatthias Ringwald 
347*591423b2SMatthias Ringwald         // first
348*591423b2SMatthias Ringwald         if (offset == 1) {
349*591423b2SMatthias Ringwald             uuid_len = this_uuid_len;
350*591423b2SMatthias Ringwald             // set format field
351*591423b2SMatthias Ringwald             response_buffer[offset] = (it.flags & ATT_PROPERTY_UUID128) ? 0x02 : 0x01;
352*591423b2SMatthias Ringwald             offset++;
353*591423b2SMatthias Ringwald         }
354*591423b2SMatthias Ringwald 
355*591423b2SMatthias Ringwald         // space?
356*591423b2SMatthias Ringwald         if (offset + 2 + uuid_len > response_buffer_size) break;
357*591423b2SMatthias Ringwald 
358*591423b2SMatthias Ringwald         // store
359*591423b2SMatthias Ringwald         little_endian_store_16(response_buffer, offset, it.handle);
360*591423b2SMatthias Ringwald         offset += 2;
361*591423b2SMatthias Ringwald 
362*591423b2SMatthias Ringwald         memcpy(response_buffer + offset, it.uuid, uuid_len);
363*591423b2SMatthias Ringwald         offset += uuid_len;
364*591423b2SMatthias Ringwald     }
365*591423b2SMatthias Ringwald 
366*591423b2SMatthias Ringwald     if (offset == 1){
367*591423b2SMatthias Ringwald         return setup_error_atribute_not_found(response_buffer, request_type, start_handle);
368*591423b2SMatthias Ringwald     }
369*591423b2SMatthias Ringwald 
370*591423b2SMatthias Ringwald     response_buffer[0] = ATT_FIND_INFORMATION_REPLY;
371*591423b2SMatthias Ringwald     return offset;
372*591423b2SMatthias Ringwald }
373*591423b2SMatthias Ringwald 
374*591423b2SMatthias Ringwald static uint16_t handle_find_information_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
375*591423b2SMatthias Ringwald                                          uint8_t * response_buffer, uint16_t response_buffer_size){
376*591423b2SMatthias Ringwald     return handle_find_information_request2(att_connection, response_buffer, response_buffer_size, little_endian_read_16(request_buffer, 1), little_endian_read_16(request_buffer, 3));
377*591423b2SMatthias Ringwald }
378*591423b2SMatthias Ringwald 
379*591423b2SMatthias Ringwald //
380*591423b2SMatthias Ringwald // MARK: ATT_FIND_BY_TYPE_VALUE
381*591423b2SMatthias Ringwald //
382*591423b2SMatthias Ringwald // "Only attributes with attribute handles between and including the Starting Handle parameter
383*591423b2SMatthias Ringwald // and the Ending Handle parameter that match the requested attri- bute type and the attribute
384*591423b2SMatthias Ringwald // value that have sufficient permissions to allow reading will be returned" -> (1)
385*591423b2SMatthias Ringwald //
386*591423b2SMatthias Ringwald // TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID
387*591423b2SMatthias Ringwald //
388*591423b2SMatthias Ringwald // NOTE: doesn't handle DYNAMIC values
389*591423b2SMatthias Ringwald // NOTE: only supports 16 bit UUIDs
390*591423b2SMatthias Ringwald //
391*591423b2SMatthias Ringwald static uint16_t handle_find_by_type_value_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size,
392*591423b2SMatthias Ringwald                                            uint16_t start_handle, uint16_t end_handle,
393*591423b2SMatthias Ringwald                                            uint16_t attribute_type, uint16_t attribute_len, uint8_t* attribute_value){
394*591423b2SMatthias Ringwald 
395*591423b2SMatthias Ringwald     log_info("ATT_FIND_BY_TYPE_VALUE_REQUEST: from %04X to %04X, type %04X, value: ", start_handle, end_handle, attribute_type);
396*591423b2SMatthias Ringwald     log_info_hexdump(attribute_value, attribute_len);
397*591423b2SMatthias Ringwald     uint8_t request_type = ATT_FIND_BY_TYPE_VALUE_REQUEST;
398*591423b2SMatthias Ringwald 
399*591423b2SMatthias Ringwald     if (start_handle > end_handle || start_handle == 0){
400*591423b2SMatthias Ringwald         return setup_error_invalid_handle(response_buffer, request_type, start_handle);
401*591423b2SMatthias Ringwald     }
402*591423b2SMatthias Ringwald 
403*591423b2SMatthias Ringwald     uint16_t offset      = 1;
404*591423b2SMatthias Ringwald     uint16_t in_group    = 0;
405*591423b2SMatthias Ringwald     uint16_t prev_handle = 0;
406*591423b2SMatthias Ringwald 
407*591423b2SMatthias Ringwald     att_iterator_t it;
408*591423b2SMatthias Ringwald     att_iterator_init(&it);
409*591423b2SMatthias Ringwald     while (att_iterator_has_next(&it)){
410*591423b2SMatthias Ringwald         att_iterator_fetch_next(&it);
411*591423b2SMatthias Ringwald 
412*591423b2SMatthias Ringwald         if (it.handle && it.handle < start_handle) continue;
413*591423b2SMatthias Ringwald         if (it.handle > end_handle) break;  // (1)
414*591423b2SMatthias Ringwald 
415*591423b2SMatthias Ringwald         // close current tag, if within a group and a new service definition starts or we reach end of att db
416*591423b2SMatthias Ringwald         if (in_group &&
417*591423b2SMatthias Ringwald             (it.handle == 0 || att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID))){
418*591423b2SMatthias Ringwald 
419*591423b2SMatthias Ringwald             log_info("End of group, handle 0x%04x", prev_handle);
420*591423b2SMatthias Ringwald             little_endian_store_16(response_buffer, offset, prev_handle);
421*591423b2SMatthias Ringwald             offset += 2;
422*591423b2SMatthias Ringwald             in_group = 0;
423*591423b2SMatthias Ringwald 
424*591423b2SMatthias Ringwald             // check if space for another handle pair available
425*591423b2SMatthias Ringwald             if (offset + 4 > response_buffer_size){
426*591423b2SMatthias Ringwald                 break;
427*591423b2SMatthias Ringwald             }
428*591423b2SMatthias Ringwald         }
429*591423b2SMatthias Ringwald 
430*591423b2SMatthias Ringwald         // keep track of previous handle
431*591423b2SMatthias Ringwald         prev_handle = it.handle;
432*591423b2SMatthias Ringwald 
433*591423b2SMatthias Ringwald         // does current attribute match
434*591423b2SMatthias Ringwald         if (it.handle && att_iterator_match_uuid16(&it, attribute_type) && attribute_len == it.value_len && memcmp(attribute_value, it.value, it.value_len) == 0){
435*591423b2SMatthias Ringwald             log_info("Begin of group, handle 0x%04x", it.handle);
436*591423b2SMatthias Ringwald             little_endian_store_16(response_buffer, offset, it.handle);
437*591423b2SMatthias Ringwald             offset += 2;
438*591423b2SMatthias Ringwald             in_group = 1;
439*591423b2SMatthias Ringwald         }
440*591423b2SMatthias Ringwald     }
441*591423b2SMatthias Ringwald 
442*591423b2SMatthias Ringwald     if (offset == 1){
443*591423b2SMatthias Ringwald         return setup_error_atribute_not_found(response_buffer, request_type, start_handle);
444*591423b2SMatthias Ringwald     }
445*591423b2SMatthias Ringwald 
446*591423b2SMatthias Ringwald     response_buffer[0] = ATT_FIND_BY_TYPE_VALUE_RESPONSE;
447*591423b2SMatthias Ringwald     return offset;
448*591423b2SMatthias Ringwald }
449*591423b2SMatthias Ringwald 
450*591423b2SMatthias Ringwald static uint16_t handle_find_by_type_value_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
451*591423b2SMatthias Ringwald                                            uint8_t * response_buffer, uint16_t response_buffer_size){
452*591423b2SMatthias Ringwald     int attribute_len = request_len - 7;
453*591423b2SMatthias Ringwald     return handle_find_by_type_value_request2(att_connection, response_buffer, response_buffer_size, little_endian_read_16(request_buffer, 1),
454*591423b2SMatthias Ringwald                                               little_endian_read_16(request_buffer, 3), little_endian_read_16(request_buffer, 5), attribute_len, &request_buffer[7]);
455*591423b2SMatthias Ringwald }
456*591423b2SMatthias Ringwald 
457*591423b2SMatthias Ringwald //
458*591423b2SMatthias Ringwald // MARK: ATT_READ_BY_TYPE_REQUEST
459*591423b2SMatthias Ringwald //
460*591423b2SMatthias Ringwald static uint16_t handle_read_by_type_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size,
461*591423b2SMatthias Ringwald                                       uint16_t start_handle, uint16_t end_handle,
462*591423b2SMatthias Ringwald                                       uint16_t attribute_type_len, uint8_t * attribute_type){
463*591423b2SMatthias Ringwald 
464*591423b2SMatthias Ringwald     log_info("ATT_READ_BY_TYPE_REQUEST: from %04X to %04X, type: ", start_handle, end_handle);
465*591423b2SMatthias Ringwald     log_info_hexdump(attribute_type, attribute_type_len);
466*591423b2SMatthias Ringwald     uint8_t request_type = ATT_READ_BY_TYPE_REQUEST;
467*591423b2SMatthias Ringwald 
468*591423b2SMatthias Ringwald     if (start_handle > end_handle || start_handle == 0){
469*591423b2SMatthias Ringwald         return setup_error_invalid_handle(response_buffer, request_type, start_handle);
470*591423b2SMatthias Ringwald     }
471*591423b2SMatthias Ringwald 
472*591423b2SMatthias Ringwald     uint16_t offset   = 1;
473*591423b2SMatthias Ringwald     uint16_t pair_len = 0;
474*591423b2SMatthias Ringwald 
475*591423b2SMatthias Ringwald     att_iterator_t it;
476*591423b2SMatthias Ringwald     att_iterator_init(&it);
477*591423b2SMatthias Ringwald     uint8_t error_code = 0;
478*591423b2SMatthias Ringwald     uint16_t first_matching_but_unreadable_handle = 0;
479*591423b2SMatthias Ringwald 
480*591423b2SMatthias Ringwald     while (att_iterator_has_next(&it)){
481*591423b2SMatthias Ringwald         att_iterator_fetch_next(&it);
482*591423b2SMatthias Ringwald 
483*591423b2SMatthias Ringwald         if (!it.handle) break;
484*591423b2SMatthias Ringwald         if (it.handle < start_handle) continue;
485*591423b2SMatthias Ringwald         if (it.handle > end_handle) break;  // (1)
486*591423b2SMatthias Ringwald 
487*591423b2SMatthias Ringwald         // does current attribute match
488*591423b2SMatthias Ringwald         if (!att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) continue;
489*591423b2SMatthias Ringwald 
490*591423b2SMatthias Ringwald         // skip handles that cannot be read but rembember that there has been at least one
491*591423b2SMatthias Ringwald         if ((it.flags & ATT_PROPERTY_READ) == 0) {
492*591423b2SMatthias Ringwald             if (first_matching_but_unreadable_handle == 0) {
493*591423b2SMatthias Ringwald                 first_matching_but_unreadable_handle = it.handle;
494*591423b2SMatthias Ringwald             }
495*591423b2SMatthias Ringwald             continue;
496*591423b2SMatthias Ringwald         }
497*591423b2SMatthias Ringwald 
498*591423b2SMatthias Ringwald         // check security requirements
499*591423b2SMatthias Ringwald         error_code = att_validate_security(att_connection, &it);
500*591423b2SMatthias Ringwald         if (error_code) break;
501*591423b2SMatthias Ringwald 
502*591423b2SMatthias Ringwald         att_update_value_len(&it, att_connection->con_handle);
503*591423b2SMatthias Ringwald 
504*591423b2SMatthias Ringwald         // check if value has same len as last one
505*591423b2SMatthias Ringwald         uint16_t this_pair_len = 2 + it.value_len;
506*591423b2SMatthias Ringwald         if (offset > 1){
507*591423b2SMatthias Ringwald             if (pair_len != this_pair_len) {
508*591423b2SMatthias Ringwald                 break;
509*591423b2SMatthias Ringwald             }
510*591423b2SMatthias Ringwald         }
511*591423b2SMatthias Ringwald 
512*591423b2SMatthias Ringwald         // first
513*591423b2SMatthias Ringwald         if (offset == 1) {
514*591423b2SMatthias Ringwald             pair_len = this_pair_len;
515*591423b2SMatthias Ringwald             response_buffer[offset] = pair_len;
516*591423b2SMatthias Ringwald             offset++;
517*591423b2SMatthias Ringwald         }
518*591423b2SMatthias Ringwald 
519*591423b2SMatthias Ringwald         // space?
520*591423b2SMatthias Ringwald         if (offset + pair_len > response_buffer_size) {
521*591423b2SMatthias Ringwald             if (offset > 2) break;
522*591423b2SMatthias Ringwald             it.value_len = response_buffer_size - 4;
523*591423b2SMatthias Ringwald             response_buffer[1] = 2 + it.value_len;
524*591423b2SMatthias Ringwald         }
525*591423b2SMatthias Ringwald 
526*591423b2SMatthias Ringwald         // store
527*591423b2SMatthias Ringwald         little_endian_store_16(response_buffer, offset, it.handle);
528*591423b2SMatthias Ringwald         offset += 2;
529*591423b2SMatthias Ringwald         uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len, att_connection->con_handle);
530*591423b2SMatthias Ringwald         offset += bytes_copied;
531*591423b2SMatthias Ringwald     }
532*591423b2SMatthias Ringwald 
533*591423b2SMatthias Ringwald     // at least one attribute could be read
534*591423b2SMatthias Ringwald     if (offset > 1){
535*591423b2SMatthias Ringwald         response_buffer[0] = ATT_READ_BY_TYPE_RESPONSE;
536*591423b2SMatthias Ringwald         return offset;
537*591423b2SMatthias Ringwald     }
538*591423b2SMatthias Ringwald 
539*591423b2SMatthias Ringwald     // first attribute had an error
540*591423b2SMatthias Ringwald     if (error_code){
541*591423b2SMatthias Ringwald         return setup_error(response_buffer, request_type, start_handle, error_code);
542*591423b2SMatthias Ringwald     }
543*591423b2SMatthias Ringwald 
544*591423b2SMatthias Ringwald     // no other errors, but all found attributes had been non-readable
545*591423b2SMatthias Ringwald     if (first_matching_but_unreadable_handle){
546*591423b2SMatthias Ringwald         return setup_error_read_not_permitted(response_buffer, request_type, first_matching_but_unreadable_handle);
547*591423b2SMatthias Ringwald     }
548*591423b2SMatthias Ringwald 
549*591423b2SMatthias Ringwald     // attribute not found
550*591423b2SMatthias Ringwald     return setup_error_atribute_not_found(response_buffer, request_type, start_handle);
551*591423b2SMatthias Ringwald }
552*591423b2SMatthias Ringwald 
553*591423b2SMatthias Ringwald static uint16_t handle_read_by_type_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
554*591423b2SMatthias Ringwald                                      uint8_t * response_buffer, uint16_t response_buffer_size){
555*591423b2SMatthias Ringwald     int attribute_type_len;
556*591423b2SMatthias Ringwald     if (request_len <= 7){
557*591423b2SMatthias Ringwald         attribute_type_len = 2;
558*591423b2SMatthias Ringwald     } else {
559*591423b2SMatthias Ringwald         attribute_type_len = 16;
560*591423b2SMatthias Ringwald     }
561*591423b2SMatthias Ringwald     return handle_read_by_type_request2(att_connection, response_buffer, response_buffer_size, little_endian_read_16(request_buffer, 1), little_endian_read_16(request_buffer, 3), attribute_type_len, &request_buffer[5]);
562*591423b2SMatthias Ringwald }
563*591423b2SMatthias Ringwald 
564*591423b2SMatthias Ringwald //
565*591423b2SMatthias Ringwald // MARK: ATT_READ_BY_TYPE_REQUEST
566*591423b2SMatthias Ringwald //
567*591423b2SMatthias Ringwald static uint16_t handle_read_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle){
568*591423b2SMatthias Ringwald 
569*591423b2SMatthias Ringwald     log_info("ATT_READ_REQUEST: handle %04x", handle);
570*591423b2SMatthias Ringwald     uint8_t request_type = ATT_READ_REQUEST;
571*591423b2SMatthias Ringwald 
572*591423b2SMatthias Ringwald     att_iterator_t it;
573*591423b2SMatthias Ringwald     int ok = att_find_handle(&it, handle);
574*591423b2SMatthias Ringwald     if (!ok){
575*591423b2SMatthias Ringwald         return setup_error_invalid_handle(response_buffer, request_type, handle);
576*591423b2SMatthias Ringwald     }
577*591423b2SMatthias Ringwald 
578*591423b2SMatthias Ringwald     // check if handle can be read
579*591423b2SMatthias Ringwald     if ((it.flags & ATT_PROPERTY_READ) == 0) {
580*591423b2SMatthias Ringwald         return setup_error_read_not_permitted(response_buffer, request_type, handle);
581*591423b2SMatthias Ringwald     }
582*591423b2SMatthias Ringwald 
583*591423b2SMatthias Ringwald     // check security requirements
584*591423b2SMatthias Ringwald     uint8_t error_code = att_validate_security(att_connection, &it);
585*591423b2SMatthias Ringwald     if (error_code) {
586*591423b2SMatthias Ringwald         return setup_error(response_buffer, request_type, handle, error_code);
587*591423b2SMatthias Ringwald     }
588*591423b2SMatthias Ringwald 
589*591423b2SMatthias Ringwald     att_update_value_len(&it, att_connection->con_handle);
590*591423b2SMatthias Ringwald 
591*591423b2SMatthias Ringwald     uint16_t offset   = 1;
592*591423b2SMatthias Ringwald     // limit data
593*591423b2SMatthias Ringwald     if (offset + it.value_len > response_buffer_size) {
594*591423b2SMatthias Ringwald         it.value_len = response_buffer_size - 1;
595*591423b2SMatthias Ringwald     }
596*591423b2SMatthias Ringwald 
597*591423b2SMatthias Ringwald     // store
598*591423b2SMatthias Ringwald     uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len, att_connection->con_handle);
599*591423b2SMatthias Ringwald     offset += bytes_copied;
600*591423b2SMatthias Ringwald 
601*591423b2SMatthias Ringwald     response_buffer[0] = ATT_READ_RESPONSE;
602*591423b2SMatthias Ringwald     return offset;
603*591423b2SMatthias Ringwald }
604*591423b2SMatthias Ringwald 
605*591423b2SMatthias Ringwald static uint16_t handle_read_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
606*591423b2SMatthias Ringwald                              uint8_t * response_buffer, uint16_t response_buffer_size){
607*591423b2SMatthias Ringwald     return handle_read_request2(att_connection, response_buffer, response_buffer_size, little_endian_read_16(request_buffer, 1));
608*591423b2SMatthias Ringwald }
609*591423b2SMatthias Ringwald 
610*591423b2SMatthias Ringwald //
611*591423b2SMatthias Ringwald // MARK: ATT_READ_BLOB_REQUEST 0x0c
612*591423b2SMatthias Ringwald //
613*591423b2SMatthias Ringwald static uint16_t handle_read_blob_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle, uint16_t value_offset){
614*591423b2SMatthias Ringwald     log_info("ATT_READ_BLOB_REQUEST: handle %04x, offset %u", handle, value_offset);
615*591423b2SMatthias Ringwald     uint8_t request_type = ATT_READ_BLOB_REQUEST;
616*591423b2SMatthias Ringwald 
617*591423b2SMatthias Ringwald     att_iterator_t it;
618*591423b2SMatthias Ringwald     int ok = att_find_handle(&it, handle);
619*591423b2SMatthias Ringwald     if (!ok){
620*591423b2SMatthias Ringwald         return setup_error_invalid_handle(response_buffer, request_type, handle);
621*591423b2SMatthias Ringwald     }
622*591423b2SMatthias Ringwald 
623*591423b2SMatthias Ringwald     // check if handle can be read
624*591423b2SMatthias Ringwald     if ((it.flags & ATT_PROPERTY_READ) == 0) {
625*591423b2SMatthias Ringwald         return setup_error_read_not_permitted(response_buffer, request_type, handle);
626*591423b2SMatthias Ringwald     }
627*591423b2SMatthias Ringwald 
628*591423b2SMatthias Ringwald     // check security requirements
629*591423b2SMatthias Ringwald     uint8_t error_code = att_validate_security(att_connection, &it);
630*591423b2SMatthias Ringwald     if (error_code) {
631*591423b2SMatthias Ringwald         return setup_error(response_buffer, request_type, handle, error_code);
632*591423b2SMatthias Ringwald     }
633*591423b2SMatthias Ringwald 
634*591423b2SMatthias Ringwald     att_update_value_len(&it, att_connection->con_handle);
635*591423b2SMatthias Ringwald 
636*591423b2SMatthias Ringwald     if (value_offset > it.value_len){
637*591423b2SMatthias Ringwald         return setup_error_invalid_offset(response_buffer, request_type, handle);
638*591423b2SMatthias Ringwald     }
639*591423b2SMatthias Ringwald 
640*591423b2SMatthias Ringwald     // limit data
641*591423b2SMatthias Ringwald     uint16_t offset   = 1;
642*591423b2SMatthias Ringwald     if (offset + it.value_len - value_offset > response_buffer_size) {
643*591423b2SMatthias Ringwald         it.value_len = response_buffer_size - 1 + value_offset;
644*591423b2SMatthias Ringwald     }
645*591423b2SMatthias Ringwald 
646*591423b2SMatthias Ringwald     // store
647*591423b2SMatthias Ringwald     uint16_t bytes_copied = att_copy_value(&it, value_offset, response_buffer + offset, it.value_len - value_offset, att_connection->con_handle);
648*591423b2SMatthias Ringwald     offset += bytes_copied;
649*591423b2SMatthias Ringwald 
650*591423b2SMatthias Ringwald     response_buffer[0] = ATT_READ_BLOB_RESPONSE;
651*591423b2SMatthias Ringwald     return offset;
652*591423b2SMatthias Ringwald }
653*591423b2SMatthias Ringwald 
654*591423b2SMatthias Ringwald static uint16_t handle_read_blob_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
655*591423b2SMatthias Ringwald                                   uint8_t * response_buffer, uint16_t response_buffer_size){
656*591423b2SMatthias Ringwald     return handle_read_blob_request2(att_connection, response_buffer, response_buffer_size, little_endian_read_16(request_buffer, 1), little_endian_read_16(request_buffer, 3));
657*591423b2SMatthias Ringwald }
658*591423b2SMatthias Ringwald 
659*591423b2SMatthias Ringwald //
660*591423b2SMatthias Ringwald // MARK: ATT_READ_MULTIPLE_REQUEST 0x0e
661*591423b2SMatthias Ringwald //
662*591423b2SMatthias Ringwald static uint16_t handle_read_multiple_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t num_handles, uint8_t * handles){
663*591423b2SMatthias Ringwald     log_info("ATT_READ_MULTIPLE_REQUEST: num handles %u", num_handles);
664*591423b2SMatthias Ringwald     uint8_t request_type = ATT_READ_MULTIPLE_REQUEST;
665*591423b2SMatthias Ringwald 
666*591423b2SMatthias Ringwald     // TODO: figure out which error to respond with
667*591423b2SMatthias Ringwald     // if (num_handles < 2){
668*591423b2SMatthias Ringwald     //     return setup_error(response_buffer, ATT_READ_MULTIPLE_REQUEST, handle, ???);
669*591423b2SMatthias Ringwald     // }
670*591423b2SMatthias Ringwald 
671*591423b2SMatthias Ringwald     uint16_t offset   = 1;
672*591423b2SMatthias Ringwald 
673*591423b2SMatthias Ringwald     int i;
674*591423b2SMatthias Ringwald     uint8_t error_code = 0;
675*591423b2SMatthias Ringwald     uint16_t handle = 0;
676*591423b2SMatthias Ringwald     for (i=0;i<num_handles;i++){
677*591423b2SMatthias Ringwald         handle = little_endian_read_16(handles, i << 1);
678*591423b2SMatthias Ringwald 
679*591423b2SMatthias Ringwald         if (handle == 0){
680*591423b2SMatthias Ringwald             return setup_error_invalid_handle(response_buffer, request_type, handle);
681*591423b2SMatthias Ringwald         }
682*591423b2SMatthias Ringwald 
683*591423b2SMatthias Ringwald         att_iterator_t it;
684*591423b2SMatthias Ringwald 
685*591423b2SMatthias Ringwald         int ok = att_find_handle(&it, handle);
686*591423b2SMatthias Ringwald         if (!ok){
687*591423b2SMatthias Ringwald             return setup_error_invalid_handle(response_buffer, request_type, handle);
688*591423b2SMatthias Ringwald         }
689*591423b2SMatthias Ringwald 
690*591423b2SMatthias Ringwald         // check if handle can be read
691*591423b2SMatthias Ringwald         if ((it.flags & ATT_PROPERTY_READ) == 0) {
692*591423b2SMatthias Ringwald             error_code = ATT_ERROR_READ_NOT_PERMITTED;
693*591423b2SMatthias Ringwald             break;
694*591423b2SMatthias Ringwald         }
695*591423b2SMatthias Ringwald 
696*591423b2SMatthias Ringwald         // check security requirements
697*591423b2SMatthias Ringwald         error_code = att_validate_security(att_connection, &it);
698*591423b2SMatthias Ringwald         if (error_code) break;
699*591423b2SMatthias Ringwald 
700*591423b2SMatthias Ringwald         att_update_value_len(&it, att_connection->con_handle);
701*591423b2SMatthias Ringwald 
702*591423b2SMatthias Ringwald         // limit data
703*591423b2SMatthias Ringwald         if (offset + it.value_len > response_buffer_size) {
704*591423b2SMatthias Ringwald             it.value_len = response_buffer_size - 1;
705*591423b2SMatthias Ringwald         }
706*591423b2SMatthias Ringwald 
707*591423b2SMatthias Ringwald         // store
708*591423b2SMatthias Ringwald         uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len, att_connection->con_handle);
709*591423b2SMatthias Ringwald         offset += bytes_copied;
710*591423b2SMatthias Ringwald     }
711*591423b2SMatthias Ringwald 
712*591423b2SMatthias Ringwald     if (error_code){
713*591423b2SMatthias Ringwald         return setup_error(response_buffer, request_type, handle, error_code);
714*591423b2SMatthias Ringwald     }
715*591423b2SMatthias Ringwald 
716*591423b2SMatthias Ringwald     response_buffer[0] = ATT_READ_MULTIPLE_RESPONSE;
717*591423b2SMatthias Ringwald     return offset;
718*591423b2SMatthias Ringwald }
719*591423b2SMatthias Ringwald static uint16_t handle_read_multiple_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
720*591423b2SMatthias Ringwald                                       uint8_t * response_buffer, uint16_t response_buffer_size){
721*591423b2SMatthias Ringwald     int num_handles = (request_len - 1) >> 1;
722*591423b2SMatthias Ringwald     return handle_read_multiple_request2(att_connection, response_buffer, response_buffer_size, num_handles, &request_buffer[1]);
723*591423b2SMatthias Ringwald }
724*591423b2SMatthias Ringwald 
725*591423b2SMatthias Ringwald //
726*591423b2SMatthias Ringwald // MARK: ATT_READ_BY_GROUP_TYPE_REQUEST 0x10
727*591423b2SMatthias Ringwald //
728*591423b2SMatthias Ringwald // Only handles GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID
729*591423b2SMatthias Ringwald // Core v4.0, vol 3, part g, 2.5.3
730*591423b2SMatthias Ringwald // "The «Primary Service» and «Secondary Service» grouping types may be used in the Read By Group Type Request.
731*591423b2SMatthias Ringwald //  The «Characteristic» grouping type shall not be used in the ATT Read By Group Type Request."
732*591423b2SMatthias Ringwald //
733*591423b2SMatthias Ringwald // NOTE: doesn't handle DYNAMIC values
734*591423b2SMatthias Ringwald //
735*591423b2SMatthias Ringwald // NOTE: we don't check for security as PRIMARY and SECONDAY SERVICE definition shouldn't be protected
736*591423b2SMatthias Ringwald // Core 4.0, vol 3, part g, 8.1
737*591423b2SMatthias Ringwald // "The list of services and characteristics that a device supports is not considered private or
738*591423b2SMatthias Ringwald //  confidential information, and therefore the Service and Characteristic Discovery procedures
739*591423b2SMatthias Ringwald //  shall always be permitted. "
740*591423b2SMatthias Ringwald //
741*591423b2SMatthias Ringwald static uint16_t handle_read_by_group_type_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size,
742*591423b2SMatthias Ringwald                                             uint16_t start_handle, uint16_t end_handle,
743*591423b2SMatthias Ringwald                                             uint16_t attribute_type_len, uint8_t * attribute_type){
744*591423b2SMatthias Ringwald 
745*591423b2SMatthias Ringwald     log_info("ATT_READ_BY_GROUP_TYPE_REQUEST: from %04X to %04X, buffer size %u, type: ", start_handle, end_handle, response_buffer_size);
746*591423b2SMatthias Ringwald     log_info_hexdump(attribute_type, attribute_type_len);
747*591423b2SMatthias Ringwald     uint8_t request_type = ATT_READ_BY_GROUP_TYPE_REQUEST;
748*591423b2SMatthias Ringwald 
749*591423b2SMatthias Ringwald     if (start_handle > end_handle || start_handle == 0){
750*591423b2SMatthias Ringwald         return setup_error_invalid_handle(response_buffer, request_type, start_handle);
751*591423b2SMatthias Ringwald     }
752*591423b2SMatthias Ringwald 
753*591423b2SMatthias Ringwald     // assert UUID is primary or secondary service uuid
754*591423b2SMatthias Ringwald     uint16_t uuid16 = uuid16_from_uuid(attribute_type_len, attribute_type);
755*591423b2SMatthias Ringwald     if (uuid16 != GATT_PRIMARY_SERVICE_UUID && uuid16 != GATT_SECONDARY_SERVICE_UUID){
756*591423b2SMatthias Ringwald         return setup_error(response_buffer, request_type, start_handle, ATT_ERROR_UNSUPPORTED_GROUP_TYPE);
757*591423b2SMatthias Ringwald     }
758*591423b2SMatthias Ringwald 
759*591423b2SMatthias Ringwald     uint16_t offset   = 1;
760*591423b2SMatthias Ringwald     uint16_t pair_len = 0;
761*591423b2SMatthias Ringwald     uint16_t in_group = 0;
762*591423b2SMatthias Ringwald     uint16_t group_start_handle = 0;
763*591423b2SMatthias Ringwald     uint8_t const * group_start_value = NULL;
764*591423b2SMatthias Ringwald     uint16_t prev_handle = 0;
765*591423b2SMatthias Ringwald 
766*591423b2SMatthias Ringwald     att_iterator_t it;
767*591423b2SMatthias Ringwald     att_iterator_init(&it);
768*591423b2SMatthias Ringwald     while (att_iterator_has_next(&it)){
769*591423b2SMatthias Ringwald         att_iterator_fetch_next(&it);
770*591423b2SMatthias Ringwald 
771*591423b2SMatthias Ringwald         if (it.handle && it.handle < start_handle) continue;
772*591423b2SMatthias Ringwald         if (it.handle > end_handle) break;  // (1)
773*591423b2SMatthias Ringwald 
774*591423b2SMatthias Ringwald         // log_info("Handle 0x%04x", it.handle);
775*591423b2SMatthias Ringwald 
776*591423b2SMatthias Ringwald         // close current tag, if within a group and a new service definition starts or we reach end of att db
777*591423b2SMatthias Ringwald         if (in_group &&
778*591423b2SMatthias Ringwald             (it.handle == 0 || att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID))){
779*591423b2SMatthias Ringwald             // log_info("End of group, handle 0x%04x, val_len: %u", prev_handle, pair_len - 4);
780*591423b2SMatthias Ringwald 
781*591423b2SMatthias Ringwald             little_endian_store_16(response_buffer, offset, group_start_handle);
782*591423b2SMatthias Ringwald             offset += 2;
783*591423b2SMatthias Ringwald             little_endian_store_16(response_buffer, offset, prev_handle);
784*591423b2SMatthias Ringwald             offset += 2;
785*591423b2SMatthias Ringwald             memcpy(response_buffer + offset, group_start_value, pair_len - 4);
786*591423b2SMatthias Ringwald             offset += pair_len - 4;
787*591423b2SMatthias Ringwald             in_group = 0;
788*591423b2SMatthias Ringwald 
789*591423b2SMatthias Ringwald             // check if space for another handle pair available
790*591423b2SMatthias Ringwald             if (offset + pair_len > response_buffer_size){
791*591423b2SMatthias Ringwald                 break;
792*591423b2SMatthias Ringwald             }
793*591423b2SMatthias Ringwald         }
794*591423b2SMatthias Ringwald 
795*591423b2SMatthias Ringwald         // keep track of previous handle
796*591423b2SMatthias Ringwald         prev_handle = it.handle;
797*591423b2SMatthias Ringwald 
798*591423b2SMatthias Ringwald         // does current attribute match
799*591423b2SMatthias Ringwald         // log_info("compare: %04x == %04x", *(uint16_t*) context->attribute_type, *(uint16_t*) uuid);
800*591423b2SMatthias Ringwald         if (it.handle && att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) {
801*591423b2SMatthias Ringwald 
802*591423b2SMatthias Ringwald             // check if value has same len as last one
803*591423b2SMatthias Ringwald             uint16_t this_pair_len = 4 + it.value_len;
804*591423b2SMatthias Ringwald             if (offset > 1){
805*591423b2SMatthias Ringwald                 if (this_pair_len != pair_len) {
806*591423b2SMatthias Ringwald                     break;
807*591423b2SMatthias Ringwald                 }
808*591423b2SMatthias Ringwald             }
809*591423b2SMatthias Ringwald 
810*591423b2SMatthias Ringwald             // log_info("Begin of group, handle 0x%04x", it.handle);
811*591423b2SMatthias Ringwald 
812*591423b2SMatthias Ringwald             // first
813*591423b2SMatthias Ringwald             if (offset == 1) {
814*591423b2SMatthias Ringwald                 pair_len = this_pair_len;
815*591423b2SMatthias Ringwald                 response_buffer[offset] = this_pair_len;
816*591423b2SMatthias Ringwald                 offset++;
817*591423b2SMatthias Ringwald             }
818*591423b2SMatthias Ringwald 
819*591423b2SMatthias Ringwald             group_start_handle = it.handle;
820*591423b2SMatthias Ringwald             group_start_value  = it.value;
821*591423b2SMatthias Ringwald             in_group = 1;
822*591423b2SMatthias Ringwald         }
823*591423b2SMatthias Ringwald     }
824*591423b2SMatthias Ringwald 
825*591423b2SMatthias Ringwald     if (offset == 1){
826*591423b2SMatthias Ringwald         return setup_error_atribute_not_found(response_buffer, request_type, start_handle);
827*591423b2SMatthias Ringwald     }
828*591423b2SMatthias Ringwald 
829*591423b2SMatthias Ringwald     response_buffer[0] = ATT_READ_BY_GROUP_TYPE_RESPONSE;
830*591423b2SMatthias Ringwald     return offset;
831*591423b2SMatthias Ringwald }
832*591423b2SMatthias Ringwald static uint16_t handle_read_by_group_type_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
833*591423b2SMatthias Ringwald                                            uint8_t * response_buffer, uint16_t response_buffer_size){
834*591423b2SMatthias Ringwald     int attribute_type_len;
835*591423b2SMatthias Ringwald     if (request_len <= 7){
836*591423b2SMatthias Ringwald         attribute_type_len = 2;
837*591423b2SMatthias Ringwald     } else {
838*591423b2SMatthias Ringwald         attribute_type_len = 16;
839*591423b2SMatthias Ringwald     }
840*591423b2SMatthias Ringwald     return handle_read_by_group_type_request2(att_connection, response_buffer, response_buffer_size, little_endian_read_16(request_buffer, 1), little_endian_read_16(request_buffer, 3), attribute_type_len, &request_buffer[5]);
841*591423b2SMatthias Ringwald }
842*591423b2SMatthias Ringwald 
843*591423b2SMatthias Ringwald //
844*591423b2SMatthias Ringwald // MARK: ATT_WRITE_REQUEST 0x12
845*591423b2SMatthias Ringwald static uint16_t handle_write_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
846*591423b2SMatthias Ringwald                               uint8_t * response_buffer, uint16_t response_buffer_size){
847*591423b2SMatthias Ringwald 
848*591423b2SMatthias Ringwald     uint8_t request_type = ATT_WRITE_REQUEST;
849*591423b2SMatthias Ringwald 
850*591423b2SMatthias Ringwald     uint16_t handle = little_endian_read_16(request_buffer, 1);
851*591423b2SMatthias Ringwald     att_iterator_t it;
852*591423b2SMatthias Ringwald     int ok = att_find_handle(&it, handle);
853*591423b2SMatthias Ringwald     if (!ok) {
854*591423b2SMatthias Ringwald         return setup_error_invalid_handle(response_buffer, request_type, handle);
855*591423b2SMatthias Ringwald     }
856*591423b2SMatthias Ringwald     if (!att_write_callback) {
857*591423b2SMatthias Ringwald         return setup_error_write_not_permitted(response_buffer, request_type, handle);
858*591423b2SMatthias Ringwald     }
859*591423b2SMatthias Ringwald     if ((it.flags & ATT_PROPERTY_WRITE) == 0) {
860*591423b2SMatthias Ringwald         return setup_error_write_not_permitted(response_buffer, request_type, handle);
861*591423b2SMatthias Ringwald     }
862*591423b2SMatthias Ringwald     if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) {
863*591423b2SMatthias Ringwald         return setup_error_write_not_permitted(response_buffer, request_type, handle);
864*591423b2SMatthias Ringwald     }
865*591423b2SMatthias Ringwald     // check security requirements
866*591423b2SMatthias Ringwald     uint8_t error_code = att_validate_security(att_connection, &it);
867*591423b2SMatthias Ringwald     if (error_code) {
868*591423b2SMatthias Ringwald         return setup_error(response_buffer, request_type, handle, error_code);
869*591423b2SMatthias Ringwald     }
870*591423b2SMatthias Ringwald     error_code = (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3);
871*591423b2SMatthias Ringwald     if (error_code) {
872*591423b2SMatthias Ringwald         return setup_error(response_buffer, request_type, handle, error_code);
873*591423b2SMatthias Ringwald     }
874*591423b2SMatthias Ringwald     response_buffer[0] = ATT_WRITE_RESPONSE;
875*591423b2SMatthias Ringwald     return 1;
876*591423b2SMatthias Ringwald }
877*591423b2SMatthias Ringwald 
878*591423b2SMatthias Ringwald //
879*591423b2SMatthias Ringwald // MARK: ATT_PREPARE_WRITE_REQUEST 0x16
880*591423b2SMatthias Ringwald static uint16_t handle_prepare_write_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
881*591423b2SMatthias Ringwald                                       uint8_t * response_buffer, uint16_t response_buffer_size){
882*591423b2SMatthias Ringwald 
883*591423b2SMatthias Ringwald     uint8_t request_type = ATT_PREPARE_WRITE_REQUEST;
884*591423b2SMatthias Ringwald 
885*591423b2SMatthias Ringwald     uint16_t handle = little_endian_read_16(request_buffer, 1);
886*591423b2SMatthias Ringwald     uint16_t offset = little_endian_read_16(request_buffer, 3);
887*591423b2SMatthias Ringwald     if (!att_write_callback) {
888*591423b2SMatthias Ringwald         return setup_error_write_not_permitted(response_buffer, request_type, handle);
889*591423b2SMatthias Ringwald     }
890*591423b2SMatthias Ringwald     att_iterator_t it;
891*591423b2SMatthias Ringwald     int ok = att_find_handle(&it, handle);
892*591423b2SMatthias Ringwald     if (!ok) {
893*591423b2SMatthias Ringwald         return setup_error_invalid_handle(response_buffer, request_type, handle);
894*591423b2SMatthias Ringwald     }
895*591423b2SMatthias Ringwald     if ((it.flags & ATT_PROPERTY_WRITE) == 0) {
896*591423b2SMatthias Ringwald         return setup_error_write_not_permitted(response_buffer, request_type, handle);
897*591423b2SMatthias Ringwald     }
898*591423b2SMatthias Ringwald     if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) {
899*591423b2SMatthias Ringwald         return setup_error_write_not_permitted(response_buffer, request_type, handle);
900*591423b2SMatthias Ringwald     }
901*591423b2SMatthias Ringwald     // check security requirements
902*591423b2SMatthias Ringwald     uint8_t error_code = att_validate_security(att_connection, &it);
903*591423b2SMatthias Ringwald     if (error_code) {
904*591423b2SMatthias Ringwald         return setup_error(response_buffer, request_type, handle, error_code);
905*591423b2SMatthias Ringwald     }
906*591423b2SMatthias Ringwald 
907*591423b2SMatthias Ringwald     error_code = (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_ACTIVE, offset, request_buffer + 5, request_len - 5);
908*591423b2SMatthias Ringwald     switch (error_code){
909*591423b2SMatthias Ringwald         case 0:
910*591423b2SMatthias Ringwald             break;
911*591423b2SMatthias Ringwald         case ATT_ERROR_INVALID_OFFSET:
912*591423b2SMatthias Ringwald         case ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH:
913*591423b2SMatthias Ringwald             // postpone to execute write request
914*591423b2SMatthias Ringwald             att_prepare_write_update_errors(error_code, handle);
915*591423b2SMatthias Ringwald             break;
916*591423b2SMatthias Ringwald         default:
917*591423b2SMatthias Ringwald             return setup_error(response_buffer, request_type, handle, error_code);
918*591423b2SMatthias Ringwald     }
919*591423b2SMatthias Ringwald 
920*591423b2SMatthias Ringwald     // response: echo request
921*591423b2SMatthias Ringwald     memcpy(response_buffer, request_buffer, request_len);
922*591423b2SMatthias Ringwald     response_buffer[0] = ATT_PREPARE_WRITE_RESPONSE;
923*591423b2SMatthias Ringwald     return request_len;
924*591423b2SMatthias Ringwald }
925*591423b2SMatthias Ringwald 
926*591423b2SMatthias Ringwald /*
927*591423b2SMatthias Ringwald  * @brief transcation queue of prepared writes, e.g., after disconnect
928*591423b2SMatthias Ringwald  */
929*591423b2SMatthias Ringwald void att_clear_transaction_queue(att_connection_t * att_connection){
930*591423b2SMatthias Ringwald     if (!att_write_callback) return;
931*591423b2SMatthias Ringwald     (*att_write_callback)(att_connection->con_handle, 0, ATT_TRANSACTION_MODE_CANCEL, 0, NULL, 0);
932*591423b2SMatthias Ringwald }
933*591423b2SMatthias Ringwald 
934*591423b2SMatthias Ringwald // MARK: ATT_EXECUTE_WRITE_REQUEST 0x18
935*591423b2SMatthias Ringwald // NOTE: security has been verified by handle_prepare_write_request
936*591423b2SMatthias Ringwald static uint16_t handle_execute_write_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
937*591423b2SMatthias Ringwald                                       uint8_t * response_buffer, uint16_t response_buffer_size){
938*591423b2SMatthias Ringwald 
939*591423b2SMatthias Ringwald     uint8_t request_type = ATT_EXECUTE_WRITE_REQUEST;
940*591423b2SMatthias Ringwald 
941*591423b2SMatthias Ringwald     if (!att_write_callback) {
942*591423b2SMatthias Ringwald         return setup_error_write_not_permitted(response_buffer, request_type, 0);
943*591423b2SMatthias Ringwald     }
944*591423b2SMatthias Ringwald     if (request_buffer[1]) {
945*591423b2SMatthias Ringwald         // deliver queued errors
946*591423b2SMatthias Ringwald         if (att_prepare_write_error_code){
947*591423b2SMatthias Ringwald             att_clear_transaction_queue(att_connection);
948*591423b2SMatthias Ringwald             uint8_t  error_code = att_prepare_write_error_code;
949*591423b2SMatthias Ringwald             uint16_t handle     = att_prepare_write_error_handle;
950*591423b2SMatthias Ringwald             att_prepare_write_reset();
951*591423b2SMatthias Ringwald             return setup_error(response_buffer, request_type, handle, error_code);
952*591423b2SMatthias Ringwald         }
953*591423b2SMatthias Ringwald         (*att_write_callback)(att_connection->con_handle, 0, ATT_TRANSACTION_MODE_EXECUTE, 0, NULL, 0);
954*591423b2SMatthias Ringwald     } else {
955*591423b2SMatthias Ringwald         att_clear_transaction_queue(att_connection);
956*591423b2SMatthias Ringwald     }
957*591423b2SMatthias Ringwald     response_buffer[0] = ATT_EXECUTE_WRITE_RESPONSE;
958*591423b2SMatthias Ringwald     return 1;
959*591423b2SMatthias Ringwald }
960*591423b2SMatthias Ringwald 
961*591423b2SMatthias Ringwald // MARK: ATT_WRITE_COMMAND 0x52
962*591423b2SMatthias Ringwald // Core 4.0, vol 3, part F, 3.4.5.3
963*591423b2SMatthias Ringwald // "No Error Response or Write Response shall be sent in response to this command"
964*591423b2SMatthias Ringwald static void handle_write_command(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
965*591423b2SMatthias Ringwald                                            uint8_t * response_buffer, uint16_t response_buffer_size){
966*591423b2SMatthias Ringwald 
967*591423b2SMatthias Ringwald     if (!att_write_callback) return;
968*591423b2SMatthias Ringwald     uint16_t handle = little_endian_read_16(request_buffer, 1);
969*591423b2SMatthias Ringwald     att_iterator_t it;
970*591423b2SMatthias Ringwald     int ok = att_find_handle(&it, handle);
971*591423b2SMatthias Ringwald     if (!ok) return;
972*591423b2SMatthias Ringwald     if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) return;
973*591423b2SMatthias Ringwald     if ((it.flags & ATT_PROPERTY_WRITE_WITHOUT_RESPONSE) == 0) return;
974*591423b2SMatthias Ringwald     if (att_validate_security(att_connection, &it)) return;
975*591423b2SMatthias Ringwald     (*att_write_callback)(att_connection->con_handle, handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3);
976*591423b2SMatthias Ringwald }
977*591423b2SMatthias Ringwald 
978*591423b2SMatthias Ringwald // MARK: helper for ATT_HANDLE_VALUE_NOTIFICATION and ATT_HANDLE_VALUE_INDICATION
979*591423b2SMatthias Ringwald static uint16_t prepare_handle_value(att_connection_t * att_connection,
980*591423b2SMatthias Ringwald                                      uint16_t handle,
981*591423b2SMatthias Ringwald                                      uint8_t *value,
982*591423b2SMatthias Ringwald                                      uint16_t value_len,
983*591423b2SMatthias Ringwald                                      uint8_t * response_buffer){
984*591423b2SMatthias Ringwald     little_endian_store_16(response_buffer, 1, handle);
985*591423b2SMatthias Ringwald     if (value_len > att_connection->mtu - 3){
986*591423b2SMatthias Ringwald         value_len = att_connection->mtu - 3;
987*591423b2SMatthias Ringwald     }
988*591423b2SMatthias Ringwald     memcpy(&response_buffer[3], value, value_len);
989*591423b2SMatthias Ringwald     return value_len + 3;
990*591423b2SMatthias Ringwald }
991*591423b2SMatthias Ringwald 
992*591423b2SMatthias Ringwald // MARK: ATT_HANDLE_VALUE_NOTIFICATION 0x1b
993*591423b2SMatthias Ringwald uint16_t att_prepare_handle_value_notification(att_connection_t * att_connection,
994*591423b2SMatthias Ringwald                                                uint16_t handle,
995*591423b2SMatthias Ringwald                                                uint8_t *value,
996*591423b2SMatthias Ringwald                                                uint16_t value_len,
997*591423b2SMatthias Ringwald                                                uint8_t * response_buffer){
998*591423b2SMatthias Ringwald 
999*591423b2SMatthias Ringwald     response_buffer[0] = ATT_HANDLE_VALUE_NOTIFICATION;
1000*591423b2SMatthias Ringwald     return prepare_handle_value(att_connection, handle, value, value_len, response_buffer);
1001*591423b2SMatthias Ringwald }
1002*591423b2SMatthias Ringwald 
1003*591423b2SMatthias Ringwald // MARK: ATT_HANDLE_VALUE_INDICATION 0x1d
1004*591423b2SMatthias Ringwald uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection,
1005*591423b2SMatthias Ringwald                                              uint16_t handle,
1006*591423b2SMatthias Ringwald                                              uint8_t *value,
1007*591423b2SMatthias Ringwald                                              uint16_t value_len,
1008*591423b2SMatthias Ringwald                                              uint8_t * response_buffer){
1009*591423b2SMatthias Ringwald 
1010*591423b2SMatthias Ringwald     response_buffer[0] = ATT_HANDLE_VALUE_INDICATION;
1011*591423b2SMatthias Ringwald     return prepare_handle_value(att_connection, handle, value, value_len, response_buffer);
1012*591423b2SMatthias Ringwald }
1013*591423b2SMatthias Ringwald 
1014*591423b2SMatthias Ringwald // MARK: Dispatcher
1015*591423b2SMatthias Ringwald uint16_t att_handle_request(att_connection_t * att_connection,
1016*591423b2SMatthias Ringwald                             uint8_t * request_buffer,
1017*591423b2SMatthias Ringwald                             uint16_t request_len,
1018*591423b2SMatthias Ringwald                             uint8_t * response_buffer){
1019*591423b2SMatthias Ringwald     uint16_t response_len = 0;
1020*591423b2SMatthias Ringwald     uint16_t response_buffer_size = att_connection->mtu;
1021*591423b2SMatthias Ringwald 
1022*591423b2SMatthias Ringwald     switch (request_buffer[0]){
1023*591423b2SMatthias Ringwald         case ATT_EXCHANGE_MTU_REQUEST:
1024*591423b2SMatthias Ringwald             response_len = handle_exchange_mtu_request(att_connection, request_buffer, request_len, response_buffer);
1025*591423b2SMatthias Ringwald             break;
1026*591423b2SMatthias Ringwald         case ATT_FIND_INFORMATION_REQUEST:
1027*591423b2SMatthias Ringwald             response_len = handle_find_information_request(att_connection, request_buffer, request_len,response_buffer, response_buffer_size);
1028*591423b2SMatthias Ringwald             break;
1029*591423b2SMatthias Ringwald         case ATT_FIND_BY_TYPE_VALUE_REQUEST:
1030*591423b2SMatthias Ringwald             response_len = handle_find_by_type_value_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1031*591423b2SMatthias Ringwald             break;
1032*591423b2SMatthias Ringwald         case ATT_READ_BY_TYPE_REQUEST:
1033*591423b2SMatthias Ringwald             response_len = handle_read_by_type_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1034*591423b2SMatthias Ringwald             break;
1035*591423b2SMatthias Ringwald         case ATT_READ_REQUEST:
1036*591423b2SMatthias Ringwald             response_len = handle_read_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1037*591423b2SMatthias Ringwald             break;
1038*591423b2SMatthias Ringwald         case ATT_READ_BLOB_REQUEST:
1039*591423b2SMatthias Ringwald             response_len = handle_read_blob_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1040*591423b2SMatthias Ringwald             break;
1041*591423b2SMatthias Ringwald         case ATT_READ_MULTIPLE_REQUEST:
1042*591423b2SMatthias Ringwald             response_len = handle_read_multiple_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1043*591423b2SMatthias Ringwald             break;
1044*591423b2SMatthias Ringwald         case ATT_READ_BY_GROUP_TYPE_REQUEST:
1045*591423b2SMatthias Ringwald             response_len = handle_read_by_group_type_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1046*591423b2SMatthias Ringwald             break;
1047*591423b2SMatthias Ringwald         case ATT_WRITE_REQUEST:
1048*591423b2SMatthias Ringwald             response_len = handle_write_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1049*591423b2SMatthias Ringwald             break;
1050*591423b2SMatthias Ringwald         case ATT_PREPARE_WRITE_REQUEST:
1051*591423b2SMatthias Ringwald             response_len = handle_prepare_write_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1052*591423b2SMatthias Ringwald             break;
1053*591423b2SMatthias Ringwald         case ATT_EXECUTE_WRITE_REQUEST:
1054*591423b2SMatthias Ringwald             response_len = handle_execute_write_request(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1055*591423b2SMatthias Ringwald             break;
1056*591423b2SMatthias Ringwald         case ATT_WRITE_COMMAND:
1057*591423b2SMatthias Ringwald             handle_write_command(att_connection, request_buffer, request_len, response_buffer, response_buffer_size);
1058*591423b2SMatthias Ringwald             break;
1059*591423b2SMatthias Ringwald         case ATT_SIGNED_WRITE_COMMAND:
1060*591423b2SMatthias Ringwald             log_info("handle_signed_write_command preprocessed by att_server.c");
1061*591423b2SMatthias Ringwald             break;
1062*591423b2SMatthias Ringwald         default:
1063*591423b2SMatthias Ringwald             log_info("Unhandled ATT Command: %02X, DATA: ", request_buffer[0]);
1064*591423b2SMatthias Ringwald             log_info_hexdump(&request_buffer[9], request_len-9);
1065*591423b2SMatthias Ringwald             break;
1066*591423b2SMatthias Ringwald     }
1067*591423b2SMatthias Ringwald     return response_len;
1068*591423b2SMatthias Ringwald }
1069*591423b2SMatthias Ringwald 
1070*591423b2SMatthias Ringwald #if 0
1071*591423b2SMatthias Ringwald 
1072*591423b2SMatthias Ringwald // test profile
1073*591423b2SMatthias Ringwald #include "profile.h"
1074*591423b2SMatthias Ringwald 
1075*591423b2SMatthias Ringwald int main(void){
1076*591423b2SMatthias Ringwald     int acl_buffer_size;
1077*591423b2SMatthias Ringwald     uint8_t acl_buffer[27];
1078*591423b2SMatthias Ringwald     att_set_db(profile_data);
1079*591423b2SMatthias Ringwald     att_dump_attributes();
1080*591423b2SMatthias Ringwald 
1081*591423b2SMatthias Ringwald     uint8_t uuid_1[] = { 0x00, 0x18};
1082*591423b2SMatthias Ringwald     acl_buffer_size = handle_find_by_type_value_request2(acl_buffer, 19, 0, 0xffff, 0x2800, 2, (uint8_t *) &uuid_1);
1083*591423b2SMatthias Ringwald     log_info_hexdump(acl_buffer, acl_buffer_size);
1084*591423b2SMatthias Ringwald 
1085*591423b2SMatthias Ringwald     uint8_t uuid_3[] = { 0x00, 0x2a};
1086*591423b2SMatthias Ringwald     acl_buffer_size = handle_read_by_type_request2(acl_buffer, 19, 0, 0xffff, 2, (uint8_t *) &uuid_3);
1087*591423b2SMatthias Ringwald     log_info_hexdump(acl_buffer, acl_buffer_size);
1088*591423b2SMatthias Ringwald 
1089*591423b2SMatthias Ringwald     acl_buffer_size = handle_find_by_type_value_request2(acl_buffer, 19, 0, 0xffff, 0x2800, 2, (uint8_t *) &uuid_1);
1090*591423b2SMatthias Ringwald     log_info_hexdump(acl_buffer, acl_buffer_size);
1091*591423b2SMatthias Ringwald 
1092*591423b2SMatthias Ringwald     uint8_t uuid_4[] = { 0x00, 0x28};
1093*591423b2SMatthias Ringwald     acl_buffer_size = handle_read_by_group_type_request2(acl_buffer, 20, 0, 0xffff, 2, (uint8_t *) &uuid_4);
1094*591423b2SMatthias Ringwald     log_info_hexdump(acl_buffer, acl_buffer_size);
1095*591423b2SMatthias Ringwald 
1096*591423b2SMatthias Ringwald     acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 0, 0xffff);
1097*591423b2SMatthias Ringwald     log_info_hexdump(acl_buffer, acl_buffer_size);
1098*591423b2SMatthias Ringwald     acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 3, 0xffff);
1099*591423b2SMatthias Ringwald     log_info_hexdump(acl_buffer, acl_buffer_size);
1100*591423b2SMatthias Ringwald     acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 5, 0xffff);
1101*591423b2SMatthias Ringwald     log_info_hexdump(acl_buffer, acl_buffer_size);
1102*591423b2SMatthias Ringwald 
1103*591423b2SMatthias Ringwald     acl_buffer_size = handle_read_request2(acl_buffer, 19, 0x0003);
1104*591423b2SMatthias Ringwald     log_info_hexdump(acl_buffer, acl_buffer_size);
1105*591423b2SMatthias Ringwald 
1106*591423b2SMatthias Ringwald     return 0;
1107*591423b2SMatthias Ringwald }
1108*591423b2SMatthias Ringwald #endif
1109