xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/src/ble_att.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *  http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <stddef.h>
21 #include <errno.h>
22 #include "ble_hs_priv.h"
23 
24 static uint16_t ble_att_preferred_mtu_val;
25 
26 /** Dispatch table for incoming ATT requests.  Sorted by op code. */
27 typedef int ble_att_rx_fn(uint16_t conn_handle, struct os_mbuf **om);
28 struct ble_att_rx_dispatch_entry {
29     uint8_t bde_op;
30     ble_att_rx_fn *bde_fn;
31 };
32 
33 /** Dispatch table for incoming ATT commands.  Must be ordered by op code. */
34 static const struct ble_att_rx_dispatch_entry ble_att_rx_dispatch[] = {
35     { BLE_ATT_OP_ERROR_RSP,            ble_att_clt_rx_error },
36     { BLE_ATT_OP_MTU_REQ,              ble_att_svr_rx_mtu },
37     { BLE_ATT_OP_MTU_RSP,              ble_att_clt_rx_mtu },
38     { BLE_ATT_OP_FIND_INFO_REQ,        ble_att_svr_rx_find_info },
39     { BLE_ATT_OP_FIND_INFO_RSP,        ble_att_clt_rx_find_info },
40     { BLE_ATT_OP_FIND_TYPE_VALUE_REQ,  ble_att_svr_rx_find_type_value },
41     { BLE_ATT_OP_FIND_TYPE_VALUE_RSP,  ble_att_clt_rx_find_type_value },
42     { BLE_ATT_OP_READ_TYPE_REQ,        ble_att_svr_rx_read_type },
43     { BLE_ATT_OP_READ_TYPE_RSP,        ble_att_clt_rx_read_type },
44     { BLE_ATT_OP_READ_REQ,             ble_att_svr_rx_read },
45     { BLE_ATT_OP_READ_RSP,             ble_att_clt_rx_read },
46     { BLE_ATT_OP_READ_BLOB_REQ,        ble_att_svr_rx_read_blob },
47     { BLE_ATT_OP_READ_BLOB_RSP,        ble_att_clt_rx_read_blob },
48     { BLE_ATT_OP_READ_MULT_REQ,        ble_att_svr_rx_read_mult },
49     { BLE_ATT_OP_READ_MULT_RSP,        ble_att_clt_rx_read_mult },
50     { BLE_ATT_OP_READ_GROUP_TYPE_REQ,  ble_att_svr_rx_read_group_type },
51     { BLE_ATT_OP_READ_GROUP_TYPE_RSP,  ble_att_clt_rx_read_group_type },
52     { BLE_ATT_OP_WRITE_REQ,            ble_att_svr_rx_write },
53     { BLE_ATT_OP_WRITE_RSP,            ble_att_clt_rx_write },
54     { BLE_ATT_OP_PREP_WRITE_REQ,       ble_att_svr_rx_prep_write },
55     { BLE_ATT_OP_PREP_WRITE_RSP,       ble_att_clt_rx_prep_write },
56     { BLE_ATT_OP_EXEC_WRITE_REQ,       ble_att_svr_rx_exec_write },
57     { BLE_ATT_OP_EXEC_WRITE_RSP,       ble_att_clt_rx_exec_write },
58     { BLE_ATT_OP_NOTIFY_REQ,           ble_att_svr_rx_notify },
59     { BLE_ATT_OP_INDICATE_REQ,         ble_att_svr_rx_indicate },
60     { BLE_ATT_OP_INDICATE_RSP,         ble_att_clt_rx_indicate },
61     { BLE_ATT_OP_WRITE_CMD,            ble_att_svr_rx_write_no_rsp },
62 };
63 
64 #define BLE_ATT_RX_DISPATCH_SZ \
65     (sizeof ble_att_rx_dispatch / sizeof ble_att_rx_dispatch[0])
66 
67 STATS_SECT_DECL(ble_att_stats) ble_att_stats;
68 STATS_NAME_START(ble_att_stats)
STATS_NAME(ble_att_stats,error_rsp_rx)69     STATS_NAME(ble_att_stats, error_rsp_rx)
70     STATS_NAME(ble_att_stats, error_rsp_tx)
71     STATS_NAME(ble_att_stats, mtu_req_rx)
72     STATS_NAME(ble_att_stats, mtu_req_tx)
73     STATS_NAME(ble_att_stats, mtu_rsp_rx)
74     STATS_NAME(ble_att_stats, mtu_rsp_tx)
75     STATS_NAME(ble_att_stats, find_info_req_rx)
76     STATS_NAME(ble_att_stats, find_info_req_tx)
77     STATS_NAME(ble_att_stats, find_info_rsp_rx)
78     STATS_NAME(ble_att_stats, find_info_rsp_tx)
79     STATS_NAME(ble_att_stats, find_type_value_req_rx)
80     STATS_NAME(ble_att_stats, find_type_value_req_tx)
81     STATS_NAME(ble_att_stats, find_type_value_rsp_rx)
82     STATS_NAME(ble_att_stats, find_type_value_rsp_tx)
83     STATS_NAME(ble_att_stats, read_type_req_rx)
84     STATS_NAME(ble_att_stats, read_type_req_tx)
85     STATS_NAME(ble_att_stats, read_type_rsp_rx)
86     STATS_NAME(ble_att_stats, read_type_rsp_tx)
87     STATS_NAME(ble_att_stats, read_req_rx)
88     STATS_NAME(ble_att_stats, read_req_tx)
89     STATS_NAME(ble_att_stats, read_rsp_rx)
90     STATS_NAME(ble_att_stats, read_rsp_tx)
91     STATS_NAME(ble_att_stats, read_blob_req_rx)
92     STATS_NAME(ble_att_stats, read_blob_req_tx)
93     STATS_NAME(ble_att_stats, read_blob_rsp_rx)
94     STATS_NAME(ble_att_stats, read_blob_rsp_tx)
95     STATS_NAME(ble_att_stats, read_mult_req_rx)
96     STATS_NAME(ble_att_stats, read_mult_req_tx)
97     STATS_NAME(ble_att_stats, read_mult_rsp_rx)
98     STATS_NAME(ble_att_stats, read_mult_rsp_tx)
99     STATS_NAME(ble_att_stats, read_group_type_req_rx)
100     STATS_NAME(ble_att_stats, read_group_type_req_tx)
101     STATS_NAME(ble_att_stats, read_group_type_rsp_rx)
102     STATS_NAME(ble_att_stats, read_group_type_rsp_tx)
103     STATS_NAME(ble_att_stats, write_req_rx)
104     STATS_NAME(ble_att_stats, write_req_tx)
105     STATS_NAME(ble_att_stats, write_rsp_rx)
106     STATS_NAME(ble_att_stats, write_rsp_tx)
107     STATS_NAME(ble_att_stats, prep_write_req_rx)
108     STATS_NAME(ble_att_stats, prep_write_req_tx)
109     STATS_NAME(ble_att_stats, prep_write_rsp_rx)
110     STATS_NAME(ble_att_stats, prep_write_rsp_tx)
111     STATS_NAME(ble_att_stats, exec_write_req_rx)
112     STATS_NAME(ble_att_stats, exec_write_req_tx)
113     STATS_NAME(ble_att_stats, exec_write_rsp_rx)
114     STATS_NAME(ble_att_stats, exec_write_rsp_tx)
115     STATS_NAME(ble_att_stats, notify_req_rx)
116     STATS_NAME(ble_att_stats, notify_req_tx)
117     STATS_NAME(ble_att_stats, indicate_req_rx)
118     STATS_NAME(ble_att_stats, indicate_req_tx)
119     STATS_NAME(ble_att_stats, indicate_rsp_rx)
120     STATS_NAME(ble_att_stats, indicate_rsp_tx)
121     STATS_NAME(ble_att_stats, write_cmd_rx)
122     STATS_NAME(ble_att_stats, write_cmd_tx)
123 STATS_NAME_END(ble_att_stats)
124 
125 static const struct ble_att_rx_dispatch_entry *
126 ble_att_rx_dispatch_entry_find(uint8_t op)
127 {
128     const struct ble_att_rx_dispatch_entry *entry;
129     int i;
130 
131     for (i = 0; i < BLE_ATT_RX_DISPATCH_SZ; i++) {
132         entry = ble_att_rx_dispatch + i;
133         if (entry->bde_op == op) {
134             return entry;
135         }
136 
137         if (entry->bde_op > op) {
138             break;
139         }
140     }
141 
142     return NULL;
143 }
144 
145 int
ble_att_conn_chan_find(uint16_t conn_handle,struct ble_hs_conn ** out_conn,struct ble_l2cap_chan ** out_chan)146 ble_att_conn_chan_find(uint16_t conn_handle, struct ble_hs_conn **out_conn,
147                        struct ble_l2cap_chan **out_chan)
148 {
149     return ble_hs_misc_conn_chan_find(conn_handle, BLE_L2CAP_CID_ATT,
150                                       out_conn, out_chan);
151 }
152 
153 void
ble_att_inc_tx_stat(uint8_t att_op)154 ble_att_inc_tx_stat(uint8_t att_op)
155 {
156     switch (att_op) {
157     case BLE_ATT_OP_ERROR_RSP:
158         STATS_INC(ble_att_stats, error_rsp_tx);
159         break;
160 
161     case BLE_ATT_OP_MTU_REQ:
162         STATS_INC(ble_att_stats, mtu_req_tx);
163         break;
164 
165     case BLE_ATT_OP_MTU_RSP:
166         STATS_INC(ble_att_stats, mtu_rsp_tx);
167         break;
168 
169     case BLE_ATT_OP_FIND_INFO_REQ:
170         STATS_INC(ble_att_stats, find_info_req_tx);
171         break;
172 
173     case BLE_ATT_OP_FIND_INFO_RSP:
174         STATS_INC(ble_att_stats, find_info_rsp_tx);
175         break;
176 
177     case BLE_ATT_OP_FIND_TYPE_VALUE_REQ:
178         STATS_INC(ble_att_stats, find_type_value_req_tx);
179         break;
180 
181     case BLE_ATT_OP_FIND_TYPE_VALUE_RSP:
182         STATS_INC(ble_att_stats, find_type_value_rsp_tx);
183         break;
184 
185     case BLE_ATT_OP_READ_TYPE_REQ:
186         STATS_INC(ble_att_stats, read_type_req_tx);
187         break;
188 
189     case BLE_ATT_OP_READ_TYPE_RSP:
190         STATS_INC(ble_att_stats, read_type_rsp_tx);
191         break;
192 
193     case BLE_ATT_OP_READ_REQ:
194         STATS_INC(ble_att_stats, read_req_tx);
195         break;
196 
197     case BLE_ATT_OP_READ_RSP:
198         STATS_INC(ble_att_stats, read_rsp_tx);
199         break;
200 
201     case BLE_ATT_OP_READ_BLOB_REQ:
202         STATS_INC(ble_att_stats, read_blob_req_tx);
203         break;
204 
205     case BLE_ATT_OP_READ_BLOB_RSP:
206         STATS_INC(ble_att_stats, read_blob_rsp_tx);
207         break;
208 
209     case BLE_ATT_OP_READ_MULT_REQ:
210         STATS_INC(ble_att_stats, read_mult_req_tx);
211         break;
212 
213     case BLE_ATT_OP_READ_MULT_RSP:
214         STATS_INC(ble_att_stats, read_mult_rsp_tx);
215         break;
216 
217     case BLE_ATT_OP_READ_GROUP_TYPE_REQ:
218         STATS_INC(ble_att_stats, read_group_type_req_tx);
219         break;
220 
221     case BLE_ATT_OP_READ_GROUP_TYPE_RSP:
222         STATS_INC(ble_att_stats, read_group_type_rsp_tx);
223         break;
224 
225     case BLE_ATT_OP_WRITE_REQ:
226         STATS_INC(ble_att_stats, write_req_tx);
227         break;
228 
229     case BLE_ATT_OP_WRITE_RSP:
230         STATS_INC(ble_att_stats, write_rsp_tx);
231         break;
232 
233     case BLE_ATT_OP_PREP_WRITE_REQ:
234         STATS_INC(ble_att_stats, prep_write_req_tx);
235         break;
236 
237     case BLE_ATT_OP_PREP_WRITE_RSP:
238         STATS_INC(ble_att_stats, prep_write_rsp_tx);
239         break;
240 
241     case BLE_ATT_OP_EXEC_WRITE_REQ:
242         STATS_INC(ble_att_stats, exec_write_req_tx);
243         break;
244 
245     case BLE_ATT_OP_EXEC_WRITE_RSP:
246         STATS_INC(ble_att_stats, exec_write_rsp_tx);
247         break;
248 
249     case BLE_ATT_OP_NOTIFY_REQ:
250         STATS_INC(ble_att_stats, notify_req_tx);
251         break;
252 
253     case BLE_ATT_OP_INDICATE_REQ:
254         STATS_INC(ble_att_stats, indicate_req_tx);
255         break;
256 
257     case BLE_ATT_OP_INDICATE_RSP:
258         STATS_INC(ble_att_stats, indicate_rsp_tx);
259         break;
260 
261     case BLE_ATT_OP_WRITE_CMD:
262         STATS_INC(ble_att_stats, write_cmd_tx);
263         break;
264 
265     default:
266         break;
267     }
268 }
269 
270 static void
ble_att_inc_rx_stat(uint8_t att_op)271 ble_att_inc_rx_stat(uint8_t att_op)
272 {
273     switch (att_op) {
274     case BLE_ATT_OP_ERROR_RSP:
275         STATS_INC(ble_att_stats, error_rsp_rx);
276         break;
277 
278     case BLE_ATT_OP_MTU_REQ:
279         STATS_INC(ble_att_stats, mtu_req_rx);
280         break;
281 
282     case BLE_ATT_OP_MTU_RSP:
283         STATS_INC(ble_att_stats, mtu_rsp_rx);
284         break;
285 
286     case BLE_ATT_OP_FIND_INFO_REQ:
287         STATS_INC(ble_att_stats, find_info_req_rx);
288         break;
289 
290     case BLE_ATT_OP_FIND_INFO_RSP:
291         STATS_INC(ble_att_stats, find_info_rsp_rx);
292         break;
293 
294     case BLE_ATT_OP_FIND_TYPE_VALUE_REQ:
295         STATS_INC(ble_att_stats, find_type_value_req_rx);
296         break;
297 
298     case BLE_ATT_OP_FIND_TYPE_VALUE_RSP:
299         STATS_INC(ble_att_stats, find_type_value_rsp_rx);
300         break;
301 
302     case BLE_ATT_OP_READ_TYPE_REQ:
303         STATS_INC(ble_att_stats, read_type_req_rx);
304         break;
305 
306     case BLE_ATT_OP_READ_TYPE_RSP:
307         STATS_INC(ble_att_stats, read_type_rsp_rx);
308         break;
309 
310     case BLE_ATT_OP_READ_REQ:
311         STATS_INC(ble_att_stats, read_req_rx);
312         break;
313 
314     case BLE_ATT_OP_READ_RSP:
315         STATS_INC(ble_att_stats, read_rsp_rx);
316         break;
317 
318     case BLE_ATT_OP_READ_BLOB_REQ:
319         STATS_INC(ble_att_stats, read_blob_req_rx);
320         break;
321 
322     case BLE_ATT_OP_READ_BLOB_RSP:
323         STATS_INC(ble_att_stats, read_blob_rsp_rx);
324         break;
325 
326     case BLE_ATT_OP_READ_MULT_REQ:
327         STATS_INC(ble_att_stats, read_mult_req_rx);
328         break;
329 
330     case BLE_ATT_OP_READ_MULT_RSP:
331         STATS_INC(ble_att_stats, read_mult_rsp_rx);
332         break;
333 
334     case BLE_ATT_OP_READ_GROUP_TYPE_REQ:
335         STATS_INC(ble_att_stats, read_group_type_req_rx);
336         break;
337 
338     case BLE_ATT_OP_READ_GROUP_TYPE_RSP:
339         STATS_INC(ble_att_stats, read_group_type_rsp_rx);
340         break;
341 
342     case BLE_ATT_OP_WRITE_REQ:
343         STATS_INC(ble_att_stats, write_req_rx);
344         break;
345 
346     case BLE_ATT_OP_WRITE_RSP:
347         STATS_INC(ble_att_stats, write_rsp_rx);
348         break;
349 
350     case BLE_ATT_OP_PREP_WRITE_REQ:
351         STATS_INC(ble_att_stats, prep_write_req_rx);
352         break;
353 
354     case BLE_ATT_OP_PREP_WRITE_RSP:
355         STATS_INC(ble_att_stats, prep_write_rsp_rx);
356         break;
357 
358     case BLE_ATT_OP_EXEC_WRITE_REQ:
359         STATS_INC(ble_att_stats, exec_write_req_rx);
360         break;
361 
362     case BLE_ATT_OP_EXEC_WRITE_RSP:
363         STATS_INC(ble_att_stats, exec_write_rsp_rx);
364         break;
365 
366     case BLE_ATT_OP_NOTIFY_REQ:
367         STATS_INC(ble_att_stats, notify_req_rx);
368         break;
369 
370     case BLE_ATT_OP_INDICATE_REQ:
371         STATS_INC(ble_att_stats, indicate_req_rx);
372         break;
373 
374     case BLE_ATT_OP_INDICATE_RSP:
375         STATS_INC(ble_att_stats, indicate_rsp_rx);
376         break;
377 
378     case BLE_ATT_OP_WRITE_CMD:
379         STATS_INC(ble_att_stats, write_cmd_rx);
380         break;
381 
382     default:
383         break;
384     }
385 }
386 
387 void
ble_att_truncate_to_mtu(const struct ble_l2cap_chan * att_chan,struct os_mbuf * txom)388 ble_att_truncate_to_mtu(const struct ble_l2cap_chan *att_chan,
389                         struct os_mbuf *txom)
390 {
391     int32_t extra_len;
392     uint16_t mtu;
393 
394     mtu = ble_att_chan_mtu(att_chan);
395     extra_len = OS_MBUF_PKTLEN(txom) - mtu;
396     if (extra_len > 0) {
397         os_mbuf_adj(txom, -extra_len);
398     }
399 }
400 
401 uint16_t
ble_att_mtu(uint16_t conn_handle)402 ble_att_mtu(uint16_t conn_handle)
403 {
404     struct ble_l2cap_chan *chan;
405     struct ble_hs_conn *conn;
406     uint16_t mtu;
407     int rc;
408 
409     ble_hs_lock();
410 
411     rc = ble_att_conn_chan_find(conn_handle, &conn, &chan);
412     if (rc == 0) {
413         mtu = ble_att_chan_mtu(chan);
414     } else {
415         mtu = 0;
416     }
417 
418     ble_hs_unlock();
419 
420     return mtu;
421 }
422 
423 void
ble_att_set_peer_mtu(struct ble_l2cap_chan * chan,uint16_t peer_mtu)424 ble_att_set_peer_mtu(struct ble_l2cap_chan *chan, uint16_t peer_mtu)
425 {
426     if (peer_mtu < BLE_ATT_MTU_DFLT) {
427         peer_mtu = BLE_ATT_MTU_DFLT;
428     }
429 
430     chan->peer_mtu = peer_mtu;
431 }
432 
433 uint16_t
ble_att_chan_mtu(const struct ble_l2cap_chan * chan)434 ble_att_chan_mtu(const struct ble_l2cap_chan *chan)
435 {
436     uint16_t mtu;
437 
438     /* If either side has not exchanged MTU size, use the default.  Otherwise,
439      * use the lesser of the two exchanged values.
440      */
441     if (!(ble_l2cap_is_mtu_req_sent(chan)) ||
442         chan->peer_mtu == 0) {
443 
444         mtu = BLE_ATT_MTU_DFLT;
445     } else {
446         mtu = min(chan->my_mtu, chan->peer_mtu);
447     }
448 
449     BLE_HS_DBG_ASSERT(mtu >= BLE_ATT_MTU_DFLT);
450 
451     return mtu;
452 }
453 
454 static void
ble_att_rx_handle_unknown_request(uint8_t op,uint16_t conn_handle,struct os_mbuf ** om)455 ble_att_rx_handle_unknown_request(uint8_t op, uint16_t conn_handle,
456                                   struct os_mbuf **om)
457 {
458     /* If this is command (bit6 is set to 1), do nothing */
459     if (op & 0x40) {
460         return;
461     }
462 
463     os_mbuf_adj(*om, OS_MBUF_PKTLEN(*om));
464     ble_att_svr_tx_error_rsp(conn_handle, *om, op, 0,
465                              BLE_ATT_ERR_REQ_NOT_SUPPORTED);
466 
467     *om = NULL;
468 }
469 
470 static int
ble_att_rx(struct ble_l2cap_chan * chan)471 ble_att_rx(struct ble_l2cap_chan *chan)
472 {
473     const struct ble_att_rx_dispatch_entry *entry;
474     uint8_t op;
475     uint16_t conn_handle;
476     struct os_mbuf **om;
477     int rc;
478 
479     conn_handle = ble_l2cap_get_conn_handle(chan);
480     if (conn_handle == BLE_HS_CONN_HANDLE_NONE) {
481         return BLE_HS_ENOTCONN;
482     }
483 
484     om = &chan->rx_buf;
485     BLE_HS_DBG_ASSERT(*om != NULL);
486 
487     rc = os_mbuf_copydata(*om, 0, 1, &op);
488     if (rc != 0) {
489         return BLE_HS_EMSGSIZE;
490     }
491 
492     entry = ble_att_rx_dispatch_entry_find(op);
493     if (entry == NULL) {
494         ble_att_rx_handle_unknown_request(op, conn_handle, om);
495         return BLE_HS_ENOTSUP;
496     }
497 
498     ble_att_inc_rx_stat(op);
499 
500     /* Strip L2CAP ATT header from the front of the mbuf. */
501     os_mbuf_adj(*om, 1);
502 
503     rc = entry->bde_fn(conn_handle, om);
504     if (rc != 0) {
505         if (rc == BLE_HS_ENOTSUP) {
506             ble_att_rx_handle_unknown_request(op, conn_handle, om);
507         }
508         return rc;
509     }
510 
511     return 0;
512 }
513 
514 uint16_t
ble_att_preferred_mtu(void)515 ble_att_preferred_mtu(void)
516 {
517     return ble_att_preferred_mtu_val;
518 }
519 
520 int
ble_att_set_preferred_mtu(uint16_t mtu)521 ble_att_set_preferred_mtu(uint16_t mtu)
522 {
523     struct ble_l2cap_chan *chan;
524     struct ble_hs_conn *conn;
525     int i;
526 
527     if (mtu < BLE_ATT_MTU_DFLT) {
528         return BLE_HS_EINVAL;
529     }
530     if (mtu > BLE_ATT_MTU_MAX) {
531         return BLE_HS_EINVAL;
532     }
533 
534     ble_att_preferred_mtu_val = mtu;
535 
536     /* Set my_mtu for established connections that haven't exchanged. */
537     ble_hs_lock();
538 
539     i = 0;
540     while ((conn = ble_hs_conn_find_by_idx(i)) != NULL) {
541         chan = ble_hs_conn_chan_find_by_scid(conn, BLE_L2CAP_CID_ATT);
542         BLE_HS_DBG_ASSERT(chan != NULL);
543 
544         if (!(chan->flags & BLE_L2CAP_CHAN_F_TXED_MTU)) {
545             chan->my_mtu = mtu;
546         }
547 
548         i++;
549     }
550 
551     ble_hs_unlock();
552 
553     return 0;
554 }
555 
556 struct ble_l2cap_chan *
ble_att_create_chan(uint16_t conn_handle)557 ble_att_create_chan(uint16_t conn_handle)
558 {
559     struct ble_l2cap_chan *chan;
560 
561     chan = ble_l2cap_chan_alloc(conn_handle);
562     if (chan == NULL) {
563         return NULL;
564     }
565 
566     chan->scid = BLE_L2CAP_CID_ATT;
567     chan->dcid = BLE_L2CAP_CID_ATT;
568     chan->my_mtu = ble_att_preferred_mtu_val;
569     chan->rx_fn = ble_att_rx;
570 
571     return chan;
572 }
573 
574 int
ble_att_init(void)575 ble_att_init(void)
576 {
577     int rc;
578 
579     ble_att_preferred_mtu_val = MYNEWT_VAL(BLE_ATT_PREFERRED_MTU);
580 
581     rc = stats_init_and_reg(
582         STATS_HDR(ble_att_stats), STATS_SIZE_INIT_PARMS(ble_att_stats,
583         STATS_SIZE_32), STATS_NAME_INIT_PARMS(ble_att_stats), "ble_att");
584     if (rc != 0) {
585         return BLE_HS_EOS;
586     }
587 
588     return 0;
589 }
590