Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

Johan Hedberg say:

====================
pull request: bluetooth-next 2014-12-31

Here's the first batch of bluetooth patches for 3.20.

 - Cleanups & fixes to ieee802154  drivers
 - Fix synchronization of mgmt commands with respective HCI commands
 - Add self-tests for LE pairing crypto functionality
 - Remove 'BlueFritz!' specific handling from core using a new quirk flag
 - Public address configuration support for ath3012
 - Refactor debugfs support into a dedicated file
 - Initial support for LE Data Length Extension feature from Bluetooth 4.2

Please let me know if there are any issues pulling. Thanks.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller
2015-01-02 15:58:21 -05:00
37 changed files with 3082 additions and 1778 deletions

View File

@@ -64,4 +64,31 @@ config BT_6LOWPAN
help
IPv6 compression over Bluetooth Low Energy.
config BT_SELFTEST
bool "Bluetooth self testing support"
depends on BT && DEBUG_KERNEL
help
Run self tests when initializing the Bluetooth subsystem. This
is a developer option and can cause significant delay when booting
the system.
When the Bluetooth subsystem is built as module, then the test
cases are run first thing at module load time. When the Bluetooth
subsystem is compiled into the kernel image, then the test cases
are run late in the initcall hierarchy.
config BT_SELFTEST_ECDH
bool "ECDH test cases"
depends on BT_LE && BT_SELFTEST
help
Run test cases for ECDH cryptographic functionality used by the
Bluetooth Low Energy Secure Connections feature.
config BT_SELFTEST_SMP
bool "SMP test cases"
depends on BT_LE && BT_SELFTEST
help
Run test cases for SMP cryptographic functionality, including both
legacy SMP as well as the Secure Connections features.
source "drivers/bluetooth/Kconfig"

View File

@@ -13,6 +13,8 @@ bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
a2mp.o amp.o ecc.o
a2mp.o amp.o ecc.o hci_request.o hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
subdir-ccflags-y += -D__CHECK_ENDIAN__

View File

@@ -31,6 +31,8 @@
#include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h>
#include "selftest.h"
#define VERSION "2.20"
/* Bluetooth sockets */
@@ -716,6 +718,10 @@ static int __init bt_init(void)
BT_INFO("Core ver %s", VERSION);
err = bt_selftest();
if (err < 0)
return err;
bt_debugfs = debugfs_create_dir("bluetooth", NULL);
err = bt_sysfs_init();

View File

@@ -25,11 +25,13 @@
/* Bluetooth HCI connection handling. */
#include <linux/export.h>
#include <linux/debugfs.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include "hci_request.h"
#include "smp.h"
#include "a2mp.h"
@@ -546,6 +548,8 @@ int hci_conn_del(struct hci_conn *conn)
hci_conn_del_sysfs(conn);
debugfs_remove_recursive(conn->debugfs);
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);

File diff suppressed because it is too large Load Diff

1076
net/bluetooth/hci_debugfs.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2014 Intel Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
void hci_debugfs_create_common(struct hci_dev *hdev);
void hci_debugfs_create_bredr(struct hci_dev *hdev);
void hci_debugfs_create_le(struct hci_dev *hdev);
void hci_debugfs_create_conn(struct hci_conn *conn);

View File

@@ -30,6 +30,8 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>
#include "hci_request.h"
#include "hci_debugfs.h"
#include "a2mp.h"
#include "amp.h"
#include "smp.h"
@@ -1282,6 +1284,55 @@ static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
memcpy(hdev->le_states, rp->le_states, 8);
}
static void hci_cc_le_read_def_data_len(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_rp_le_read_def_data_len *rp = (void *) skb->data;
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
if (rp->status)
return;
hdev->le_def_tx_len = le16_to_cpu(rp->tx_len);
hdev->le_def_tx_time = le16_to_cpu(rp->tx_time);
}
static void hci_cc_le_write_def_data_len(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_cp_le_write_def_data_len *sent;
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%2.2x", hdev->name, status);
if (status)
return;
sent = hci_sent_cmd_data(hdev, HCI_OP_LE_WRITE_DEF_DATA_LEN);
if (!sent)
return;
hdev->le_def_tx_len = le16_to_cpu(sent->tx_len);
hdev->le_def_tx_time = le16_to_cpu(sent->tx_time);
}
static void hci_cc_le_read_max_data_len(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_rp_le_read_max_data_len *rp = (void *) skb->data;
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
if (rp->status)
return;
hdev->le_max_tx_len = le16_to_cpu(rp->tx_len);
hdev->le_max_tx_time = le16_to_cpu(rp->tx_time);
hdev->le_max_rx_len = le16_to_cpu(rp->rx_len);
hdev->le_max_rx_time = le16_to_cpu(rp->rx_time);
}
static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -2115,6 +2166,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
} else
conn->state = BT_CONNECTED;
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
if (test_bit(HCI_AUTH, &hdev->flags))
@@ -2130,7 +2182,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
sizeof(cp), &cp);
hci_update_page_scan(hdev, NULL);
hci_update_page_scan(hdev);
}
/* Set packet type for incoming connection */
@@ -2316,7 +2368,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
hci_remove_link_key(hdev, &conn->dst);
hci_update_page_scan(hdev, NULL);
hci_update_page_scan(hdev);
}
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
@@ -2854,6 +2906,18 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_read_supported_states(hdev, skb);
break;
case HCI_OP_LE_READ_DEF_DATA_LEN:
hci_cc_le_read_def_data_len(hdev, skb);
break;
case HCI_OP_LE_WRITE_DEF_DATA_LEN:
hci_cc_le_write_def_data_len(hdev, skb);
break;
case HCI_OP_LE_READ_MAX_DATA_LEN:
hci_cc_le_read_max_data_len(hdev, skb);
break;
case HCI_OP_WRITE_LE_HOST_SUPPORTED:
hci_cc_write_le_host_supported(hdev, skb);
break;
@@ -3584,6 +3648,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
break;
@@ -4124,6 +4189,7 @@ static void hci_phy_link_complete_evt(struct hci_dev *hdev,
hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
hci_conn_drop(hcon);
hci_debugfs_create_conn(hcon);
hci_conn_add_sysfs(hcon);
amp_physical_cfm(bredr_hcon, hcon);
@@ -4330,6 +4396,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->le_conn_latency = le16_to_cpu(ev->latency);
conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout);
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
hci_proto_connect_cfm(conn, ev->status);

555
net/bluetooth/hci_request.c Normal file
View File

@@ -0,0 +1,555 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2014 Intel Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "smp.h"
#include "hci_request.h"
void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
{
skb_queue_head_init(&req->cmd_q);
req->hdev = hdev;
req->err = 0;
}
int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
{
struct hci_dev *hdev = req->hdev;
struct sk_buff *skb;
unsigned long flags;
BT_DBG("length %u", skb_queue_len(&req->cmd_q));
/* If an error occurred during request building, remove all HCI
* commands queued on the HCI request queue.
*/
if (req->err) {
skb_queue_purge(&req->cmd_q);
return req->err;
}
/* Do not allow empty requests */
if (skb_queue_empty(&req->cmd_q))
return -ENODATA;
skb = skb_peek_tail(&req->cmd_q);
bt_cb(skb)->req.complete = complete;
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
queue_work(hdev->workqueue, &hdev->cmd_work);
return 0;
}
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param)
{
int len = HCI_COMMAND_HDR_SIZE + plen;
struct hci_command_hdr *hdr;
struct sk_buff *skb;
skb = bt_skb_alloc(len, GFP_ATOMIC);
if (!skb)
return NULL;
hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE);
hdr->opcode = cpu_to_le16(opcode);
hdr->plen = plen;
if (plen)
memcpy(skb_put(skb, plen), param, plen);
BT_DBG("skb len %d", skb->len);
bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
bt_cb(skb)->opcode = opcode;
return skb;
}
/* Queue a command to an asynchronous HCI request */
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
const void *param, u8 event)
{
struct hci_dev *hdev = req->hdev;
struct sk_buff *skb;
BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
/* If an error occurred during request building, there is no point in
* queueing the HCI command. We can simply return.
*/
if (req->err)
return;
skb = hci_prepare_cmd(hdev, opcode, plen, param);
if (!skb) {
BT_ERR("%s no memory for command (opcode 0x%4.4x)",
hdev->name, opcode);
req->err = -ENOMEM;
return;
}
if (skb_queue_empty(&req->cmd_q))
bt_cb(skb)->req.start = true;
bt_cb(skb)->req.event = event;
skb_queue_tail(&req->cmd_q, skb);
}
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
const void *param)
{
hci_req_add_ev(req, opcode, plen, param, 0);
}
void hci_req_add_le_scan_disable(struct hci_request *req)
{
struct hci_cp_le_set_scan_enable cp;
memset(&cp, 0, sizeof(cp));
cp.enable = LE_SCAN_DISABLE;
hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
}
static void add_to_white_list(struct hci_request *req,
struct hci_conn_params *params)
{
struct hci_cp_le_add_to_white_list cp;
cp.bdaddr_type = params->addr_type;
bacpy(&cp.bdaddr, &params->addr);
hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp);
}
static u8 update_white_list(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
struct hci_conn_params *params;
struct bdaddr_list *b;
uint8_t white_list_entries = 0;
/* Go through the current white list programmed into the
* controller one by one and check if that address is still
* in the list of pending connections or list of devices to
* report. If not present in either list, then queue the
* command to remove it from the controller.
*/
list_for_each_entry(b, &hdev->le_white_list, list) {
struct hci_cp_le_del_from_white_list cp;
if (hci_pend_le_action_lookup(&hdev->pend_le_conns,
&b->bdaddr, b->bdaddr_type) ||
hci_pend_le_action_lookup(&hdev->pend_le_reports,
&b->bdaddr, b->bdaddr_type)) {
white_list_entries++;
continue;
}
cp.bdaddr_type = b->bdaddr_type;
bacpy(&cp.bdaddr, &b->bdaddr);
hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST,
sizeof(cp), &cp);
}
/* Since all no longer valid white list entries have been
* removed, walk through the list of pending connections
* and ensure that any new device gets programmed into
* the controller.
*
* If the list of the devices is larger than the list of
* available white list entries in the controller, then
* just abort and return filer policy value to not use the
* white list.
*/
list_for_each_entry(params, &hdev->pend_le_conns, action) {
if (hci_bdaddr_list_lookup(&hdev->le_white_list,
&params->addr, params->addr_type))
continue;
if (white_list_entries >= hdev->le_white_list_size) {
/* Select filter policy to accept all advertising */
return 0x00;
}
if (hci_find_irk_by_addr(hdev, &params->addr,
params->addr_type)) {
/* White list can not be used with RPAs */
return 0x00;
}
white_list_entries++;
add_to_white_list(req, params);
}
/* After adding all new pending connections, walk through
* the list of pending reports and also add these to the
* white list if there is still space.
*/
list_for_each_entry(params, &hdev->pend_le_reports, action) {
if (hci_bdaddr_list_lookup(&hdev->le_white_list,
&params->addr, params->addr_type))
continue;
if (white_list_entries >= hdev->le_white_list_size) {
/* Select filter policy to accept all advertising */
return 0x00;
}
if (hci_find_irk_by_addr(hdev, &params->addr,
params->addr_type)) {
/* White list can not be used with RPAs */
return 0x00;
}
white_list_entries++;
add_to_white_list(req, params);
}
/* Select filter policy to use white list */
return 0x01;
}
void hci_req_add_le_passive_scan(struct hci_request *req)
{
struct hci_cp_le_set_scan_param param_cp;
struct hci_cp_le_set_scan_enable enable_cp;
struct hci_dev *hdev = req->hdev;
u8 own_addr_type;
u8 filter_policy;
/* Set require_privacy to false since no SCAN_REQ are send
* during passive scanning. Not using an non-resolvable address
* here is important so that peer devices using direct
* advertising with our address will be correctly reported
* by the controller.
*/
if (hci_update_random_address(req, false, &own_addr_type))
return;
/* Adding or removing entries from the white list must
* happen before enabling scanning. The controller does
* not allow white list modification while scanning.
*/
filter_policy = update_white_list(req);
/* When the controller is using random resolvable addresses and
* with that having LE privacy enabled, then controllers with
* Extended Scanner Filter Policies support can now enable support
* for handling directed advertising.
*
* So instead of using filter polices 0x00 (no whitelist)
* and 0x01 (whitelist enabled) use the new filter policies
* 0x02 (no whitelist) and 0x03 (whitelist enabled).
*/
if (test_bit(HCI_PRIVACY, &hdev->dev_flags) &&
(hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY))
filter_policy |= 0x02;
memset(&param_cp, 0, sizeof(param_cp));
param_cp.type = LE_SCAN_PASSIVE;
param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
param_cp.window = cpu_to_le16(hdev->le_scan_window);
param_cp.own_address_type = own_addr_type;
param_cp.filter_policy = filter_policy;
hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
&param_cp);
memset(&enable_cp, 0, sizeof(enable_cp));
enable_cp.enable = LE_SCAN_ENABLE;
enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
&enable_cp);
}
static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
{
struct hci_dev *hdev = req->hdev;
/* If we're advertising or initiating an LE connection we can't
* go ahead and change the random address at this time. This is
* because the eventual initiator address used for the
* subsequently created connection will be undefined (some
* controllers use the new address and others the one we had
* when the operation started).
*
* In this kind of scenario skip the update and let the random
* address be updated at the next cycle.
*/
if (test_bit(HCI_LE_ADV, &hdev->dev_flags) ||
hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) {
BT_DBG("Deferring random address update");
set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
return;
}
hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa);
}
int hci_update_random_address(struct hci_request *req, bool require_privacy,
u8 *own_addr_type)
{
struct hci_dev *hdev = req->hdev;
int err;
/* If privacy is enabled use a resolvable private address. If
* current RPA has expired or there is something else than
* the current RPA in use, then generate a new one.
*/
if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) {
int to;
*own_addr_type = ADDR_LE_DEV_RANDOM;
if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) &&
!bacmp(&hdev->random_addr, &hdev->rpa))
return 0;
err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
if (err < 0) {
BT_ERR("%s failed to generate new RPA", hdev->name);
return err;
}
set_random_addr(req, &hdev->rpa);
to = msecs_to_jiffies(hdev->rpa_timeout * 1000);
queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to);
return 0;
}
/* In case of required privacy without resolvable private address,
* use an non-resolvable private address. This is useful for active
* scanning and non-connectable advertising.
*/
if (require_privacy) {
bdaddr_t nrpa;
while (true) {
/* The non-resolvable private address is generated
* from random six bytes with the two most significant
* bits cleared.
*/
get_random_bytes(&nrpa, 6);
nrpa.b[5] &= 0x3f;
/* The non-resolvable private address shall not be
* equal to the public address.
*/
if (bacmp(&hdev->bdaddr, &nrpa))
break;
}
*own_addr_type = ADDR_LE_DEV_RANDOM;
set_random_addr(req, &nrpa);
return 0;
}
/* If forcing static address is in use or there is no public
* address use the static address as random address (but skip
* the HCI command if the current random address is already the
* static one.
*
* In case BR/EDR has been disabled on a dual-mode controller
* and a static address has been configured, then use that
* address instead of the public BR/EDR address.
*/
if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
!bacmp(&hdev->bdaddr, BDADDR_ANY) ||
(!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) &&
bacmp(&hdev->static_addr, BDADDR_ANY))) {
*own_addr_type = ADDR_LE_DEV_RANDOM;
if (bacmp(&hdev->static_addr, &hdev->random_addr))
hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
&hdev->static_addr);
return 0;
}
/* Neither privacy nor static address is being used so use a
* public address.
*/
*own_addr_type = ADDR_LE_DEV_PUBLIC;
return 0;
}
static bool disconnected_whitelist_entries(struct hci_dev *hdev)
{
struct bdaddr_list *b;
list_for_each_entry(b, &hdev->whitelist, list) {
struct hci_conn *conn;
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &b->bdaddr);
if (!conn)
return true;
if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
return true;
}
return false;
}
void __hci_update_page_scan(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
u8 scan;
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
return;
if (!hdev_is_powered(hdev))
return;
if (mgmt_powering_down(hdev))
return;
if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) ||
disconnected_whitelist_entries(hdev))
scan = SCAN_PAGE;
else
scan = SCAN_DISABLED;
if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE))
return;
if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
scan |= SCAN_INQUIRY;
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
}
void hci_update_page_scan(struct hci_dev *hdev)
{
struct hci_request req;
hci_req_init(&req, hdev);
__hci_update_page_scan(&req);
hci_req_run(&req, NULL);
}
/* This function controls the background scanning based on hdev->pend_le_conns
* list. If there are pending LE connection we start the background scanning,
* otherwise we stop it.
*
* This function requires the caller holds hdev->lock.
*/
void __hci_update_background_scan(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
struct hci_conn *conn;
if (!test_bit(HCI_UP, &hdev->flags) ||
test_bit(HCI_INIT, &hdev->flags) ||
test_bit(HCI_SETUP, &hdev->dev_flags) ||
test_bit(HCI_CONFIG, &hdev->dev_flags) ||
test_bit(HCI_AUTO_OFF, &hdev->dev_flags) ||
test_bit(HCI_UNREGISTER, &hdev->dev_flags))
return;
/* No point in doing scanning if LE support hasn't been enabled */
if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
return;
/* If discovery is active don't interfere with it */
if (hdev->discovery.state != DISCOVERY_STOPPED)
return;
/* Reset RSSI and UUID filters when starting background scanning
* since these filters are meant for service discovery only.
*
* The Start Discovery and Start Service Discovery operations
* ensure to set proper values for RSSI threshold and UUID
* filter list. So it is safe to just reset them here.
*/
hci_discovery_filter_clear(hdev);
if (list_empty(&hdev->pend_le_conns) &&
list_empty(&hdev->pend_le_reports)) {
/* If there is no pending LE connections or devices
* to be scanned for, we should stop the background
* scanning.
*/
/* If controller is not scanning we are done. */
if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
return;
hci_req_add_le_scan_disable(req);
BT_DBG("%s stopping background scanning", hdev->name);
} else {
/* If there is at least one pending LE connection, we should
* keep the background scan running.
*/
/* If controller is connecting, we should not start scanning
* since some controllers are not able to scan and connect at
* the same time.
*/
conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
if (conn)
return;
/* If controller is currently scanning, we stop it to ensure we
* don't miss any advertising (due to duplicates filter).
*/
if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
hci_req_add_le_scan_disable(req);
hci_req_add_le_passive_scan(req);
BT_DBG("%s starting background scanning", hdev->name);
}
}
static void update_background_scan_complete(struct hci_dev *hdev, u8 status)
{
if (status)
BT_DBG("HCI request failed to update background scanning: "
"status 0x%2.2x", status);
}
void hci_update_background_scan(struct hci_dev *hdev)
{
int err;
struct hci_request req;
hci_req_init(&req, hdev);
__hci_update_background_scan(&req);
err = hci_req_run(&req, update_background_scan_complete);
if (err && err != -ENODATA)
BT_ERR("Failed to run HCI request: err %d", err);
}

View File

@@ -0,0 +1,54 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2014 Intel Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
struct hci_request {
struct hci_dev *hdev;
struct sk_buff_head cmd_q;
/* If something goes wrong when building the HCI request, the error
* value is stored in this field.
*/
int err;
};
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
const void *param);
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
const void *param, u8 event);
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);
void hci_req_add_le_scan_disable(struct hci_request *req);
void hci_req_add_le_passive_scan(struct hci_request *req);
void hci_update_page_scan(struct hci_dev *hdev);
void __hci_update_page_scan(struct hci_request *req);
int hci_update_random_address(struct hci_request *req, bool require_privacy,
u8 *own_addr_type);
void hci_update_background_scan(struct hci_dev *hdev);
void __hci_update_background_scan(struct hci_request *req);

View File

@@ -32,6 +32,7 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/mgmt.h>
#include "hci_request.h"
#include "smp.h"
#define MGMT_VERSION 1
@@ -138,7 +139,7 @@ struct pending_cmd {
size_t param_len;
struct sock *sk;
void *user_data;
void (*cmd_complete)(struct pending_cmd *cmd, u8 status);
int (*cmd_complete)(struct pending_cmd *cmd, u8 status);
};
/* HCI to MGMT error code conversion table */
@@ -1486,16 +1487,16 @@ static void cmd_complete_rsp(struct pending_cmd *cmd, void *data)
cmd_status_rsp(cmd, data);
}
static void generic_cmd_complete(struct pending_cmd *cmd, u8 status)
static int generic_cmd_complete(struct pending_cmd *cmd, u8 status)
{
cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
cmd->param_len);
return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status,
cmd->param, cmd->param_len);
}
static void addr_cmd_complete(struct pending_cmd *cmd, u8 status)
static int addr_cmd_complete(struct pending_cmd *cmd, u8 status)
{
cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
sizeof(struct mgmt_addr_info));
return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
sizeof(struct mgmt_addr_info));
}
static u8 mgmt_bredr_support(struct hci_dev *hdev)
@@ -1566,7 +1567,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
* entries.
*/
hci_req_init(&req, hdev);
hci_update_page_scan(hdev, &req);
__hci_update_page_scan(&req);
update_class(&req);
hci_req_run(&req, NULL);
@@ -1813,7 +1814,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
if (conn_changed || discov_changed) {
new_settings(hdev, cmd->sk);
hci_update_page_scan(hdev, NULL);
hci_update_page_scan(hdev);
if (discov_changed)
mgmt_update_adv_data(hdev);
hci_update_background_scan(hdev);
@@ -1847,7 +1848,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
return err;
if (changed) {
hci_update_page_scan(hdev, NULL);
hci_update_page_scan(hdev);
hci_update_background_scan(hdev);
return new_settings(hdev, sk);
}
@@ -2227,9 +2228,8 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
hci_req_init(&req, hdev);
update_adv_data(&req);
update_scan_rsp_data(&req);
__hci_update_background_scan(&req);
hci_req_run(&req, NULL);
hci_update_background_scan(hdev);
}
unlock:
@@ -3098,16 +3098,17 @@ static struct pending_cmd *find_pairing(struct hci_conn *conn)
return NULL;
}
static void pairing_complete(struct pending_cmd *cmd, u8 status)
static int pairing_complete(struct pending_cmd *cmd, u8 status)
{
struct mgmt_rp_pair_device rp;
struct hci_conn *conn = cmd->user_data;
int err;
bacpy(&rp.addr.bdaddr, &conn->dst);
rp.addr.type = link_to_bdaddr(conn->type, conn->dst_type);
cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
&rp, sizeof(rp));
err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
&rp, sizeof(rp));
/* So we don't get further callbacks for this connection */
conn->connect_cfm_cb = NULL;
@@ -3122,6 +3123,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
hci_conn_put(conn);
return err;
}
void mgmt_smp_complete(struct hci_conn *conn, bool complete)
@@ -3947,9 +3950,10 @@ failed:
return err;
}
static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
static int service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
{
cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1);
return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status,
cmd->param, 1);
}
static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
@@ -4697,7 +4701,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_req_init(&req, hdev);
write_fast_connectable(&req, false);
hci_update_page_scan(hdev, &req);
__hci_update_page_scan(&req);
/* Since only the advertising data flags will change, there
* is no need to update the scan response data.
@@ -5091,10 +5095,11 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
return err;
}
static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
static int conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
{
struct hci_conn *conn = cmd->user_data;
struct mgmt_rp_get_conn_info rp;
int err;
memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
@@ -5108,11 +5113,13 @@ static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
rp.max_tx_power = HCI_TX_POWER_INVALID;
}
cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
&rp, sizeof(rp));
err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
&rp, sizeof(rp));
hci_conn_drop(conn);
hci_conn_put(conn);
return err;
}
static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status)
@@ -5286,11 +5293,12 @@ unlock:
return err;
}
static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
static int clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
{
struct hci_conn *conn = cmd->user_data;
struct mgmt_rp_get_clock_info rp;
struct hci_dev *hdev;
int err;
memset(&rp, 0, sizeof(rp));
memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
@@ -5310,12 +5318,15 @@ static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
}
complete:
cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp));
err = cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp,
sizeof(rp));
if (conn) {
hci_conn_drop(conn);
hci_conn_put(conn);
}
return err;
}
static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
@@ -5425,6 +5436,65 @@ unlock:
return err;
}
static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
{
struct hci_conn *conn;
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
if (!conn)
return false;
if (conn->dst_type != type)
return false;
if (conn->state != BT_CONNECTED)
return false;
return true;
}
/* This function requires the caller holds hdev->lock */
static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
u8 addr_type, u8 auto_connect)
{
struct hci_dev *hdev = req->hdev;
struct hci_conn_params *params;
params = hci_conn_params_add(hdev, addr, addr_type);
if (!params)
return -EIO;
if (params->auto_connect == auto_connect)
return 0;
list_del_init(&params->action);
switch (auto_connect) {
case HCI_AUTO_CONN_DISABLED:
case HCI_AUTO_CONN_LINK_LOSS:
__hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_REPORT:
list_add(&params->action, &hdev->pend_le_reports);
__hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
if (!is_connected(hdev, addr, addr_type)) {
list_add(&params->action, &hdev->pend_le_conns);
__hci_update_background_scan(req);
}
break;
}
params->auto_connect = auto_connect;
BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
auto_connect);
return 0;
}
static void device_added(struct sock *sk, struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 type, u8 action)
{
@@ -5437,10 +5507,31 @@ static void device_added(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk);
}
static void add_device_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
BT_DBG("status 0x%02x", status);
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_ADD_DEVICE, hdev);
if (!cmd)
goto unlock;
cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
unlock:
hci_dev_unlock(hdev);
}
static int add_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_add_device *cp = data;
struct pending_cmd *cmd;
struct hci_request req;
u8 auto_conn, addr_type;
int err;
@@ -5457,14 +5548,24 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
hci_req_init(&req, hdev);
hci_dev_lock(hdev);
cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEVICE, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto unlock;
}
cmd->cmd_complete = addr_cmd_complete;
if (cp->addr.type == BDADDR_BREDR) {
/* Only incoming connections action is supported for now */
if (cp->action != 0x01) {
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
err = cmd->cmd_complete(cmd,
MGMT_STATUS_INVALID_PARAMS);
mgmt_pending_remove(cmd);
goto unlock;
}
@@ -5473,7 +5574,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
if (err)
goto unlock;
hci_update_page_scan(hdev, NULL);
__hci_update_page_scan(&req);
goto added;
}
@@ -5493,19 +5594,25 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
/* If the connection parameters don't exist for this device,
* they will be created and configured with defaults.
*/
if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type,
auto_conn) < 0) {
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_FAILED,
&cp->addr, sizeof(cp->addr));
err = cmd->cmd_complete(cmd, MGMT_STATUS_FAILED);
mgmt_pending_remove(cmd);
goto unlock;
}
added:
device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
err = hci_req_run(&req, add_device_complete);
if (err < 0) {
/* ENODATA means no HCI commands were needed (e.g. if
* the adapter is powered off).
*/
if (err == -ENODATA)
err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
mgmt_pending_remove(cmd);
}
unlock:
hci_dev_unlock(hdev);
@@ -5523,24 +5630,55 @@ static void device_removed(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk);
}
static void remove_device_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
BT_DBG("status 0x%02x", status);
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
if (!cmd)
goto unlock;
cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
unlock:
hci_dev_unlock(hdev);
}
static int remove_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_remove_device *cp = data;
struct pending_cmd *cmd;
struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
hci_req_init(&req, hdev);
hci_dev_lock(hdev);
cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEVICE, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto unlock;
}
cmd->cmd_complete = addr_cmd_complete;
if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
struct hci_conn_params *params;
u8 addr_type;
if (!bdaddr_type_is_valid(cp->addr.type)) {
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
err = cmd->cmd_complete(cmd,
MGMT_STATUS_INVALID_PARAMS);
mgmt_pending_remove(cmd);
goto unlock;
}
@@ -5549,14 +5687,13 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
&cp->addr.bdaddr,
cp->addr.type);
if (err) {
err = cmd_complete(sk, hdev->id,
MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
err = cmd->cmd_complete(cmd,
MGMT_STATUS_INVALID_PARAMS);
mgmt_pending_remove(cmd);
goto unlock;
}
hci_update_page_scan(hdev, NULL);
__hci_update_page_scan(&req);
device_removed(sk, hdev, &cp->addr.bdaddr,
cp->addr.type);
@@ -5571,23 +5708,23 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
addr_type);
if (!params) {
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
err = cmd->cmd_complete(cmd,
MGMT_STATUS_INVALID_PARAMS);
mgmt_pending_remove(cmd);
goto unlock;
}
if (params->auto_connect == HCI_AUTO_CONN_DISABLED) {
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
err = cmd->cmd_complete(cmd,
MGMT_STATUS_INVALID_PARAMS);
mgmt_pending_remove(cmd);
goto unlock;
}
list_del(&params->action);
list_del(&params->list);
kfree(params);
hci_update_background_scan(hdev);
__hci_update_background_scan(&req);
device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
} else {
@@ -5595,9 +5732,9 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
struct bdaddr_list *b, *btmp;
if (cp->addr.type) {
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
err = cmd->cmd_complete(cmd,
MGMT_STATUS_INVALID_PARAMS);
mgmt_pending_remove(cmd);
goto unlock;
}
@@ -5607,7 +5744,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
kfree(b);
}
hci_update_page_scan(hdev, NULL);
__hci_update_page_scan(&req);
list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -5620,12 +5757,19 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
BT_DBG("All LE connection parameters were removed");
hci_update_background_scan(hdev);
__hci_update_background_scan(&req);
}
complete:
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
err = hci_req_run(&req, remove_device_complete);
if (err < 0) {
/* ENODATA means no HCI commands were needed (e.g. if
* the adapter is powered off).
*/
if (err == -ENODATA)
err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
mgmt_pending_remove(cmd);
}
unlock:
hci_dev_unlock(hdev);
@@ -6037,8 +6181,9 @@ void mgmt_index_removed(struct hci_dev *hdev)
}
/* This function requires the caller holds hdev->lock */
static void restart_le_actions(struct hci_dev *hdev)
static void restart_le_actions(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
struct hci_conn_params *p;
list_for_each_entry(p, &hdev->le_conn_params, list) {
@@ -6060,7 +6205,7 @@ static void restart_le_actions(struct hci_dev *hdev)
}
}
hci_update_background_scan(hdev);
__hci_update_background_scan(req);
}
static void powered_complete(struct hci_dev *hdev, u8 status)
@@ -6071,8 +6216,6 @@ static void powered_complete(struct hci_dev *hdev, u8 status)
hci_dev_lock(hdev);
restart_le_actions(hdev);
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
new_settings(hdev, match.sk);
@@ -6130,6 +6273,8 @@ static int powered_update_hci(struct hci_dev *hdev)
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
enable_advertising(&req);
restart_le_actions(&req);
}
link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
@@ -6139,7 +6284,7 @@ static int powered_update_hci(struct hci_dev *hdev)
if (lmp_bredr_capable(hdev)) {
write_fast_connectable(&req, false);
hci_update_page_scan(hdev, &req);
__hci_update_page_scan(&req);
update_class(&req);
update_name(&req);
update_eir(&req);

View File

@@ -771,7 +771,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
bacpy(&addr.l2_bdaddr, dst);
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
addr.l2_psm = cpu_to_le16(L2CAP_PSM_RFCOMM);
addr.l2_cid = 0;
addr.l2_bdaddr_type = BDADDR_BREDR;
*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
@@ -2038,7 +2038,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
/* Bind socket */
bacpy(&addr.l2_bdaddr, ba);
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
addr.l2_psm = cpu_to_le16(L2CAP_PSM_RFCOMM);
addr.l2_cid = 0;
addr.l2_bdaddr_type = BDADDR_BREDR;
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));

244
net/bluetooth/selftest.c Normal file
View File

@@ -0,0 +1,244 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2014 Intel Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "ecc.h"
#include "smp.h"
#include "selftest.h"
#if IS_ENABLED(CONFIG_BT_SELFTEST_ECDH)
static const u8 priv_a_1[32] __initconst = {
0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
};
static const u8 priv_b_1[32] __initconst = {
0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55,
};
static const u8 pub_a_1[64] __initconst = {
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
};
static const u8 pub_b_1[64] __initconst = {
0x90, 0xa1, 0xaa, 0x2f, 0xb2, 0x77, 0x90, 0x55,
0x9f, 0xa6, 0x15, 0x86, 0xfd, 0x8a, 0xb5, 0x47,
0x00, 0x4c, 0x9e, 0xf1, 0x84, 0x22, 0x59, 0x09,
0x96, 0x1d, 0xaf, 0x1f, 0xf0, 0xf0, 0xa1, 0x1e,
0x4a, 0x21, 0xb1, 0x15, 0xf9, 0xaf, 0x89, 0x5f,
0x76, 0x36, 0x8e, 0xe2, 0x30, 0x11, 0x2d, 0x47,
0x60, 0x51, 0xb8, 0x9a, 0x3a, 0x70, 0x56, 0x73,
0x37, 0xad, 0x9d, 0x42, 0x3e, 0xf3, 0x55, 0x4c,
};
static const u8 dhkey_1[32] __initconst = {
0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99,
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec,
};
static const u8 priv_a_2[32] __initconst = {
0x63, 0x76, 0x45, 0xd0, 0xf7, 0x73, 0xac, 0xb7,
0xff, 0xdd, 0x03, 0x72, 0xb9, 0x72, 0x85, 0xb4,
0x41, 0xb6, 0x5d, 0x0c, 0x5d, 0x54, 0x84, 0x60,
0x1a, 0xa3, 0x9a, 0x3c, 0x69, 0x16, 0xa5, 0x06,
};
static const u8 priv_b_2[32] __initconst = {
0xba, 0x30, 0x55, 0x50, 0x19, 0xa2, 0xca, 0xa3,
0xa5, 0x29, 0x08, 0xc6, 0xb5, 0x03, 0x88, 0x7e,
0x03, 0x2b, 0x50, 0x73, 0xd4, 0x2e, 0x50, 0x97,
0x64, 0xcd, 0x72, 0x0d, 0x67, 0xa0, 0x9a, 0x52,
};
static const u8 pub_a_2[64] __initconst = {
0xdd, 0x78, 0x5c, 0x74, 0x03, 0x9b, 0x7e, 0x98,
0xcb, 0x94, 0x87, 0x4a, 0xad, 0xfa, 0xf8, 0xd5,
0x43, 0x3e, 0x5c, 0xaf, 0xea, 0xb5, 0x4c, 0xf4,
0x9e, 0x80, 0x79, 0x57, 0x7b, 0xa4, 0x31, 0x2c,
0x4f, 0x5d, 0x71, 0x43, 0x77, 0x43, 0xf8, 0xea,
0xd4, 0x3e, 0xbd, 0x17, 0x91, 0x10, 0x21, 0xd0,
0x1f, 0x87, 0x43, 0x8e, 0x40, 0xe2, 0x52, 0xcd,
0xbe, 0xdf, 0x98, 0x38, 0x18, 0x12, 0x95, 0x91,
};
static const u8 pub_b_2[64] __initconst = {
0xcc, 0x00, 0x65, 0xe1, 0xf5, 0x6c, 0x0d, 0xcf,
0xec, 0x96, 0x47, 0x20, 0x66, 0xc9, 0xdb, 0x84,
0x81, 0x75, 0xa8, 0x4d, 0xc0, 0xdf, 0xc7, 0x9d,
0x1b, 0x3f, 0x3d, 0xf2, 0x3f, 0xe4, 0x65, 0xf4,
0x79, 0xb2, 0xec, 0xd8, 0xca, 0x55, 0xa1, 0xa8,
0x43, 0x4d, 0x6b, 0xca, 0x10, 0xb0, 0xc2, 0x01,
0xc2, 0x33, 0x4e, 0x16, 0x24, 0xc4, 0xef, 0xee,
0x99, 0xd8, 0xbb, 0xbc, 0x48, 0xd0, 0x01, 0x02,
};
static const u8 dhkey_2[32] __initconst = {
0x69, 0xeb, 0x21, 0x32, 0xf2, 0xc6, 0x05, 0x41,
0x60, 0x19, 0xcd, 0x5e, 0x94, 0xe1, 0xe6, 0x5f,
0x33, 0x07, 0xe3, 0x38, 0x4b, 0x68, 0xe5, 0x62,
0x3f, 0x88, 0x6d, 0x2f, 0x3a, 0x84, 0x85, 0xab,
};
static const u8 priv_a_3[32] __initconst = {
0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
};
static const u8 pub_a_3[64] __initconst = {
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
};
static const u8 dhkey_3[32] __initconst = {
0x2d, 0xab, 0x00, 0x48, 0xcb, 0xb3, 0x7b, 0xda,
0x55, 0x7b, 0x8b, 0x72, 0xa8, 0x57, 0x87, 0xc3,
0x87, 0x27, 0x99, 0x32, 0xfc, 0x79, 0x5f, 0xae,
0x7c, 0x1c, 0xf9, 0x49, 0xe6, 0xd7, 0xaa, 0x70,
};
static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32],
const u8 pub_a[64], const u8 pub_b[64],
const u8 dhkey[32])
{
u8 dhkey_a[32], dhkey_b[32];
ecdh_shared_secret(pub_b, priv_a, dhkey_a);
ecdh_shared_secret(pub_a, priv_b, dhkey_b);
if (memcmp(dhkey_a, dhkey, 32))
return -EINVAL;
if (memcmp(dhkey_b, dhkey, 32))
return -EINVAL;
return 0;
}
static int __init test_ecdh(void)
{
ktime_t calltime, delta, rettime;
unsigned long long duration;
int err;
calltime = ktime_get();
err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1);
if (err) {
BT_ERR("ECDH sample 1 failed");
return err;
}
err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2);
if (err) {
BT_ERR("ECDH sample 2 failed");
return err;
}
err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3);
if (err) {
BT_ERR("ECDH sample 3 failed");
return err;
}
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
BT_INFO("ECDH test passed in %lld usecs", duration);
return 0;
}
#else
static inline int test_ecdh(void)
{
return 0;
}
#endif
static int __init run_selftest(void)
{
int err;
BT_INFO("Starting self testing");
err = test_ecdh();
if (err)
goto done;
err = bt_selftest_smp();
done:
BT_INFO("Finished self testing");
return err;
}
#if IS_MODULE(CONFIG_BT)
/* This is run when CONFIG_BT_SELFTEST=y and CONFIG_BT=m and is just a
* wrapper to allow running this at module init.
*
* If CONFIG_BT_SELFTEST=n, then this code is not compiled at all.
*/
int __init bt_selftest(void)
{
return run_selftest();
}
#else
/* This is run when CONFIG_BT_SELFTEST=y and CONFIG_BT=y and is run
* via late_initcall() as last item in the initialization sequence.
*
* If CONFIG_BT_SELFTEST=n, then this code is not compiled at all.
*/
static int __init bt_selftest_init(void)
{
return run_selftest();
}
late_initcall(bt_selftest_init);
#endif

45
net/bluetooth/selftest.h Normal file
View File

@@ -0,0 +1,45 @@
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2014 Intel Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#if IS_ENABLED(CONFIG_BT_SELFTEST) && IS_MODULE(CONFIG_BT)
/* When CONFIG_BT_SELFTEST=y and the CONFIG_BT=m, then the self testing
* is run at module loading time.
*/
int bt_selftest(void);
#else
/* When CONFIG_BT_SELFTEST=y and CONFIG_BT=y, then the self testing
* is run via late_initcall() to make sure that subsys_initcall() of
* the Bluetooth subsystem and device_initcall() of the Crypto subsystem
* do not clash.
*
* When CONFIG_BT_SELFTEST=n, then this turns into an empty call that
* has no impact.
*/
static inline int bt_selftest(void)
{
return 0;
}
#endif

View File

@@ -223,8 +223,9 @@ static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
return err;
}
static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16])
static int smp_f5(struct crypto_hash *tfm_cmac, const u8 w[32],
const u8 n1[16], const u8 n2[16], const u8 a1[7],
const u8 a2[7], u8 mackey[16], u8 ltk[16])
{
/* The btle, salt and length "magic" values are as defined in
* the SMP section of the Bluetooth core specification. In ASCII
@@ -276,7 +277,7 @@ static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
}
static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
const u8 n1[16], u8 n2[16], const u8 r[16],
const u8 n1[16], const u8 n2[16], const u8 r[16],
const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
u8 res[16])
{
@@ -3021,3 +3022,331 @@ void smp_unregister(struct hci_dev *hdev)
smp_del_chan(chan);
}
}
#if IS_ENABLED(CONFIG_BT_SELFTEST_SMP)
static int __init test_ah(struct crypto_blkcipher *tfm_aes)
{
const u8 irk[16] = {
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
const u8 r[3] = { 0x94, 0x81, 0x70 };
const u8 exp[3] = { 0xaa, 0xfb, 0x0d };
u8 res[3];
int err;
err = smp_ah(tfm_aes, irk, r, res);
if (err)
return err;
if (memcmp(res, exp, 3))
return -EINVAL;
return 0;
}
static int __init test_c1(struct crypto_blkcipher *tfm_aes)
{
const u8 k[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const u8 r[16] = {
0xe0, 0x2e, 0x70, 0xc6, 0x4e, 0x27, 0x88, 0x63,
0x0e, 0x6f, 0xad, 0x56, 0x21, 0xd5, 0x83, 0x57 };
const u8 preq[7] = { 0x01, 0x01, 0x00, 0x00, 0x10, 0x07, 0x07 };
const u8 pres[7] = { 0x02, 0x03, 0x00, 0x00, 0x08, 0x00, 0x05 };
const u8 _iat = 0x01;
const u8 _rat = 0x00;
const bdaddr_t ra = { { 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1 } };
const bdaddr_t ia = { { 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1 } };
const u8 exp[16] = {
0x86, 0x3b, 0xf1, 0xbe, 0xc5, 0x4d, 0xa7, 0xd2,
0xea, 0x88, 0x89, 0x87, 0xef, 0x3f, 0x1e, 0x1e };
u8 res[16];
int err;
err = smp_c1(tfm_aes, k, r, preq, pres, _iat, &ia, _rat, &ra, res);
if (err)
return err;
if (memcmp(res, exp, 16))
return -EINVAL;
return 0;
}
static int __init test_s1(struct crypto_blkcipher *tfm_aes)
{
const u8 k[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const u8 r1[16] = {
0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 };
const u8 r2[16] = {
0x00, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99 };
const u8 exp[16] = {
0x62, 0xa0, 0x6d, 0x79, 0xae, 0x16, 0x42, 0x5b,
0x9b, 0xf4, 0xb0, 0xe8, 0xf0, 0xe1, 0x1f, 0x9a };
u8 res[16];
int err;
err = smp_s1(tfm_aes, k, r1, r2, res);
if (err)
return err;
if (memcmp(res, exp, 16))
return -EINVAL;
return 0;
}
static int __init test_f4(struct crypto_hash *tfm_cmac)
{
const u8 u[32] = {
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 };
const u8 v[32] = {
0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 };
const u8 x[16] = {
0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
const u8 z = 0x00;
const u8 exp[16] = {
0x2d, 0x87, 0x74, 0xa9, 0xbe, 0xa1, 0xed, 0xf1,
0x1c, 0xbd, 0xa9, 0x07, 0xf1, 0x16, 0xc9, 0xf2 };
u8 res[16];
int err;
err = smp_f4(tfm_cmac, u, v, x, z, res);
if (err)
return err;
if (memcmp(res, exp, 16))
return -EINVAL;
return 0;
}
static int __init test_f5(struct crypto_hash *tfm_cmac)
{
const u8 w[32] = {
0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99,
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
const u8 n1[16] = {
0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
const u8 n2[16] = {
0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
const u8 a1[7] = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56, 0x00 };
const u8 a2[7] = { 0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7, 0x00 };
const u8 exp_ltk[16] = {
0x38, 0x0a, 0x75, 0x94, 0xb5, 0x22, 0x05, 0x98,
0x23, 0xcd, 0xd7, 0x69, 0x11, 0x79, 0x86, 0x69 };
const u8 exp_mackey[16] = {
0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 };
u8 mackey[16], ltk[16];
int err;
err = smp_f5(tfm_cmac, w, n1, n2, a1, a2, mackey, ltk);
if (err)
return err;
if (memcmp(mackey, exp_mackey, 16))
return -EINVAL;
if (memcmp(ltk, exp_ltk, 16))
return -EINVAL;
return 0;
}
static int __init test_f6(struct crypto_hash *tfm_cmac)
{
const u8 w[16] = {
0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 };
const u8 n1[16] = {
0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
const u8 n2[16] = {
0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
const u8 r[16] = {
0xc8, 0x0f, 0x2d, 0x0c, 0xd2, 0x42, 0xda, 0x08,
0x54, 0xbb, 0x53, 0xb4, 0x3b, 0x34, 0xa3, 0x12 };
const u8 io_cap[3] = { 0x02, 0x01, 0x01 };
const u8 a1[7] = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56, 0x00 };
const u8 a2[7] = { 0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7, 0x00 };
const u8 exp[16] = {
0x61, 0x8f, 0x95, 0xda, 0x09, 0x0b, 0x6c, 0xd2,
0xc5, 0xe8, 0xd0, 0x9c, 0x98, 0x73, 0xc4, 0xe3 };
u8 res[16];
int err;
err = smp_f6(tfm_cmac, w, n1, n2, r, io_cap, a1, a2, res);
if (err)
return err;
if (memcmp(res, exp, 16))
return -EINVAL;
return 0;
}
static int __init test_g2(struct crypto_hash *tfm_cmac)
{
const u8 u[32] = {
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 };
const u8 v[32] = {
0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 };
const u8 x[16] = {
0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
const u8 y[16] = {
0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
const u32 exp_val = 0x2f9ed5ba % 1000000;
u32 val;
int err;
err = smp_g2(tfm_cmac, u, v, x, y, &val);
if (err)
return err;
if (val != exp_val)
return -EINVAL;
return 0;
}
static int __init test_h6(struct crypto_hash *tfm_cmac)
{
const u8 w[16] = {
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
const u8 key_id[4] = { 0x72, 0x62, 0x65, 0x6c };
const u8 exp[16] = {
0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8,
0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d };
u8 res[16];
int err;
err = smp_h6(tfm_cmac, w, key_id, res);
if (err)
return err;
if (memcmp(res, exp, 16))
return -EINVAL;
return 0;
}
static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
struct crypto_hash *tfm_cmac)
{
ktime_t calltime, delta, rettime;
unsigned long long duration;
int err;
calltime = ktime_get();
err = test_ah(tfm_aes);
if (err) {
BT_ERR("smp_ah test failed");
return err;
}
err = test_c1(tfm_aes);
if (err) {
BT_ERR("smp_c1 test failed");
return err;
}
err = test_s1(tfm_aes);
if (err) {
BT_ERR("smp_s1 test failed");
return err;
}
err = test_f4(tfm_cmac);
if (err) {
BT_ERR("smp_f4 test failed");
return err;
}
err = test_f5(tfm_cmac);
if (err) {
BT_ERR("smp_f5 test failed");
return err;
}
err = test_f6(tfm_cmac);
if (err) {
BT_ERR("smp_f6 test failed");
return err;
}
err = test_g2(tfm_cmac);
if (err) {
BT_ERR("smp_g2 test failed");
return err;
}
err = test_h6(tfm_cmac);
if (err) {
BT_ERR("smp_h6 test failed");
return err;
}
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
BT_INFO("SMP test passed in %lld usecs", duration);
return 0;
}
int __init bt_selftest_smp(void)
{
struct crypto_blkcipher *tfm_aes;
struct crypto_hash *tfm_cmac;
int err;
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm_aes)) {
BT_ERR("Unable to create ECB crypto context");
return PTR_ERR(tfm_aes);
}
tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm_cmac)) {
BT_ERR("Unable to create CMAC crypto context");
crypto_free_blkcipher(tfm_aes);
return PTR_ERR(tfm_cmac);
}
err = run_selftests(tfm_aes, tfm_cmac);
crypto_free_hash(tfm_cmac);
crypto_free_blkcipher(tfm_aes);
return err;
}
#endif

View File

@@ -192,4 +192,17 @@ int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
int smp_register(struct hci_dev *hdev);
void smp_unregister(struct hci_dev *hdev);
#if IS_ENABLED(CONFIG_BT_SELFTEST_SMP)
int bt_selftest_smp(void);
#else
static inline int bt_selftest_smp(void)
{
return 0;
}
#endif
#endif /* __SMP_H */