Commit 970282d0 authored by David S. Miller's avatar David S. Miller

Merge branch 'for-upstream' of...

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

Johan Hedberg says:

====================
pull request: bluetooth-next 2015-03-19

This wont the last 4.1 bluetooth-next pull request, but we've piled up
enough patches in less than a week that I wanted to save you from a
single huge "last-minute" pull somewhere closer to the merge window.

The main changes are:

 - Simultaneous LE & BR/EDR discovery support for HW that can do it
 - Complete LE OOB pairing support
 - More fine-grained mgmt-command access control (normal user can now do
   harmless read-only operations).
 - Added RF power amplifier support in cc2520 ieee802154 driver
 - Some cleanups/fixes in ieee802154 code

Please let me know if there are any issues pulling. Thanks.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c9bdc0dd ea6edfbc
......@@ -13,11 +13,15 @@ Required properties:
- cca-gpio: GPIO spec for the CCA pin
- vreg-gpio: GPIO spec for the VREG pin
- reset-gpio: GPIO spec for the RESET pin
Optional properties:
- amplified: include if the CC2520 is connected to a CC2591 amplifier
Example:
cc2520@0 {
compatible = "ti,cc2520";
reg = <0>;
spi-max-frequency = <4000000>;
amplified;
pinctrl-names = "default";
pinctrl-0 = <&cc2520_cape_pins>;
fifo-gpio = <&gpio1 18 0>;
......
......@@ -215,8 +215,8 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
/* QCA ROME chipset */
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME},
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME},
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
/* Broadcom BCM2035 */
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
......@@ -3019,6 +3019,7 @@ static int btusb_probe(struct usb_interface *intf,
hdev->shutdown = btusb_shutdown_intel;
hdev->set_bdaddr = btusb_set_bdaddr_intel;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
}
if (id->driver_info & BTUSB_INTEL_NEW) {
......@@ -3042,6 +3043,7 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_ATH3012) {
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
}
......@@ -3085,6 +3087,8 @@ static int btusb_probe(struct usb_interface *intf,
/* Fake CSR devices with broken commands */
if (bcdDevice <= 0x100)
hdev->setup = btusb_setup_csr;
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
}
if (id->driver_info & BTUSB_SNIFFER) {
......
......@@ -325,7 +325,7 @@ at86rf230_read_subreg(struct at86rf230_local *lp,
int rc;
rc = __at86rf230_read(lp, addr, data);
if (rc > 0)
if (!rc)
*data = (*data & mask) >> shift;
return rc;
......
......@@ -714,11 +714,45 @@ static irqreturn_t cc2520_sfd_isr(int irq, void *data)
return IRQ_HANDLED;
}
static int cc2520_get_platform_data(struct spi_device *spi,
struct cc2520_platform_data *pdata)
{
struct device_node *np = spi->dev.of_node;
struct cc2520_private *priv = spi_get_drvdata(spi);
if (!np) {
struct cc2520_platform_data *spi_pdata = spi->dev.platform_data;
if (!spi_pdata)
return -ENOENT;
*pdata = *spi_pdata;
return 0;
}
pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0);
priv->fifo_pin = pdata->fifo;
pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0);
pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0);
pdata->cca = of_get_named_gpio(np, "cca-gpio", 0);
pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
pdata->amplified = of_property_read_bool(np, "amplified");
return 0;
}
static int cc2520_hw_init(struct cc2520_private *priv)
{
u8 status = 0, state = 0xff;
int ret;
int timeout = 100;
struct cc2520_platform_data pdata;
ret = cc2520_get_platform_data(priv->spi, &pdata);
if (ret)
goto err_ret;
ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state);
if (ret)
......@@ -741,11 +775,47 @@ static int cc2520_hw_init(struct cc2520_private *priv)
dev_vdbg(&priv->spi->dev, "oscillator brought up\n");
/* Registers default value: section 28.1 in Datasheet */
/* If the CC2520 is connected to a CC2591 amplifier, we must both
* configure GPIOs on the CC2520 to correctly configure the CC2591
* and change a couple settings of the CC2520 to work with the
* amplifier. See section 8 page 17 of TI application note AN065.
* http://www.ti.com/lit/an/swra229a/swra229a.pdf
*/
if (pdata.amplified) {
ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF9);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_GPIOCTRL0, 0x46);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_GPIOCTRL5, 0x47);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_GPIOPOLARITY, 0x1e);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_TXCTRL, 0xc1);
if (ret)
goto err_ret;
} else {
ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11);
if (ret)
goto err_ret;
}
/* Registers default value: section 28.1 in Datasheet */
ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A);
if (ret)
goto err_ret;
......@@ -770,10 +840,6 @@ static int cc2520_hw_init(struct cc2520_private *priv)
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10);
if (ret)
goto err_ret;
......@@ -808,40 +874,10 @@ static int cc2520_hw_init(struct cc2520_private *priv)
return ret;
}
static struct cc2520_platform_data *
cc2520_get_platform_data(struct spi_device *spi)
{
struct cc2520_platform_data *pdata;
struct device_node *np = spi->dev.of_node;
struct cc2520_private *priv = spi_get_drvdata(spi);
if (!np)
return spi->dev.platform_data;
pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
goto done;
pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0);
priv->fifo_pin = pdata->fifo;
pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0);
pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0);
pdata->cca = of_get_named_gpio(np, "cca-gpio", 0);
pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
spi->dev.platform_data = pdata;
done:
return pdata;
}
static int cc2520_probe(struct spi_device *spi)
{
struct cc2520_private *priv;
struct cc2520_platform_data *pdata;
struct cc2520_platform_data pdata;
int ret;
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
......@@ -850,8 +886,8 @@ static int cc2520_probe(struct spi_device *spi)
spi_set_drvdata(spi, priv);
pdata = cc2520_get_platform_data(spi);
if (!pdata) {
ret = cc2520_get_platform_data(spi, &pdata);
if (ret < 0) {
dev_err(&spi->dev, "no platform data\n");
return -EINVAL;
}
......@@ -869,76 +905,76 @@ static int cc2520_probe(struct spi_device *spi)
init_completion(&priv->tx_complete);
/* Request all the gpio's */
if (!gpio_is_valid(pdata->fifo)) {
if (!gpio_is_valid(pdata.fifo)) {
dev_err(&spi->dev, "fifo gpio is not valid\n");
ret = -EINVAL;
goto err_hw_init;
}
ret = devm_gpio_request_one(&spi->dev, pdata->fifo,
ret = devm_gpio_request_one(&spi->dev, pdata.fifo,
GPIOF_IN, "fifo");
if (ret)
goto err_hw_init;
if (!gpio_is_valid(pdata->cca)) {
if (!gpio_is_valid(pdata.cca)) {
dev_err(&spi->dev, "cca gpio is not valid\n");
ret = -EINVAL;
goto err_hw_init;
}
ret = devm_gpio_request_one(&spi->dev, pdata->cca,
ret = devm_gpio_request_one(&spi->dev, pdata.cca,
GPIOF_IN, "cca");
if (ret)
goto err_hw_init;
if (!gpio_is_valid(pdata->fifop)) {
if (!gpio_is_valid(pdata.fifop)) {
dev_err(&spi->dev, "fifop gpio is not valid\n");
ret = -EINVAL;
goto err_hw_init;
}
ret = devm_gpio_request_one(&spi->dev, pdata->fifop,
ret = devm_gpio_request_one(&spi->dev, pdata.fifop,
GPIOF_IN, "fifop");
if (ret)
goto err_hw_init;
if (!gpio_is_valid(pdata->sfd)) {
if (!gpio_is_valid(pdata.sfd)) {
dev_err(&spi->dev, "sfd gpio is not valid\n");
ret = -EINVAL;
goto err_hw_init;
}
ret = devm_gpio_request_one(&spi->dev, pdata->sfd,
ret = devm_gpio_request_one(&spi->dev, pdata.sfd,
GPIOF_IN, "sfd");
if (ret)
goto err_hw_init;
if (!gpio_is_valid(pdata->reset)) {
if (!gpio_is_valid(pdata.reset)) {
dev_err(&spi->dev, "reset gpio is not valid\n");
ret = -EINVAL;
goto err_hw_init;
}
ret = devm_gpio_request_one(&spi->dev, pdata->reset,
ret = devm_gpio_request_one(&spi->dev, pdata.reset,
GPIOF_OUT_INIT_LOW, "reset");
if (ret)
goto err_hw_init;
if (!gpio_is_valid(pdata->vreg)) {
if (!gpio_is_valid(pdata.vreg)) {
dev_err(&spi->dev, "vreg gpio is not valid\n");
ret = -EINVAL;
goto err_hw_init;
}
ret = devm_gpio_request_one(&spi->dev, pdata->vreg,
ret = devm_gpio_request_one(&spi->dev, pdata.vreg,
GPIOF_OUT_INIT_LOW, "vreg");
if (ret)
goto err_hw_init;
gpio_set_value(pdata->vreg, HIGH);
gpio_set_value(pdata.vreg, HIGH);
usleep_range(100, 150);
gpio_set_value(pdata->reset, HIGH);
gpio_set_value(pdata.reset, HIGH);
usleep_range(200, 250);
ret = cc2520_hw_init(priv);
......@@ -947,7 +983,7 @@ static int cc2520_probe(struct spi_device *spi)
/* Set up fifop interrupt */
ret = devm_request_irq(&spi->dev,
gpio_to_irq(pdata->fifop),
gpio_to_irq(pdata.fifop),
cc2520_fifop_isr,
IRQF_TRIGGER_RISING,
dev_name(&spi->dev),
......@@ -959,7 +995,7 @@ static int cc2520_probe(struct spi_device *spi)
/* Set up sfd interrupt */
ret = devm_request_irq(&spi->dev,
gpio_to_irq(pdata->sfd),
gpio_to_irq(pdata.sfd),
cc2520_sfd_isr,
IRQF_TRIGGER_FALLING,
dev_name(&spi->dev),
......
......@@ -21,6 +21,7 @@ struct cc2520_platform_data {
int sfd;
int reset;
int vreg;
bool amplified;
};
#endif
......@@ -335,6 +335,11 @@ static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk,
int bt_to_errno(__u16 code);
void hci_sock_set_flag(struct sock *sk, int nr);
void hci_sock_clear_flag(struct sock *sk, int nr);
int hci_sock_test_flag(struct sock *sk, int nr);
unsigned short hci_sock_get_channel(struct sock *sk);
int hci_sock_init(void);
void hci_sock_cleanup(void);
......
......@@ -160,6 +160,14 @@ enum {
* during the hdev->setup vendor callback.
*/
HCI_QUIRK_STRICT_DUPLICATE_FILTER,
/* When this quirk is set, LE scan and BR/EDR inquiry is done
* simultaneously, otherwise it's interleaved.
*
* This quirk can be set before hci_register_dev is called or
* during the hdev->setup vendor callback.
*/
HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
};
/* HCI device flags */
......@@ -179,6 +187,16 @@ enum {
HCI_RESET,
};
/* HCI socket flags */
enum {
HCI_SOCK_TRUSTED,
HCI_MGMT_INDEX_EVENTS,
HCI_MGMT_UNCONF_INDEX_EVENTS,
HCI_MGMT_EXT_INDEX_EVENTS,
HCI_MGMT_GENERIC_EVENTS,
HCI_MGMT_OOB_DATA_EVENTS,
};
/*
* BR/EDR and/or LE controller flags: the flags defined here should represent
* states from the controller.
......@@ -447,6 +465,10 @@ enum {
#define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */
#define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */
#define EIR_DEVICE_ID 0x10 /* device ID */
#define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */
#define EIR_LE_ROLE 0x1C /* LE role */
#define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */
#define EIR_LE_SC_RANDOM 0x23 /* LE SC Random Value */
/* Low Energy Advertising Flags */
#define LE_AD_LIMITED 0x01 /* Limited Discoverable */
......
......@@ -596,7 +596,6 @@ enum {
HCI_CONN_SC_ENABLED,
HCI_CONN_AES_CCM,
HCI_CONN_POWER_SAVE,
HCI_CONN_REMOTE_OOB,
HCI_CONN_FLUSH_KEY,
HCI_CONN_ENCRYPT,
HCI_CONN_AUTH,
......@@ -1284,14 +1283,15 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
/* ----- HCI Sockets ----- */
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
struct sock *skip_sk);
int flag, struct sock *skip_sk);
void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
void hci_sock_dev_event(struct hci_dev *hdev, int event);
#define HCI_MGMT_VAR_LEN (1 << 0)
#define HCI_MGMT_NO_HDEV (1 << 1)
#define HCI_MGMT_UNCONFIGURED (1 << 2)
#define HCI_MGMT_VAR_LEN BIT(0)
#define HCI_MGMT_NO_HDEV BIT(1)
#define HCI_MGMT_UNTRUSTED BIT(2)
#define HCI_MGMT_UNCONFIGURED BIT(3)
struct hci_mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
......@@ -1305,6 +1305,7 @@ struct hci_mgmt_chan {
unsigned short channel;
size_t handler_count;
const struct hci_mgmt_handler *handlers;
void (*hdev_init) (struct sock *sk, struct hci_dev *hdev);
};
int hci_mgmt_chan_register(struct hci_mgmt_chan *c);
......@@ -1329,9 +1330,6 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c);
#define DISCOV_BREDR_INQUIRY_LEN 0x08
#define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */
int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk,
struct msghdr *msg, size_t msglen);
int mgmt_new_settings(struct hci_dev *hdev);
void mgmt_index_added(struct hci_dev *hdev);
void mgmt_index_removed(struct hci_dev *hdev);
......
......@@ -44,6 +44,7 @@
#define MGMT_STATUS_INVALID_INDEX 0x11
#define MGMT_STATUS_RFKILLED 0x12
#define MGMT_STATUS_ALREADY_PAIRED 0x13
#define MGMT_STATUS_PERMISSION_DENIED 0x14
struct mgmt_hdr {
__le16 opcode;
......@@ -505,6 +506,39 @@ struct mgmt_cp_start_service_discovery {
} __packed;
#define MGMT_START_SERVICE_DISCOVERY_SIZE 4
#define MGMT_OP_READ_LOCAL_OOB_EXT_DATA 0x003B
struct mgmt_cp_read_local_oob_ext_data {
__u8 type;
} __packed;
#define MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE 1
struct mgmt_rp_read_local_oob_ext_data {
__u8 type;
__le16 eir_len;
__u8 eir[0];
} __packed;
#define MGMT_OP_READ_EXT_INDEX_LIST 0x003C
#define MGMT_READ_EXT_INDEX_LIST_SIZE 0
struct mgmt_rp_read_ext_index_list {
__le16 num_controllers;
struct {
__le16 index;
__u8 type;
__u8 bus;
} entry[0];
} __packed;
#define MGMT_OP_READ_ADV_FEATURES 0x0003D
#define MGMT_READ_ADV_FEATURES_SIZE 0
struct mgmt_rp_read_adv_features {
__le32 supported_flags;
__u8 max_adv_data_len;
__u8 max_scan_rsp_len;
__u8 max_instances;
__u8 num_instances;
__u8 instance[0];
} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
......@@ -692,3 +726,19 @@ struct mgmt_ev_new_conn_param {
#define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e
#define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f
struct mgmt_ev_ext_index {
__u8 type;
__u8 bus;
} __packed;
#define MGMT_EV_EXT_INDEX_ADDED 0x0020
#define MGMT_EV_EXT_INDEX_REMOVED 0x0021
#define MGMT_EV_LOCAL_OOB_DATA_UPDATED 0x0022
struct mgmt_ev_local_oob_data_updated {
__u8 type;
__le16 eir_len;
__u8 eir[0];
} __packed;
......@@ -13,7 +13,7 @@ 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 hci_request.o
a2mp.o amp.o ecc.o hci_request.o mgmt_util.o
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
......
......@@ -2902,12 +2902,26 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
&hdev->quirks)) {
/* If we were running LE only scan, change discovery
* state. If we were running both LE and BR/EDR inquiry
* simultaneously, and BR/EDR inquiry is already
* finished, stop discovery, otherwise BR/EDR inquiry
* will stop discovery when finished.
*/
if (!test_bit(HCI_INQUIRY, &hdev->flags))
hci_discovery_set_state(hdev,
DISCOVERY_STOPPED);
} else {
hci_inquiry_cache_flush(hdev);
err = hci_req_run(&req, inquiry_complete);
if (err) {
BT_ERR("Inquiry request failed: err %d", err);
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
hci_discovery_set_state(hdev,
DISCOVERY_STOPPED);
}
}
hci_dev_unlock(hdev);
......
......@@ -166,7 +166,7 @@ static int remote_oob_show(struct seq_file *f, void *ptr)
seq_printf(f, "%pMR (type %u) %u %*phN %*phN %*phN %*phN\n",
&data->bdaddr, data->bdaddr_type, data->present,
16, data->hash192, 16, data->rand192,
16, data->hash256, 19, data->rand256);
16, data->hash256, 16, data->rand256);
}
hci_dev_unlock(hdev);
......
......@@ -2126,6 +2126,15 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
goto unlock;
if (list_empty(&discov->resolve)) {
/* When BR/EDR inquiry is active and no LE scanning is in
* progress, then change discovery state to indicate completion.
*
* When running LE scanning and BR/EDR inquiry simultaneously
* and the LE scan already finished, then change the discovery
* state to indicate completion.
*/
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
!test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
goto unlock;
}
......@@ -2135,6 +2144,15 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
e->name_state = NAME_PENDING;
hci_discovery_set_state(hdev, DISCOVERY_RESOLVING);
} else {
/* When BR/EDR inquiry is active and no LE scanning is in
* progress, then change discovery state to indicate completion.
*
* When running LE scanning and BR/EDR inquiry simultaneously
* and the LE scan already finished, then change the discovery
* state to indicate completion.
*/
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
!test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
}
......@@ -3889,7 +3907,6 @@ static u8 bredr_oob_data_present(struct hci_conn *conn)
if (!data)
return 0x00;
if (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) {
if (bredr_sc_enabled(hdev)) {
/* When Secure Connections is enabled, then just
* return the present value stored with the OOB
......@@ -3921,9 +3938,6 @@ static u8 bredr_oob_data_present(struct hci_conn *conn)
return 0x00;
return 0x01;
}
return 0x00;
}
static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
......@@ -4010,8 +4024,6 @@ static void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->remote_cap = ev->capability;
conn->remote_auth = ev->authentication;
if (ev->oob_data)
set_bit(HCI_CONN_REMOTE_OOB, &conn->flags);
unlock:
hci_dev_unlock(hdev);
......
......@@ -30,6 +30,9 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci_mon.h>
#include <net/bluetooth/mgmt.h>
#include "mgmt_util.h"
static LIST_HEAD(mgmt_chan_list);
static DEFINE_MUTEX(mgmt_chan_list_lock);
......@@ -47,8 +50,29 @@ struct hci_pinfo {
struct hci_filter filter;
__u32 cmsg_mask;
unsigned short channel;
unsigned long flags;
};
void hci_sock_set_flag(struct sock *sk, int nr)
{
set_bit(nr, &hci_pi(sk)->flags);
}
void hci_sock_clear_flag(struct sock *sk, int nr)
{
clear_bit(nr, &hci_pi(sk)->flags);
}
int hci_sock_test_flag(struct sock *sk, int nr)
{
return test_bit(nr, &hci_pi(sk)->flags);
}
unsigned short hci_sock_get_channel(struct sock *sk)
{
return hci_pi(sk)->channel;
}
static inline int hci_test_bit(int nr, const void *addr)
{
return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
......@@ -188,7 +212,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
/* Send frame to sockets with specific channel */
void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
struct sock *skip_sk)
int flag, struct sock *skip_sk)
{
struct sock *sk;
......@@ -199,6 +223,10 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
sk_for_each(sk, &hci_sk_list.head) {
struct sk_buff *nskb;
/* Ignore socket without the flag set */
if (!hci_sock_test_flag(sk, flag))
continue;
/* Skip the original socket */
if (sk == skip_sk)
continue;
......@@ -266,7 +294,8 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len);
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy, NULL);
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb_copy);
}
......@@ -373,7 +402,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
skb = create_monitor_event(hdev, event);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, NULL);
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
......@@ -752,6 +782,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
goto done;
}
/* The monitor interface is restricted to CAP_NET_RAW
* capabilities and with that implicitly trusted.
*/
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
send_monitor_replay(sk);
atomic_inc(&monitor_promisc);
......@@ -768,11 +803,29 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
goto done;
}
if (!capable(CAP_NET_ADMIN)) {
err = -EPERM;
goto done;
/* Users with CAP_NET_ADMIN capabilities are allowed
* access to all management commands and events. For
* untrusted users the interface is restricted and
* also only untrusted events are sent.
*/
if (capable(CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
/* At the moment the index and unconfigured index events
* are enabled unconditionally. Setting them on each
* socket when binding keeps this functionality. They
* however might be cleared later and then sending of these
* events will be disabled, but that is then intentional.
*
* This also enables generic events that are safe to be
* received by untrusted users. Example for such events
* are changes to settings, class of device, name etc.
*/
if (haddr.hci_channel == HCI_CHANNEL_CONTROL) {
hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS);
hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS);
hci_sock_set_flag(sk, HCI_MGMT_GENERIC_EVENTS);
}
break;
}
......@@ -901,6 +954,117 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
return err ? : copied;
}
static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk,
struct msghdr *msg, size_t msglen)
{
void *buf;
u8 *cp;
struct mgmt_hdr *hdr;
u16 opcode, index, len;
struct hci_dev *hdev = NULL;
const struct hci_mgmt_handler *handler;
bool var_len, no_hdev;
int err;
BT_DBG("got %zu bytes", msglen);
if (msglen < sizeof(*hdr))
return -EINVAL;
buf = kmalloc(msglen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (memcpy_from_msg(buf, msg, msglen)) {
err = -EFAULT;
goto done;
}
hdr = buf;
opcode = __le16_to_cpu(hdr->opcode);
index = __le16_to_cpu(hdr->index);
len = __le16_to_cpu(hdr->len);
if (len != msglen - sizeof(*hdr)) {
err = -EINVAL;
goto done;
}
if (opcode >= chan->handler_count ||
chan->handlers[opcode].func == NULL) {
BT_DBG("Unknown op %u", opcode);
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_UNKNOWN_COMMAND);
goto done;
}
handler = &chan->handlers[opcode];
if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) &&
!(handler->flags & HCI_MGMT_UNTRUSTED)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_PERMISSION_DENIED);
goto done;
}
if (index != MGMT_INDEX_NONE) {
hdev = hci_dev_get(index);
if (!hdev) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
if (hci_dev_test_flag(hdev, HCI_SETUP) ||
hci_dev_test_flag(hdev, HCI_CONFIG) ||
hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
!(handler->flags & HCI_MGMT_UNCONFIGURED)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
}
no_hdev = (handler->flags & HCI_MGMT_NO_HDEV);
if (no_hdev != !hdev) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
var_len = (handler->flags & HCI_MGMT_VAR_LEN);
if ((var_len && len < handler->data_len) ||
(!var_len && len != handler->data_len)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_PARAMS);
goto done;
}
if (hdev && chan->hdev_init)
chan->hdev_init(sk, hdev);
cp = buf + sizeof(*hdr);
err = handler->func(sk, hdev, cp, len);
if (err < 0)
goto done;
err = msglen;
done:
if (hdev)
hci_dev_put(hdev);
kfree(buf);
return err;
}
static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
size_t len)
{
......@@ -934,7 +1098,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
mutex_lock(&mgmt_chan_list_lock);
chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
if (chan)
err = mgmt_control(chan, sk, msg, len);
err = hci_mgmt_cmd(chan, sk, msg, len);
else
err = -EINVAL;
......
......@@ -35,6 +35,7 @@
#include "hci_request.h"
#include "smp.h"
#include "mgmt_util.h"
#define MGMT_VERSION 1
#define MGMT_REVISION 9
......@@ -96,6 +97,9 @@ static const u16 mgmt_commands[] = {
MGMT_OP_SET_EXTERNAL_CONFIG,
MGMT_OP_SET_PUBLIC_ADDRESS,
MGMT_OP_START_SERVICE_DISCOVERY,
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
MGMT_OP_READ_EXT_INDEX_LIST,
MGMT_OP_READ_ADV_FEATURES,
};
static const u16 mgmt_events[] = {
......@@ -128,6 +132,9 @@ static const u16 mgmt_events[] = {
MGMT_EV_UNCONF_INDEX_ADDED,
MGMT_EV_UNCONF_INDEX_REMOVED,
MGMT_EV_NEW_CONFIG_OPTIONS,
MGMT_EV_EXT_INDEX_ADDED,
MGMT_EV_EXT_INDEX_REMOVED,
MGMT_EV_LOCAL_OOB_DATA_UPDATED,
};
#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
......@@ -135,17 +142,6 @@ static const u16 mgmt_events[] = {
#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00"
struct mgmt_pending_cmd {
struct list_head list;
u16 opcode;
int index;
void *param;
size_t param_len;
struct sock *sk;
void *user_data;
int (*cmd_complete)(struct mgmt_pending_cmd *cmd, u8 status);
};
/* HCI to MGMT error code conversion table */
static u8 mgmt_status_table[] = {
MGMT_STATUS_SUCCESS,
......@@ -219,106 +215,32 @@ static u8 mgmt_status(u8 hci_status)
return MGMT_STATUS_FAILED;
}
static int mgmt_send_event(u16 event, struct hci_dev *hdev,
unsigned short channel, void *data, u16 data_len,
struct sock *skip_sk)
static int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data,
u16 len, int flag)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(event);
if (hdev)
hdr->index = cpu_to_le16(hdev->id);
else
hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
memcpy(skb_put(skb, data_len), data, data_len);
/* Time stamp */
__net_timestamp(skb);
hci_send_to_channel(channel, skb, skip_sk);
kfree_skb(skb);
return 0;
return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
flag, NULL);
}
static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len,
struct sock *skip_sk)
static int mgmt_limited_event(u16 event, struct hci_dev *hdev, void *data,
u16 len, int flag, struct sock *skip_sk)
{
return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
skip_sk);
flag, skip_sk);
}
static int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
static int mgmt_generic_event(u16 event, struct hci_dev *hdev, void *data,
u16 len, struct sock *skip_sk)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_status *ev;
int err;
BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev));
ev = (void *) skb_put(skb, sizeof(*ev));
ev->status = status;
ev->opcode = cpu_to_le16(cmd);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
return err;
return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
HCI_MGMT_GENERIC_EVENTS, skip_sk);
}
static int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
void *rp, size_t rp_len)
static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len,
struct sock *skip_sk)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev;
int err;
BT_DBG("sock %p", sk);
skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
ev->opcode = cpu_to_le16(cmd);
ev->status = status;
if (rp)
memcpy(ev->data, rp, rp_len);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
return err;
return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
HCI_SOCK_TRUSTED, skip_sk);
}
static int read_version(struct sock *sk, struct hci_dev *hdev, void *data,
......@@ -489,6 +411,82 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev,
return err;
}
static int read_ext_index_list(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
struct mgmt_rp_read_ext_index_list *rp;
struct hci_dev *d;
size_t rp_len;
u16 count;
int err;
BT_DBG("sock %p", sk);
read_lock(&hci_dev_list_lock);
count = 0;
list_for_each_entry(d, &hci_dev_list, list) {
if (d->dev_type == HCI_BREDR || d->dev_type == HCI_AMP)
count++;
}
rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count);
rp = kmalloc(rp_len, GFP_ATOMIC);
if (!rp) {
read_unlock(&hci_dev_list_lock);
return -ENOMEM;
}
count = 0;
list_for_each_entry(d, &hci_dev_list, list) {
if (hci_dev_test_flag(d, HCI_SETUP) ||
hci_dev_test_flag(d, HCI_CONFIG) ||
hci_dev_test_flag(d, HCI_USER_CHANNEL))
continue;
/* Devices marked as raw-only are neither configured
* nor unconfigured controllers.
*/
if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks))
continue;
if (d->dev_type == HCI_BREDR) {
if (hci_dev_test_flag(d, HCI_UNCONFIGURED))
rp->entry[count].type = 0x01;
else
rp->entry[count].type = 0x00;
} else if (d->dev_type == HCI_AMP) {
rp->entry[count].type = 0x02;
} else {
continue;
}
rp->entry[count].bus = d->bus;
rp->entry[count++].index = cpu_to_le16(d->id);
BT_DBG("Added hci%u", d->id);
}
rp->num_controllers = cpu_to_le16(count);
rp_len = sizeof(*rp) + (sizeof(rp->entry[0]) * count);
read_unlock(&hci_dev_list_lock);
/* If this command is called at least once, then all the
* default index and unconfigured index events are disabled
* and from now on only extended index events are used.
*/
hci_sock_set_flag(sk, HCI_MGMT_EXT_INDEX_EVENTS);
hci_sock_clear_flag(sk, HCI_MGMT_INDEX_EVENTS);
hci_sock_clear_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS);
err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE,
MGMT_OP_READ_EXT_INDEX_LIST, 0, rp, rp_len);
kfree(rp);
return err;
}
static bool is_configured(struct hci_dev *hdev)
{
if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) &&
......@@ -521,7 +519,7 @@ static int new_options(struct hci_dev *hdev, struct sock *skip)
{
__le32 options = get_missing_options(hdev);
return mgmt_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options,
return mgmt_generic_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options,
sizeof(options), skip);
}
......@@ -779,33 +777,16 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
return ptr;
}
static struct mgmt_pending_cmd *mgmt_pending_find(u16 opcode,
struct hci_dev *hdev)
static struct mgmt_pending_cmd *pending_find(u16 opcode, struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (cmd->opcode == opcode)
return cmd;
}
return NULL;
return mgmt_pending_find(HCI_CHANNEL_CONTROL, opcode, hdev);
}
static struct mgmt_pending_cmd *mgmt_pending_find_data(u16 opcode,
static struct mgmt_pending_cmd *pending_find_data(u16 opcode,
struct hci_dev *hdev,
const void *data)
{
struct mgmt_pending_cmd *cmd;
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (cmd->user_data != data)
continue;
if (cmd->opcode == opcode)
return cmd;
}
return NULL;
return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data);
}
static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
......@@ -866,7 +847,7 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev)
/* If there's a pending mgmt command the flags will not yet have
* their final values, so check for this first.
*/
cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
if (cmd) {
struct mgmt_mode *cp = cmd->param;
if (cp->val == 0x01)
......@@ -1074,7 +1055,7 @@ static bool get_connectable(struct hci_dev *hdev)
/* If there's a pending mgmt command the flag will not yet have
* it's final value, so check for this first.
*/
cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
if (cmd) {
struct mgmt_mode *cp = cmd->param;
return cp->val;
......@@ -1222,63 +1203,6 @@ static int read_controller_info(struct sock *sk, struct hci_dev *hdev,
sizeof(rp));
}
static void mgmt_pending_free(struct mgmt_pending_cmd *cmd)
{
sock_put(cmd->sk);
kfree(cmd->param);
kfree(cmd);
}
static struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_pending_cmd *cmd;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return NULL;
cmd->opcode = opcode;
cmd->index = hdev->id;
cmd->param = kmemdup(data, len, GFP_KERNEL);
if (!cmd->param) {
kfree(cmd);
return NULL;
}
cmd->param_len = len;
cmd->sk = sk;
sock_hold(sk);
list_add(&cmd->list, &hdev->mgmt_pending);
return cmd;
}
static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
void (*cb)(struct mgmt_pending_cmd *cmd,
void *data),
void *data)
{
struct mgmt_pending_cmd *cmd, *tmp;
list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
if (opcode > 0 && cmd->opcode != opcode)
continue;
cb(cmd, data);
}
}
static void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
{
list_del(&cmd->list);
mgmt_pending_free(cmd);
}
static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
{
__le32 settings = cpu_to_le32(get_current_settings(hdev));
......@@ -1305,9 +1229,10 @@ static bool hci_stop_discovery(struct hci_request *req)
switch (hdev->discovery.state) {
case DISCOVERY_FINDING:
if (test_bit(HCI_INQUIRY, &hdev->flags)) {
if (test_bit(HCI_INQUIRY, &hdev->flags))
hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
} else {
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
cancel_delayed_work(&hdev->le_scan_disable);
hci_req_add_le_scan_disable(req);
}
......@@ -1413,7 +1338,7 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
if (pending_find(MGMT_OP_SET_POWERED, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
MGMT_STATUS_BUSY);
goto failed;
......@@ -1466,11 +1391,10 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
static int new_settings(struct hci_dev *hdev, struct sock *skip)
{
__le32 ev;
__le32 ev = cpu_to_le32(get_current_settings(hdev));
ev = cpu_to_le32(get_current_settings(hdev));
return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
return mgmt_generic_event(MGMT_EV_NEW_SETTINGS, hdev, &ev,
sizeof(ev), skip);
}
int mgmt_new_settings(struct hci_dev *hdev)
......@@ -1566,7 +1490,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
if (!cmd)
goto unlock;
......@@ -1651,8 +1575,8 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
if (pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
MGMT_STATUS_BUSY);
goto failed;
......@@ -1823,7 +1747,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
if (!cmd)
goto unlock;
......@@ -1918,8 +1842,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
if (pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
MGMT_STATUS_BUSY);
goto failed;
......@@ -2058,7 +1982,7 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
if (pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
MGMT_STATUS_BUSY);
goto failed;
......@@ -2137,7 +2061,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
if (pending_find(MGMT_OP_SET_SSP, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
MGMT_STATUS_BUSY);
goto failed;
......@@ -2196,7 +2120,7 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_dev_lock(hdev);
if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
if (pending_find(MGMT_OP_SET_SSP, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
MGMT_STATUS_BUSY);
goto unlock;
......@@ -2318,8 +2242,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto unlock;
}
if (mgmt_pending_find(MGMT_OP_SET_LE, hdev) ||
mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev)) {
if (pending_find(MGMT_OP_SET_LE, hdev) ||
pending_find(MGMT_OP_SET_ADVERTISING, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
MGMT_STATUS_BUSY);
goto unlock;
......@@ -2403,7 +2327,7 @@ static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status)
hci_dev_lock(hdev);
cmd = mgmt_pending_find(mgmt_op, hdev);
cmd = pending_find(mgmt_op, hdev);
if (!cmd)
goto unlock;
......@@ -2897,7 +2821,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
if (pending_find(MGMT_OP_DISCONNECT, hdev)) {
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
MGMT_STATUS_BUSY, &rp, sizeof(rp));
goto failed;
......@@ -3361,7 +3285,7 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev);
if (!cmd) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
MGMT_STATUS_INVALID_PARAMS);
......@@ -3539,7 +3463,7 @@ static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
cmd = pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
if (!cmd)
goto unlock;
......@@ -3591,8 +3515,8 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
if (err < 0)
goto failed;
err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len,
sk);
err = mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev,
data, len, sk);
goto failed;
}
......@@ -3649,7 +3573,7 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
if (pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
MGMT_STATUS_BUSY);
goto unlock;
......@@ -3819,55 +3743,50 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
return err;
}
static bool trigger_discovery(struct hci_request *req, u8 *status)
static bool trigger_bredr_inquiry(struct hci_request *req, u8 *status)
{
struct hci_dev *hdev = req->hdev;
struct hci_cp_le_set_scan_param param_cp;
struct hci_cp_le_set_scan_enable enable_cp;
struct hci_cp_inquiry inq_cp;
struct hci_cp_inquiry cp;
/* General inquiry access code (GIAC) */
u8 lap[3] = { 0x33, 0x8b, 0x9e };
u8 own_addr_type;
int err;
switch (hdev->discovery.type) {
case DISCOV_TYPE_BREDR:
*status = mgmt_bredr_support(hdev);
if (*status)
return false;
if (test_bit(HCI_INQUIRY, &hdev->flags)) {
if (hci_dev_test_flag(hdev, HCI_INQUIRY)) {
*status = MGMT_STATUS_BUSY;
return false;
}
hci_inquiry_cache_flush(hdev);
memset(&inq_cp, 0, sizeof(inq_cp));
memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
hci_req_add(req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
break;
memset(&cp, 0, sizeof(cp));
memcpy(&cp.lap, lap, sizeof(cp.lap));
cp.length = DISCOV_BREDR_INQUIRY_LEN;
hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
return true;
}
static bool trigger_le_scan(struct hci_request *req, u16 interval, u8 *status)
{
struct hci_dev *hdev = req->hdev;
struct hci_cp_le_set_scan_param param_cp;
struct hci_cp_le_set_scan_enable enable_cp;
u8 own_addr_type;
int err;
case DISCOV_TYPE_LE:
case DISCOV_TYPE_INTERLEAVED:
*status = mgmt_le_support(hdev);
if (*status)
return false;
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
*status = MGMT_STATUS_NOT_SUPPORTED;
return false;
}
if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
/* Don't let discovery abort an outgoing
* connection attempt that's using directed
* advertising.
/* Don't let discovery abort an outgoing connection attempt
* that's using directed advertising.
*/
if (hci_conn_hash_lookup_state(hdev, LE_LINK,
BT_CONNECT)) {
if (hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) {
*status = MGMT_STATUS_REJECTED;
return false;
}
......@@ -3875,18 +3794,16 @@ static bool trigger_discovery(struct hci_request *req, u8 *status)
disable_advertising(req);
}
/* If controller is scanning, it means the background scanning
* is running. Thus, we should temporarily stop it in order to
* set the discovery scanning parameters.
/* If controller is scanning, it means the background scanning is
* running. Thus, we should temporarily stop it in order to set the
* discovery scanning parameters.
*/
if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
hci_req_add_le_scan_disable(req);
memset(&param_cp, 0, sizeof(param_cp));
/* All active scans will be done with either a resolvable
* private address (when privacy feature has been enabled)
* or non-resolvable private address.
/* All active scans will be done with either a resolvable private
* address (when privacy feature has been enabled) or non-resolvable
* private address.
*/
err = hci_update_random_address(req, true, &own_addr_type);
if (err < 0) {
......@@ -3894,18 +3811,61 @@ static bool trigger_discovery(struct hci_request *req, u8 *status)
return false;
}
memset(&param_cp, 0, sizeof(param_cp));
param_cp.type = LE_SCAN_ACTIVE;
param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
param_cp.interval = cpu_to_le16(interval);
param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
param_cp.own_address_type = own_addr_type;
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);
return true;
}
static bool trigger_discovery(struct hci_request *req, u8 *status)
{
struct hci_dev *hdev = req->hdev;
switch (hdev->discovery.type) {
case DISCOV_TYPE_BREDR:
if (!trigger_bredr_inquiry(req, status))
return false;
break;
case DISCOV_TYPE_INTERLEAVED:
if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
&hdev->quirks)) {
/* During simultaneous discovery, we double LE scan
* interval. We must leave some time for the controller
* to do BR/EDR inquiry.
*/
if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT * 2,
status))
return false;
if (!trigger_bredr_inquiry(req, status))
return false;
return true;
}
if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
*status = MGMT_STATUS_NOT_SUPPORTED;
return false;
}
/* fall through */
case DISCOV_TYPE_LE:
if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT, status))
return false;
break;
default:
......@@ -3926,9 +3886,9 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
cmd = pending_find(MGMT_OP_START_DISCOVERY, hdev);
if (!cmd)
cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
cmd = pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
if (cmd) {
cmd->cmd_complete(cmd, mgmt_status(status));
......@@ -3950,6 +3910,17 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
break;
case DISCOV_TYPE_INTERLEAVED:
/* When running simultaneous discovery, the LE scanning time
* should occupy the whole discovery time sine BR/EDR inquiry
* and LE scanning are scheduled by the controller.
*
* For interleaving discovery in comparison, BR/EDR inquiry
* and LE scanning are done sequentially with separate
* timeouts.
*/
if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
else
timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
break;
case DISCOV_TYPE_BREDR:
......@@ -4172,7 +4143,7 @@ static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode)
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
cmd = pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
if (cmd) {
cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
......@@ -4481,8 +4452,8 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
if (mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev) ||
mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
if (pending_find(MGMT_OP_SET_ADVERTISING, hdev) ||
pending_find(MGMT_OP_SET_LE, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
MGMT_STATUS_BUSY);
goto unlock;
......@@ -4625,7 +4596,7 @@ static void fast_connectable_complete(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev);
cmd = pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev);
if (!cmd)
goto unlock;
......@@ -4671,7 +4642,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) {
if (pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_BUSY);
goto unlock;
......@@ -4723,7 +4694,7 @@ static void set_bredr_complete(struct hci_dev *hdev, u8 status, u16 opcode)
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_SET_BREDR, hdev);
cmd = pending_find(MGMT_OP_SET_BREDR, hdev);
if (!cmd)
goto unlock;
......@@ -4823,7 +4794,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
}
}
if (mgmt_pending_find(MGMT_OP_SET_BREDR, hdev)) {
if (pending_find(MGMT_OP_SET_BREDR, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
MGMT_STATUS_BUSY);
goto unlock;
......@@ -4868,7 +4839,7 @@ static void sc_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev);
cmd = pending_find(MGMT_OP_SET_SECURE_CONN, hdev);
if (!cmd)
goto unlock;
......@@ -4959,7 +4930,7 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) {
if (pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
MGMT_STATUS_BUSY);
goto failed;
......@@ -5352,7 +5323,7 @@ static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status,
goto unlock;
}
cmd = mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
cmd = pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
if (!cmd)
goto unlock;
......@@ -5405,7 +5376,7 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
if (mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
if (pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
MGMT_STATUS_BUSY, &rp, sizeof(rp));
goto unlock;
......@@ -5541,7 +5512,7 @@ static void get_clock_info_complete(struct hci_dev *hdev, u8 status, u16 opcode)
conn = NULL;
}
cmd = mgmt_pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn);
cmd = pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn);
if (!cmd)
goto unlock;
......@@ -5707,7 +5678,7 @@ static void add_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_ADD_DEVICE, hdev);
cmd = pending_find(MGMT_OP_ADD_DEVICE, hdev);
if (!cmd)
goto unlock;
......@@ -5830,7 +5801,7 @@ static void remove_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
cmd = pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
if (!cmd)
goto unlock;
......@@ -6162,215 +6133,339 @@ static int set_public_address(struct sock *sk, struct hci_dev *hdev,
return err;
}
static const struct hci_mgmt_handler mgmt_handlers[] = {
{ NULL }, /* 0x0000 (no command) */
{ read_version, MGMT_READ_VERSION_SIZE,
HCI_MGMT_NO_HDEV },
{ read_commands, MGMT_READ_COMMANDS_SIZE,
HCI_MGMT_NO_HDEV },
{ read_index_list, MGMT_READ_INDEX_LIST_SIZE,
HCI_MGMT_NO_HDEV },
{ read_controller_info, MGMT_READ_INFO_SIZE, 0 },
{ set_powered, MGMT_SETTING_SIZE, 0 },
{ set_discoverable, MGMT_SET_DISCOVERABLE_SIZE, 0 },
{ set_connectable, MGMT_SETTING_SIZE, 0 },
{ set_fast_connectable, MGMT_SETTING_SIZE, 0 },
{ set_bondable, MGMT_SETTING_SIZE, 0 },
{ set_link_security, MGMT_SETTING_SIZE, 0 },
{ set_ssp, MGMT_SETTING_SIZE, 0 },
{ set_hs, MGMT_SETTING_SIZE, 0 },
{ set_le, MGMT_SETTING_SIZE, 0 },
{ set_dev_class, MGMT_SET_DEV_CLASS_SIZE, 0 },
{ set_local_name, MGMT_SET_LOCAL_NAME_SIZE, 0 },
{ add_uuid, MGMT_ADD_UUID_SIZE, 0 },
{ remove_uuid, MGMT_REMOVE_UUID_SIZE, 0 },
{ load_link_keys, MGMT_LOAD_LINK_KEYS_SIZE,
HCI_MGMT_VAR_LEN },
{ load_long_term_keys, MGMT_LOAD_LONG_TERM_KEYS_SIZE,
HCI_MGMT_VAR_LEN },
{ disconnect, MGMT_DISCONNECT_SIZE, 0 },
{ get_connections, MGMT_GET_CONNECTIONS_SIZE, 0 },
{ pin_code_reply, MGMT_PIN_CODE_REPLY_SIZE, 0 },
{ pin_code_neg_reply, MGMT_PIN_CODE_NEG_REPLY_SIZE, 0 },
{ set_io_capability, MGMT_SET_IO_CAPABILITY_SIZE, 0 },
{ pair_device, MGMT_PAIR_DEVICE_SIZE, 0 },
{ cancel_pair_device, MGMT_CANCEL_PAIR_DEVICE_SIZE, 0 },
{ unpair_device, MGMT_UNPAIR_DEVICE_SIZE, 0 },
{ user_confirm_reply, MGMT_USER_CONFIRM_REPLY_SIZE, 0 },
{ user_confirm_neg_reply, MGMT_USER_CONFIRM_NEG_REPLY_SIZE, 0 },
{ user_passkey_reply, MGMT_USER_PASSKEY_REPLY_SIZE, 0 },
{ user_passkey_neg_reply, MGMT_USER_PASSKEY_NEG_REPLY_SIZE, 0 },
{ read_local_oob_data, MGMT_READ_LOCAL_OOB_DATA_SIZE },
{ add_remote_oob_data, MGMT_ADD_REMOTE_OOB_DATA_SIZE,
HCI_MGMT_VAR_LEN },
{ remove_remote_oob_data, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE, 0 },
{ start_discovery, MGMT_START_DISCOVERY_SIZE, 0 },
{ stop_discovery, MGMT_STOP_DISCOVERY_SIZE, 0 },
{ confirm_name, MGMT_CONFIRM_NAME_SIZE, 0 },
{ block_device, MGMT_BLOCK_DEVICE_SIZE, 0 },
{ unblock_device, MGMT_UNBLOCK_DEVICE_SIZE, 0 },
{ set_device_id, MGMT_SET_DEVICE_ID_SIZE, 0 },
{ set_advertising, MGMT_SETTING_SIZE, 0 },
{ set_bredr, MGMT_SETTING_SIZE, 0 },
{ set_static_address, MGMT_SET_STATIC_ADDRESS_SIZE, 0 },
{ set_scan_params, MGMT_SET_SCAN_PARAMS_SIZE, 0 },
{ set_secure_conn, MGMT_SETTING_SIZE, 0 },
{ set_debug_keys, MGMT_SETTING_SIZE, 0 },
{ set_privacy, MGMT_SET_PRIVACY_SIZE, 0 },
{ load_irks, MGMT_LOAD_IRKS_SIZE,
HCI_MGMT_VAR_LEN },
{ get_conn_info, MGMT_GET_CONN_INFO_SIZE, 0 },
{ get_clock_info, MGMT_GET_CLOCK_INFO_SIZE, 0 },
{ add_device, MGMT_ADD_DEVICE_SIZE, 0 },
{ remove_device, MGMT_REMOVE_DEVICE_SIZE, 0 },
{ load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE,
HCI_MGMT_VAR_LEN },
{ read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE,
HCI_MGMT_NO_HDEV },
{ read_config_info, MGMT_READ_CONFIG_INFO_SIZE,
HCI_MGMT_UNCONFIGURED },
{ set_external_config, MGMT_SET_EXTERNAL_CONFIG_SIZE,
HCI_MGMT_UNCONFIGURED },
{ set_public_address, MGMT_SET_PUBLIC_ADDRESS_SIZE,
HCI_MGMT_UNCONFIGURED },
{ start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE,
HCI_MGMT_VAR_LEN },
};
static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
u8 data_len)
{
eir[eir_len++] = sizeof(type) + data_len;
eir[eir_len++] = type;
memcpy(&eir[eir_len], data, data_len);
eir_len += data_len;
int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk,
struct msghdr *msg, size_t msglen)
return eir_len;
}
static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
void *buf;
u8 *cp;
struct mgmt_hdr *hdr;
u16 opcode, index, len;
struct hci_dev *hdev = NULL;
const struct hci_mgmt_handler *handler;
bool var_len, no_hdev;
struct mgmt_cp_read_local_oob_ext_data *cp = data;
struct mgmt_rp_read_local_oob_ext_data *rp;
size_t rp_len;
u16 eir_len;
u8 status, flags, role, addr[7], hash[16], rand[16];
int err;
BT_DBG("got %zu bytes", msglen);
if (msglen < sizeof(*hdr))
return -EINVAL;
BT_DBG("%s", hdev->name);
buf = kmalloc(msglen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (!hdev_is_powered(hdev))
return mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
MGMT_STATUS_NOT_POWERED,
&cp->type, sizeof(cp->type));
if (memcpy_from_msg(buf, msg, msglen)) {
err = -EFAULT;
goto done;
switch (cp->type) {
case BIT(BDADDR_BREDR):
status = mgmt_bredr_support(hdev);
if (status)
return mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
status, &cp->type,
sizeof(cp->type));
eir_len = 5;
break;
case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
status = mgmt_le_support(hdev);
if (status)
return mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
status, &cp->type,
sizeof(cp->type));
eir_len = 9 + 3 + 18 + 18 + 3;
break;
default:
return mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
MGMT_STATUS_INVALID_PARAMS,
&cp->type, sizeof(cp->type));
}
hdr = buf;
opcode = __le16_to_cpu(hdr->opcode);
index = __le16_to_cpu(hdr->index);
len = __le16_to_cpu(hdr->len);
hci_dev_lock(hdev);
if (len != msglen - sizeof(*hdr)) {
err = -EINVAL;
goto done;
rp_len = sizeof(*rp) + eir_len;
rp = kmalloc(rp_len, GFP_ATOMIC);
if (!rp) {
hci_dev_unlock(hdev);
return -ENOMEM;
}
if (opcode >= chan->handler_count ||
chan->handlers[opcode].func == NULL) {
BT_DBG("Unknown op %u", opcode);
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_UNKNOWN_COMMAND);
eir_len = 0;
switch (cp->type) {
case BIT(BDADDR_BREDR):
eir_len = eir_append_data(rp->eir, eir_len, EIR_CLASS_OF_DEV,
hdev->dev_class, 3);
break;
case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
smp_generate_oob(hdev, hash, rand) < 0) {
hci_dev_unlock(hdev);
err = mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
MGMT_STATUS_FAILED,
&cp->type, sizeof(cp->type));
goto done;
}
handler = &chan->handlers[opcode];
if (index != MGMT_INDEX_NONE) {
hdev = hci_dev_get(index);
if (!hdev) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
memcpy(addr, &hdev->rpa, 6);
addr[6] = 0x01;
} else if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
!bacmp(&hdev->bdaddr, BDADDR_ANY) ||
(!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
bacmp(&hdev->static_addr, BDADDR_ANY))) {
memcpy(addr, &hdev->static_addr, 6);
addr[6] = 0x01;
} else {
memcpy(addr, &hdev->bdaddr, 6);
addr[6] = 0x00;
}
if (hci_dev_test_flag(hdev, HCI_SETUP) ||
hci_dev_test_flag(hdev, HCI_CONFIG) ||
hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_BDADDR,
addr, sizeof(addr));
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
!(handler->flags & HCI_MGMT_UNCONFIGURED)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
}
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
role = 0x02;
else
role = 0x01;
no_hdev = (handler->flags & HCI_MGMT_NO_HDEV);
if (no_hdev != !hdev) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_ROLE,
&role, sizeof(role));
if (hci_dev_test_flag(hdev, HCI_SC_ENABLED)) {
eir_len = eir_append_data(rp->eir, eir_len,
EIR_LE_SC_CONFIRM,
hash, sizeof(hash));
eir_len = eir_append_data(rp->eir, eir_len,
EIR_LE_SC_RANDOM,
rand, sizeof(rand));
}
var_len = (handler->flags & HCI_MGMT_VAR_LEN);
if ((var_len && len < handler->data_len) ||
(!var_len && len != handler->data_len)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_PARAMS);
goto done;
flags = get_adv_discov_flags(hdev);
if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
flags |= LE_AD_NO_BREDR;
eir_len = eir_append_data(rp->eir, eir_len, EIR_FLAGS,
&flags, sizeof(flags));
break;
}
if (hdev)
mgmt_init_hdev(sk, hdev);
rp->type = cp->type;
rp->eir_len = cpu_to_le16(eir_len);
hci_dev_unlock(hdev);
cp = buf + sizeof(*hdr);
hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS);
err = handler->func(sk, hdev, cp, len);
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
MGMT_STATUS_SUCCESS, rp, sizeof(*rp) + eir_len);
if (err < 0)
goto done;
err = msglen;
err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
rp, sizeof(*rp) + eir_len,
HCI_MGMT_OOB_DATA_EVENTS, sk);
done:
if (hdev)
hci_dev_put(hdev);
kfree(rp);
return err;
}
static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
struct mgmt_rp_read_adv_features *rp;
size_t rp_len;
int err;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
rp_len = sizeof(*rp);
rp = kmalloc(rp_len, GFP_ATOMIC);
if (!rp) {
hci_dev_unlock(hdev);
return -ENOMEM;
}
rp->supported_flags = cpu_to_le32(0);
rp->max_adv_data_len = 31;
rp->max_scan_rsp_len = 31;
rp->max_instances = 0;
rp->num_instances = 0;
hci_dev_unlock(hdev);
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES,
MGMT_STATUS_SUCCESS, rp, rp_len);
kfree(rp);
kfree(buf);
return err;
}
static const struct hci_mgmt_handler mgmt_handlers[] = {
{ NULL }, /* 0x0000 (no command) */
{ read_version, MGMT_READ_VERSION_SIZE,
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED },
{ read_commands, MGMT_READ_COMMANDS_SIZE,
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED },
{ read_index_list, MGMT_READ_INDEX_LIST_SIZE,
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED },
{ read_controller_info, MGMT_READ_INFO_SIZE,
HCI_MGMT_UNTRUSTED },
{ set_powered, MGMT_SETTING_SIZE },
{ set_discoverable, MGMT_SET_DISCOVERABLE_SIZE },
{ set_connectable, MGMT_SETTING_SIZE },
{ set_fast_connectable, MGMT_SETTING_SIZE },
{ set_bondable, MGMT_SETTING_SIZE },
{ set_link_security, MGMT_SETTING_SIZE },
{ set_ssp, MGMT_SETTING_SIZE },
{ set_hs, MGMT_SETTING_SIZE },
{ set_le, MGMT_SETTING_SIZE },
{ set_dev_class, MGMT_SET_DEV_CLASS_SIZE },
{ set_local_name, MGMT_SET_LOCAL_NAME_SIZE },
{ add_uuid, MGMT_ADD_UUID_SIZE },
{ remove_uuid, MGMT_REMOVE_UUID_SIZE },
{ load_link_keys, MGMT_LOAD_LINK_KEYS_SIZE,
HCI_MGMT_VAR_LEN },
{ load_long_term_keys, MGMT_LOAD_LONG_TERM_KEYS_SIZE,
HCI_MGMT_VAR_LEN },
{ disconnect, MGMT_DISCONNECT_SIZE },
{ get_connections, MGMT_GET_CONNECTIONS_SIZE },
{ pin_code_reply, MGMT_PIN_CODE_REPLY_SIZE },
{ pin_code_neg_reply, MGMT_PIN_CODE_NEG_REPLY_SIZE },
{ set_io_capability, MGMT_SET_IO_CAPABILITY_SIZE },
{ pair_device, MGMT_PAIR_DEVICE_SIZE },
{ cancel_pair_device, MGMT_CANCEL_PAIR_DEVICE_SIZE },
{ unpair_device, MGMT_UNPAIR_DEVICE_SIZE },
{ user_confirm_reply, MGMT_USER_CONFIRM_REPLY_SIZE },
{ user_confirm_neg_reply, MGMT_USER_CONFIRM_NEG_REPLY_SIZE },
{ user_passkey_reply, MGMT_USER_PASSKEY_REPLY_SIZE },
{ user_passkey_neg_reply, MGMT_USER_PASSKEY_NEG_REPLY_SIZE },
{ read_local_oob_data, MGMT_READ_LOCAL_OOB_DATA_SIZE },
{ add_remote_oob_data, MGMT_ADD_REMOTE_OOB_DATA_SIZE,
HCI_MGMT_VAR_LEN },
{ remove_remote_oob_data, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE },
{ start_discovery, MGMT_START_DISCOVERY_SIZE },
{ stop_discovery, MGMT_STOP_DISCOVERY_SIZE },
{ confirm_name, MGMT_CONFIRM_NAME_SIZE },
{ block_device, MGMT_BLOCK_DEVICE_SIZE },
{ unblock_device, MGMT_UNBLOCK_DEVICE_SIZE },
{ set_device_id, MGMT_SET_DEVICE_ID_SIZE },
{ set_advertising, MGMT_SETTING_SIZE },
{ set_bredr, MGMT_SETTING_SIZE },
{ set_static_address, MGMT_SET_STATIC_ADDRESS_SIZE },
{ set_scan_params, MGMT_SET_SCAN_PARAMS_SIZE },
{ set_secure_conn, MGMT_SETTING_SIZE },
{ set_debug_keys, MGMT_SETTING_SIZE },
{ set_privacy, MGMT_SET_PRIVACY_SIZE },
{ load_irks, MGMT_LOAD_IRKS_SIZE,
HCI_MGMT_VAR_LEN },
{ get_conn_info, MGMT_GET_CONN_INFO_SIZE },
{ get_clock_info, MGMT_GET_CLOCK_INFO_SIZE },
{ add_device, MGMT_ADD_DEVICE_SIZE },
{ remove_device, MGMT_REMOVE_DEVICE_SIZE },
{ load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE,
HCI_MGMT_VAR_LEN },
{ read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE,
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED },
{ read_config_info, MGMT_READ_CONFIG_INFO_SIZE,
HCI_MGMT_UNCONFIGURED |
HCI_MGMT_UNTRUSTED },
{ set_external_config, MGMT_SET_EXTERNAL_CONFIG_SIZE,
HCI_MGMT_UNCONFIGURED },
{ set_public_address, MGMT_SET_PUBLIC_ADDRESS_SIZE,
HCI_MGMT_UNCONFIGURED },
{ start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE,
HCI_MGMT_VAR_LEN },
{ read_local_oob_ext_data, MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE },
{ read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE,
HCI_MGMT_NO_HDEV |
HCI_MGMT_UNTRUSTED },
{ read_adv_features, MGMT_READ_ADV_FEATURES_SIZE },
};
void mgmt_index_added(struct hci_dev *hdev)
{
if (hdev->dev_type != HCI_BREDR)
return;
struct mgmt_ev_ext_index ev;
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return;
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL);
else
mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
switch (hdev->dev_type) {
case HCI_BREDR:
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
mgmt_index_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev,
NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS);
ev.type = 0x01;
} else {
mgmt_index_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0,
HCI_MGMT_INDEX_EVENTS);
ev.type = 0x00;
}
break;
case HCI_AMP:
ev.type = 0x02;
break;
default:
return;
}
ev.bus = hdev->bus;
mgmt_index_event(MGMT_EV_EXT_INDEX_ADDED, hdev, &ev, sizeof(ev),
HCI_MGMT_EXT_INDEX_EVENTS);
}
void mgmt_index_removed(struct hci_dev *hdev)
{
struct mgmt_ev_ext_index ev;
u8 status = MGMT_STATUS_INVALID_INDEX;
if (hdev->dev_type != HCI_BREDR)
return;
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return;
switch (hdev->dev_type) {
case HCI_BREDR:
mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL);
else
mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
mgmt_index_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev,
NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS);
ev.type = 0x01;
} else {
mgmt_index_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0,
HCI_MGMT_INDEX_EVENTS);
ev.type = 0x00;
}
break;
case HCI_AMP:
ev.type = 0x02;
break;
default:
return;
}
ev.bus = hdev->bus;
mgmt_index_event(MGMT_EV_EXT_INDEX_REMOVED, hdev, &ev, sizeof(ev),
HCI_MGMT_EXT_INDEX_EVENTS);
}
/* This function requires the caller holds hdev->lock */
......@@ -6535,7 +6630,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
zero_cod, sizeof(zero_cod), NULL);
new_settings:
......@@ -6552,7 +6647,7 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
struct mgmt_pending_cmd *cmd;
u8 status;
cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
cmd = pending_find(MGMT_OP_SET_POWERED, hdev);
if (!cmd)
return;
......@@ -6752,17 +6847,6 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
mgmt_event(MGMT_EV_NEW_CONN_PARAM, hdev, &ev, sizeof(ev), NULL);
}
static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
u8 data_len)
{
eir[eir_len++] = sizeof(type) + data_len;
eir[eir_len++] = type;
memcpy(&eir[eir_len], data, data_len);
eir_len += data_len;
return eir_len;
}
void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
u32 flags, u8 *name, u8 name_len)
{
......@@ -6828,7 +6912,7 @@ bool mgmt_powering_down(struct hci_dev *hdev)
struct mgmt_pending_cmd *cmd;
struct mgmt_mode *cp;
cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
cmd = pending_find(MGMT_OP_SET_POWERED, hdev);
if (!cmd)
return false;
......@@ -6885,7 +6969,7 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
hdev);
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
cmd = pending_find(MGMT_OP_DISCONNECT, hdev);
if (!cmd)
return;
......@@ -6937,7 +7021,7 @@ void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
{
struct mgmt_pending_cmd *cmd;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
cmd = pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
if (!cmd)
return;
......@@ -6950,7 +7034,7 @@ void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
{
struct mgmt_pending_cmd *cmd;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
cmd = pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
if (!cmd)
return;
......@@ -6995,7 +7079,7 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
{
struct mgmt_pending_cmd *cmd;
cmd = mgmt_pending_find(opcode, hdev);
cmd = pending_find(opcode, hdev);
if (!cmd)
return -ENOENT;
......@@ -7187,8 +7271,8 @@ void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
if (!status)
mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3,
NULL);
mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
dev_class, 3, NULL);
if (match.sk)
sock_put(match.sk);
......@@ -7206,18 +7290,18 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH);
cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
cmd = pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
if (!cmd) {
memcpy(hdev->dev_name, name, sizeof(hdev->dev_name));
/* If this is a HCI command related to powering on the
* HCI dev don't send any mgmt signals.
*/
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
if (pending_find(MGMT_OP_SET_POWERED, hdev))
return;
}
mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
cmd ? cmd->sk : NULL);
}
......@@ -7229,7 +7313,7 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
BT_DBG("%s status %u", hdev->name, status);
cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
if (!cmd)
return;
......@@ -7511,6 +7595,7 @@ static struct hci_mgmt_chan chan = {
.channel = HCI_CHANNEL_CONTROL,
.handler_count = ARRAY_SIZE(mgmt_handlers),
.handlers = mgmt_handlers,
.hdev_init = mgmt_init_hdev,
};
int mgmt_init(void)
......
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2015 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 <net/bluetooth/mgmt.h>
#include "mgmt_util.h"
int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
void *data, u16 data_len, int flag, struct sock *skip_sk)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(event);
if (hdev)
hdr->index = cpu_to_le16(hdev->id);
else
hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
memcpy(skb_put(skb, data_len), data, data_len);
/* Time stamp */
__net_timestamp(skb);
hci_send_to_channel(channel, skb, flag, skip_sk);
kfree_skb(skb);
return 0;
}
int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_status *ev;
int err;
BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev));
ev = (void *) skb_put(skb, sizeof(*ev));
ev->status = status;
ev->opcode = cpu_to_le16(cmd);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
return err;
}
int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
void *rp, size_t rp_len)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev;
int err;
BT_DBG("sock %p", sk);
skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
ev->opcode = cpu_to_le16(cmd);
ev->status = status;
if (rp)
memcpy(ev->data, rp, rp_len);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
return err;
}
struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (hci_sock_get_channel(cmd->sk) != channel)
continue;
if (cmd->opcode == opcode)
return cmd;
}
return NULL;
}
struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
u16 opcode,
struct hci_dev *hdev,
const void *data)
{
struct mgmt_pending_cmd *cmd;
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (cmd->user_data != data)
continue;
if (cmd->opcode == opcode)
return cmd;
}
return NULL;
}
void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
void *data)
{
struct mgmt_pending_cmd *cmd, *tmp;
list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
if (opcode > 0 && cmd->opcode != opcode)
continue;
cb(cmd, data);
}
}
struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_pending_cmd *cmd;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return NULL;
cmd->opcode = opcode;
cmd->index = hdev->id;
cmd->param = kmemdup(data, len, GFP_KERNEL);
if (!cmd->param) {
kfree(cmd);
return NULL;
}
cmd->param_len = len;
cmd->sk = sk;
sock_hold(sk);
list_add(&cmd->list, &hdev->mgmt_pending);
return cmd;
}
void mgmt_pending_free(struct mgmt_pending_cmd *cmd)
{
sock_put(cmd->sk);
kfree(cmd->param);
kfree(cmd);
}
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
{
list_del(&cmd->list);
mgmt_pending_free(cmd);
}
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2015 Intel Coropration
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 mgmt_pending_cmd {
struct list_head list;
u16 opcode;
int index;
void *param;
size_t param_len;
struct sock *sk;
void *user_data;
int (*cmd_complete)(struct mgmt_pending_cmd *cmd, u8 status);
};
int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
void *data, u16 data_len, int flag, struct sock *skip_sk);
int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status);
int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
void *rp, size_t rp_len);
struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
struct hci_dev *hdev);
struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
u16 opcode,
struct hci_dev *hdev,
const void *data);
void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
void *data);
struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
struct hci_dev *hdev,
void *data, u16 len);
void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
......@@ -70,7 +70,19 @@ enum {
SMP_FLAG_DEBUG_KEY,
SMP_FLAG_WAIT_USER,
SMP_FLAG_DHKEY_PENDING,
SMP_FLAG_OOB,
SMP_FLAG_REMOTE_OOB,
SMP_FLAG_LOCAL_OOB,
};
struct smp_dev {
/* Secure Connections OOB data */
u8 local_pk[64];
u8 local_sk[32];
u8 local_rand[16];
bool debug_key;
struct crypto_blkcipher *tfm_aes;
struct crypto_hash *tfm_cmac;
};
struct smp_chan {
......@@ -84,7 +96,8 @@ struct smp_chan {
u8 rrnd[16]; /* SMP Pairing Random (remote) */
u8 pcnf[16]; /* SMP Pairing Confirm */
u8 tk[16]; /* SMP Temporary Key */
u8 rr[16];
u8 rr[16]; /* Remote OOB ra/rb value */
u8 lr[16]; /* Local OOB ra/rb value */
u8 enc_key_size;
u8 remote_key_dist;
bdaddr_t id_addr;
......@@ -478,18 +491,18 @@ bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
const bdaddr_t *bdaddr)
{
struct l2cap_chan *chan = hdev->smp_data;
struct crypto_blkcipher *tfm;
struct smp_dev *smp;
u8 hash[3];
int err;
if (!chan || !chan->data)
return false;
tfm = chan->data;
smp = chan->data;
BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
err = smp_ah(smp->tfm_aes, irk, &bdaddr->b[3], hash);
if (err)
return false;
......@@ -499,20 +512,20 @@ bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa)
{
struct l2cap_chan *chan = hdev->smp_data;
struct crypto_blkcipher *tfm;
struct smp_dev *smp;
int err;
if (!chan || !chan->data)
return -EOPNOTSUPP;
tfm = chan->data;
smp = chan->data;
get_random_bytes(&rpa->b[3], 3);
rpa->b[5] &= 0x3f; /* Clear two most significant bits */
rpa->b[5] |= 0x40; /* Set second most significant bit */
err = smp_ah(tfm, irk, &rpa->b[3], rpa->b);
err = smp_ah(smp->tfm_aes, irk, &rpa->b[3], rpa->b);
if (err < 0)
return err;
......@@ -521,6 +534,53 @@ int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa)
return 0;
}
int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16])
{
struct l2cap_chan *chan = hdev->smp_data;
struct smp_dev *smp;
int err;
if (!chan || !chan->data)
return -EOPNOTSUPP;
smp = chan->data;
if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) {
BT_DBG("Using debug keys");
memcpy(smp->local_pk, debug_pk, 64);
memcpy(smp->local_sk, debug_sk, 32);
smp->debug_key = true;
} else {
while (true) {
/* Generate local key pair for Secure Connections */
if (!ecc_make_key(smp->local_pk, smp->local_sk))
return -EIO;
/* This is unlikely, but we need to check that
* we didn't accidentially generate a debug key.
*/
if (memcmp(smp->local_sk, debug_sk, 32))
break;
}
smp->debug_key = false;
}
SMP_DBG("OOB Public Key X: %32phN", smp->local_pk);
SMP_DBG("OOB Public Key Y: %32phN", smp->local_pk + 32);
SMP_DBG("OOB Private Key: %32phN", smp->local_sk);
get_random_bytes(smp->local_rand, 16);
err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->local_pk,
smp->local_rand, 0, hash);
if (err < 0)
return err;
memcpy(rand, smp->local_rand, 16);
return 0;
}
static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
{
struct l2cap_chan *chan = conn->smp;
......@@ -621,10 +681,12 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
bdaddr_type);
if (oob_data && oob_data->present) {
set_bit(SMP_FLAG_OOB, &smp->flags);
set_bit(SMP_FLAG_REMOTE_OOB, &smp->flags);
oob_flag = SMP_OOB_PRESENT;
memcpy(smp->rr, oob_data->rand256, 16);
memcpy(smp->pcnf, oob_data->hash256, 16);
SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf);
SMP_DBG("OOB Remote Random: %16phN", smp->rr);
}
} else {
......@@ -681,9 +743,9 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
mgmt_smp_complete(hcon, complete);
kfree(smp->csrk);
kfree(smp->slave_csrk);
kfree(smp->link_key);
kzfree(smp->csrk);
kzfree(smp->slave_csrk);
kzfree(smp->link_key);
crypto_free_blkcipher(smp->tfm_aes);
crypto_free_hash(smp->tfm_cmac);
......@@ -717,7 +779,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
}
chan->data = NULL;
kfree(smp);
kzfree(smp);
hci_conn_drop(hcon);
}
......@@ -818,6 +880,12 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
return 0;
}
/* If this function is used for SC -> legacy fallback we
* can only recover the just-works case.
*/
if (test_bit(SMP_FLAG_SC, &smp->flags))
return -EINVAL;
/* Not Just Works/Confirm results in MITM Authentication */
if (smp->method != JUST_CFM) {
set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
......@@ -1097,13 +1165,13 @@ static void sc_generate_link_key(struct smp_chan *smp)
return;
if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) {
kfree(smp->link_key);
kzfree(smp->link_key);
smp->link_key = NULL;
return;
}
if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) {
kfree(smp->link_key);
kzfree(smp->link_key);
smp->link_key = NULL;
return;
}
......@@ -1300,7 +1368,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(smp->tfm_aes)) {
BT_ERR("Unable to create ECB crypto context");
kfree(smp);
kzfree(smp);
return NULL;
}
......@@ -1308,7 +1376,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
if (IS_ERR(smp->tfm_cmac)) {
BT_ERR("Unable to create CMAC crypto context");
crypto_free_blkcipher(smp->tfm_aes);
kfree(smp);
kzfree(smp);
return NULL;
}
......@@ -1675,6 +1743,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
/* If the remote side's OOB flag is set it means it has
* successfully received our local OOB data - therefore set the
* flag to indicate that local OOB is in use.
*/
if (req->oob_flag == SMP_OOB_PRESENT)
set_bit(SMP_FLAG_LOCAL_OOB, &smp->flags);
/* SMP over BR/EDR requires special treatment */
if (conn->hcon->type == ACL_LINK) {
/* We must have a BR/EDR SC link */
......@@ -1737,6 +1812,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
/* Strictly speaking we shouldn't allow Pairing Confirm for the
* SC case, however some implementations incorrectly copy RFU auth
* req bits from our security request, which may create a false
* positive SC enablement.
*/
SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
if (test_bit(SMP_FLAG_SC, &smp->flags)) {
SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
/* Clear bits which are generated but not distributed */
......@@ -1745,8 +1827,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
return 0;
}
SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
/* Request setup of TK */
ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
if (ret)
......@@ -1761,6 +1841,25 @@ static u8 sc_send_public_key(struct smp_chan *smp)
BT_DBG("");
if (test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags)) {
struct l2cap_chan *chan = hdev->smp_data;
struct smp_dev *smp_dev;
if (!chan || !chan->data)
return SMP_UNSPECIFIED;
smp_dev = chan->data;
memcpy(smp->local_pk, smp_dev->local_pk, 64);
memcpy(smp->local_sk, smp_dev->local_sk, 32);
memcpy(smp->lr, smp_dev->local_rand, 16);
if (smp_dev->debug_key)
set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
goto done;
}
if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) {
BT_DBG("Using debug keys");
memcpy(smp->local_pk, debug_pk, 64);
......@@ -1780,8 +1879,9 @@ static u8 sc_send_public_key(struct smp_chan *smp)
}
}
done:
SMP_DBG("Local Public Key X: %32phN", smp->local_pk);
SMP_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]);
SMP_DBG("Local Public Key Y: %32phN", smp->local_pk + 32);
SMP_DBG("Local Private Key: %32phN", smp->local_sk);
smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk);
......@@ -1819,6 +1919,13 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
if (hci_dev_test_flag(hdev, HCI_SC_ONLY) && !(auth & SMP_AUTH_SC))
return SMP_AUTH_REQUIREMENTS;
/* If the remote side's OOB flag is set it means it has
* successfully received our local OOB data - therefore set the
* flag to indicate that local OOB is in use.
*/
if (rsp->oob_flag == SMP_OOB_PRESENT)
set_bit(SMP_FLAG_LOCAL_OOB, &smp->flags);
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
......@@ -1885,10 +1992,6 @@ static u8 sc_check_confirm(struct smp_chan *smp)
BT_DBG("");
/* Public Key exchange must happen before any other steps */
if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
return SMP_UNSPECIFIED;
if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
return sc_passkey_round(smp, SMP_CMD_PAIRING_CONFIRM);
......@@ -1901,6 +2004,47 @@ static u8 sc_check_confirm(struct smp_chan *smp)
return 0;
}
/* Work-around for some implementations that incorrectly copy RFU bits
* from our security request and thereby create the impression that
* we're doing SC when in fact the remote doesn't support it.
*/
static int fixup_sc_false_positive(struct smp_chan *smp)
{
struct l2cap_conn *conn = smp->conn;
struct hci_conn *hcon = conn->hcon;
struct hci_dev *hdev = hcon->hdev;
struct smp_cmd_pairing *req, *rsp;
u8 auth;
/* The issue is only observed when we're in slave role */
if (hcon->out)
return SMP_UNSPECIFIED;
if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) {
BT_ERR("Refusing SMP SC -> legacy fallback in SC-only mode");
return SMP_UNSPECIFIED;
}
BT_ERR("Trying to fall back to legacy SMP");
req = (void *) &smp->preq[1];
rsp = (void *) &smp->prsp[1];
/* Rebuild key dist flags which may have been cleared for SC */
smp->remote_key_dist = (req->init_key_dist & rsp->resp_key_dist);
auth = req->auth_req & AUTH_REQ_MASK(hdev);
if (tk_request(conn, 0, auth, rsp->io_capability, req->io_capability)) {
BT_ERR("Failed to fall back to legacy SMP");
return SMP_UNSPECIFIED;
}
clear_bit(SMP_FLAG_SC, &smp->flags);
return 0;
}
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct l2cap_chan *chan = conn->smp;
......@@ -1914,9 +2058,20 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
skb_pull(skb, sizeof(smp->pcnf));
if (test_bit(SMP_FLAG_SC, &smp->flags))
if (test_bit(SMP_FLAG_SC, &smp->flags)) {
int ret;
/* Public Key exchange must happen before any other steps */
if (test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
return sc_check_confirm(smp);
BT_ERR("Unexpected SMP Pairing Confirm");
ret = fixup_sc_false_positive(smp);
if (ret)
return ret;
}
if (conn->hcon->out) {
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd);
......@@ -2374,7 +2529,8 @@ static u8 sc_select_method(struct smp_chan *smp)
struct smp_cmd_pairing *local, *remote;
u8 local_mitm, remote_mitm, local_io, remote_io, method;
if (test_bit(SMP_FLAG_OOB, &smp->flags))
if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags) ||
test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags))
return REQ_OOB;
/* The preq/prsp contain the raw Pairing Request/Response PDUs
......@@ -2428,6 +2584,16 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(smp->remote_pk, key, 64);
if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags)) {
err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk,
smp->rr, 0, cfm.confirm_val);
if (err)
return SMP_UNSPECIFIED;
if (memcmp(cfm.confirm_val, smp->pcnf, 16))
return SMP_CONFIRM_FAILED;
}
/* Non-initiating device sends its public key after receiving
* the key from the initiating device.
*/
......@@ -2438,7 +2604,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
}
SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk);
SMP_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]);
SMP_DBG("Remote Public Key Y: %32phN", smp->remote_pk + 32);
if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey))
return SMP_UNSPECIFIED;
......@@ -2476,14 +2642,6 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
}
if (smp->method == REQ_OOB) {
err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk,
smp->rr, 0, cfm.confirm_val);
if (err)
return SMP_UNSPECIFIED;
if (memcmp(cfm.confirm_val, smp->pcnf, 16))
return SMP_CONFIRM_FAILED;
if (hcon->out)
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
sizeof(smp->prnd), smp->prnd);
......@@ -2556,6 +2714,8 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
put_unaligned_le32(hcon->passkey_notify, r);
else if (smp->method == REQ_OOB)
memcpy(r, smp->lr, 16);
err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r,
io_cap, remote_addr, local_addr, e);
......@@ -2930,27 +3090,49 @@ static const struct l2cap_ops smp_root_chan_ops = {
static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
{
struct l2cap_chan *chan;
struct smp_dev *smp;
struct crypto_blkcipher *tfm_aes;
struct crypto_hash *tfm_cmac;
if (cid == L2CAP_CID_SMP_BREDR) {
tfm_aes = NULL;
smp = NULL;
goto create_chan;
}
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
smp = kzalloc(sizeof(*smp), GFP_KERNEL);
if (!smp)
return ERR_PTR(-ENOMEM);
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm_aes)) {
BT_ERR("Unable to create crypto context");
BT_ERR("Unable to create ECB crypto context");
kzfree(smp);
return ERR_CAST(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);
kzfree(smp);
return ERR_CAST(tfm_cmac);
}
smp->tfm_aes = tfm_aes;
smp->tfm_cmac = tfm_cmac;
create_chan:
chan = l2cap_chan_create();
if (!chan) {
crypto_free_blkcipher(tfm_aes);
if (smp) {
crypto_free_blkcipher(smp->tfm_aes);
crypto_free_hash(smp->tfm_cmac);
kzfree(smp);
}
return ERR_PTR(-ENOMEM);
}
chan->data = tfm_aes;
chan->data = smp;
l2cap_add_scid(chan, cid);
......@@ -2983,14 +3165,18 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
static void smp_del_chan(struct l2cap_chan *chan)
{
struct crypto_blkcipher *tfm_aes;
struct smp_dev *smp;
BT_DBG("chan %p", chan);
tfm_aes = chan->data;
if (tfm_aes) {
smp = chan->data;
if (smp) {
chan->data = NULL;
crypto_free_blkcipher(tfm_aes);
if (smp->tfm_aes)
crypto_free_blkcipher(smp->tfm_aes);
if (smp->tfm_cmac)
crypto_free_hash(smp->tfm_cmac);
kzfree(smp);
}
l2cap_chan_put(chan);
......
......@@ -188,6 +188,7 @@ int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
const bdaddr_t *bdaddr);
int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]);
int smp_register(struct hci_dev *hdev);
void smp_unregister(struct hci_dev *hdev);
......
#ifndef __MAC802154_DRVIER_OPS
#ifndef __MAC802154_DRIVER_OPS
#define __MAC802154_DRIVER_OPS
#include <linux/types.h>
......@@ -220,4 +220,4 @@ drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
return local->ops->set_promiscuous_mode(&local->hw, on);
}
#endif /* __MAC802154_DRVIER_OPS */
#endif /* __MAC802154_DRIVER_OPS */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment