xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/src/ble_hs_flow.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 "syscfg/syscfg.h"
21 #include "nimble/ble_hci_trans.h"
22 #include "ble_hs_priv.h"
23 
24 #if MYNEWT_VAL(BLE_HS_FLOW_CTRL)
25 
26 #define BLE_HS_FLOW_ITVL_TICKS  \
27     ble_npl_time_ms_to_ticks32(MYNEWT_VAL(BLE_HS_FLOW_CTRL_ITVL))
28 
29 /**
30  * The number of freed buffers since the most-recent
31  * number-of-completed-packets event was sent.  This is used to determine if an
32  * immediate event transmission is required.
33  */
34 static uint16_t ble_hs_flow_num_completed_pkts;
35 
36 /** Periodically sends number-of-completed-packets events.  */
37 static struct ble_npl_callout ble_hs_flow_timer;
38 
39 static ble_npl_event_fn ble_hs_flow_event_cb;
40 
41 static struct ble_npl_event ble_hs_flow_ev;
42 
43 static int
ble_hs_flow_tx_num_comp_pkts(void)44 ble_hs_flow_tx_num_comp_pkts(void)
45 {
46     uint8_t buf[
47         BLE_HCI_HOST_NUM_COMP_PKTS_HDR_LEN +
48         BLE_HCI_HOST_NUM_COMP_PKTS_ENT_LEN
49     ];
50     struct hci_host_num_comp_pkts_entry entry;
51     struct ble_hs_conn *conn;
52     int rc;
53 
54     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
55 
56     /* For each connection with completed packets, send a separate
57      * host-number-of-completed-packets command.
58      */
59     for (conn = ble_hs_conn_first();
60          conn != NULL;
61          conn = SLIST_NEXT(conn, bhc_next)) {
62 
63         if (conn->bhc_completed_pkts > 0) {
64             /* Only specify one connection per command. */
65             buf[0] = 1;
66 
67             /* Append entry for this connection. */
68             entry.conn_handle = conn->bhc_handle;
69             entry.num_pkts = conn->bhc_completed_pkts;
70             rc = ble_hs_hci_cmd_build_host_num_comp_pkts_entry(
71                 &entry,
72                 buf + BLE_HCI_HOST_NUM_COMP_PKTS_HDR_LEN,
73                 sizeof buf - BLE_HCI_HOST_NUM_COMP_PKTS_HDR_LEN);
74             BLE_HS_DBG_ASSERT(rc == 0);
75 
76             conn->bhc_completed_pkts = 0;
77 
78             /* The host-number-of-completed-packets command does not elicit a
79              * response from the controller, so don't use the normal blocking
80              * HCI API when sending it.
81              */
82             rc = ble_hs_hci_cmd_send_buf(
83                 BLE_HCI_OP(BLE_HCI_OGF_CTLR_BASEBAND,
84                            BLE_HCI_OCF_CB_HOST_NUM_COMP_PKTS),
85                 buf, sizeof(buf));
86             if (rc != 0) {
87                 return rc;
88             }
89         }
90     }
91 
92     return 0;
93 }
94 
95 static void
ble_hs_flow_event_cb(struct ble_npl_event * ev)96 ble_hs_flow_event_cb(struct ble_npl_event *ev)
97 {
98     int rc;
99 
100     ble_hs_lock();
101 
102     if (ble_hs_flow_num_completed_pkts > 0) {
103         rc = ble_hs_flow_tx_num_comp_pkts();
104         if (rc != 0) {
105             ble_hs_sched_reset(rc);
106         }
107 
108         ble_hs_flow_num_completed_pkts = 0;
109     }
110 
111     ble_hs_unlock();
112 }
113 
114 static void
ble_hs_flow_inc_completed_pkts(struct ble_hs_conn * conn)115 ble_hs_flow_inc_completed_pkts(struct ble_hs_conn *conn)
116 {
117     uint16_t num_free;
118 
119     int rc;
120 
121     BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
122 
123     conn->bhc_completed_pkts++;
124     ble_hs_flow_num_completed_pkts++;
125 
126     if (ble_hs_flow_num_completed_pkts > MYNEWT_VAL(BLE_ACL_BUF_COUNT)) {
127         ble_hs_sched_reset(BLE_HS_ECONTROLLER);
128         return;
129     }
130 
131     /* If the number of free buffers is at or below the configured threshold,
132      * send an immediate number-of-copmleted-packets event.
133      */
134     num_free = MYNEWT_VAL(BLE_ACL_BUF_COUNT) - ble_hs_flow_num_completed_pkts;
135     if (num_free <= MYNEWT_VAL(BLE_HS_FLOW_CTRL_THRESH)) {
136         ble_npl_eventq_put(ble_hs_evq_get(), &ble_hs_flow_ev);
137         ble_npl_callout_stop(&ble_hs_flow_timer);
138     } else if (ble_hs_flow_num_completed_pkts == 1) {
139         rc = ble_npl_callout_reset(&ble_hs_flow_timer, BLE_HS_FLOW_ITVL_TICKS);
140         BLE_HS_DBG_ASSERT_EVAL(rc == 0);
141     }
142 }
143 
144 static os_error_t
ble_hs_flow_acl_free(struct os_mempool_ext * mpe,void * data,void * arg)145 ble_hs_flow_acl_free(struct os_mempool_ext *mpe, void *data, void *arg)
146 {
147     struct ble_hs_conn *conn;
148     const struct os_mbuf *om;
149     uint16_t conn_handle;
150     int rc;
151 
152     om = data;
153 
154     /* An ACL data packet must be a single mbuf, and it must contain the
155      * corresponding connection handle in its user header.
156      */
157     assert(OS_MBUF_IS_PKTHDR(om));
158     assert(OS_MBUF_USRHDR_LEN(om) >= sizeof conn_handle);
159 
160     /* Copy the connection handle out of the mbuf. */
161     memcpy(&conn_handle, OS_MBUF_USRHDR(om), sizeof conn_handle);
162 
163     /* Free the mbuf back to its pool. */
164     rc = os_memblock_put_from_cb(&mpe->mpe_mp, data);
165     if (rc != 0) {
166         return rc;
167     }
168 
169     /* Allow nested locks - there are too many places where acl buffers can get
170      * freed.
171      */
172     ble_hs_lock_nested();
173 
174     conn = ble_hs_conn_find(conn_handle);
175     if (conn != NULL) {
176         ble_hs_flow_inc_completed_pkts(conn);
177     }
178 
179     ble_hs_unlock_nested();
180 
181     return 0;
182 }
183 #endif /* MYNEWT_VAL(BLE_HS_FLOW_CTRL) */
184 
185 void
ble_hs_flow_connection_broken(uint16_t conn_handle)186 ble_hs_flow_connection_broken(uint16_t conn_handle)
187 {
188 #if MYNEWT_VAL(BLE_HS_FLOW_CTRL) &&                 \
189     MYNEWT_VAL(BLE_HS_FLOW_CTRL_TX_ON_DISCONNECT)
190     ble_hs_lock();
191     ble_hs_flow_tx_num_comp_pkts();
192     ble_hs_unlock();
193 #endif
194 }
195 
196 /**
197  * Fills the user header of an incoming data packet.  On function return, the
198  * header contains the connection handle associated with the sender.
199  *
200  * If flow control is disabled, this function is a no-op.
201  */
202 void
ble_hs_flow_fill_acl_usrhdr(struct os_mbuf * om)203 ble_hs_flow_fill_acl_usrhdr(struct os_mbuf *om)
204 {
205 #if MYNEWT_VAL(BLE_HS_FLOW_CTRL)
206     const struct hci_data_hdr *hdr;
207     uint16_t *conn_handle;
208 
209     BLE_HS_DBG_ASSERT(OS_MBUF_USRHDR_LEN(om) >= sizeof *conn_handle);
210     conn_handle = OS_MBUF_USRHDR(om);
211 
212     hdr = (void *)om->om_data;
213     *conn_handle = BLE_HCI_DATA_HANDLE(hdr->hdh_handle_pb_bc);
214 #endif
215 }
216 
217 /**
218  * Sends the HCI commands to the controller required for enabling host flow
219  * control.
220  *
221  * If flow control is disabled, this function is a no-op.
222  */
223 int
ble_hs_flow_startup(void)224 ble_hs_flow_startup(void)
225 {
226 #if MYNEWT_VAL(BLE_HS_FLOW_CTRL)
227     struct hci_host_buf_size buf_size_cmd;
228     int rc;
229 
230     ble_npl_event_init(&ble_hs_flow_ev, ble_hs_flow_event_cb, NULL);
231 
232     /* Assume failure. */
233     ble_hci_trans_set_acl_free_cb(NULL, NULL);
234     ble_npl_callout_stop(&ble_hs_flow_timer);
235 
236     rc = ble_hs_hci_cmd_tx_set_ctlr_to_host_fc(BLE_HCI_CTLR_TO_HOST_FC_ACL);
237     if (rc != 0) {
238         return rc;
239     }
240 
241     buf_size_cmd = (struct hci_host_buf_size) {
242         .acl_pkt_len = MYNEWT_VAL(BLE_ACL_BUF_SIZE),
243         .num_acl_pkts = MYNEWT_VAL(BLE_ACL_BUF_COUNT),
244     };
245     rc = ble_hs_hci_cmd_tx_host_buf_size(&buf_size_cmd);
246     if (rc != 0) {
247         ble_hs_hci_cmd_tx_set_ctlr_to_host_fc(BLE_HCI_CTLR_TO_HOST_FC_OFF);
248         return rc;
249     }
250 
251     /* Flow control successfully enabled. */
252     ble_hs_flow_num_completed_pkts = 0;
253     ble_hci_trans_set_acl_free_cb(ble_hs_flow_acl_free, NULL);
254     ble_npl_callout_init(&ble_hs_flow_timer, ble_hs_evq_get(),
255                          ble_hs_flow_event_cb, NULL);
256 #endif
257 
258     return 0;
259 }
260