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