1/******************************************************************************
2 *
3 *  Copyright (C) 1999-2012 Broadcom Corporation
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at:
8 *
9 *  http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 ******************************************************************************/
18
19/******************************************************************************
20 *
21 *  This file contains functions for the SMP L2Cap interface
22 *
23 ******************************************************************************/
24
25#include "bt_target.h"
26
27#include <string.h>
28#include "btm_ble_api.h"
29#include "l2c_api.h"
30
31#include "smp_int.h"
32
33static void smp_tx_complete_callback(uint16_t cid, uint16_t num_pkt);
34
35static void smp_connect_callback(uint16_t channel, const RawAddress& bd_addr,
36                                 bool connected, uint16_t reason,
37                                 tBT_TRANSPORT transport);
38static void smp_data_received(uint16_t channel, const RawAddress& bd_addr,
39                              BT_HDR* p_buf);
40
41static void smp_br_connect_callback(uint16_t channel, const RawAddress& bd_addr,
42                                    bool connected, uint16_t reason,
43                                    tBT_TRANSPORT transport);
44static void smp_br_data_received(uint16_t channel, const RawAddress& bd_addr,
45                                 BT_HDR* p_buf);
46
47/*******************************************************************************
48 *
49 * Function         smp_l2cap_if_init
50 *
51 * Description      This function is called during the SMP task startup
52 *                  to register interface functions with L2CAP.
53 *
54 ******************************************************************************/
55void smp_l2cap_if_init(void) {
56  tL2CAP_FIXED_CHNL_REG fixed_reg;
57  SMP_TRACE_EVENT("SMDBG l2c %s", __func__);
58  fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE;
59  fixed_reg.fixed_chnl_opts.max_transmit = 0;
60  fixed_reg.fixed_chnl_opts.rtrans_tout = 0;
61  fixed_reg.fixed_chnl_opts.mon_tout = 0;
62  fixed_reg.fixed_chnl_opts.mps = 0;
63  fixed_reg.fixed_chnl_opts.tx_win_sz = 0;
64
65  fixed_reg.pL2CA_FixedConn_Cb = smp_connect_callback;
66  fixed_reg.pL2CA_FixedData_Cb = smp_data_received;
67  fixed_reg.pL2CA_FixedTxComplete_Cb = smp_tx_complete_callback;
68
69  fixed_reg.pL2CA_FixedCong_Cb =
70      NULL; /* do not handle congestion on this channel */
71  fixed_reg.default_idle_tout =
72      60; /* set 60 seconds timeout, 0xffff default idle timeout */
73
74  L2CA_RegisterFixedChannel(L2CAP_SMP_CID, &fixed_reg);
75
76  fixed_reg.pL2CA_FixedConn_Cb = smp_br_connect_callback;
77  fixed_reg.pL2CA_FixedData_Cb = smp_br_data_received;
78
79  L2CA_RegisterFixedChannel(L2CAP_SMP_BR_CID, &fixed_reg);
80}
81
82/*******************************************************************************
83 *
84 * Function         smp_connect_callback
85 *
86 * Description      This callback function is called by L2CAP to indicate that
87 *                  SMP channel is
88 *                      connected (conn = true)/disconnected (conn = false).
89 *
90 ******************************************************************************/
91static void smp_connect_callback(uint16_t channel, const RawAddress& bd_addr,
92                                 bool connected, uint16_t reason,
93                                 tBT_TRANSPORT transport) {
94  tSMP_CB* p_cb = &smp_cb;
95  tSMP_INT_DATA int_data;
96
97  SMP_TRACE_EVENT("SMDBG l2c %s", __func__);
98
99  if (transport == BT_TRANSPORT_BR_EDR || bd_addr.IsEmpty()) return;
100
101  if (bd_addr == p_cb->pairing_bda) {
102    VLOG(2) << __func__ << " for pairing BDA: " << bd_addr
103            << " Event: " << ((connected) ? "connected" : "disconnected");
104
105    if (connected) {
106      if (!p_cb->connect_initialized) {
107        p_cb->connect_initialized = true;
108        /* initiating connection established */
109        p_cb->role = L2CA_GetBleConnRole(bd_addr);
110
111        /* initialize local i/r key to be default keys */
112        p_cb->local_r_key = p_cb->local_i_key = SMP_SEC_DEFAULT_KEY;
113        p_cb->loc_auth_req = p_cb->peer_auth_req = SMP_DEFAULT_AUTH_REQ;
114        p_cb->cb_evt = SMP_IO_CAP_REQ_EVT;
115        smp_sm_event(p_cb, SMP_L2CAP_CONN_EVT, NULL);
116      }
117    } else {
118      int_data.reason = reason;
119      /* Disconnected while doing security */
120      smp_sm_event(p_cb, SMP_L2CAP_DISCONN_EVT, &int_data);
121    }
122  }
123}
124
125/*******************************************************************************
126 *
127 * Function         smp_data_received
128 *
129 * Description      This function is called when data is received from L2CAP on
130 *                  SMP channel.
131 *
132 *
133 * Returns          void
134 *
135 ******************************************************************************/
136static void smp_data_received(uint16_t channel, const RawAddress& bd_addr,
137                              BT_HDR* p_buf) {
138  tSMP_CB* p_cb = &smp_cb;
139  uint8_t* p = (uint8_t*)(p_buf + 1) + p_buf->offset;
140  uint8_t cmd;
141  SMP_TRACE_EVENT("SMDBG l2c %s", __func__);
142
143  STREAM_TO_UINT8(cmd, p);
144
145  /* sanity check */
146  if ((SMP_OPCODE_MAX < cmd) || (SMP_OPCODE_MIN > cmd)) {
147    SMP_TRACE_WARNING("Ignore received command with RESERVED code 0x%02x", cmd);
148    osi_free(p_buf);
149    return;
150  }
151
152  /* reject the pairing request if there is an on-going SMP pairing */
153  if (SMP_OPCODE_PAIRING_REQ == cmd || SMP_OPCODE_SEC_REQ == cmd) {
154    if ((p_cb->state == SMP_STATE_IDLE) &&
155        (p_cb->br_state == SMP_BR_STATE_IDLE) &&
156        !(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) {
157      p_cb->role = L2CA_GetBleConnRole(bd_addr);
158      p_cb->pairing_bda = bd_addr;
159    } else if (bd_addr != p_cb->pairing_bda) {
160      osi_free(p_buf);
161      smp_reject_unexpected_pairing_command(bd_addr);
162      return;
163    }
164    /* else, out of state pairing request/security request received, passed into
165     * SM */
166  }
167
168  if (bd_addr == p_cb->pairing_bda) {
169    alarm_set_on_mloop(p_cb->smp_rsp_timer_ent, SMP_WAIT_FOR_RSP_TIMEOUT_MS,
170                       smp_rsp_timeout, NULL);
171
172    if (cmd == SMP_OPCODE_CONFIRM) {
173      SMP_TRACE_DEBUG(
174          "in %s cmd = 0x%02x, peer_auth_req = 0x%02x,"
175          "loc_auth_req = 0x%02x",
176          __func__, cmd, p_cb->peer_auth_req, p_cb->loc_auth_req);
177
178      if ((p_cb->peer_auth_req & SMP_SC_SUPPORT_BIT) &&
179          (p_cb->loc_auth_req & SMP_SC_SUPPORT_BIT)) {
180        cmd = SMP_OPCODE_PAIR_COMMITM;
181      }
182    }
183
184    p_cb->rcvd_cmd_code = cmd;
185    p_cb->rcvd_cmd_len = (uint8_t)p_buf->len;
186    smp_sm_event(p_cb, cmd, p);
187  }
188
189  osi_free(p_buf);
190}
191
192/*******************************************************************************
193 *
194 * Function         smp_tx_complete_callback
195 *
196 * Description      SMP channel tx complete callback
197 *
198 ******************************************************************************/
199static void smp_tx_complete_callback(uint16_t cid, uint16_t num_pkt) {
200  tSMP_CB* p_cb = &smp_cb;
201
202  if (p_cb->total_tx_unacked >= num_pkt)
203    p_cb->total_tx_unacked -= num_pkt;
204  else
205    SMP_TRACE_ERROR("Unexpected %s: num_pkt = %d", __func__, num_pkt);
206
207  uint8_t reason = SMP_SUCCESS;
208  if (p_cb->total_tx_unacked == 0 && p_cb->wait_for_authorization_complete) {
209    if (cid == L2CAP_SMP_CID)
210      smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason);
211    else
212      smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &reason);
213  }
214}
215
216/*******************************************************************************
217 *
218 * Function         smp_br_connect_callback
219 *
220 * Description      This callback function is called by L2CAP to indicate that
221 *                  SMP BR channel is
222 *                      connected (conn = true)/disconnected (conn = false).
223 *
224 ******************************************************************************/
225static void smp_br_connect_callback(uint16_t channel, const RawAddress& bd_addr,
226                                    bool connected, uint16_t reason,
227                                    tBT_TRANSPORT transport) {
228  tSMP_CB* p_cb = &smp_cb;
229  tSMP_INT_DATA int_data;
230
231  SMP_TRACE_EVENT("%s", __func__);
232
233  if (transport != BT_TRANSPORT_BR_EDR) {
234    SMP_TRACE_WARNING("%s is called on unexpected transport %d", __func__,
235                      transport);
236    return;
237  }
238
239  if (bd_addr != p_cb->pairing_bda) return;
240
241  VLOG(1) << __func__ << " for pairing BDA: " << bd_addr
242          << " Event: " << ((connected) ? "connected" : "disconnected");
243
244  if (connected) {
245    if (!p_cb->connect_initialized) {
246      p_cb->connect_initialized = true;
247      /* initialize local i/r key to be default keys */
248      p_cb->local_r_key = p_cb->local_i_key = SMP_BR_SEC_DEFAULT_KEY;
249      p_cb->loc_auth_req = p_cb->peer_auth_req = 0;
250      p_cb->cb_evt = SMP_BR_KEYS_REQ_EVT;
251      smp_br_state_machine_event(p_cb, SMP_BR_L2CAP_CONN_EVT, NULL);
252    }
253  } else {
254    int_data.reason = reason;
255    /* Disconnected while doing security */
256    smp_br_state_machine_event(p_cb, SMP_BR_L2CAP_DISCONN_EVT, &int_data);
257  }
258}
259
260/*******************************************************************************
261 *
262 * Function         smp_br_data_received
263 *
264 * Description      This function is called when data is received from L2CAP on
265 *                  SMP BR channel.
266 *
267 * Returns          void
268 *
269 ******************************************************************************/
270static void smp_br_data_received(uint16_t channel, const RawAddress& bd_addr,
271                                 BT_HDR* p_buf) {
272  tSMP_CB* p_cb = &smp_cb;
273  uint8_t* p = (uint8_t*)(p_buf + 1) + p_buf->offset;
274  uint8_t cmd;
275  SMP_TRACE_EVENT("SMDBG l2c %s", __func__);
276
277  STREAM_TO_UINT8(cmd, p);
278
279  /* sanity check */
280  if ((SMP_OPCODE_MAX < cmd) || (SMP_OPCODE_MIN > cmd)) {
281    SMP_TRACE_WARNING("Ignore received command with RESERVED code 0x%02x", cmd);
282    osi_free(p_buf);
283    return;
284  }
285
286  /* reject the pairing request if there is an on-going SMP pairing */
287  if (SMP_OPCODE_PAIRING_REQ == cmd) {
288    if ((p_cb->state == SMP_STATE_IDLE) &&
289        (p_cb->br_state == SMP_BR_STATE_IDLE)) {
290      p_cb->role = HCI_ROLE_SLAVE;
291      p_cb->smp_over_br = true;
292      p_cb->pairing_bda = bd_addr;
293    } else if (bd_addr != p_cb->pairing_bda) {
294      osi_free(p_buf);
295      smp_reject_unexpected_pairing_command(bd_addr);
296      return;
297    }
298    /* else, out of state pairing request received, passed into State Machine */
299  }
300
301  if (bd_addr == p_cb->pairing_bda) {
302    alarm_set_on_mloop(p_cb->smp_rsp_timer_ent, SMP_WAIT_FOR_RSP_TIMEOUT_MS,
303                       smp_rsp_timeout, NULL);
304
305    p_cb->rcvd_cmd_code = cmd;
306    p_cb->rcvd_cmd_len = (uint8_t)p_buf->len;
307    smp_br_state_machine_event(p_cb, cmd, p);
308  }
309
310  osi_free(p_buf);
311}
312