Commit 204dfe17 authored by David S. Miller's avatar David S. Miller

Merge branch 'for-upstream' of...

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

Johan Hedberg says:

====================
pull request: bluetooth-next 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: default avatarDavid S. Miller <davem@davemloft.net>
parents ad979896 af4168c5
...@@ -180,6 +180,17 @@ config BT_HCIUART_AG6XX ...@@ -180,6 +180,17 @@ config BT_HCIUART_AG6XX
Say Y here to compile support for Intel AG6XX protocol. 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 config BT_HCIBCM203X
tristate "HCI BCM203x USB driver" tristate "HCI BCM203x USB driver"
depends on USB depends on USB
...@@ -331,4 +342,16 @@ config BT_WILINK ...@@ -331,4 +342,16 @@ config BT_WILINK
Say Y here to compile support for Texas Instrument's WiLink7 driver Say Y here to compile support for Texas Instrument's WiLink7 driver
into the kernel or say M to compile it as module (btwilink). 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 endmenu
...@@ -20,6 +20,7 @@ obj-$(CONFIG_BT_ATH3K) += ath3k.o ...@@ -20,6 +20,7 @@ obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_WILINK) += btwilink.o obj-$(CONFIG_BT_WILINK) += btwilink.o
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
obj-$(CONFIG_BT_BCM) += btbcm.o obj-$(CONFIG_BT_BCM) += btbcm.o
obj-$(CONFIG_BT_RTL) += btrtl.o obj-$(CONFIG_BT_RTL) += btrtl.o
obj-$(CONFIG_BT_QCA) += btqca.o obj-$(CONFIG_BT_QCA) += btqca.o
...@@ -37,6 +38,7 @@ hci_uart-$(CONFIG_BT_HCIUART_INTEL) += hci_intel.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_BCM) += hci_bcm.o
hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.o hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.o
hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.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) hci_uart-objs := $(hci_uart-y)
ccflags-y += -D__CHECK_ENDIAN__ ccflags-y += -D__CHECK_ENDIAN__
...@@ -185,10 +185,8 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id ...@@ -185,10 +185,8 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id
data->state = BCM203X_LOAD_MINIDRV; data->state = BCM203X_LOAD_MINIDRV;
data->urb = usb_alloc_urb(0, GFP_KERNEL); data->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!data->urb) { if (!data->urb)
BT_ERR("Can't allocate URB");
return -ENOMEM; return -ENOMEM;
}
if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) { if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
BT_ERR("Mini driver request failed"); BT_ERR("Mini driver request failed");
......
...@@ -55,8 +55,8 @@ static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version) ...@@ -55,8 +55,8 @@ static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
} }
edl = (struct edl_event_hdr *)(skb->data); edl = (struct edl_event_hdr *)(skb->data);
if (!edl || !edl->data) { if (!edl) {
BT_ERR("%s: TLV with no header or no data", hdev->name); BT_ERR("%s: TLV with no header", hdev->name);
err = -EILSEQ; err = -EILSEQ;
goto out; goto out;
} }
...@@ -224,8 +224,8 @@ static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size, ...@@ -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); edl = (struct edl_event_hdr *)(skb->data);
if (!edl || !edl->data) { if (!edl) {
BT_ERR("%s: TLV with no header or no data", hdev->name); BT_ERR("%s: TLV with no header", hdev->name);
err = -EILSEQ; err = -EILSEQ;
goto out; goto out;
} }
......
/*
* 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 @@ ...@@ -33,6 +33,7 @@
#define RTL_ROM_LMP_8723B 0x8723 #define RTL_ROM_LMP_8723B 0x8723
#define RTL_ROM_LMP_8821A 0x8821 #define RTL_ROM_LMP_8821A 0x8821
#define RTL_ROM_LMP_8761A 0x8761 #define RTL_ROM_LMP_8761A 0x8761
#define RTL_ROM_LMP_8822B 0x8822
static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version) 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, ...@@ -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; const unsigned char *patch_length_base, *patch_offset_base;
u32 patch_offset = 0; u32 patch_offset = 0;
u16 patch_length, num_patches; u16 patch_length, num_patches;
const u16 project_id_to_lmp_subver[] = { static const struct {
RTL_ROM_LMP_8723A, __u16 lmp_subver;
RTL_ROM_LMP_8723B, __u8 id;
RTL_ROM_LMP_8821A, } project_id_to_lmp_subver[] = {
RTL_ROM_LMP_8761A { 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); ret = rtl_read_rom_version(hdev, &rom_version);
...@@ -134,14 +139,20 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver, ...@@ -134,14 +139,20 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
return -EINVAL; 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); BT_ERR("%s: unknown project id %d", hdev->name, project_id);
return -EINVAL; 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, 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; return -EINVAL;
} }
...@@ -257,6 +268,26 @@ static int rtl_download_firmware(struct hci_dev *hdev, ...@@ -257,6 +268,26 @@ static int rtl_download_firmware(struct hci_dev *hdev,
return ret; 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) static int btrtl_setup_rtl8723a(struct hci_dev *hdev)
{ {
const struct firmware *fw; const struct firmware *fw;
...@@ -296,25 +327,74 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver, ...@@ -296,25 +327,74 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
unsigned char *fw_data = NULL; unsigned char *fw_data = NULL;
const struct firmware *fw; const struct firmware *fw;
int ret; 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); BT_INFO("%s: rtl: loading %s", hdev->name, fw_name);
ret = request_firmware(&fw, fw_name, &hdev->dev); ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) { if (ret < 0) {
BT_ERR("%s: Failed to load %s", hdev->name, fw_name); 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); ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
if (ret < 0) if (ret < 0)
goto out; 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); ret = rtl_download_firmware(hdev, fw_data, ret);
kfree(fw_data);
if (ret < 0)
goto out;
out: out:
release_firmware(fw); release_firmware(fw);
kfree(fw_data);
err_req_fw:
if (cfg_sz)
kfree(cfg_buff);
return ret; return ret;
} }
...@@ -377,6 +457,9 @@ int btrtl_setup_realtek(struct hci_dev *hdev) ...@@ -377,6 +457,9 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
case RTL_ROM_LMP_8761A: case RTL_ROM_LMP_8761A:
return btrtl_setup_rtl8723b(hdev, lmp_subver, return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8761a_fw.bin"); "rtl_bt/rtl8761a_fw.bin");
case RTL_ROM_LMP_8822B:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8822b_fw.bin");
default: default:
BT_INFO("rtl: assuming no firmware upload needed."); BT_INFO("rtl: assuming no firmware upload needed.");
return 0; return 0;
......
...@@ -62,6 +62,7 @@ static struct usb_driver btusb_driver; ...@@ -62,6 +62,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_REALTEK 0x20000 #define BTUSB_REALTEK 0x20000
#define BTUSB_BCM2045 0x40000 #define BTUSB_BCM2045 0x40000
#define BTUSB_IFNUM_2 0x80000 #define BTUSB_IFNUM_2 0x80000
#define BTUSB_CW6622 0x100000
static const struct usb_device_id btusb_table[] = { static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */ /* Generic Bluetooth USB device */
...@@ -248,6 +249,7 @@ static const struct usb_device_id blacklist_table[] = { ...@@ -248,6 +249,7 @@ static const struct usb_device_id blacklist_table[] = {
/* QCA ROME chipset */ /* QCA ROME chipset */
{ USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME }, { 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, 0xe300), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0489, 0xe092), .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[] = { ...@@ -290,7 +292,8 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC }, { USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC },
/* CONWISE Technology based adapters with buggy SCO support */ /* 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) */ /* Roper Class 1 Bluetooth Dongle (Silicon Wave based) */
{ USB_DEVICE(0x1310, 0x0001), .driver_info = BTUSB_SWAVE }, { USB_DEVICE(0x1310, 0x0001), .driver_info = BTUSB_SWAVE },
...@@ -2221,9 +2224,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) ...@@ -2221,9 +2224,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING, err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
TASK_INTERRUPTIBLE, TASK_INTERRUPTIBLE,
msecs_to_jiffies(5000)); msecs_to_jiffies(5000));
if (err == 1) { if (err == -EINTR) {
BT_ERR("%s: Firmware loading interrupted", hdev->name); BT_ERR("%s: Firmware loading interrupted", hdev->name);
err = -EINTR;
goto done; goto done;
} }
...@@ -2275,7 +2277,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) ...@@ -2275,7 +2277,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
TASK_INTERRUPTIBLE, TASK_INTERRUPTIBLE,
msecs_to_jiffies(1000)); msecs_to_jiffies(1000));
if (err == 1) { if (err == -EINTR) {
BT_ERR("%s: Device boot interrupted", hdev->name); BT_ERR("%s: Device boot interrupted", hdev->name);
return -EINTR; return -EINTR;
} }
...@@ -2845,6 +2847,9 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -2845,6 +2847,9 @@ static int btusb_probe(struct usb_interface *intf,
hdev->send = btusb_send_frame; hdev->send = btusb_send_frame;
hdev->notify = btusb_notify; 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) if (id->driver_info & BTUSB_BCM2045)
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks); set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
......
...@@ -798,7 +798,7 @@ static int bcm_remove(struct platform_device *pdev) ...@@ -798,7 +798,7 @@ static int bcm_remove(struct platform_device *pdev)
static const struct hci_uart_proto bcm_proto = { static const struct hci_uart_proto bcm_proto = {
.id = HCI_UART_BCM, .id = HCI_UART_BCM,
.name = "BCM", .name = "Broadcom",
.manufacturer = 15, .manufacturer = 15,
.init_speed = 115200, .init_speed = 115200,
.oper_speed = 4000000, .oper_speed = 4000000,
......
...@@ -128,7 +128,7 @@ static int intel_wait_booting(struct hci_uart *hu) ...@@ -128,7 +128,7 @@ static int intel_wait_booting(struct hci_uart *hu)
TASK_INTERRUPTIBLE, TASK_INTERRUPTIBLE,
msecs_to_jiffies(1000)); msecs_to_jiffies(1000));
if (err == 1) { if (err == -EINTR) {
bt_dev_err(hu->hdev, "Device boot interrupted"); bt_dev_err(hu->hdev, "Device boot interrupted");
return -EINTR; return -EINTR;
} }
...@@ -151,7 +151,7 @@ static int intel_wait_lpm_transaction(struct hci_uart *hu) ...@@ -151,7 +151,7 @@ static int intel_wait_lpm_transaction(struct hci_uart *hu)
TASK_INTERRUPTIBLE, TASK_INTERRUPTIBLE,
msecs_to_jiffies(1000)); msecs_to_jiffies(1000));
if (err == 1) { if (err == -EINTR) {
bt_dev_err(hu->hdev, "LPM transaction interrupted"); bt_dev_err(hu->hdev, "LPM transaction interrupted");
return -EINTR; return -EINTR;
} }
...@@ -813,7 +813,7 @@ static int intel_setup(struct hci_uart *hu) ...@@ -813,7 +813,7 @@ static int intel_setup(struct hci_uart *hu)
err = wait_on_bit_timeout(&intel->flags, STATE_DOWNLOADING, err = wait_on_bit_timeout(&intel->flags, STATE_DOWNLOADING,
TASK_INTERRUPTIBLE, TASK_INTERRUPTIBLE,
msecs_to_jiffies(5000)); msecs_to_jiffies(5000));
if (err == 1) { if (err == -EINTR) {
bt_dev_err(hdev, "Firmware loading interrupted"); bt_dev_err(hdev, "Firmware loading interrupted");
err = -EINTR; err = -EINTR;
goto done; goto done;
......
...@@ -810,6 +810,9 @@ static int __init hci_uart_init(void) ...@@ -810,6 +810,9 @@ static int __init hci_uart_init(void)
#ifdef CONFIG_BT_HCIUART_AG6XX #ifdef CONFIG_BT_HCIUART_AG6XX
ag6xx_init(); ag6xx_init();
#endif #endif
#ifdef CONFIG_BT_HCIUART_MRVL
mrvl_init();
#endif
return 0; return 0;
} }
...@@ -845,6 +848,9 @@ static void __exit hci_uart_exit(void) ...@@ -845,6 +848,9 @@ static void __exit hci_uart_exit(void)
#ifdef CONFIG_BT_HCIUART_AG6XX #ifdef CONFIG_BT_HCIUART_AG6XX
ag6xx_deinit(); ag6xx_deinit();
#endif #endif
#ifdef CONFIG_BT_HCIUART_MRVL
mrvl_deinit();
#endif
/* Release tty registration of line discipline */ /* Release tty registration of line discipline */
err = tty_unregister_ldisc(N_HCI); err = tty_unregister_ldisc(N_HCI);
......
/*
*
* 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) ...@@ -397,7 +397,7 @@ static int qca_open(struct hci_uart *hu)
skb_queue_head_init(&qca->txq); skb_queue_head_init(&qca->txq);
skb_queue_head_init(&qca->tx_wait_q); skb_queue_head_init(&qca->tx_wait_q);
spin_lock_init(&qca->hci_ibs_lock); 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) { if (!qca->workqueue) {
BT_ERR("QCA Workqueue not initialized properly"); BT_ERR("QCA Workqueue not initialized properly");
kfree(qca); kfree(qca);
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#define HCIUARTGETFLAGS _IOR('U', 204, int) #define HCIUARTGETFLAGS _IOR('U', 204, int)
/* UART protocols */ /* UART protocols */
#define HCI_UART_MAX_PROTO 10 #define HCI_UART_MAX_PROTO 12
#define HCI_UART_H4 0 #define HCI_UART_H4 0
#define HCI_UART_BCSP 1 #define HCI_UART_BCSP 1
...@@ -47,6 +47,8 @@ ...@@ -47,6 +47,8 @@
#define HCI_UART_BCM 7 #define HCI_UART_BCM 7
#define HCI_UART_QCA 8 #define HCI_UART_QCA 8
#define HCI_UART_AG6XX 9 #define HCI_UART_AG6XX 9
#define HCI_UART_NOKIA 10
#define HCI_UART_MRVL 11
#define HCI_UART_RAW_DEVICE 0 #define HCI_UART_RAW_DEVICE 0
#define HCI_UART_RESET_ON_INIT 1 #define HCI_UART_RESET_ON_INIT 1
...@@ -189,3 +191,8 @@ int qca_deinit(void); ...@@ -189,3 +191,8 @@ int qca_deinit(void);
int ag6xx_init(void); int ag6xx_init(void);
int ag6xx_deinit(void); int ag6xx_deinit(void);
#endif #endif
#ifdef CONFIG_BT_HCIUART_MRVL
int mrvl_init(void);
int mrvl_deinit(void);
#endif
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
static int numlbs = 2; static int numlbs = 2;
static LIST_HEAD(fakelb_phys); static LIST_HEAD(fakelb_phys);
static DEFINE_SPINLOCK(fakelb_phys_lock); static DEFINE_MUTEX(fakelb_phys_lock);
static LIST_HEAD(fakelb_ifup_phys); static LIST_HEAD(fakelb_ifup_phys);
static DEFINE_RWLOCK(fakelb_ifup_phys_lock); static DEFINE_RWLOCK(fakelb_ifup_phys_lock);
...@@ -188,9 +188,9 @@ static int fakelb_add_one(struct device *dev) ...@@ -188,9 +188,9 @@ static int fakelb_add_one(struct device *dev)
if (err) if (err)
goto err_reg; goto err_reg;
spin_lock(&fakelb_phys_lock); mutex_lock(&fakelb_phys_lock);
list_add_tail(&phy->list, &fakelb_phys); list_add_tail(&phy->list, &fakelb_phys);
spin_unlock(&fakelb_phys_lock); mutex_unlock(&fakelb_phys_lock);
return 0; return 0;
...@@ -222,10 +222,10 @@ static int fakelb_probe(struct platform_device *pdev) ...@@ -222,10 +222,10 @@ static int fakelb_probe(struct platform_device *pdev)
return 0; return 0;
err_slave: err_slave:
spin_lock(&fakelb_phys_lock); mutex_lock(&fakelb_phys_lock);
list_for_each_entry_safe(phy, tmp, &fakelb_phys, list) list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
fakelb_del(phy); fakelb_del(phy);
spin_unlock(&fakelb_phys_lock); mutex_unlock(&fakelb_phys_lock);
return err; return err;
} }
...@@ -233,10 +233,10 @@ static int fakelb_remove(struct platform_device *pdev) ...@@ -233,10 +233,10 @@ static int fakelb_remove(struct platform_device *pdev)
{ {
struct fakelb_phy *phy, *tmp; 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) list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
fakelb_del(phy); fakelb_del(phy);
spin_unlock(&fakelb_phys_lock); mutex_unlock(&fakelb_phys_lock);
return 0; return 0;
} }
......
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
#include <net/sock.h> #include <net/sock.h>
#include <linux/seq_file.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 #ifndef AF_BLUETOOTH
#define AF_BLUETOOTH 31 #define AF_BLUETOOTH 31
...@@ -371,6 +372,7 @@ void hci_sock_set_flag(struct sock *sk, int nr); ...@@ -371,6 +372,7 @@ void hci_sock_set_flag(struct sock *sk, int nr);
void hci_sock_clear_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); int hci_sock_test_flag(struct sock *sk, int nr);
unsigned short hci_sock_get_channel(struct sock *sk); unsigned short hci_sock_get_channel(struct sock *sk);
u32 hci_sock_get_cookie(struct sock *sk);
int hci_sock_init(void); int hci_sock_init(void);
void hci_sock_cleanup(void); void hci_sock_cleanup(void);
......
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
#define HCI_SDIO 6 #define HCI_SDIO 6
#define HCI_SPI 7 #define HCI_SPI 7
#define HCI_I2C 8 #define HCI_I2C 8
#define HCI_SMD 9
/* HCI controller types */ /* HCI controller types */
#define HCI_PRIMARY 0x00 #define HCI_PRIMARY 0x00
...@@ -207,7 +208,11 @@ enum { ...@@ -207,7 +208,11 @@ enum {
HCI_MGMT_INDEX_EVENTS, HCI_MGMT_INDEX_EVENTS,
HCI_MGMT_UNCONF_INDEX_EVENTS, HCI_MGMT_UNCONF_INDEX_EVENTS,
HCI_MGMT_EXT_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, HCI_MGMT_OOB_DATA_EVENTS,
}; };
......
...@@ -211,6 +211,7 @@ struct hci_dev { ...@@ -211,6 +211,7 @@ struct hci_dev {
__u8 dev_name[HCI_MAX_NAME_LENGTH]; __u8 dev_name[HCI_MAX_NAME_LENGTH];
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH]; __u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
__u8 eir[HCI_MAX_EIR_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH];
__u16 appearance;
__u8 dev_class[3]; __u8 dev_class[3];
__u8 major_class; __u8 major_class;
__u8 minor_class; __u8 minor_class;
...@@ -399,7 +400,9 @@ struct hci_dev { ...@@ -399,7 +400,9 @@ struct hci_dev {
struct delayed_work rpa_expired; struct delayed_work rpa_expired;
bdaddr_t rpa; bdaddr_t rpa;
#if IS_ENABLED(CONFIG_BT_LEDS)
struct led_trigger *power_led; struct led_trigger *power_led;
#endif
int (*open)(struct hci_dev *hdev); int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev);
...@@ -1026,8 +1029,8 @@ int hci_resume_dev(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_reset_dev(struct hci_dev *hdev);
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
int hci_recv_diag(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, ...); __printf(2, 3) 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_fw_info(struct hci_dev *hdev, const char *fmt, ...);
int hci_dev_open(__u16 dev); int hci_dev_open(__u16 dev);
int hci_dev_close(__u16 dev); int hci_dev_close(__u16 dev);
int hci_dev_do_close(struct hci_dev *hdev); 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); ...@@ -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, void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
int flag, struct sock *skip_sk); int flag, struct sock *skip_sk);
void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_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); 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); ...@@ -1449,6 +1455,7 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c);
#define DISCOV_BREDR_INQUIRY_LEN 0x08 #define DISCOV_BREDR_INQUIRY_LEN 0x08
#define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */ #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); int mgmt_new_settings(struct hci_dev *hdev);
void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_added(struct hci_dev *hdev);
void mgmt_index_removed(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev);
......
...@@ -45,6 +45,10 @@ struct hci_mon_hdr { ...@@ -45,6 +45,10 @@ struct hci_mon_hdr {
#define HCI_MON_VENDOR_DIAG 11 #define HCI_MON_VENDOR_DIAG 11
#define HCI_MON_SYSTEM_NOTE 12 #define HCI_MON_SYSTEM_NOTE 12
#define HCI_MON_USER_LOGGING 13 #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 { struct hci_mon_new_index {
__u8 type; __u8 type;
......
...@@ -586,6 +586,24 @@ struct mgmt_rp_get_adv_size_info { ...@@ -586,6 +586,24 @@ struct mgmt_rp_get_adv_size_info {
#define MGMT_OP_START_LIMITED_DISCOVERY 0x0041 #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 #define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete { struct mgmt_ev_cmd_complete {
__le16 opcode; __le16 opcode;
...@@ -800,3 +818,9 @@ struct mgmt_ev_advertising_added { ...@@ -800,3 +818,9 @@ struct mgmt_ev_advertising_added {
struct mgmt_ev_advertising_removed { struct mgmt_ev_advertising_removed {
__u8 instance; __u8 instance;
} __packed; } __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, ...@@ -101,8 +101,6 @@ static void lowpan_ndisc_802154_update(struct neighbour *n, u32 flags,
ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short); ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short);
if (!lowpan_802154_is_valid_src_short_addr(neigh->short_addr)) if (!lowpan_802154_is_valid_src_short_addr(neigh->short_addr))
neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC); 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); write_unlock_bh(&n->lock);
} }
......
...@@ -26,11 +26,13 @@ ...@@ -26,11 +26,13 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/stringify.h>
#include <asm/ioctls.h> #include <asm/ioctls.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include "leds.h"
#include "selftest.h" #include "selftest.h"
/* Bluetooth sockets */ /* Bluetooth sockets */
...@@ -712,13 +714,16 @@ static struct net_proto_family bt_sock_family_ops = { ...@@ -712,13 +714,16 @@ static struct net_proto_family bt_sock_family_ops = {
struct dentry *bt_debugfs; struct dentry *bt_debugfs;
EXPORT_SYMBOL_GPL(bt_debugfs); EXPORT_SYMBOL_GPL(bt_debugfs);
#define VERSION __stringify(BT_SUBSYS_VERSION) "." \
__stringify(BT_SUBSYS_REVISION)
static int __init bt_init(void) static int __init bt_init(void)
{ {
int err; int err;
sock_skb_cb_check_size(sizeof(struct bt_skb_cb)); 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(); err = bt_selftest();
if (err < 0) if (err < 0)
...@@ -726,6 +731,8 @@ static int __init bt_init(void) ...@@ -726,6 +731,8 @@ static int __init bt_init(void)
bt_debugfs = debugfs_create_dir("bluetooth", NULL); bt_debugfs = debugfs_create_dir("bluetooth", NULL);
bt_leds_init();
err = bt_sysfs_init(); err = bt_sysfs_init();
if (err < 0) if (err < 0)
return err; return err;
...@@ -785,6 +792,8 @@ static void __exit bt_exit(void) ...@@ -785,6 +792,8 @@ static void __exit bt_exit(void)
bt_sysfs_cleanup(); bt_sysfs_cleanup();
bt_leds_cleanup();
debugfs_remove_recursive(bt_debugfs); debugfs_remove_recursive(bt_debugfs);
} }
...@@ -792,7 +801,7 @@ subsys_initcall(bt_init); ...@@ -792,7 +801,7 @@ subsys_initcall(bt_init);
module_exit(bt_exit); module_exit(bt_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth Core ver " BT_SUBSYS_VERSION); MODULE_DESCRIPTION("Bluetooth Core ver " VERSION);
MODULE_VERSION(BT_SUBSYS_VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_BLUETOOTH); MODULE_ALIAS_NETPROTO(PF_BLUETOOTH);
...@@ -1562,6 +1562,7 @@ int hci_dev_do_close(struct hci_dev *hdev) ...@@ -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); auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
if (!auto_off && hdev->dev_type == HCI_PRIMARY && if (!auto_off && hdev->dev_type == HCI_PRIMARY &&
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
hci_dev_test_flag(hdev, HCI_MGMT)) hci_dev_test_flag(hdev, HCI_MGMT))
__mgmt_power_off(hdev); __mgmt_power_off(hdev);
......
...@@ -971,14 +971,14 @@ void __hci_req_enable_advertising(struct hci_request *req) ...@@ -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); 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; size_t name_len;
int max_len;
max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
name_len = strlen(hdev->dev_name); name_len = strlen(hdev->dev_name);
if (name_len > 0) { if (name_len > 0 && max_len > 0) {
size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
if (name_len > max_len) { if (name_len > max_len) {
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) ...@@ -997,22 +997,42 @@ static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
return ad_len; 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, static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
u8 *ptr) u8 *ptr)
{ {
struct adv_info *adv_instance; struct adv_info *adv_instance;
u32 instance_flags;
u8 scan_rsp_len = 0;
adv_instance = hci_find_adv_instance(hdev, instance); adv_instance = hci_find_adv_instance(hdev, instance);
if (!adv_instance) if (!adv_instance)
return 0; return 0;
/* TODO: Set the appropriate entries based on advertising instance flags instance_flags = adv_instance->flags;
* here once flags other than 0 are supported.
*/ 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, memcpy(ptr, adv_instance->scan_rsp_data,
adv_instance->scan_rsp_len); 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) 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) ...@@ -1194,7 +1214,7 @@ static void adv_timeout_expire(struct work_struct *work)
hci_req_init(&req, hdev); 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)) if (list_empty(&hdev->adv_instances))
__hci_req_disable_advertising(&req); __hci_req_disable_advertising(&req);
...@@ -1284,8 +1304,9 @@ static void cancel_adv_timeout(struct hci_dev *hdev) ...@@ -1284,8 +1304,9 @@ static void cancel_adv_timeout(struct hci_dev *hdev)
* setting. * setting.
* - force == false: Only instances that have a timeout will be removed. * - 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, void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
u8 instance, bool force) struct hci_request *req, u8 instance,
bool force)
{ {
struct adv_info *adv_instance, *n, *next_instance = NULL; struct adv_info *adv_instance, *n, *next_instance = NULL;
int err; int err;
...@@ -1311,7 +1332,7 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req, ...@@ -1311,7 +1332,7 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
rem_inst = adv_instance->instance; rem_inst = adv_instance->instance;
err = hci_remove_adv_instance(hdev, rem_inst); err = hci_remove_adv_instance(hdev, rem_inst);
if (!err) if (!err)
mgmt_advertising_removed(NULL, hdev, rem_inst); mgmt_advertising_removed(sk, hdev, rem_inst);
} }
} else { } else {
adv_instance = hci_find_adv_instance(hdev, instance); 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, ...@@ -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); err = hci_remove_adv_instance(hdev, instance);
if (!err) 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, ...@@ -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 * function. To be safe hard-code one of the
* values that's suitable for SCO. * 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, hci_req_add(req, HCI_OP_REJECT_SYNC_CONN_REQ,
sizeof(rej), &rej); sizeof(rej), &rej);
......
...@@ -73,8 +73,9 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance); ...@@ -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, int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
bool force); bool force);
void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req, void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
u8 instance, bool force); struct hci_request *req, u8 instance,
bool force);
void __hci_req_update_class(struct hci_request *req); void __hci_req_update_class(struct hci_request *req);
......
This diff is collapsed.
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#include "leds.h" #include "leds.h"
DEFINE_LED_TRIGGER(bt_power_led_trigger);
struct hci_basic_led_trigger { struct hci_basic_led_trigger {
struct led_trigger led_trigger; struct led_trigger led_trigger;
struct hci_dev *hdev; struct hci_dev *hdev;
...@@ -24,6 +26,21 @@ void hci_leds_update_powered(struct hci_dev *hdev, bool enabled) ...@@ -24,6 +26,21 @@ void hci_leds_update_powered(struct hci_dev *hdev, bool enabled)
if (hdev->power_led) if (hdev->power_led)
led_trigger_event(hdev->power_led, led_trigger_event(hdev->power_led,
enabled ? LED_FULL : LED_OFF); 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) static void power_activate(struct led_classdev *led_cdev)
...@@ -72,3 +89,13 @@ void hci_leds_init(struct hci_dev *hdev) ...@@ -72,3 +89,13 @@ void hci_leds_init(struct hci_dev *hdev)
/* initialize power_led */ /* initialize power_led */
hdev->power_led = led_allocate_basic(hdev, power_activate, "power"); 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 @@ ...@@ -7,10 +7,20 @@
*/ */
#if IS_ENABLED(CONFIG_BT_LEDS) #if IS_ENABLED(CONFIG_BT_LEDS)
void hci_leds_update_powered(struct hci_dev *hdev, bool enabled); void hci_leds_update_powered(struct hci_dev *hdev, bool enabled);
void hci_leds_init(struct hci_dev *hdev); void hci_leds_init(struct hci_dev *hdev);
void bt_leds_init(void);
void bt_leds_cleanup(void);
#else #else
static inline void hci_leds_update_powered(struct hci_dev *hdev, static inline void hci_leds_update_powered(struct hci_dev *hdev,
bool enabled) {} bool enabled) {}
static inline void hci_leds_init(struct hci_dev *hdev) {} 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 #endif
This diff is collapsed.
...@@ -21,12 +21,41 @@ ...@@ -21,12 +21,41 @@
SOFTWARE IS DISCLAIMED. SOFTWARE IS DISCLAIMED.
*/ */
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci_mon.h>
#include <net/bluetooth/mgmt.h> #include <net/bluetooth/mgmt.h>
#include "mgmt_util.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, int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
void *data, u16 data_len, int flag, struct sock *skip_sk) 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, ...@@ -52,14 +81,18 @@ int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
__net_timestamp(skb); __net_timestamp(skb);
hci_send_to_channel(channel, skb, flag, skip_sk); 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; return 0;
} }
int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) 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_hdr *hdr;
struct mgmt_ev_cmd_status *ev; struct mgmt_ev_cmd_status *ev;
int err; int err;
...@@ -80,17 +113,30 @@ int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) ...@@ -80,17 +113,30 @@ int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
ev->status = status; ev->status = status;
ev->opcode = cpu_to_le16(cmd); 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); err = sock_queue_rcv_skb(sk, skb);
if (err < 0) if (err < 0)
kfree_skb(skb); kfree_skb(skb);
if (mskb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(mskb);
}
return err; return err;
} }
int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
void *rp, size_t rp_len) void *rp, size_t rp_len)
{ {
struct sk_buff *skb; struct sk_buff *skb, *mskb;
struct mgmt_hdr *hdr; struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev; struct mgmt_ev_cmd_complete *ev;
int err; int err;
...@@ -114,10 +160,24 @@ int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, ...@@ -114,10 +160,24 @@ int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
if (rp) if (rp)
memcpy(ev->data, rp, rp_len); 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); err = sock_queue_rcv_skb(sk, skb);
if (err < 0) if (err < 0)
kfree_skb(skb); kfree_skb(skb);
if (mskb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(mskb);
}
return err; return err;
} }
......
...@@ -3387,7 +3387,10 @@ int smp_register(struct hci_dev *hdev) ...@@ -3387,7 +3387,10 @@ int smp_register(struct hci_dev *hdev)
if (!lmp_sc_capable(hdev)) { if (!lmp_sc_capable(hdev)) {
debugfs_create_file("force_bredr_smp", 0644, hdev->debugfs, debugfs_create_file("force_bredr_smp", 0644, hdev->debugfs,
hdev, &force_bredr_smp_fops); 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)) { if (WARN_ON(hdev->smp_bredr_data)) {
......
...@@ -663,6 +663,7 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name, ...@@ -663,6 +663,7 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
/* TODO check this */ /* TODO check this */
SET_NETDEV_DEV(ndev, &local->phy->dev); SET_NETDEV_DEV(ndev, &local->phy->dev);
dev_net_set(ndev, wpan_phy_net(local->hw.phy));
sdata = netdev_priv(ndev); sdata = netdev_priv(ndev);
ndev->ieee802154_ptr = &sdata->wpan_dev; ndev->ieee802154_ptr = &sdata->wpan_dev;
memcpy(sdata->name, ndev->name, IFNAMSIZ); memcpy(sdata->name, ndev->name, IFNAMSIZ);
......
...@@ -101,11 +101,16 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, ...@@ -101,11 +101,16 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
sdata->dev->stats.rx_bytes += skb->len; sdata->dev->stats.rx_bytes += skb->len;
switch (mac_cb(skb)->type) { 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: case IEEE802154_FC_TYPE_DATA:
return ieee802154_deliver_skb(skb); return ieee802154_deliver_skb(skb);
default: default:
pr_warn("ieee802154: bad frame received (type = %d)\n", pr_warn_ratelimited("ieee802154: bad frame received "
mac_cb(skb)->type); "(type = %d)\n", mac_cb(skb)->type);
goto fail; goto fail;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment