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

Johan Hedberg says:

====================
pull request: bluetooth-next 2016-09-19

Here's the main bluetooth-next pull request for the 4.9 kernel.

 - Added new messages for monitor sockets for better mgmt tracing
 - Added local name and appearance support in scan response
 - Added new Qualcomm WCNSS SMD based HCI driver
 - Minor fixes & cleanup to 802.15.4 code
 - New USB ID to btusb driver
 - Added Marvell support to HCI UART driver
 - Add combined LED trigger for controller power
 - Other minor fixes here and there

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

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-09-20 22:52:50 -04:00
commit 204dfe1798
32 changed files with 1600 additions and 150 deletions

View File

@ -180,6 +180,17 @@ config BT_HCIUART_AG6XX
Say Y here to compile support for Intel AG6XX protocol.
config BT_HCIUART_MRVL
bool "Marvell protocol support"
depends on BT_HCIUART
select BT_HCIUART_H4
help
Marvell is serial protocol for communication between Bluetooth
device and host. This protocol is required for most Marvell Bluetooth
devices with UART interface.
Say Y here to compile support for HCI MRVL protocol.
config BT_HCIBCM203X
tristate "HCI BCM203x USB driver"
depends on USB
@ -331,4 +342,16 @@ config BT_WILINK
Say Y here to compile support for Texas Instrument's WiLink7 driver
into the kernel or say M to compile it as module (btwilink).
config BT_QCOMSMD
tristate "Qualcomm SMD based HCI support"
depends on QCOM_SMD && QCOM_WCNSS_CTRL
select BT_QCA
help
Qualcomm SMD based HCI driver.
This driver is used to bridge HCI data onto the shared memory
channels to the WCNSS core.
Say Y here to compile support for HCI over Qualcomm SMD into the
kernel or say M to compile as a module.
endmenu

View File

@ -20,6 +20,7 @@ obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_WILINK) += btwilink.o
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
obj-$(CONFIG_BT_BCM) += btbcm.o
obj-$(CONFIG_BT_RTL) += btrtl.o
obj-$(CONFIG_BT_QCA) += btqca.o
@ -37,6 +38,7 @@ hci_uart-$(CONFIG_BT_HCIUART_INTEL) += hci_intel.o
hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o
hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.o
hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o
hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o
hci_uart-objs := $(hci_uart-y)
ccflags-y += -D__CHECK_ENDIAN__

View File

@ -185,10 +185,8 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id
data->state = BCM203X_LOAD_MINIDRV;
data->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!data->urb) {
BT_ERR("Can't allocate URB");
if (!data->urb)
return -ENOMEM;
}
if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
BT_ERR("Mini driver request failed");

View File

@ -55,8 +55,8 @@ static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
}
edl = (struct edl_event_hdr *)(skb->data);
if (!edl || !edl->data) {
BT_ERR("%s: TLV with no header or no data", hdev->name);
if (!edl) {
BT_ERR("%s: TLV with no header", hdev->name);
err = -EILSEQ;
goto out;
}
@ -224,8 +224,8 @@ static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size,
}
edl = (struct edl_event_hdr *)(skb->data);
if (!edl || !edl->data) {
BT_ERR("%s: TLV with no header or no data", hdev->name);
if (!edl) {
BT_ERR("%s: TLV with no header", hdev->name);
err = -EILSEQ;
goto out;
}

View File

@ -0,0 +1,182 @@
/*
* Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
#include <linux/soc/qcom/wcnss_ctrl.h>
#include <linux/platform_device.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btqca.h"
struct btqcomsmd {
struct hci_dev *hdev;
struct qcom_smd_channel *acl_channel;
struct qcom_smd_channel *cmd_channel;
};
static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type,
const void *data, size_t count)
{
struct sk_buff *skb;
/* Use GFP_ATOMIC as we're in IRQ context */
skb = bt_skb_alloc(count, GFP_ATOMIC);
if (!skb) {
hdev->stat.err_rx++;
return -ENOMEM;
}
hci_skb_pkt_type(skb) = type;
memcpy(skb_put(skb, count), data, count);
return hci_recv_frame(hdev, skb);
}
static int btqcomsmd_acl_callback(struct qcom_smd_channel *channel,
const void *data, size_t count)
{
struct btqcomsmd *btq = qcom_smd_get_drvdata(channel);
btq->hdev->stat.byte_rx += count;
return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
}
static int btqcomsmd_cmd_callback(struct qcom_smd_channel *channel,
const void *data, size_t count)
{
struct btqcomsmd *btq = qcom_smd_get_drvdata(channel);
return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
}
static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btqcomsmd *btq = hci_get_drvdata(hdev);
int ret;
switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
ret = qcom_smd_send(btq->acl_channel, skb->data, skb->len);
hdev->stat.acl_tx++;
hdev->stat.byte_tx += skb->len;
break;
case HCI_COMMAND_PKT:
ret = qcom_smd_send(btq->cmd_channel, skb->data, skb->len);
hdev->stat.cmd_tx++;
break;
default:
ret = -EILSEQ;
break;
}
kfree_skb(skb);
return ret;
}
static int btqcomsmd_open(struct hci_dev *hdev)
{
return 0;
}
static int btqcomsmd_close(struct hci_dev *hdev)
{
return 0;
}
static int btqcomsmd_probe(struct platform_device *pdev)
{
struct btqcomsmd *btq;
struct hci_dev *hdev;
void *wcnss;
int ret;
btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL);
if (!btq)
return -ENOMEM;
wcnss = dev_get_drvdata(pdev->dev.parent);
btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL",
btqcomsmd_acl_callback);
if (IS_ERR(btq->acl_channel))
return PTR_ERR(btq->acl_channel);
btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
btqcomsmd_cmd_callback);
if (IS_ERR(btq->cmd_channel))
return PTR_ERR(btq->cmd_channel);
qcom_smd_set_drvdata(btq->acl_channel, btq);
qcom_smd_set_drvdata(btq->cmd_channel, btq);
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
hci_set_drvdata(hdev, btq);
btq->hdev = hdev;
SET_HCIDEV_DEV(hdev, &pdev->dev);
hdev->bus = HCI_SMD;
hdev->open = btqcomsmd_open;
hdev->close = btqcomsmd_close;
hdev->send = btqcomsmd_send;
hdev->set_bdaddr = qca_set_bdaddr_rome;
ret = hci_register_dev(hdev);
if (ret < 0) {
hci_free_dev(hdev);
return ret;
}
platform_set_drvdata(pdev, btq);
return 0;
}
static int btqcomsmd_remove(struct platform_device *pdev)
{
struct btqcomsmd *btq = platform_get_drvdata(pdev);
hci_unregister_dev(btq->hdev);
hci_free_dev(btq->hdev);
return 0;
}
static const struct of_device_id btqcomsmd_of_match[] = {
{ .compatible = "qcom,wcnss-bt", },
{ },
};
static struct platform_driver btqcomsmd_driver = {
.probe = btqcomsmd_probe,
.remove = btqcomsmd_remove,
.driver = {
.name = "btqcomsmd",
.of_match_table = btqcomsmd_of_match,
},
};
module_platform_driver(btqcomsmd_driver);
MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
MODULE_LICENSE("GPL v2");

View File

@ -33,6 +33,7 @@
#define RTL_ROM_LMP_8723B 0x8723
#define RTL_ROM_LMP_8821A 0x8821
#define RTL_ROM_LMP_8761A 0x8761
#define RTL_ROM_LMP_8822B 0x8822
static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
{
@ -78,11 +79,15 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
const unsigned char *patch_length_base, *patch_offset_base;
u32 patch_offset = 0;
u16 patch_length, num_patches;
const u16 project_id_to_lmp_subver[] = {
RTL_ROM_LMP_8723A,
RTL_ROM_LMP_8723B,
RTL_ROM_LMP_8821A,
RTL_ROM_LMP_8761A
static const struct {
__u16 lmp_subver;
__u8 id;
} project_id_to_lmp_subver[] = {
{ RTL_ROM_LMP_8723A, 0 },
{ RTL_ROM_LMP_8723B, 1 },
{ RTL_ROM_LMP_8821A, 2 },
{ RTL_ROM_LMP_8761A, 3 },
{ RTL_ROM_LMP_8822B, 8 },
};
ret = rtl_read_rom_version(hdev, &rom_version);
@ -134,14 +139,20 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
return -EINVAL;
}
if (project_id >= ARRAY_SIZE(project_id_to_lmp_subver)) {
/* Find project_id in table */
for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) {
if (project_id == project_id_to_lmp_subver[i].id)
break;
}
if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
BT_ERR("%s: unknown project id %d", hdev->name, project_id);
return -EINVAL;
}
if (lmp_subver != project_id_to_lmp_subver[project_id]) {
if (lmp_subver != project_id_to_lmp_subver[i].lmp_subver) {
BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
project_id_to_lmp_subver[project_id], lmp_subver);
project_id_to_lmp_subver[i].lmp_subver, lmp_subver);
return -EINVAL;
}
@ -257,6 +268,26 @@ out:
return ret;
}
static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
{
const struct firmware *fw;
int ret;
BT_INFO("%s: rtl: loading %s", hdev->name, name);
ret = request_firmware(&fw, name, &hdev->dev);
if (ret < 0) {
BT_ERR("%s: Failed to load %s", hdev->name, name);
return ret;
}
ret = fw->size;
*buff = kmemdup(fw->data, ret, GFP_KERNEL);
release_firmware(fw);
return ret;
}
static int btrtl_setup_rtl8723a(struct hci_dev *hdev)
{
const struct firmware *fw;
@ -296,25 +327,74 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
unsigned char *fw_data = NULL;
const struct firmware *fw;
int ret;
int cfg_sz;
u8 *cfg_buff = NULL;
u8 *tbuff;
char *cfg_name = NULL;
switch (lmp_subver) {
case RTL_ROM_LMP_8723B:
cfg_name = "rtl_bt/rtl8723b_config.bin";
break;
case RTL_ROM_LMP_8821A:
cfg_name = "rtl_bt/rtl8821a_config.bin";
break;
case RTL_ROM_LMP_8761A:
cfg_name = "rtl_bt/rtl8761a_config.bin";
break;
case RTL_ROM_LMP_8822B:
cfg_name = "rtl_bt/rtl8822b_config.bin";
break;
default:
BT_ERR("%s: rtl: no config according to lmp_subver %04x",
hdev->name, lmp_subver);
break;
}
if (cfg_name) {
cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
if (cfg_sz < 0)
cfg_sz = 0;
} else
cfg_sz = 0;
BT_INFO("%s: rtl: loading %s", hdev->name, fw_name);
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
return ret;
goto err_req_fw;
}
ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
if (ret < 0)
goto out;
if (cfg_sz) {
tbuff = kzalloc(ret + cfg_sz, GFP_KERNEL);
if (!tbuff) {
ret = -ENOMEM;
goto out;
}
memcpy(tbuff, fw_data, ret);
kfree(fw_data);
memcpy(tbuff + ret, cfg_buff, cfg_sz);
ret += cfg_sz;
fw_data = tbuff;
}
BT_INFO("cfg_sz %d, total size %d", cfg_sz, ret);
ret = rtl_download_firmware(hdev, fw_data, ret);
kfree(fw_data);
if (ret < 0)
goto out;
out:
release_firmware(fw);
kfree(fw_data);
err_req_fw:
if (cfg_sz)
kfree(cfg_buff);
return ret;
}
@ -377,6 +457,9 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
case RTL_ROM_LMP_8761A:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8761a_fw.bin");
case RTL_ROM_LMP_8822B:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8822b_fw.bin");
default:
BT_INFO("rtl: assuming no firmware upload needed.");
return 0;

View File

@ -62,6 +62,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_REALTEK 0x20000
#define BTUSB_BCM2045 0x40000
#define BTUSB_IFNUM_2 0x80000
#define BTUSB_CW6622 0x100000
static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@ -248,6 +249,7 @@ static const struct usb_device_id blacklist_table[] = {
/* QCA ROME chipset */
{ USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe009), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0489, 0xe092), .driver_info = BTUSB_QCA_ROME },
@ -290,7 +292,8 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC },
/* CONWISE Technology based adapters with buggy SCO support */
{ USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
{ USB_DEVICE(0x0e5e, 0x6622),
.driver_info = BTUSB_BROKEN_ISOC | BTUSB_CW6622},
/* Roper Class 1 Bluetooth Dongle (Silicon Wave based) */
{ USB_DEVICE(0x1310, 0x0001), .driver_info = BTUSB_SWAVE },
@ -2221,9 +2224,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(5000));
if (err == 1) {
if (err == -EINTR) {
BT_ERR("%s: Firmware loading interrupted", hdev->name);
err = -EINTR;
goto done;
}
@ -2275,7 +2277,7 @@ done:
TASK_INTERRUPTIBLE,
msecs_to_jiffies(1000));
if (err == 1) {
if (err == -EINTR) {
BT_ERR("%s: Device boot interrupted", hdev->name);
return -EINTR;
}
@ -2845,6 +2847,9 @@ static int btusb_probe(struct usb_interface *intf,
hdev->send = btusb_send_frame;
hdev->notify = btusb_notify;
if (id->driver_info & BTUSB_CW6622)
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
if (id->driver_info & BTUSB_BCM2045)
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);

View File

@ -798,7 +798,7 @@ static int bcm_remove(struct platform_device *pdev)
static const struct hci_uart_proto bcm_proto = {
.id = HCI_UART_BCM,
.name = "BCM",
.name = "Broadcom",
.manufacturer = 15,
.init_speed = 115200,
.oper_speed = 4000000,

View File

@ -128,7 +128,7 @@ static int intel_wait_booting(struct hci_uart *hu)
TASK_INTERRUPTIBLE,
msecs_to_jiffies(1000));
if (err == 1) {
if (err == -EINTR) {
bt_dev_err(hu->hdev, "Device boot interrupted");
return -EINTR;
}
@ -151,7 +151,7 @@ static int intel_wait_lpm_transaction(struct hci_uart *hu)
TASK_INTERRUPTIBLE,
msecs_to_jiffies(1000));
if (err == 1) {
if (err == -EINTR) {
bt_dev_err(hu->hdev, "LPM transaction interrupted");
return -EINTR;
}
@ -813,7 +813,7 @@ static int intel_setup(struct hci_uart *hu)
err = wait_on_bit_timeout(&intel->flags, STATE_DOWNLOADING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(5000));
if (err == 1) {
if (err == -EINTR) {
bt_dev_err(hdev, "Firmware loading interrupted");
err = -EINTR;
goto done;

View File

@ -810,6 +810,9 @@ static int __init hci_uart_init(void)
#ifdef CONFIG_BT_HCIUART_AG6XX
ag6xx_init();
#endif
#ifdef CONFIG_BT_HCIUART_MRVL
mrvl_init();
#endif
return 0;
}
@ -845,6 +848,9 @@ static void __exit hci_uart_exit(void)
#ifdef CONFIG_BT_HCIUART_AG6XX
ag6xx_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_MRVL
mrvl_deinit();
#endif
/* Release tty registration of line discipline */
err = tty_unregister_ldisc(N_HCI);

View File

@ -0,0 +1,387 @@
/*
*
* Bluetooth HCI UART driver for marvell devices
*
* Copyright (C) 2016 Marvell International Ltd.
* Copyright (C) 2016 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
#define HCI_FW_REQ_PKT 0xA5
#define HCI_CHIP_VER_PKT 0xAA
#define MRVL_ACK 0x5A
#define MRVL_NAK 0xBF
#define MRVL_RAW_DATA 0x1F
enum {
STATE_CHIP_VER_PENDING,
STATE_FW_REQ_PENDING,
};
struct mrvl_data {
struct sk_buff *rx_skb;
struct sk_buff_head txq;
struct sk_buff_head rawq;
unsigned long flags;
unsigned int tx_len;
u8 id, rev;
};
struct hci_mrvl_pkt {
__le16 lhs;
__le16 rhs;
} __packed;
#define HCI_MRVL_PKT_SIZE 4
static int mrvl_open(struct hci_uart *hu)
{
struct mrvl_data *mrvl;
BT_DBG("hu %p", hu);
mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL);
if (!mrvl)
return -ENOMEM;
skb_queue_head_init(&mrvl->txq);
skb_queue_head_init(&mrvl->rawq);
set_bit(STATE_CHIP_VER_PENDING, &mrvl->flags);
hu->priv = mrvl;
return 0;
}
static int mrvl_close(struct hci_uart *hu)
{
struct mrvl_data *mrvl = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&mrvl->txq);
skb_queue_purge(&mrvl->rawq);
kfree_skb(mrvl->rx_skb);
kfree(mrvl);
hu->priv = NULL;
return 0;
}
static int mrvl_flush(struct hci_uart *hu)
{
struct mrvl_data *mrvl = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&mrvl->txq);
skb_queue_purge(&mrvl->rawq);
return 0;
}
static struct sk_buff *mrvl_dequeue(struct hci_uart *hu)
{
struct mrvl_data *mrvl = hu->priv;
struct sk_buff *skb;
skb = skb_dequeue(&mrvl->txq);
if (!skb) {
/* Any raw data ? */
skb = skb_dequeue(&mrvl->rawq);
} else {
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
}
return skb;
}
static int mrvl_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct mrvl_data *mrvl = hu->priv;
skb_queue_tail(&mrvl->txq, skb);
return 0;
}
static void mrvl_send_ack(struct hci_uart *hu, unsigned char type)
{
struct mrvl_data *mrvl = hu->priv;
struct sk_buff *skb;
/* No H4 payload, only 1 byte header */
skb = bt_skb_alloc(0, GFP_ATOMIC);
if (!skb) {
bt_dev_err(hu->hdev, "Unable to alloc ack/nak packet");
return;
}
hci_skb_pkt_type(skb) = type;
skb_queue_tail(&mrvl->txq, skb);
hci_uart_tx_wakeup(hu);
}
static int mrvl_recv_fw_req(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_mrvl_pkt *pkt = (void *)skb->data;
struct hci_uart *hu = hci_get_drvdata(hdev);
struct mrvl_data *mrvl = hu->priv;
int ret = 0;
if ((pkt->lhs ^ pkt->rhs) != 0xffff) {
bt_dev_err(hdev, "Corrupted mrvl header");
mrvl_send_ack(hu, MRVL_NAK);
ret = -EINVAL;
goto done;
}
mrvl_send_ack(hu, MRVL_ACK);
if (!test_bit(STATE_FW_REQ_PENDING, &mrvl->flags)) {
bt_dev_err(hdev, "Received unexpected firmware request");
ret = -EINVAL;
goto done;
}
mrvl->tx_len = le16_to_cpu(pkt->lhs);
clear_bit(STATE_FW_REQ_PENDING, &mrvl->flags);
smp_mb__after_atomic();
wake_up_bit(&mrvl->flags, STATE_FW_REQ_PENDING);
done:
kfree_skb(skb);
return ret;
}
static int mrvl_recv_chip_ver(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_mrvl_pkt *pkt = (void *)skb->data;
struct hci_uart *hu = hci_get_drvdata(hdev);
struct mrvl_data *mrvl = hu->priv;
u16 version = le16_to_cpu(pkt->lhs);
int ret = 0;
if ((pkt->lhs ^ pkt->rhs) != 0xffff) {
bt_dev_err(hdev, "Corrupted mrvl header");
mrvl_send_ack(hu, MRVL_NAK);
ret = -EINVAL;
goto done;
}
mrvl_send_ack(hu, MRVL_ACK);
if (!test_bit(STATE_CHIP_VER_PENDING, &mrvl->flags)) {
bt_dev_err(hdev, "Received unexpected chip version");
goto done;
}
mrvl->id = version;
mrvl->rev = version >> 8;
bt_dev_info(hdev, "Controller id = %x, rev = %x", mrvl->id, mrvl->rev);
clear_bit(STATE_CHIP_VER_PENDING, &mrvl->flags);
smp_mb__after_atomic();
wake_up_bit(&mrvl->flags, STATE_CHIP_VER_PENDING);
done:
kfree_skb(skb);
return ret;
}
#define HCI_RECV_CHIP_VER \
.type = HCI_CHIP_VER_PKT, \
.hlen = HCI_MRVL_PKT_SIZE, \
.loff = 0, \
.lsize = 0, \
.maxlen = HCI_MRVL_PKT_SIZE
#define HCI_RECV_FW_REQ \
.type = HCI_FW_REQ_PKT, \
.hlen = HCI_MRVL_PKT_SIZE, \
.loff = 0, \
.lsize = 0, \
.maxlen = HCI_MRVL_PKT_SIZE
static const struct h4_recv_pkt mrvl_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
{ HCI_RECV_FW_REQ, .recv = mrvl_recv_fw_req },
{ HCI_RECV_CHIP_VER, .recv = mrvl_recv_chip_ver },
};
static int mrvl_recv(struct hci_uart *hu, const void *data, int count)
{
struct mrvl_data *mrvl = hu->priv;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count,
mrvl_recv_pkts,
ARRAY_SIZE(mrvl_recv_pkts));
if (IS_ERR(mrvl->rx_skb)) {
int err = PTR_ERR(mrvl->rx_skb);
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
mrvl->rx_skb = NULL;
return err;
}
return count;
}
static int mrvl_load_firmware(struct hci_dev *hdev, const char *name)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct mrvl_data *mrvl = hu->priv;
const struct firmware *fw = NULL;
const u8 *fw_ptr, *fw_max;
int err;
err = request_firmware(&fw, name, &hdev->dev);
if (err < 0) {
bt_dev_err(hdev, "Failed to load firmware file %s", name);
return err;
}
fw_ptr = fw->data;
fw_max = fw->data + fw->size;
bt_dev_info(hdev, "Loading %s", name);
set_bit(STATE_FW_REQ_PENDING, &mrvl->flags);
while (fw_ptr <= fw_max) {
struct sk_buff *skb;
/* Controller drives the firmware load by sending firmware
* request packets containing the expected fragment size.
*/
err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(2000));
if (err == 1) {
bt_dev_err(hdev, "Firmware load interrupted");
err = -EINTR;
break;
} else if (err) {
bt_dev_err(hdev, "Firmware request timeout");
err = -ETIMEDOUT;
break;
}
bt_dev_dbg(hdev, "Firmware request, expecting %d bytes",
mrvl->tx_len);
if (fw_ptr == fw_max) {
/* Controller requests a null size once firmware is
* fully loaded. If controller expects more data, there
* is an issue.
*/
if (!mrvl->tx_len) {
bt_dev_info(hdev, "Firmware loading complete");
} else {
bt_dev_err(hdev, "Firmware loading failure");
err = -EINVAL;
}
break;
}
if (fw_ptr + mrvl->tx_len > fw_max) {
mrvl->tx_len = fw_max - fw_ptr;
bt_dev_dbg(hdev, "Adjusting tx_len to %d",
mrvl->tx_len);
}
skb = bt_skb_alloc(mrvl->tx_len, GFP_KERNEL);
if (!skb) {
bt_dev_err(hdev, "Failed to alloc mem for FW packet");
err = -ENOMEM;
break;
}
bt_cb(skb)->pkt_type = MRVL_RAW_DATA;
memcpy(skb_put(skb, mrvl->tx_len), fw_ptr, mrvl->tx_len);
fw_ptr += mrvl->tx_len;
set_bit(STATE_FW_REQ_PENDING, &mrvl->flags);
skb_queue_tail(&mrvl->rawq, skb);
hci_uart_tx_wakeup(hu);
}
release_firmware(fw);
return err;
}
static int mrvl_setup(struct hci_uart *hu)
{
int err;
hci_uart_set_flow_control(hu, true);
err = mrvl_load_firmware(hu->hdev, "mrvl/helper_uart_3000000.bin");
if (err) {
bt_dev_err(hu->hdev, "Unable to download firmware helper");
return -EINVAL;
}
hci_uart_set_baudrate(hu, 3000000);
hci_uart_set_flow_control(hu, false);
err = mrvl_load_firmware(hu->hdev, "mrvl/uart8897_bt.bin");
if (err)
return err;
return 0;
}
static const struct hci_uart_proto mrvl_proto = {
.id = HCI_UART_MRVL,
.name = "Marvell",
.init_speed = 115200,
.open = mrvl_open,
.close = mrvl_close,
.flush = mrvl_flush,
.setup = mrvl_setup,
.recv = mrvl_recv,
.enqueue = mrvl_enqueue,
.dequeue = mrvl_dequeue,
};
int __init mrvl_init(void)
{
return hci_uart_register_proto(&mrvl_proto);
}
int __exit mrvl_deinit(void)
{
return hci_uart_unregister_proto(&mrvl_proto);
}

View File

@ -397,7 +397,7 @@ static int qca_open(struct hci_uart *hu)
skb_queue_head_init(&qca->txq);
skb_queue_head_init(&qca->tx_wait_q);
spin_lock_init(&qca->hci_ibs_lock);
qca->workqueue = create_singlethread_workqueue("qca_wq");
qca->workqueue = alloc_ordered_workqueue("qca_wq", 0);
if (!qca->workqueue) {
BT_ERR("QCA Workqueue not initialized properly");
kfree(qca);

View File

@ -35,7 +35,7 @@
#define HCIUARTGETFLAGS _IOR('U', 204, int)
/* UART protocols */
#define HCI_UART_MAX_PROTO 10
#define HCI_UART_MAX_PROTO 12
#define HCI_UART_H4 0
#define HCI_UART_BCSP 1
@ -47,6 +47,8 @@
#define HCI_UART_BCM 7
#define HCI_UART_QCA 8
#define HCI_UART_AG6XX 9
#define HCI_UART_NOKIA 10
#define HCI_UART_MRVL 11
#define HCI_UART_RAW_DEVICE 0
#define HCI_UART_RESET_ON_INIT 1
@ -189,3 +191,8 @@ int qca_deinit(void);
int ag6xx_init(void);
int ag6xx_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_MRVL
int mrvl_init(void);
int mrvl_deinit(void);
#endif

View File

@ -30,7 +30,7 @@
static int numlbs = 2;
static LIST_HEAD(fakelb_phys);
static DEFINE_SPINLOCK(fakelb_phys_lock);
static DEFINE_MUTEX(fakelb_phys_lock);
static LIST_HEAD(fakelb_ifup_phys);
static DEFINE_RWLOCK(fakelb_ifup_phys_lock);
@ -188,9 +188,9 @@ static int fakelb_add_one(struct device *dev)
if (err)
goto err_reg;
spin_lock(&fakelb_phys_lock);
mutex_lock(&fakelb_phys_lock);
list_add_tail(&phy->list, &fakelb_phys);
spin_unlock(&fakelb_phys_lock);
mutex_unlock(&fakelb_phys_lock);
return 0;
@ -222,10 +222,10 @@ static int fakelb_probe(struct platform_device *pdev)
return 0;
err_slave:
spin_lock(&fakelb_phys_lock);
mutex_lock(&fakelb_phys_lock);
list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
fakelb_del(phy);
spin_unlock(&fakelb_phys_lock);
mutex_unlock(&fakelb_phys_lock);
return err;
}
@ -233,10 +233,10 @@ static int fakelb_remove(struct platform_device *pdev)
{
struct fakelb_phy *phy, *tmp;
spin_lock(&fakelb_phys_lock);
mutex_lock(&fakelb_phys_lock);
list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
fakelb_del(phy);
spin_unlock(&fakelb_phys_lock);
mutex_unlock(&fakelb_phys_lock);
return 0;
}

View File

@ -29,7 +29,8 @@
#include <net/sock.h>
#include <linux/seq_file.h>
#define BT_SUBSYS_VERSION "2.21"
#define BT_SUBSYS_VERSION 2
#define BT_SUBSYS_REVISION 22
#ifndef AF_BLUETOOTH
#define AF_BLUETOOTH 31
@ -371,6 +372,7 @@ 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);
u32 hci_sock_get_cookie(struct sock *sk);
int hci_sock_init(void);
void hci_sock_cleanup(void);

View File

@ -63,6 +63,7 @@
#define HCI_SDIO 6
#define HCI_SPI 7
#define HCI_I2C 8
#define HCI_SMD 9
/* HCI controller types */
#define HCI_PRIMARY 0x00
@ -207,7 +208,11 @@ enum {
HCI_MGMT_INDEX_EVENTS,
HCI_MGMT_UNCONF_INDEX_EVENTS,
HCI_MGMT_EXT_INDEX_EVENTS,
HCI_MGMT_GENERIC_EVENTS,
HCI_MGMT_EXT_INFO_EVENTS,
HCI_MGMT_OPTION_EVENTS,
HCI_MGMT_SETTING_EVENTS,
HCI_MGMT_DEV_CLASS_EVENTS,
HCI_MGMT_LOCAL_NAME_EVENTS,
HCI_MGMT_OOB_DATA_EVENTS,
};

View File

@ -211,6 +211,7 @@ struct hci_dev {
__u8 dev_name[HCI_MAX_NAME_LENGTH];
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
__u8 eir[HCI_MAX_EIR_LENGTH];
__u16 appearance;
__u8 dev_class[3];
__u8 major_class;
__u8 minor_class;
@ -399,7 +400,9 @@ struct hci_dev {
struct delayed_work rpa_expired;
bdaddr_t rpa;
#if IS_ENABLED(CONFIG_BT_LEDS)
struct led_trigger *power_led;
#endif
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
@ -1026,8 +1029,8 @@ int hci_resume_dev(struct hci_dev *hdev);
int hci_reset_dev(struct hci_dev *hdev);
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);
__printf(2, 3) void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
__printf(2, 3) void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);
int hci_dev_open(__u16 dev);
int hci_dev_close(__u16 dev);
int hci_dev_do_close(struct hci_dev *hdev);
@ -1404,6 +1407,9 @@ 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,
int flag, struct sock *skip_sk);
void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
void hci_send_monitor_ctrl_event(struct hci_dev *hdev, u16 event,
void *data, u16 data_len, ktime_t tstamp,
int flag, struct sock *skip_sk);
void hci_sock_dev_event(struct hci_dev *hdev, int event);
@ -1449,6 +1455,7 @@ 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 */
void mgmt_fill_version_info(void *ver);
int mgmt_new_settings(struct hci_dev *hdev);
void mgmt_index_added(struct hci_dev *hdev);
void mgmt_index_removed(struct hci_dev *hdev);

View File

@ -45,6 +45,10 @@ struct hci_mon_hdr {
#define HCI_MON_VENDOR_DIAG 11
#define HCI_MON_SYSTEM_NOTE 12
#define HCI_MON_USER_LOGGING 13
#define HCI_MON_CTRL_OPEN 14
#define HCI_MON_CTRL_CLOSE 15
#define HCI_MON_CTRL_COMMAND 16
#define HCI_MON_CTRL_EVENT 17
struct hci_mon_new_index {
__u8 type;

View File

@ -586,6 +586,24 @@ struct mgmt_rp_get_adv_size_info {
#define MGMT_OP_START_LIMITED_DISCOVERY 0x0041
#define MGMT_OP_READ_EXT_INFO 0x0042
#define MGMT_READ_EXT_INFO_SIZE 0
struct mgmt_rp_read_ext_info {
bdaddr_t bdaddr;
__u8 version;
__le16 manufacturer;
__le32 supported_settings;
__le32 current_settings;
__le16 eir_len;
__u8 eir[0];
} __packed;
#define MGMT_OP_SET_APPEARANCE 0x0043
struct mgmt_cp_set_appearance {
__u16 appearance;
} __packed;
#define MGMT_SET_APPEARANCE_SIZE 2
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
@ -800,3 +818,9 @@ struct mgmt_ev_advertising_added {
struct mgmt_ev_advertising_removed {
__u8 instance;
} __packed;
#define MGMT_EV_EXT_INFO_CHANGED 0x0025
struct mgmt_ev_ext_info_changed {
__le16 eir_len;
__u8 eir[0];
} __packed;

View File

@ -101,8 +101,6 @@ static void lowpan_ndisc_802154_update(struct neighbour *n, u32 flags,
ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short);
if (!lowpan_802154_is_valid_src_short_addr(neigh->short_addr))
neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
} else {
neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
}
write_unlock_bh(&n->lock);
}

View File

@ -26,11 +26,13 @@
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/stringify.h>
#include <asm/ioctls.h>
#include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h>
#include "leds.h"
#include "selftest.h"
/* Bluetooth sockets */
@ -712,13 +714,16 @@ static struct net_proto_family bt_sock_family_ops = {
struct dentry *bt_debugfs;
EXPORT_SYMBOL_GPL(bt_debugfs);
#define VERSION __stringify(BT_SUBSYS_VERSION) "." \
__stringify(BT_SUBSYS_REVISION)
static int __init bt_init(void)
{
int err;
sock_skb_cb_check_size(sizeof(struct bt_skb_cb));
BT_INFO("Core ver %s", BT_SUBSYS_VERSION);
BT_INFO("Core ver %s", VERSION);
err = bt_selftest();
if (err < 0)
@ -726,6 +731,8 @@ static int __init bt_init(void)
bt_debugfs = debugfs_create_dir("bluetooth", NULL);
bt_leds_init();
err = bt_sysfs_init();
if (err < 0)
return err;
@ -785,6 +792,8 @@ static void __exit bt_exit(void)
bt_sysfs_cleanup();
bt_leds_cleanup();
debugfs_remove_recursive(bt_debugfs);
}
@ -792,7 +801,7 @@ subsys_initcall(bt_init);
module_exit(bt_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth Core ver " BT_SUBSYS_VERSION);
MODULE_VERSION(BT_SUBSYS_VERSION);
MODULE_DESCRIPTION("Bluetooth Core ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_BLUETOOTH);

View File

@ -1562,6 +1562,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
if (!auto_off && hdev->dev_type == HCI_PRIMARY &&
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
hci_dev_test_flag(hdev, HCI_MGMT))
__mgmt_power_off(hdev);

View File

@ -971,14 +971,14 @@ void __hci_req_enable_advertising(struct hci_request *req)
hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
}
static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
static u8 append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
{
u8 ad_len = 0;
size_t name_len;
int max_len;
max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
name_len = strlen(hdev->dev_name);
if (name_len > 0) {
size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
if (name_len > 0 && max_len > 0) {
if (name_len > max_len) {
name_len = max_len;
@ -997,22 +997,42 @@ static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
return ad_len;
}
static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
{
return append_local_name(hdev, ptr, 0);
}
static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
u8 *ptr)
{
struct adv_info *adv_instance;
u32 instance_flags;
u8 scan_rsp_len = 0;
adv_instance = hci_find_adv_instance(hdev, instance);
if (!adv_instance)
return 0;
/* TODO: Set the appropriate entries based on advertising instance flags
* here once flags other than 0 are supported.
*/
instance_flags = adv_instance->flags;
if ((instance_flags & MGMT_ADV_FLAG_APPEARANCE) && hdev->appearance) {
ptr[0] = 3;
ptr[1] = EIR_APPEARANCE;
put_unaligned_le16(hdev->appearance, ptr + 2);
scan_rsp_len += 4;
ptr += 4;
}
memcpy(ptr, adv_instance->scan_rsp_data,
adv_instance->scan_rsp_len);
return adv_instance->scan_rsp_len;
scan_rsp_len += adv_instance->scan_rsp_len;
ptr += adv_instance->scan_rsp_len;
if (instance_flags & MGMT_ADV_FLAG_LOCAL_NAME)
scan_rsp_len = append_local_name(hdev, ptr, scan_rsp_len);
return scan_rsp_len;
}
void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
@ -1194,7 +1214,7 @@ static void adv_timeout_expire(struct work_struct *work)
hci_req_init(&req, hdev);
hci_req_clear_adv_instance(hdev, &req, instance, false);
hci_req_clear_adv_instance(hdev, NULL, &req, instance, false);
if (list_empty(&hdev->adv_instances))
__hci_req_disable_advertising(&req);
@ -1284,8 +1304,9 @@ static void cancel_adv_timeout(struct hci_dev *hdev)
* setting.
* - force == false: Only instances that have a timeout will be removed.
*/
void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
u8 instance, bool force)
void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
struct hci_request *req, u8 instance,
bool force)
{
struct adv_info *adv_instance, *n, *next_instance = NULL;
int err;
@ -1311,7 +1332,7 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
rem_inst = adv_instance->instance;
err = hci_remove_adv_instance(hdev, rem_inst);
if (!err)
mgmt_advertising_removed(NULL, hdev, rem_inst);
mgmt_advertising_removed(sk, hdev, rem_inst);
}
} else {
adv_instance = hci_find_adv_instance(hdev, instance);
@ -1325,7 +1346,7 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
err = hci_remove_adv_instance(hdev, instance);
if (!err)
mgmt_advertising_removed(NULL, hdev, instance);
mgmt_advertising_removed(sk, hdev, instance);
}
}
@ -1716,7 +1737,7 @@ void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
* function. To be safe hard-code one of the
* values that's suitable for SCO.
*/
rej.reason = HCI_ERROR_REMOTE_LOW_RESOURCES;
rej.reason = HCI_ERROR_REJ_LIMITED_RESOURCES;
hci_req_add(req, HCI_OP_REJECT_SYNC_CONN_REQ,
sizeof(rej), &rej);

View File

@ -73,8 +73,9 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance);
int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
bool force);
void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
u8 instance, bool force);
void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
struct hci_request *req, u8 instance,
bool force);
void __hci_req_update_class(struct hci_request *req);

View File

@ -26,6 +26,7 @@
#include <linux/export.h>
#include <linux/utsname.h>
#include <linux/sched.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@ -38,6 +39,8 @@
static LIST_HEAD(mgmt_chan_list);
static DEFINE_MUTEX(mgmt_chan_list_lock);
static DEFINE_IDA(sock_cookie_ida);
static atomic_t monitor_promisc = ATOMIC_INIT(0);
/* ----- HCI socket interface ----- */
@ -52,6 +55,8 @@ struct hci_pinfo {
__u32 cmsg_mask;
unsigned short channel;
unsigned long flags;
__u32 cookie;
char comm[TASK_COMM_LEN];
};
void hci_sock_set_flag(struct sock *sk, int nr)
@ -74,6 +79,38 @@ unsigned short hci_sock_get_channel(struct sock *sk)
return hci_pi(sk)->channel;
}
u32 hci_sock_get_cookie(struct sock *sk)
{
return hci_pi(sk)->cookie;
}
static bool hci_sock_gen_cookie(struct sock *sk)
{
int id = hci_pi(sk)->cookie;
if (!id) {
id = ida_simple_get(&sock_cookie_ida, 1, 0, GFP_KERNEL);
if (id < 0)
id = 0xffffffff;
hci_pi(sk)->cookie = id;
get_task_comm(hci_pi(sk)->comm, current);
return true;
}
return false;
}
static void hci_sock_free_cookie(struct sock *sk)
{
int id = hci_pi(sk)->cookie;
if (id) {
hci_pi(sk)->cookie = 0xffffffff;
ida_simple_remove(&sock_cookie_ida, id);
}
}
static inline int hci_test_bit(int nr, const void *addr)
{
return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
@ -305,6 +342,60 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
kfree_skb(skb_copy);
}
void hci_send_monitor_ctrl_event(struct hci_dev *hdev, u16 event,
void *data, u16 data_len, ktime_t tstamp,
int flag, struct sock *skip_sk)
{
struct sock *sk;
__le16 index;
if (hdev)
index = cpu_to_le16(hdev->id);
else
index = cpu_to_le16(MGMT_INDEX_NONE);
read_lock(&hci_sk_list.lock);
sk_for_each(sk, &hci_sk_list.head) {
struct hci_mon_hdr *hdr;
struct sk_buff *skb;
if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL)
continue;
/* Ignore socket without the flag set */
if (!hci_sock_test_flag(sk, flag))
continue;
/* Skip the original socket */
if (sk == skip_sk)
continue;
skb = bt_skb_alloc(6 + data_len, GFP_ATOMIC);
if (!skb)
continue;
put_unaligned_le32(hci_pi(sk)->cookie, skb_put(skb, 4));
put_unaligned_le16(event, skb_put(skb, 2));
if (data)
memcpy(skb_put(skb, data_len), data, data_len);
skb->tstamp = tstamp;
hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
hdr->opcode = cpu_to_le16(HCI_MON_CTRL_EVENT);
hdr->index = index;
hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
read_unlock(&hci_sk_list.lock);
}
static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
{
struct hci_mon_hdr *hdr;
@ -384,6 +475,129 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
return skb;
}
static struct sk_buff *create_monitor_ctrl_open(struct sock *sk)
{
struct hci_mon_hdr *hdr;
struct sk_buff *skb;
u16 format;
u8 ver[3];
u32 flags;
/* No message needed when cookie is not present */
if (!hci_pi(sk)->cookie)
return NULL;
switch (hci_pi(sk)->channel) {
case HCI_CHANNEL_RAW:
format = 0x0000;
ver[0] = BT_SUBSYS_VERSION;
put_unaligned_le16(BT_SUBSYS_REVISION, ver + 1);
break;
case HCI_CHANNEL_USER:
format = 0x0001;
ver[0] = BT_SUBSYS_VERSION;
put_unaligned_le16(BT_SUBSYS_REVISION, ver + 1);
break;
case HCI_CHANNEL_CONTROL:
format = 0x0002;
mgmt_fill_version_info(ver);
break;
default:
/* No message for unsupported format */
return NULL;
}
skb = bt_skb_alloc(14 + TASK_COMM_LEN , GFP_ATOMIC);
if (!skb)
return NULL;
flags = hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) ? 0x1 : 0x0;
put_unaligned_le32(hci_pi(sk)->cookie, skb_put(skb, 4));
put_unaligned_le16(format, skb_put(skb, 2));
memcpy(skb_put(skb, sizeof(ver)), ver, sizeof(ver));
put_unaligned_le32(flags, skb_put(skb, 4));
*skb_put(skb, 1) = TASK_COMM_LEN;
memcpy(skb_put(skb, TASK_COMM_LEN), hci_pi(sk)->comm, TASK_COMM_LEN);
__net_timestamp(skb);
hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
hdr->opcode = cpu_to_le16(HCI_MON_CTRL_OPEN);
if (hci_pi(sk)->hdev)
hdr->index = cpu_to_le16(hci_pi(sk)->hdev->id);
else
hdr->index = cpu_to_le16(HCI_DEV_NONE);
hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
return skb;
}
static struct sk_buff *create_monitor_ctrl_close(struct sock *sk)
{
struct hci_mon_hdr *hdr;
struct sk_buff *skb;
/* No message needed when cookie is not present */
if (!hci_pi(sk)->cookie)
return NULL;
switch (hci_pi(sk)->channel) {
case HCI_CHANNEL_RAW:
case HCI_CHANNEL_USER:
case HCI_CHANNEL_CONTROL:
break;
default:
/* No message for unsupported format */
return NULL;
}
skb = bt_skb_alloc(4, GFP_ATOMIC);
if (!skb)
return NULL;
put_unaligned_le32(hci_pi(sk)->cookie, skb_put(skb, 4));
__net_timestamp(skb);
hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
hdr->opcode = cpu_to_le16(HCI_MON_CTRL_CLOSE);
if (hci_pi(sk)->hdev)
hdr->index = cpu_to_le16(hci_pi(sk)->hdev->id);
else
hdr->index = cpu_to_le16(HCI_DEV_NONE);
hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
return skb;
}
static struct sk_buff *create_monitor_ctrl_command(struct sock *sk, u16 index,
u16 opcode, u16 len,
const void *buf)
{
struct hci_mon_hdr *hdr;
struct sk_buff *skb;
skb = bt_skb_alloc(6 + len, GFP_ATOMIC);
if (!skb)
return NULL;
put_unaligned_le32(hci_pi(sk)->cookie, skb_put(skb, 4));
put_unaligned_le16(opcode, skb_put(skb, 2));
if (buf)
memcpy(skb_put(skb, len), buf, len);
__net_timestamp(skb);
hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
hdr->opcode = cpu_to_le16(HCI_MON_CTRL_COMMAND);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
return skb;
}
static void __printf(2, 3)
send_monitor_note(struct sock *sk, const char *fmt, ...)
{
@ -458,6 +672,26 @@ static void send_monitor_replay(struct sock *sk)
read_unlock(&hci_dev_list_lock);
}
static void send_monitor_control_replay(struct sock *mon_sk)
{
struct sock *sk;
read_lock(&hci_sk_list.lock);
sk_for_each(sk, &hci_sk_list.head) {
struct sk_buff *skb;
skb = create_monitor_ctrl_open(sk);
if (!skb)
continue;
if (sock_queue_rcv_skb(mon_sk, skb))
kfree_skb(skb);
}
read_unlock(&hci_sk_list.lock);
}
/* Generate internal stack event */
static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
{
@ -585,6 +819,7 @@ static int hci_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct hci_dev *hdev;
struct sk_buff *skb;
BT_DBG("sock %p sk %p", sock, sk);
@ -593,8 +828,24 @@ static int hci_sock_release(struct socket *sock)
hdev = hci_pi(sk)->hdev;
if (hci_pi(sk)->channel == HCI_CHANNEL_MONITOR)
switch (hci_pi(sk)->channel) {
case HCI_CHANNEL_MONITOR:
atomic_dec(&monitor_promisc);
break;
case HCI_CHANNEL_RAW:
case HCI_CHANNEL_USER:
case HCI_CHANNEL_CONTROL:
/* Send event to monitor */
skb = create_monitor_ctrl_close(sk);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
hci_sock_free_cookie(sk);
break;
}
bt_sock_unlink(&hci_sk_list, sk);
@ -721,6 +972,27 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
goto done;
}
/* When calling an ioctl on an unbound raw socket, then ensure
* that the monitor gets informed. Ensure that the resulting event
* is only send once by checking if the cookie exists or not. The
* socket cookie will be only ever generated once for the lifetime
* of a given socket.
*/
if (hci_sock_gen_cookie(sk)) {
struct sk_buff *skb;
if (capable(CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
/* Send event to monitor */
skb = create_monitor_ctrl_open(sk);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
release_sock(sk);
switch (cmd) {
@ -784,6 +1056,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
struct sockaddr_hci haddr;
struct sock *sk = sock->sk;
struct hci_dev *hdev = NULL;
struct sk_buff *skb;
int len, err = 0;
BT_DBG("sock %p sk %p", sock, sk);
@ -822,7 +1095,35 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
atomic_inc(&hdev->promisc);
}
hci_pi(sk)->channel = haddr.hci_channel;
if (!hci_sock_gen_cookie(sk)) {
/* In the case when a cookie has already been assigned,
* then there has been already an ioctl issued against
* an unbound socket and with that triggerd an open
* notification. Send a close notification first to
* allow the state transition to bounded.
*/
skb = create_monitor_ctrl_close(sk);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
if (capable(CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
hci_pi(sk)->hdev = hdev;
/* Send event to monitor */
skb = create_monitor_ctrl_open(sk);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
break;
case HCI_CHANNEL_USER:
@ -884,9 +1185,38 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
}
}
atomic_inc(&hdev->promisc);
hci_pi(sk)->channel = haddr.hci_channel;
if (!hci_sock_gen_cookie(sk)) {
/* In the case when a cookie has already been assigned,
* this socket will transition from a raw socket into
* an user channel socket. For a clean transition, send
* the close notification first.
*/
skb = create_monitor_ctrl_close(sk);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
/* The user channel is restricted to CAP_NET_ADMIN
* capabilities and with that implicitly trusted.
*/
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
hci_pi(sk)->hdev = hdev;
/* Send event to monitor */
skb = create_monitor_ctrl_open(sk);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
atomic_inc(&hdev->promisc);
break;
case HCI_CHANNEL_MONITOR:
@ -900,6 +1230,8 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
goto done;
}
hci_pi(sk)->channel = haddr.hci_channel;
/* The monitor interface is restricted to CAP_NET_RAW
* capabilities and with that implicitly trusted.
*/
@ -908,9 +1240,10 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
send_monitor_note(sk, "Linux version %s (%s)",
init_utsname()->release,
init_utsname()->machine);
send_monitor_note(sk, "Bluetooth subsystem version %s",
BT_SUBSYS_VERSION);
send_monitor_note(sk, "Bluetooth subsystem version %u.%u",
BT_SUBSYS_VERSION, BT_SUBSYS_REVISION);
send_monitor_replay(sk);
send_monitor_control_replay(sk);
atomic_inc(&monitor_promisc);
break;
@ -925,6 +1258,8 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
err = -EPERM;
goto done;
}
hci_pi(sk)->channel = haddr.hci_channel;
break;
default:
@ -946,6 +1281,8 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
if (capable(CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
hci_pi(sk)->channel = haddr.hci_channel;
/* At the moment the index and unconfigured index events
* are enabled unconditionally. Setting them on each
* socket when binding keeps this functionality. They
@ -956,16 +1293,40 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
* received by untrusted users. Example for such events
* are changes to settings, class of device, name etc.
*/
if (haddr.hci_channel == HCI_CHANNEL_CONTROL) {
if (hci_pi(sk)->channel == HCI_CHANNEL_CONTROL) {
if (!hci_sock_gen_cookie(sk)) {
/* In the case when a cookie has already been
* assigned, this socket will transtion from
* a raw socket into a control socket. To
* allow for a clean transtion, send the
* close notification first.
*/
skb = create_monitor_ctrl_close(sk);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
/* Send event to monitor */
skb = create_monitor_ctrl_open(sk);
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
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);
hci_sock_set_flag(sk, HCI_MGMT_OPTION_EVENTS);
hci_sock_set_flag(sk, HCI_MGMT_SETTING_EVENTS);
hci_sock_set_flag(sk, HCI_MGMT_DEV_CLASS_EVENTS);
hci_sock_set_flag(sk, HCI_MGMT_LOCAL_NAME_EVENTS);
}
break;
}
hci_pi(sk)->channel = haddr.hci_channel;
sk->sk_state = BT_BOUND;
done:
@ -1133,6 +1494,19 @@ static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk,
goto done;
}
if (chan->channel == HCI_CHANNEL_CONTROL) {
struct sk_buff *skb;
/* Send event to monitor */
skb = create_monitor_ctrl_command(sk, index, opcode, len,
buf + sizeof(*hdr));
if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
if (opcode >= chan->handler_count ||
chan->handlers[opcode].func == NULL) {
BT_DBG("Unknown op %u", opcode);
@ -1440,6 +1814,9 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
BT_DBG("sk %p, opt %d", sk, optname);
if (level != SOL_HCI)
return -ENOPROTOOPT;
lock_sock(sk);
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
@ -1523,6 +1900,9 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
BT_DBG("sk %p, opt %d", sk, optname);
if (level != SOL_HCI)
return -ENOPROTOOPT;
if (get_user(len, optlen))
return -EFAULT;

View File

@ -11,6 +11,8 @@
#include "leds.h"
DEFINE_LED_TRIGGER(bt_power_led_trigger);
struct hci_basic_led_trigger {
struct led_trigger led_trigger;
struct hci_dev *hdev;
@ -24,6 +26,21 @@ void hci_leds_update_powered(struct hci_dev *hdev, bool enabled)
if (hdev->power_led)
led_trigger_event(hdev->power_led,
enabled ? LED_FULL : LED_OFF);
if (!enabled) {
struct hci_dev *d;
read_lock(&hci_dev_list_lock);
list_for_each_entry(d, &hci_dev_list, list) {
if (test_bit(HCI_UP, &d->flags))
enabled = true;
}
read_unlock(&hci_dev_list_lock);
}
led_trigger_event(bt_power_led_trigger, enabled ? LED_FULL : LED_OFF);
}
static void power_activate(struct led_classdev *led_cdev)
@ -72,3 +89,13 @@ void hci_leds_init(struct hci_dev *hdev)
/* initialize power_led */
hdev->power_led = led_allocate_basic(hdev, power_activate, "power");
}
void bt_leds_init(void)
{
led_trigger_register_simple("bluetooth-power", &bt_power_led_trigger);
}
void bt_leds_cleanup(void)
{
led_trigger_unregister_simple(bt_power_led_trigger);
}

View File

@ -7,10 +7,20 @@
*/
#if IS_ENABLED(CONFIG_BT_LEDS)
void hci_leds_update_powered(struct hci_dev *hdev, bool enabled);
void hci_leds_init(struct hci_dev *hdev);
void bt_leds_init(void);
void bt_leds_cleanup(void);
#else
static inline void hci_leds_update_powered(struct hci_dev *hdev,
bool enabled) {}
static inline void hci_leds_init(struct hci_dev *hdev) {}
static inline void bt_leds_init(void) {}
static inline void bt_leds_cleanup(void) {}
#endif

View File

@ -38,7 +38,7 @@
#include "mgmt_util.h"
#define MGMT_VERSION 1
#define MGMT_REVISION 13
#define MGMT_REVISION 14
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@ -104,6 +104,8 @@ static const u16 mgmt_commands[] = {
MGMT_OP_REMOVE_ADVERTISING,
MGMT_OP_GET_ADV_SIZE_INFO,
MGMT_OP_START_LIMITED_DISCOVERY,
MGMT_OP_READ_EXT_INFO,
MGMT_OP_SET_APPEARANCE,
};
static const u16 mgmt_events[] = {
@ -141,6 +143,7 @@ static const u16 mgmt_events[] = {
MGMT_EV_LOCAL_OOB_DATA_UPDATED,
MGMT_EV_ADVERTISING_ADDED,
MGMT_EV_ADVERTISING_REMOVED,
MGMT_EV_EXT_INFO_CHANGED,
};
static const u16 mgmt_untrusted_commands[] = {
@ -149,6 +152,7 @@ static const u16 mgmt_untrusted_commands[] = {
MGMT_OP_READ_UNCONF_INDEX_LIST,
MGMT_OP_READ_CONFIG_INFO,
MGMT_OP_READ_EXT_INDEX_LIST,
MGMT_OP_READ_EXT_INFO,
};
static const u16 mgmt_untrusted_events[] = {
@ -162,6 +166,7 @@ static const u16 mgmt_untrusted_events[] = {
MGMT_EV_NEW_CONFIG_OPTIONS,
MGMT_EV_EXT_INDEX_ADDED,
MGMT_EV_EXT_INDEX_REMOVED,
MGMT_EV_EXT_INFO_CHANGED,
};
#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
@ -256,13 +261,6 @@ static int mgmt_limited_event(u16 event, struct hci_dev *hdev, void *data,
flag, skip_sk);
}
static int mgmt_generic_event(u16 event, struct hci_dev *hdev, void *data,
u16 len, struct sock *skip_sk)
{
return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
HCI_MGMT_GENERIC_EVENTS, skip_sk);
}
static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len,
struct sock *skip_sk)
{
@ -278,6 +276,14 @@ static u8 le_addr_type(u8 mgmt_addr_type)
return ADDR_LE_DEV_RANDOM;
}
void mgmt_fill_version_info(void *ver)
{
struct mgmt_rp_read_version *rp = ver;
rp->version = MGMT_VERSION;
rp->revision = cpu_to_le16(MGMT_REVISION);
}
static int read_version(struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len)
{
@ -285,8 +291,7 @@ static int read_version(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("sock %p", sk);
rp.version = MGMT_VERSION;
rp.revision = cpu_to_le16(MGMT_REVISION);
mgmt_fill_version_info(&rp);
return mgmt_cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0,
&rp, sizeof(rp));
@ -572,8 +577,8 @@ static int new_options(struct hci_dev *hdev, struct sock *skip)
{
__le32 options = get_missing_options(hdev);
return mgmt_generic_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options,
sizeof(options), skip);
return mgmt_limited_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options,
sizeof(options), HCI_MGMT_OPTION_EVENTS, skip);
}
static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
@ -862,6 +867,107 @@ static int read_controller_info(struct sock *sk, struct hci_dev *hdev,
sizeof(rp));
}
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;
}
static inline u16 eir_append_le16(u8 *eir, u16 eir_len, u8 type, u16 data)
{
eir[eir_len++] = sizeof(type) + sizeof(data);
eir[eir_len++] = type;
put_unaligned_le16(data, &eir[eir_len]);
eir_len += sizeof(data);
return eir_len;
}
static u16 append_eir_data_to_buf(struct hci_dev *hdev, u8 *eir)
{
u16 eir_len = 0;
size_t name_len;
if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
eir_len = eir_append_data(eir, eir_len, EIR_CLASS_OF_DEV,
hdev->dev_class, 3);
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED))
eir_len = eir_append_le16(eir, eir_len, EIR_APPEARANCE,
hdev->appearance);
name_len = strlen(hdev->dev_name);
eir_len = eir_append_data(eir, eir_len, EIR_NAME_COMPLETE,
hdev->dev_name, name_len);
name_len = strlen(hdev->short_name);
eir_len = eir_append_data(eir, eir_len, EIR_NAME_SHORT,
hdev->short_name, name_len);
return eir_len;
}
static int read_ext_controller_info(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
char buf[512];
struct mgmt_rp_read_ext_info *rp = (void *)buf;
u16 eir_len;
BT_DBG("sock %p %s", sk, hdev->name);
memset(&buf, 0, sizeof(buf));
hci_dev_lock(hdev);
bacpy(&rp->bdaddr, &hdev->bdaddr);
rp->version = hdev->hci_ver;
rp->manufacturer = cpu_to_le16(hdev->manufacturer);
rp->supported_settings = cpu_to_le32(get_supported_settings(hdev));
rp->current_settings = cpu_to_le32(get_current_settings(hdev));
eir_len = append_eir_data_to_buf(hdev, rp->eir);
rp->eir_len = cpu_to_le16(eir_len);
hci_dev_unlock(hdev);
/* If this command is called at least once, then the events
* for class of device and local name changes are disabled
* and only the new extended controller information event
* is used.
*/
hci_sock_set_flag(sk, HCI_MGMT_EXT_INFO_EVENTS);
hci_sock_clear_flag(sk, HCI_MGMT_DEV_CLASS_EVENTS);
hci_sock_clear_flag(sk, HCI_MGMT_LOCAL_NAME_EVENTS);
return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_EXT_INFO, 0, rp,
sizeof(*rp) + eir_len);
}
static int ext_info_changed(struct hci_dev *hdev, struct sock *skip)
{
char buf[512];
struct mgmt_ev_ext_info_changed *ev = (void *)buf;
u16 eir_len;
memset(buf, 0, sizeof(buf));
eir_len = append_eir_data_to_buf(hdev, ev->eir);
ev->eir_len = cpu_to_le16(eir_len);
return mgmt_limited_event(MGMT_EV_EXT_INFO_CHANGED, hdev, ev,
sizeof(*ev) + eir_len,
HCI_MGMT_EXT_INFO_EVENTS, skip);
}
static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
{
__le32 settings = cpu_to_le32(get_current_settings(hdev));
@ -922,7 +1028,7 @@ static int clean_up_hci_state(struct hci_dev *hdev)
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
}
hci_req_clear_adv_instance(hdev, NULL, 0x00, false);
hci_req_clear_adv_instance(hdev, NULL, NULL, 0x00, false);
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
__hci_req_disable_advertising(&req);
@ -1000,8 +1106,8 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip)
{
__le32 ev = cpu_to_le32(get_current_settings(hdev));
return mgmt_generic_event(MGMT_EV_NEW_SETTINGS, hdev, &ev,
sizeof(ev), skip);
return mgmt_limited_event(MGMT_EV_NEW_SETTINGS, hdev, &ev,
sizeof(ev), HCI_MGMT_SETTING_EVENTS, skip);
}
int mgmt_new_settings(struct hci_dev *hdev)
@ -1690,7 +1796,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
enabled = lmp_host_le_capable(hdev);
if (!val)
hci_req_clear_adv_instance(hdev, NULL, 0x00, true);
hci_req_clear_adv_instance(hdev, NULL, NULL, 0x00, true);
if (!hdev_is_powered(hdev) || val == enabled) {
bool changed = false;
@ -2513,8 +2619,8 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("");
if (cp->io_capability > SMP_IO_KEYBOARD_DISPLAY)
return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY,
MGMT_STATUS_INVALID_PARAMS, NULL, 0);
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY,
MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock(hdev);
@ -2932,6 +3038,35 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
}
static void adv_expire(struct hci_dev *hdev, u32 flags)
{
struct adv_info *adv_instance;
struct hci_request req;
int err;
adv_instance = hci_find_adv_instance(hdev, hdev->cur_adv_instance);
if (!adv_instance)
return;
/* stop if current instance doesn't need to be changed */
if (!(adv_instance->flags & flags))
return;
cancel_adv_timeout(hdev);
adv_instance = hci_get_next_instance(hdev, adv_instance->instance);
if (!adv_instance)
return;
hci_req_init(&req, hdev);
err = __hci_req_schedule_adv_instance(&req, adv_instance->instance,
true);
if (err)
return;
hci_req_run(&req, NULL);
}
static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
{
struct mgmt_cp_set_local_name *cp;
@ -2947,13 +3082,17 @@ static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
cp = cmd->param;
if (status)
if (status) {
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
mgmt_status(status));
else
} else {
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
cp, sizeof(*cp));
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
adv_expire(hdev, MGMT_ADV_FLAG_LOCAL_NAME);
}
mgmt_pending_remove(cmd);
unlock:
@ -2993,8 +3132,9 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
if (err < 0)
goto failed;
err = mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev,
data, len, sk);
err = mgmt_limited_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data,
len, HCI_MGMT_LOCAL_NAME_EVENTS, sk);
ext_info_changed(hdev, sk);
goto failed;
}
@ -3029,6 +3169,40 @@ failed:
return err;
}
static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
struct mgmt_cp_set_appearance *cp = data;
u16 apperance;
int err;
BT_DBG("");
if (!lmp_le_capable(hdev))
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_APPEARANCE,
MGMT_STATUS_NOT_SUPPORTED);
apperance = le16_to_cpu(cp->appearance);
hci_dev_lock(hdev);
if (hdev->appearance != apperance) {
hdev->appearance = apperance;
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
adv_expire(hdev, MGMT_ADV_FLAG_APPEARANCE);
ext_info_changed(hdev, sk);
}
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_APPEARANCE, 0, NULL,
0);
hci_dev_unlock(hdev);
return err;
}
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb)
{
@ -4869,7 +5043,7 @@ static int clock_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
int err;
memset(&rp, 0, sizeof(rp));
memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
if (status)
goto complete;
@ -5501,17 +5675,6 @@ unlock:
return err;
}
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;
}
static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb)
{
@ -5815,6 +5978,8 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev)
flags |= MGMT_ADV_FLAG_DISCOV;
flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
flags |= MGMT_ADV_FLAG_APPEARANCE;
flags |= MGMT_ADV_FLAG_LOCAL_NAME;
if (hdev->adv_tx_power != HCI_TX_POWER_INVALID)
flags |= MGMT_ADV_FLAG_TX_POWER;
@ -5871,28 +6036,59 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
return err;
}
static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
u8 len, bool is_adv_data)
static u8 tlv_data_max_len(u32 adv_flags, bool is_adv_data)
{
u8 max_len = HCI_MAX_AD_LENGTH;
int i, cur_len;
bool flags_managed = false;
bool tx_power_managed = false;
if (is_adv_data) {
if (adv_flags & (MGMT_ADV_FLAG_DISCOV |
MGMT_ADV_FLAG_LIMITED_DISCOV |
MGMT_ADV_FLAG_MANAGED_FLAGS)) {
flags_managed = true;
MGMT_ADV_FLAG_MANAGED_FLAGS))
max_len -= 3;
}
if (adv_flags & MGMT_ADV_FLAG_TX_POWER) {
tx_power_managed = true;
if (adv_flags & MGMT_ADV_FLAG_TX_POWER)
max_len -= 3;
}
} else {
/* at least 1 byte of name should fit in */
if (adv_flags & MGMT_ADV_FLAG_LOCAL_NAME)
max_len -= 3;
if (adv_flags & (MGMT_ADV_FLAG_APPEARANCE))
max_len -= 4;
}
return max_len;
}
static bool flags_managed(u32 adv_flags)
{
return adv_flags & (MGMT_ADV_FLAG_DISCOV |
MGMT_ADV_FLAG_LIMITED_DISCOV |
MGMT_ADV_FLAG_MANAGED_FLAGS);
}
static bool tx_power_managed(u32 adv_flags)
{
return adv_flags & MGMT_ADV_FLAG_TX_POWER;
}
static bool name_managed(u32 adv_flags)
{
return adv_flags & MGMT_ADV_FLAG_LOCAL_NAME;
}
static bool appearance_managed(u32 adv_flags)
{
return adv_flags & MGMT_ADV_FLAG_APPEARANCE;
}
static bool tlv_data_is_valid(u32 adv_flags, u8 *data, u8 len, bool is_adv_data)
{
int i, cur_len;
u8 max_len;
max_len = tlv_data_max_len(adv_flags, is_adv_data);
if (len > max_len)
return false;
@ -5900,10 +6096,21 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
for (i = 0, cur_len = 0; i < len; i += (cur_len + 1)) {
cur_len = data[i];
if (flags_managed && data[i + 1] == EIR_FLAGS)
if (data[i + 1] == EIR_FLAGS &&
(!is_adv_data || flags_managed(adv_flags)))
return false;
if (tx_power_managed && data[i + 1] == EIR_TX_POWER)
if (data[i + 1] == EIR_TX_POWER && tx_power_managed(adv_flags))
return false;
if (data[i + 1] == EIR_NAME_COMPLETE && name_managed(adv_flags))
return false;
if (data[i + 1] == EIR_NAME_SHORT && name_managed(adv_flags))
return false;
if (data[i + 1] == EIR_APPEARANCE &&
appearance_managed(adv_flags))
return false;
/* If the current field length would exceed the total data
@ -6027,8 +6234,8 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len, true) ||
!tlv_data_is_valid(hdev, flags, cp->data + cp->adv_data_len,
if (!tlv_data_is_valid(flags, cp->data, cp->adv_data_len, true) ||
!tlv_data_is_valid(flags, cp->data + cp->adv_data_len,
cp->scan_rsp_len, false)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
MGMT_STATUS_INVALID_PARAMS);
@ -6175,7 +6382,7 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
hci_req_clear_adv_instance(hdev, &req, cp->instance, true);
hci_req_clear_adv_instance(hdev, sk, &req, cp->instance, true);
if (list_empty(&hdev->adv_instances))
__hci_req_disable_advertising(&req);
@ -6211,23 +6418,6 @@ unlock:
return err;
}
static u8 tlv_data_max_len(u32 adv_flags, bool is_adv_data)
{
u8 max_len = HCI_MAX_AD_LENGTH;
if (is_adv_data) {
if (adv_flags & (MGMT_ADV_FLAG_DISCOV |
MGMT_ADV_FLAG_LIMITED_DISCOV |
MGMT_ADV_FLAG_MANAGED_FLAGS))
max_len -= 3;
if (adv_flags & MGMT_ADV_FLAG_TX_POWER)
max_len -= 3;
}
return max_len;
}
static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
@ -6356,6 +6546,9 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE },
{ get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE },
{ start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
{ read_ext_controller_info,MGMT_READ_EXT_INFO_SIZE,
HCI_MGMT_UNTRUSTED },
{ set_appearance, MGMT_SET_APPEARANCE_SIZE },
};
void mgmt_index_added(struct hci_dev *hdev)
@ -6494,9 +6687,12 @@ void __mgmt_power_off(struct hci_dev *hdev)
mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
zero_cod, sizeof(zero_cod), NULL);
if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) {
mgmt_limited_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
zero_cod, sizeof(zero_cod),
HCI_MGMT_DEV_CLASS_EVENTS, NULL);
ext_info_changed(hdev, NULL);
}
new_settings(hdev, match.sk);
@ -7092,9 +7288,11 @@ void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match);
mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
if (!status)
mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
dev_class, 3, NULL);
if (!status) {
mgmt_limited_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class,
3, HCI_MGMT_DEV_CLASS_EVENTS, NULL);
ext_info_changed(hdev, NULL);
}
if (match.sk)
sock_put(match.sk);
@ -7123,8 +7321,9 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
return;
}
mgmt_generic_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
cmd ? cmd->sk : NULL);
mgmt_limited_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
HCI_MGMT_LOCAL_NAME_EVENTS, cmd ? cmd->sk : NULL);
ext_info_changed(hdev, cmd ? cmd->sk : NULL);
}
static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])

View File

@ -21,12 +21,41 @@
SOFTWARE IS DISCLAIMED.
*/
#include <asm/unaligned.h>
#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 struct sk_buff *create_monitor_ctrl_event(__le16 index, u32 cookie,
u16 opcode, u16 len, void *buf)
{
struct hci_mon_hdr *hdr;
struct sk_buff *skb;
skb = bt_skb_alloc(6 + len, GFP_ATOMIC);
if (!skb)
return NULL;
put_unaligned_le32(cookie, skb_put(skb, 4));
put_unaligned_le16(opcode, skb_put(skb, 2));
if (buf)
memcpy(skb_put(skb, len), buf, len);
__net_timestamp(skb);
hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
hdr->opcode = cpu_to_le16(HCI_MON_CTRL_EVENT);
hdr->index = index;
hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
return skb;
}
int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
void *data, u16 data_len, int flag, struct sock *skip_sk)
{
@ -52,14 +81,18 @@ int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
__net_timestamp(skb);
hci_send_to_channel(channel, skb, flag, skip_sk);
kfree_skb(skb);
if (channel == HCI_CHANNEL_CONTROL)
hci_send_monitor_ctrl_event(hdev, event, data, data_len,
skb_get_ktime(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 sk_buff *skb, *mskb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_status *ev;
int err;
@ -80,17 +113,30 @@ int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
ev->status = status;
ev->opcode = cpu_to_le16(cmd);
mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
MGMT_EV_CMD_STATUS, sizeof(*ev), ev);
if (mskb)
skb->tstamp = mskb->tstamp;
else
__net_timestamp(skb);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
if (mskb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(mskb);
}
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 sk_buff *skb, *mskb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev;
int err;
@ -114,10 +160,24 @@ int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
if (rp)
memcpy(ev->data, rp, rp_len);
mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
MGMT_EV_CMD_COMPLETE,
sizeof(*ev) + rp_len, ev);
if (mskb)
skb->tstamp = mskb->tstamp;
else
__net_timestamp(skb);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
if (mskb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(mskb);
}
return err;
}

View File

@ -3387,7 +3387,10 @@ int smp_register(struct hci_dev *hdev)
if (!lmp_sc_capable(hdev)) {
debugfs_create_file("force_bredr_smp", 0644, hdev->debugfs,
hdev, &force_bredr_smp_fops);
return 0;
/* Flag can be already set here (due to power toggle) */
if (!hci_dev_test_flag(hdev, HCI_FORCE_BREDR_SMP))
return 0;
}
if (WARN_ON(hdev->smp_bredr_data)) {

View File

@ -663,6 +663,7 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
/* TODO check this */
SET_NETDEV_DEV(ndev, &local->phy->dev);
dev_net_set(ndev, wpan_phy_net(local->hw.phy));
sdata = netdev_priv(ndev);
ndev->ieee802154_ptr = &sdata->wpan_dev;
memcpy(sdata->name, ndev->name, IFNAMSIZ);

View File

@ -101,11 +101,16 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
sdata->dev->stats.rx_bytes += skb->len;
switch (mac_cb(skb)->type) {
case IEEE802154_FC_TYPE_BEACON:
case IEEE802154_FC_TYPE_ACK:
case IEEE802154_FC_TYPE_MAC_CMD:
goto fail;
case IEEE802154_FC_TYPE_DATA:
return ieee802154_deliver_skb(skb);
default:
pr_warn("ieee802154: bad frame received (type = %d)\n",
mac_cb(skb)->type);
pr_warn_ratelimited("ieee802154: bad frame received "
"(type = %d)\n", mac_cb(skb)->type);
goto fail;
}