xref: /btstack/src/mesh/mesh_configuration_client.c (revision 1126947f35b036bbff3dcbd407fc40f9fee175aa)
1 /*
2  * Copyright (C) 2019 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "mesh_configuration_client.c"
39 
40 #include <string.h>
41 #include <stdio.h>
42 
43 #include "mesh/mesh_configuration_client.h"
44 
45 #include "bluetooth_company_id.h"
46 #include "btstack_debug.h"
47 #include "btstack_memory.h"
48 #include "btstack_util.h"
49 
50 #include "mesh/mesh_access.h"
51 #include "mesh/mesh_foundation.h"
52 #include "mesh/mesh_generic_model.h"
53 #include "mesh/mesh_keys.h"
54 #include "mesh/mesh_network.h"
55 #include "mesh/mesh_upper_transport.h"
56 
57 #define MESH_VENDOR_MODEL_SIZE 4
58 #define MESH_SIG_MODEL_SIZE    2
59 
60 // Mesh Composition Data Element iterator
61 
62 uint16_t mesh_subevent_configuration_composition_data_get_num_elements(const uint8_t * event, uint16_t size){
63     uint16_t pos = 16;
64     uint16_t num_elements = 0;
65 
66     while ((pos + 4) <= size){
67         // location descriptor
68         pos += 2;
69         uint8_t num_sig_model_ids = event[pos++];
70         uint8_t num_vendor_model_ids = event[pos++];
71         pos += (num_sig_model_ids + num_vendor_model_ids) * 2;
72         num_elements++;
73     }
74     return num_elements;
75 }
76 
77 void mesh_composition_data_iterator_init(mesh_composite_data_iterator_t * it, const uint8_t * elements, uint16_t size){
78     it->elements = elements;
79     it->size = size;
80     it->offset = 16;
81 }
82 
83 bool mesh_composition_data_iterator_has_next_element(mesh_composite_data_iterator_t * it){
84     uint16_t sig_model_list_size    = it->elements[it->offset + 2] * MESH_SIG_MODEL_SIZE;
85     uint16_t vendor_model_list_size = it->elements[it->offset + 3] * MESH_VENDOR_MODEL_SIZE;
86     uint16_t element_len =  2 + sig_model_list_size + vendor_model_list_size;
87 
88     return (it->offset + element_len) <= it->size;
89 }
90 
91 void mesh_composition_data_iterator_next_element(mesh_composite_data_iterator_t * it){
92     uint16_t sig_model_list_size    = it->elements[it->offset + 2] * MESH_SIG_MODEL_SIZE;
93     uint16_t vendor_model_list_size = it->elements[it->offset + 3] * MESH_VENDOR_MODEL_SIZE;
94     uint16_t element_len =  2 + sig_model_list_size + vendor_model_list_size;
95 
96     it->sig_model_iterator.models = &it->elements[it->offset + 4];
97     it->sig_model_iterator.size = sig_model_list_size;
98     it->sig_model_iterator.offset = 0;
99 
100     it->vendor_model_iterator.models = &it->elements[it->offset + 4 + it->sig_model_iterator.size];
101     it->vendor_model_iterator.size = vendor_model_list_size;
102     it->vendor_model_iterator.offset = 0;
103 
104     it->loc = little_endian_read_16(it->elements, it->offset);
105     it->offset += element_len;
106 }
107 
108 uint16_t mesh_composition_data_iterator_element_loc(mesh_composite_data_iterator_t * it){
109     return it->loc;
110 }
111 
112 bool mesh_composition_data_iterator_has_next_sig_model(mesh_composite_data_iterator_t * it){
113     return (it->sig_model_iterator.offset + MESH_SIG_MODEL_SIZE) <= it->sig_model_iterator.size;
114 }
115 
116 void mesh_composition_data_iterator_next_sig_model(mesh_composite_data_iterator_t * it){
117     it->sig_model_iterator.id = little_endian_read_16(it->sig_model_iterator.models, it->sig_model_iterator.offset);
118     it->sig_model_iterator.offset += 2;
119 }
120 
121 uint16_t mesh_composition_data_iterator_sig_model_id(mesh_composite_data_iterator_t * it){
122     return (uint16_t)it->sig_model_iterator.id;
123 }
124 
125 bool mesh_composition_data_iterator_has_next_vendor_model(mesh_composite_data_iterator_t * it){
126     return (it->vendor_model_iterator.offset + MESH_VENDOR_MODEL_SIZE) <= it->vendor_model_iterator.size;
127 }
128 
129 void mesh_composition_data_iterator_next_vendor_modeld(mesh_composite_data_iterator_t * it){
130     uint16_t vendor_id = little_endian_read_16(it->vendor_model_iterator.models, it->vendor_model_iterator.offset);
131     it->vendor_model_iterator.offset += 2;
132     uint16_t model_id = little_endian_read_16(it->vendor_model_iterator.models, it->vendor_model_iterator.offset);
133     it->vendor_model_iterator.offset += 2;
134     it->vendor_model_iterator.id = mesh_model_get_model_identifier(vendor_id, model_id);
135 }
136 
137 uint32_t mesh_composition_data_iterator_vendor_model_id(mesh_composite_data_iterator_t * it){
138     return it->vendor_model_iterator.id;
139 }
140 
141 // Configuration client messages
142 
143 static const mesh_access_message_t mesh_configuration_client_beacon_get = {
144         MESH_FOUNDATION_OPERATION_BEACON_GET, ""
145 };
146 static const mesh_access_message_t mesh_configuration_client_beacon_set = {
147         MESH_FOUNDATION_OPERATION_BEACON_SET, "1"
148 };
149 
150 
151 static const mesh_access_message_t mesh_configuration_client_composition_data_get = {
152         MESH_FOUNDATION_OPERATION_COMPOSITION_DATA_GET, "1"
153 };
154 
155 
156 static const mesh_access_message_t mesh_configuration_client_default_ttl_get = {
157         MESH_FOUNDATION_OPERATION_DEFAULT_TTL_GET, ""
158 };
159 static const mesh_access_message_t mesh_configuration_client_default_ttl_set = {
160         MESH_FOUNDATION_OPERATION_DEFAULT_TTL_SET, "1"
161 };
162 
163 
164 static const mesh_access_message_t mesh_configuration_client_gatt_proxy_get = {
165         MESH_FOUNDATION_OPERATION_GATT_PROXY_GET, ""
166 };
167 static const mesh_access_message_t mesh_configuration_client_gatt_proxy_set = {
168         MESH_FOUNDATION_OPERATION_GATT_PROXY_SET, "1"
169 };
170 
171 
172 static const mesh_access_message_t mesh_configuration_client_relay_get = {
173         MESH_FOUNDATION_OPERATION_RELAY_GET, ""
174 };
175 static const mesh_access_message_t mesh_configuration_client_relay_set = {
176         MESH_FOUNDATION_OPERATION_RELAY_SET, "11"
177 };
178 
179 #if 0
180 static const mesh_access_message_t mesh_configuration_client_publication_get = {
181         MESH_FOUNDATION_OPERATION_MODEL_PUBLICATION_GET, "2m"
182 };
183 static const mesh_access_message_t mesh_configuration_client_publication_set = {
184         MESH_FOUNDATION_OPERATION_MODEL_PUBLICATION_SET, "222111m"
185 };
186 static const mesh_access_message_t mesh_configuration_client_publication_virtual_address_set = {
187         MESH_FOUNDATION_OPERATION_MODEL_PUBLICATION_VIRTUAL_ADDRESS_SET, "2P2111m"
188 };
189 #endif
190 
191 
192 static void mesh_configuration_client_send_acknowledged(uint16_t src, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, mesh_pdu_t *pdu, uint32_t ack_opcode){
193     uint8_t  ttl  = mesh_foundation_default_ttl_get();
194     mesh_upper_transport_setup_access_pdu_header(pdu, netkey_index, appkey_index, ttl, src, dest, 0);
195     mesh_access_send_acknowledged_pdu(pdu, mesh_access_acknowledged_message_retransmissions(), ack_opcode);
196 }
197 
198 static uint8_t mesh_access_validate_envelop_params(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index){
199     btstack_assert(mesh_model != NULL);
200     // TODO: validate other params
201     UNUSED(dest);
202     UNUSED(netkey_index);
203     UNUSED(appkey_index);
204 
205     return ERROR_CODE_SUCCESS;
206 }
207 
208 uint8_t mesh_configuration_client_send_config_beacon_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index){
209     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
210     if (status != ERROR_CODE_SUCCESS) return status;
211 
212     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_beacon_get);
213     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
214 
215     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_BEACON_STATUS);
216     return ERROR_CODE_SUCCESS;
217 }
218 
219 uint8_t mesh_configuration_client_send_config_beacon_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint8_t beacon){
220     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
221     if (status != ERROR_CODE_SUCCESS) return status;
222 
223     if (beacon > 1) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
224 
225     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_beacon_set, beacon);
226     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
227 
228     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_BEACON_STATUS);
229     return ERROR_CODE_SUCCESS;
230 }
231 
232 uint8_t mesh_configuration_client_send_composition_data_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint8_t page){
233     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
234     if (status != ERROR_CODE_SUCCESS) return status;
235 
236     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_composition_data_get, page);
237     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
238 
239     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_COMPOSITION_DATA_GET);
240     return ERROR_CODE_SUCCESS;
241 }
242 
243 uint8_t mesh_configuration_client_send_default_ttl_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index){
244     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
245     if (status != ERROR_CODE_SUCCESS) return status;
246 
247     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_default_ttl_get);
248     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
249 
250     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_DEFAULT_TTL_GET);
251     return ERROR_CODE_SUCCESS;
252 }
253 
254 uint8_t mesh_configuration_client_send_default_ttl_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint8_t ttl){
255     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
256     if (status != ERROR_CODE_SUCCESS) return status;
257 
258     if (ttl == 0x01 || ttl >= 0x80) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
259 
260     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_default_ttl_set, ttl);
261     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
262 
263     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_DEFAULT_TTL_SET);
264     return ERROR_CODE_SUCCESS;
265 }
266 
267 uint8_t mesh_configuration_client_send_gatt_proxy_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index){
268     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
269     if (status != ERROR_CODE_SUCCESS) return status;
270 
271     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_gatt_proxy_get);
272     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
273 
274     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_GATT_PROXY_GET);
275     return ERROR_CODE_SUCCESS;
276 }
277 
278 uint8_t mesh_configuration_client_send_gatt_proxy_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint8_t gatt_proxy_state){
279     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
280     if (status != ERROR_CODE_SUCCESS) return status;
281 
282     if (gatt_proxy_state > 2) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
283 
284     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_gatt_proxy_set, gatt_proxy_state);
285     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
286 
287     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_GATT_PROXY_SET);
288     return ERROR_CODE_SUCCESS;
289 }
290 
291 uint8_t mesh_configuration_client_send_relay_get(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index){
292     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
293     if (status != ERROR_CODE_SUCCESS) return status;
294 
295     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_relay_get);
296     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
297 
298     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_RELAY_GET);
299     return ERROR_CODE_SUCCESS;
300 }
301 
302 uint8_t mesh_configuration_client_send_relay_set(mesh_model_t * mesh_model, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint8_t relay, uint8_t relay_retransmit_count, uint8_t relay_retransmit_interval_steps){
303     uint8_t status = mesh_access_validate_envelop_params(mesh_model, dest, netkey_index, appkey_index);
304     if (status != ERROR_CODE_SUCCESS) return status;
305 
306     if (relay_retransmit_count > 0x07) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
307     if (relay_retransmit_interval_steps > 0x1F) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
308 
309     mesh_network_pdu_t * network_pdu = mesh_access_setup_unsegmented_message(&mesh_configuration_client_relay_set, relay, (relay_retransmit_count << 5) | relay_retransmit_interval_steps);
310     if (!network_pdu) return BTSTACK_MEMORY_ALLOC_FAILED;
311 
312     mesh_configuration_client_send_acknowledged(mesh_access_get_element_address(mesh_model), dest, netkey_index, appkey_index, (mesh_pdu_t *) network_pdu, MESH_FOUNDATION_OPERATION_RELAY_SET);
313     return ERROR_CODE_SUCCESS;
314 }
315 
316 // Model Operations
317 static void mesh_configuration_client_composition_data_status_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
318     // Composition Data has variable of element descriptions, with two lists of model lists
319     // Pass raw data to application but provide convenient setters instead of parsing pdu here
320 
321     // reuse part of the mesh_network_t / mesh_transport_t struct to create event without memcpy or allocation
322     uint8_t * data = mesh_pdu_data(pdu);
323     uint8_t * event = &data[-6];
324 
325     // TODO: list of element descriptions, see Table 4.4
326     int pos = 0;
327     event[pos++] = HCI_EVENT_MESH_META;
328     // Composite Data might be larger than 251 bytes - in this case only lower 8 bit are stored here. packet size is correct
329     event[pos++] = (uint8_t) (6 + mesh_pdu_len(pdu));
330     event[pos++] = MESH_SUBEVENT_CONFIGURATION_COMPOSITION_DATA;
331     // dest
332     little_endian_store_16(event, pos, mesh_pdu_src(pdu));
333     pos += 2;
334     event[pos++] = ERROR_CODE_SUCCESS;
335 
336 
337     (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos);
338     mesh_access_message_processed(pdu);
339 }
340 
341 uint8_t mesh_subevent_configuration_composition_data_get_cid(const uint8_t * event){
342     return little_endian_read_16(event, 1);
343 }
344 
345 uint8_t mesh_subevent_configuration_composition_data_get_pid(const uint8_t * event){
346     return little_endian_read_16(event, 3);
347 }
348 
349 uint8_t mesh_subevent_configuration_composition_data_get_vid(const uint8_t * event){
350     return little_endian_read_16(event, 5);
351 }
352 
353 uint8_t mesh_subevent_configuration_composition_data_get_crpl(const uint8_t * event){
354     return little_endian_read_16(event, 7);
355 }
356 
357 uint8_t mesh_subevent_configuration_composition_data_get_features(const uint8_t * event){
358     return little_endian_read_16(event, 9);
359 }
360 
361 
362 static inline void mesh_configuration_client_handle_uint8_value(mesh_model_t *mesh_model, mesh_pdu_t * pdu, uint8_t subevent_type){
363     mesh_access_parser_state_t parser;
364     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
365 
366     uint8_t value = mesh_access_parser_get_u8(&parser);
367 
368     uint8_t event[7] = {HCI_EVENT_MESH_META, 5, subevent_type};
369     int pos = 3;
370     // dest
371     little_endian_store_16(event, pos, mesh_pdu_src(pdu));
372     pos += 2;
373     event[pos++] = ERROR_CODE_SUCCESS;
374     event[pos++] = value;
375 
376     (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos);
377     mesh_access_message_processed(pdu);
378 }
379 
380 static void mesh_configuration_client_beacon_status_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
381     mesh_configuration_client_handle_uint8_value(mesh_model, pdu, MESH_SUBEVENT_CONFIGURATION_BEACON);
382 }
383 
384 static void mesh_configuration_client_default_ttl_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
385     mesh_configuration_client_handle_uint8_value(mesh_model, pdu, MESH_SUBEVENT_CONFIGURATION_DEFAULT_TTL);
386 }
387 
388 static void mesh_configuration_client_gatt_proxy_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
389     mesh_configuration_client_handle_uint8_value(mesh_model, pdu, MESH_SUBEVENT_CONFIGURATION_GATT_PROXY);
390 }
391 
392 static void mesh_configuration_client_relay_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
393     mesh_access_parser_state_t parser;
394     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
395 
396     uint8_t relay = mesh_access_parser_get_u8(&parser);
397     uint8_t retransmition = mesh_access_parser_get_u8(&parser);
398 
399     uint8_t event[9] = {HCI_EVENT_MESH_META, 5, MESH_SUBEVENT_CONFIGURATION_RELAY};
400     int pos = 3;
401     // dest
402     little_endian_store_16(event, pos, mesh_pdu_src(pdu));
403     pos += 2;
404     event[pos++] = ERROR_CODE_SUCCESS;
405     event[pos++] = relay;
406     event[pos++] = (retransmition >> 5) + 1;
407     event[pos++] = ((retransmition & 0x07) + 1) * 10;
408 
409     (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos);
410     mesh_access_message_processed(pdu);
411 }
412 
413 const static mesh_operation_t mesh_configuration_client_model_operations[] = {
414     { MESH_FOUNDATION_OPERATION_BEACON_STATUS,            1, mesh_configuration_client_beacon_status_handler },
415     { MESH_FOUNDATION_OPERATION_COMPOSITION_DATA_STATUS, 10, mesh_configuration_client_composition_data_status_handler },
416     { MESH_FOUNDATION_OPERATION_DEFAULT_TTL_STATUS,       1, mesh_configuration_client_default_ttl_handler },
417     { MESH_FOUNDATION_OPERATION_GATT_PROXY_STATUS,        1, mesh_configuration_client_gatt_proxy_handler },
418     { MESH_FOUNDATION_OPERATION_RELAY_STATUS,             2, mesh_configuration_client_relay_handler },
419     { 0, 0, NULL }
420 };
421 
422 const mesh_operation_t * mesh_configuration_client_get_operations(void){
423     return mesh_configuration_client_model_operations;
424 }
425 
426 void mesh_configuration_client_register_packet_handler(mesh_model_t *configuration_client_model, btstack_packet_handler_t events_packet_handler){
427     btstack_assert(events_packet_handler != NULL);
428     btstack_assert(configuration_client_model != NULL);
429 
430     configuration_client_model->model_packet_handler = events_packet_handler;
431 }
432 
433