/* conn.c - Bluetooth connection handling */ /* * Copyright (c) 2015 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hci_core.h" #include "conn_internal.h" #include "l2cap.h" #include "keys.h" #include "smp.h" #if !defined(CONFIG_BLUETOOTH_DEBUG_CONN) #undef BT_DBG #define BT_DBG(fmt, ...) #endif /* How long until we cancel HCI_LE_Create_Connection */ #define CONN_TIMEOUT (3 * sys_clock_ticks_per_sec) static struct bt_conn conns[CONFIG_BLUETOOTH_MAX_CONN]; static struct bt_conn_cb *callback_list; #if defined(CONFIG_BLUETOOTH_DEBUG_CONN) static const char *state2str(bt_conn_state_t state) { switch (state) { case BT_CONN_DISCONNECTED: return "disconnected"; case BT_CONN_CONNECT_SCAN: return "connect-scan"; case BT_CONN_CONNECT: return "connect"; case BT_CONN_CONNECTED: return "connected"; case BT_CONN_DISCONNECT: return "disconnect"; default: return "(unknown)"; } } #endif static void notify_connected(struct bt_conn *conn) { struct bt_conn_cb *cb; for (cb = callback_list; cb; cb = cb->_next) { if (cb->connected) { cb->connected(conn); } } } static void notify_disconnected(struct bt_conn *conn) { struct bt_conn_cb *cb; for (cb = callback_list; cb; cb = cb->_next) { if (cb->disconnected) { cb->disconnected(conn); } } } #if defined(CONFIG_BLUETOOTH_SMP) void bt_conn_identity_resolved(struct bt_conn *conn) { const bt_addr_le_t *rpa; struct bt_conn_cb *cb; if (conn->role == BT_HCI_ROLE_MASTER) { rpa = &conn->resp_addr; } else { rpa = &conn->init_addr; } for (cb = callback_list; cb; cb = cb->_next) { if (cb->identity_resolved) { cb->identity_resolved(conn, rpa, &conn->dst); } } } void bt_conn_security_changed(struct bt_conn *conn) { struct bt_conn_cb *cb; for (cb = callback_list; cb; cb = cb->_next) { if (cb->security_changed) { cb->security_changed(conn, conn->sec_level); } } } int bt_conn_le_start_encryption(struct bt_conn *conn, uint64_t rand, uint16_t ediv, const uint8_t *ltk, size_t len) { struct bt_hci_cp_le_start_encryption *cp; struct bt_buf *buf; buf = bt_hci_cmd_create(BT_HCI_OP_LE_START_ENCRYPTION, sizeof(*cp)); if (!buf) { return -ENOBUFS; } cp = bt_buf_add(buf, sizeof(*cp)); cp->handle = sys_cpu_to_le16(conn->handle); cp->rand = rand; cp->ediv = ediv; memcpy(cp->ltk, ltk, len); if (len < sizeof(cp->ltk)) { memset(cp->ltk + len, 0, sizeof(cp->ltk) - len); } return bt_hci_cmd_send_sync(BT_HCI_OP_LE_START_ENCRYPTION, buf, NULL); } int bt_conn_security(struct bt_conn *conn, bt_security_t sec) { int err = 0; if (conn->state != BT_CONN_CONNECTED) { return -ENOTCONN; } /* nothing to do */ if (conn->sec_level >= sec || conn->required_sec_level >= sec) { return 0; } /* for now we only support legacy pairing */ if (sec > BT_SECURITY_HIGH) { return -EINVAL; } conn->required_sec_level = sec; #if defined(CONFIG_BLUETOOTH_CENTRAL) if (conn->role == BT_HCI_ROLE_MASTER) { struct bt_keys *keys; keys = bt_keys_find(BT_KEYS_LTK, &conn->dst); if (keys) { if (sec > BT_SECURITY_MEDIUM && keys->type != BT_KEYS_AUTHENTICATED) { err = bt_smp_send_pairing_req(conn); goto done; } err = bt_conn_le_start_encryption(conn, keys->ltk.rand, keys->ltk.ediv, keys->ltk.val, keys->enc_size); goto done; } err = bt_smp_send_pairing_req(conn); goto done; } #endif /* CONFIG_BLUETOOTH_CENTRAL */ #if defined(CONFIG_BLUETOOTH_PERIPHERAL) err = bt_smp_send_security_req(conn); #endif /* CONFIG_BLUETOOTH_PERIPHERAL */ done: /* reset required security level in case of error */ if (err) { conn->required_sec_level = conn->sec_level; } return err; } #endif /* CONFIG_BLUETOOTH_SMP */ void bt_conn_cb_register(struct bt_conn_cb *cb) { cb->_next = callback_list; callback_list = cb; } static void bt_conn_reset_rx_state(struct bt_conn *conn) { if (!conn->rx_len) { return; } bt_buf_put(conn->rx); conn->rx = NULL; conn->rx_len = 0; } void bt_conn_recv(struct bt_conn *conn, struct bt_buf *buf, uint8_t flags) { struct bt_l2cap_hdr *hdr; uint16_t len; BT_DBG("handle %u len %u flags %02x\n", conn->handle, buf->len, flags); /* Check packet boundary flags */ switch (flags) { case 0x02: /* First packet */ hdr = (void *)buf->data; len = sys_le16_to_cpu(hdr->len); BT_DBG("First, len %u final %u\n", buf->len, len); if (conn->rx_len) { BT_ERR("Unexpected first L2CAP frame\n"); bt_conn_reset_rx_state(conn); } conn->rx_len = (sizeof(*hdr) + len) - buf->len; BT_DBG("rx_len %u\n", conn->rx_len); if (conn->rx_len) { conn->rx = buf; return; } break; case 0x01: /* Continuation */ if (!conn->rx_len) { BT_ERR("Unexpected L2CAP continuation\n"); bt_conn_reset_rx_state(conn); bt_buf_put(buf); return; } if (buf->len > conn->rx_len) { BT_ERR("L2CAP data overflow\n"); bt_conn_reset_rx_state(conn); bt_buf_put(buf); return; } BT_DBG("Cont, len %u rx_len %u\n", buf->len, conn->rx_len); if (buf->len > bt_buf_tailroom(conn->rx)) { BT_ERR("Not enough buffer space for L2CAP data\n"); bt_conn_reset_rx_state(conn); bt_buf_put(buf); return; } memcpy(bt_buf_add(conn->rx, buf->len), buf->data, buf->len); conn->rx_len -= buf->len; bt_buf_put(buf); if (conn->rx_len) { return; } buf = conn->rx; conn->rx = NULL; conn->rx_len = 0; break; default: BT_ERR("Unexpected ACL flags (0x%02x)\n", flags); bt_conn_reset_rx_state(conn); bt_buf_put(buf); return; } hdr = (void *)buf->data; len = sys_le16_to_cpu(hdr->len); if (sizeof(*hdr) + len != buf->len) { BT_ERR("ACL len mismatch (%u != %u)\n", len, buf->len); bt_buf_put(buf); return; } BT_DBG("Successfully parsed %u byte L2CAP packet\n", buf->len); bt_l2cap_recv(conn, buf); } void bt_conn_send(struct bt_conn *conn, struct bt_buf *buf) { uint16_t len, remaining = buf->len; struct bt_hci_acl_hdr *hdr; struct nano_fifo frags; uint8_t *ptr; BT_DBG("conn handle %u buf len %u\n", conn->handle, buf->len); if (conn->state != BT_CONN_CONNECTED) { BT_ERR("not connected!\n"); return; } nano_fifo_init(&frags); len = min(remaining, bt_dev.le_mtu); hdr = bt_buf_push(buf, sizeof(*hdr)); hdr->handle = sys_cpu_to_le16(conn->handle); hdr->len = sys_cpu_to_le16(len); buf->len -= remaining - len; ptr = bt_buf_tail(buf); nano_fifo_put(&frags, buf); remaining -= len; while (remaining) { buf = bt_l2cap_create_pdu(conn); len = min(remaining, bt_dev.le_mtu); /* Copy from original buffer */ memcpy(bt_buf_add(buf, len), ptr, len); ptr += len; hdr = bt_buf_push(buf, sizeof(*hdr)); hdr->handle = sys_cpu_to_le16(conn->handle | (1 << 12)); hdr->len = sys_cpu_to_le16(len); nano_fifo_put(&frags, buf); remaining -= len; } while ((buf = nano_fifo_get(&frags))) { nano_fifo_put(&conn->tx_queue, buf); } } static void conn_tx_fiber(int arg1, int arg2) { struct bt_conn *conn = (struct bt_conn *)arg1; struct bt_buf *buf; BT_DBG("Started for handle %u\n", conn->handle); while (conn->state == BT_CONN_CONNECTED) { int err; /* Wait until the controller can accept ACL packets */ BT_DBG("calling sem_take_wait\n"); nano_fiber_sem_take_wait(&bt_dev.le_pkts_sem); /* check for disconnection */ if (conn->state != BT_CONN_CONNECTED) { nano_fiber_sem_give(&bt_dev.le_pkts_sem); break; } /* Get next ACL packet for connection */ buf = nano_fifo_get_wait(&conn->tx_queue); if (conn->state != BT_CONN_CONNECTED) { nano_fiber_sem_give(&bt_dev.le_pkts_sem); bt_buf_put(buf); break; } BT_DBG("passing buf %p len %u to driver\n", buf, buf->len); err = bt_dev.drv->send(buf); if (err) { BT_ERR("Unable to send to driver (err %d)\n", err); } else { conn->pending_pkts++; } bt_buf_put(buf); } BT_DBG("handle %u disconnected - cleaning up\n", conn->handle); /* Give back any allocated buffers */ while ((buf = nano_fifo_get(&conn->tx_queue))) { bt_buf_put(buf); } /* Return any unacknowledged packets */ if (conn->pending_pkts) { while (conn->pending_pkts--) { nano_fiber_sem_give(&bt_dev.le_pkts_sem); } } bt_conn_reset_rx_state(conn); BT_DBG("handle %u exiting\n", conn->handle); bt_conn_put(conn); } struct bt_conn *bt_conn_add(const bt_addr_le_t *peer) { struct bt_conn *conn = NULL; int i; for (i = 0; i < ARRAY_SIZE(conns); i++) { if (!atomic_get(&conns[i].ref)) { conn = &conns[i]; break; } } if (!conn) { return NULL; } memset(conn, 0, sizeof(*conn)); atomic_set(&conn->ref, 1); bt_addr_le_copy(&conn->dst, peer); #if defined(CONFIG_BLUETOOTH_SMP) conn->sec_level = BT_SECURITY_LOW; conn->required_sec_level = BT_SECURITY_LOW; #endif /* CONFIG_BLUETOOTH_SMP */ return conn; } static void timeout_fiber(int arg1, int arg2) { struct bt_conn *conn = (struct bt_conn *)arg1; ARG_UNUSED(arg2); conn->timeout = NULL; bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); bt_conn_put(conn); } void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state) { bt_conn_state_t old_state; BT_DBG("%s -> %s\n", state2str(conn->state), state2str(state)); if (conn->state == state) { BT_WARN("no transition\n"); return; } old_state = conn->state; conn->state = state; /* Actions needed for exiting the old state */ switch (old_state) { case BT_CONN_DISCONNECTED: /* Take a reference for the first state transition after * bt_conn_add() and keep it until reaching DISCONNECTED * again. */ bt_conn_get(conn); break; case BT_CONN_CONNECT: if (conn->timeout) { fiber_fiber_delayed_start_cancel(conn->timeout); conn->timeout = NULL; /* Drop the reference taken by timeout fiber */ bt_conn_put(conn); } break; default: break; } /* Actions needed for entering the new state */ switch (conn->state){ case BT_CONN_CONNECTED: nano_fifo_init(&conn->tx_queue); fiber_start(conn->stack, sizeof(conn->stack), conn_tx_fiber, (int)bt_conn_get(conn), 0, 7, 0); bt_l2cap_connected(conn); notify_connected(conn); break; case BT_CONN_DISCONNECTED: /* Send dummy buffer to wake up and stop the tx fiber * for states where it was running */ if (old_state == BT_CONN_CONNECTED || old_state == BT_CONN_DISCONNECT) { bt_l2cap_disconnected(conn); notify_disconnected(conn); nano_fifo_put(&conn->tx_queue, bt_buf_get(BT_DUMMY, 0)); } /* Release the reference we took for the very first * state transition. */ bt_conn_put(conn); break; case BT_CONN_CONNECT_SCAN: break; case BT_CONN_CONNECT: /* Add LE Create Connection timeout */ conn->timeout = fiber_delayed_start(conn->stack, sizeof(conn->stack), timeout_fiber, (int)bt_conn_get(conn), 0, 7, 0, CONN_TIMEOUT); break; case BT_CONN_DISCONNECT: break; default: BT_WARN("no valid (%u) state was set\n", state); break; } } struct bt_conn *bt_conn_lookup_handle(uint16_t handle) { int i; for (i = 0; i < ARRAY_SIZE(conns); i++) { if (!atomic_get(&conns[i].ref)) { continue; } /* We only care about connections with a valid handle */ if (conns[i].state != BT_CONN_CONNECTED && conns[i].state != BT_CONN_DISCONNECT) { continue; } if (conns[i].handle == handle) { return bt_conn_get(&conns[i]); } } return NULL; } struct bt_conn *bt_conn_lookup_addr_le(const bt_addr_le_t *peer) { int i; for (i = 0; i < ARRAY_SIZE(conns); i++) { if (!atomic_get(&conns[i].ref)) { continue; } if (!bt_addr_le_cmp(peer, &conns[i].dst)) { return bt_conn_get(&conns[i]); } } return NULL; } struct bt_conn *bt_conn_lookup_state(const bt_addr_le_t *peer, const bt_conn_state_t state) { int i; for (i = 0; i < ARRAY_SIZE(conns); i++) { if (!atomic_get(&conns[i].ref)) { continue; } if (bt_addr_le_cmp(peer, BT_ADDR_LE_ANY) && bt_addr_le_cmp(peer, &conns[i].dst)) { continue; } if (conns[i].state == state) { return bt_conn_get(&conns[i]); } } return NULL; } struct bt_conn *bt_conn_get(struct bt_conn *conn) { atomic_inc(&conn->ref); BT_DBG("handle %u ref %u\n", conn->handle, atomic_get(&conn->ref)); return conn; } void bt_conn_put(struct bt_conn *conn) { atomic_dec(&conn->ref); BT_DBG("handle %u ref %u\n", conn->handle, atomic_get(&conn->ref)); } const bt_addr_le_t *bt_conn_get_dst(const struct bt_conn *conn) { return &conn->dst; } void bt_conn_set_auto_conn(struct bt_conn *conn, bool auto_conn) { if (auto_conn) { atomic_set_bit(conn->flags, BT_CONN_AUTO_CONNECT); } else { atomic_clear_bit(conn->flags, BT_CONN_AUTO_CONNECT); } } static int bt_hci_disconnect(struct bt_conn *conn, uint8_t reason) { struct bt_buf *buf; struct bt_hci_cp_disconnect *disconn; int err; buf = bt_hci_cmd_create(BT_HCI_OP_DISCONNECT, sizeof(*disconn)); if (!buf) { return -ENOBUFS; } disconn = bt_buf_add(buf, sizeof(*disconn)); disconn->handle = sys_cpu_to_le16(conn->handle); disconn->reason = reason; err = bt_hci_cmd_send(BT_HCI_OP_DISCONNECT, buf); if (err) { return err; } bt_conn_set_state(conn, BT_CONN_DISCONNECT); return 0; } static int bt_hci_connect_le_cancel(struct bt_conn *conn) { int err; if (conn->timeout) { fiber_fiber_delayed_start_cancel(conn->timeout); conn->timeout = NULL; /* Drop the reference took by timeout fiber */ bt_conn_put(conn); } err = bt_hci_cmd_send(BT_HCI_OP_LE_CREATE_CONN_CANCEL, NULL); if (err) { return err; } return 0; } int bt_conn_disconnect(struct bt_conn *conn, uint8_t reason) { /* Disconnection is initiated by us, so auto connection shall * be disabled. Otherwise the passive scan would be enabled * and we could send LE Create Connection as soon as the remote * starts advertising. */ bt_conn_set_auto_conn(conn, false); switch (conn->state) { case BT_CONN_CONNECT_SCAN: bt_conn_set_state(conn, BT_CONN_DISCONNECTED); bt_le_scan_update(); return 0; case BT_CONN_CONNECT: return bt_hci_connect_le_cancel(conn); case BT_CONN_CONNECTED: return bt_hci_disconnect(conn, reason); case BT_CONN_DISCONNECT: return 0; case BT_CONN_DISCONNECTED: default: return -ENOTCONN; } } struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer) { struct bt_conn *conn; conn = bt_conn_lookup_addr_le(peer); if (conn) { switch (conn->state) { case BT_CONN_CONNECT_SCAN: case BT_CONN_CONNECT: case BT_CONN_CONNECTED: return conn; default: bt_conn_put(conn); return NULL; } } conn = bt_conn_add(peer); if (!conn) { return NULL; } bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); bt_le_scan_update(); return conn; } int bt_conn_le_conn_update(struct bt_conn *conn, uint16_t min, uint16_t max, uint16_t latency, uint16_t timeout) { struct hci_cp_le_conn_update *conn_update; struct bt_buf *buf; buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_UPDATE, sizeof(*conn_update)); if (!buf) { return -ENOBUFS; } conn_update = bt_buf_add(buf, sizeof(*conn_update)); memset(conn_update, 0, sizeof(*conn_update)); conn_update->handle = sys_cpu_to_le16(conn->handle); conn_update->conn_interval_min = sys_cpu_to_le16(min); conn_update->conn_interval_max = sys_cpu_to_le16(max); conn_update->conn_latency = sys_cpu_to_le16(latency); conn_update->supervision_timeout = sys_cpu_to_le16(timeout); return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_UPDATE, buf); }