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