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:
commit
204dfe1798
|
@ -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
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -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 @@ static int rtl_download_firmware(struct hci_dev *hdev,
|
|||
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;
|
||||
|
|
|
@ -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 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
|||
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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
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 @@ static int set_public_address(struct sock *sk, struct hci_dev *hdev,
|
|||
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 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
|
|||
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])
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue