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: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
970282d0e1
|
@ -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 */
|
||||
ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
/* 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 @@ err_ret:
|
|||
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 @@ out:
|
|||
|
||||
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);
|
||||
|
||||
hci_inquiry_cache_flush(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);
|
||||
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_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,7 +2126,16 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
goto unlock;
|
||||
|
||||
if (list_empty(&discov->resolve)) {
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
/* 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,7 +2144,16 @@ 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 {
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
unlock:
|
||||
|
@ -3889,41 +3907,37 @@ 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
|
||||
* data. The stored value contains the right present
|
||||
* information. However it can only be trusted when
|
||||
* not in Secure Connection Only mode.
|
||||
*/
|
||||
if (!hci_dev_test_flag(hdev, HCI_SC_ONLY))
|
||||
return data->present;
|
||||
|
||||
/* When Secure Connections Only mode is enabled, then
|
||||
* the P-256 values are required. If they are not
|
||||
* available, then do not declare that OOB data is
|
||||
* present.
|
||||
*/
|
||||
if (!memcmp(data->rand256, ZERO_KEY, 16) ||
|
||||
!memcmp(data->hash256, ZERO_KEY, 16))
|
||||
return 0x00;
|
||||
|
||||
return 0x02;
|
||||
}
|
||||
|
||||
/* When Secure Connections is not enabled or actually
|
||||
* not supported by the hardware, then check that if
|
||||
* P-192 data values are present.
|
||||
if (bredr_sc_enabled(hdev)) {
|
||||
/* When Secure Connections is enabled, then just
|
||||
* return the present value stored with the OOB
|
||||
* data. The stored value contains the right present
|
||||
* information. However it can only be trusted when
|
||||
* not in Secure Connection Only mode.
|
||||
*/
|
||||
if (!memcmp(data->rand192, ZERO_KEY, 16) ||
|
||||
!memcmp(data->hash192, ZERO_KEY, 16))
|
||||
if (!hci_dev_test_flag(hdev, HCI_SC_ONLY))
|
||||
return data->present;
|
||||
|
||||
/* When Secure Connections Only mode is enabled, then
|
||||
* the P-256 values are required. If they are not
|
||||
* available, then do not declare that OOB data is
|
||||
* present.
|
||||
*/
|
||||
if (!memcmp(data->rand256, ZERO_KEY, 16) ||
|
||||
!memcmp(data->hash256, ZERO_KEY, 16))
|
||||
return 0x00;
|
||||
|
||||
return 0x01;
|
||||
return 0x02;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
/* When Secure Connections is not enabled or actually
|
||||
* not supported by the hardware, then check that if
|
||||
* P-192 data values are present.
|
||||
*/
|
||||
if (!memcmp(data->rand192, ZERO_KEY, 16) ||
|
||||
!memcmp(data->hash192, ZERO_KEY, 16))
|
||||
return 0x00;
|
||||
|
||||
return 0x01;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
|
1059
net/bluetooth/mgmt.c
1059
net/bluetooth/mgmt.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
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,8 +2058,19 @@ 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))
|
||||
return sc_check_confirm(smp);
|
||||
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),
|
||||
|
@ -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 crypto_blkcipher *tfm_aes;
|
||||
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 @@ create_chan:
|
|||
|
||||
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);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#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 */
|
||||
|
|
Загрузка…
Ссылка в новой задаче