bluetooth-next pull request for net-next:

- Add support for MediaTek MT7922 and MT7921
  - Enable support for AOSP extention in Qualcomm WCN399x and Realtek
    8822C/8852A.
  - Add initial support for link quality and audio/codec offload.
  - Rework of sockets sendmsg to avoid locking issues.
  - Add vhci suspend/resume emulation.
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmFXkGYZHGx1aXoudm9u
 LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKVQdD/9dtVeMRjzAQbvPI5InAi4N
 AjEy7IUAV27AE0QVZW/Q201BNnFixAivDWiQXHFiTV1ocrmX/qiW15AsKOTlpVKx
 BAzy97KVLPHNoNBN3XV9PwP8OovX7zkWTL/XPHzg1lIAfniWiInuQDrlU/F3TKOO
 2yJOFTy3x9RrwZcXfZUHltBo6766SC40zW4H+3WA42jljOPKXR1jH6lSIzSezBFt
 qsaw/CS/aW1Z8JAA8fhZurCmoljHMgRNOsnh8AfHPCYsUZSsw9ZE6wMrDUvjXBtm
 Zp9pI+h3mwc9tW/BGSZSpcktUDdXlxo9cXSCrXtxHjmrUxAsNGtHmXE7adogWSHs
 PaXfst8qFdsqp+kjrx+ZbMksfhhq2/ysYNIFWvSGim3VBBw5x74tu/VebSw19PjC
 1ZWzOt/5J5WCBD6BTGApCQg+YTg1u9koPRG441ZwIJ82eYgIQx80Y78uj/tG7mUH
 HC3GMxEwgQzYnQsDfDI936umNAgcdGw0DL7Tu71z4zM+Tn0WcvcQgHNk42zN5gZI
 XMeulxdj++pUoBYweDhlgJ88lr+gTeVwvfrYGWHpNvKsfmEcQJoqBvxTS1OJvMM5
 WfTwJgJCG+o4Jfj4oH2haUeMEUFQXikV0C7Dlw6MmhmcLmzhpbaMRdLQvcDocbTA
 hxhxSYhK6LQTRWv2h71MMw==
 =QRkQ
 -----END PGP SIGNATURE-----

Merge tag 'for-net-next-2021-10-01' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

Luiz Augusto von Dentz says:

====================
bluetooth-next pull request for net-next:

 - Add support for MediaTek MT7922 and MT7921
 - Enable support for AOSP extention in Qualcomm WCN399x and Realtek
   8822C/8852A.
 - Add initial support for link quality and audio/codec offload.
 - Rework of sockets sendmsg to avoid locking issues.
 - Add vhci suspend/resume emulation.

====================

Link: https://lore.kernel.org/r/20211001230850.3635543-1-luiz.dentz@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2021-10-05 07:41:14 -07:00
commit d0f1c248b4
35 changed files with 2826 additions and 1121 deletions

View file

@ -1037,8 +1037,9 @@ static bool btintel_firmware_version(struct hci_dev *hdev,
params = (void *)(fw_ptr + sizeof(*cmd));
bt_dev_info(hdev, "Boot Address: 0x%x",
le32_to_cpu(params->boot_addr));
*boot_addr = le32_to_cpu(params->boot_addr);
bt_dev_info(hdev, "Boot Address: 0x%x", *boot_addr);
bt_dev_info(hdev, "Firmware Version: %u-%u.%u",
params->fw_build_num, params->fw_build_ww,
@ -1071,9 +1072,6 @@ int btintel_download_firmware(struct hci_dev *hdev,
/* Skip version checking */
break;
default:
/* Skip reading firmware file version in bootloader mode */
if (ver->fw_variant == 0x06)
break;
/* Skip download if firmware has the same version */
if (btintel_firmware_version(hdev, ver->fw_build_num,
@ -1114,19 +1112,16 @@ static int btintel_download_fw_tlv(struct hci_dev *hdev,
int err;
u32 css_header_ver;
/* Skip reading firmware file version in bootloader mode */
if (ver->img_type != 0x01) {
/* Skip download if firmware has the same version */
if (btintel_firmware_version(hdev, ver->min_fw_build_nn,
ver->min_fw_build_cw,
ver->min_fw_build_yy,
fw, boot_param)) {
bt_dev_info(hdev, "Firmware already loaded");
/* Return -EALREADY to indicate that firmware has
* already been loaded.
*/
return -EALREADY;
}
/* Skip download if firmware has the same version */
if (btintel_firmware_version(hdev, ver->min_fw_build_nn,
ver->min_fw_build_cw,
ver->min_fw_build_yy,
fw, boot_param)) {
bt_dev_info(hdev, "Firmware already loaded");
/* Return -EALREADY to indicate that firmware has
* already been loaded.
*/
return -EALREADY;
}
/* The firmware variant determines if the device is in bootloader
@ -1285,12 +1280,16 @@ static int btintel_read_debug_features(struct hci_dev *hdev,
static int btintel_set_debug_features(struct hci_dev *hdev,
const struct intel_debug_features *features)
{
u8 mask[11] = { 0x0a, 0x92, 0x02, 0x07, 0x00, 0x00, 0x00, 0x00,
u8 mask[11] = { 0x0a, 0x92, 0x02, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00 };
u8 period[5] = { 0x04, 0x91, 0x02, 0x05, 0x00 };
u8 trace_enable = 0x02;
struct sk_buff *skb;
if (!features)
if (!features) {
bt_dev_warn(hdev, "Debug features not read");
return -EINVAL;
}
if (!(features->page1[0] & 0x3f)) {
bt_dev_info(hdev, "Telemetry exception format not supported");
@ -1303,11 +1302,95 @@ static int btintel_set_debug_features(struct hci_dev *hdev,
PTR_ERR(skb));
return PTR_ERR(skb);
}
kfree_skb(skb);
skb = __hci_cmd_sync(hdev, 0xfc8b, 5, period, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Setting periodicity for link statistics traces failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
kfree_skb(skb);
skb = __hci_cmd_sync(hdev, 0xfca1, 1, &trace_enable, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Enable tracing of link statistics events failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
kfree_skb(skb);
bt_dev_info(hdev, "set debug features: trace_enable 0x%02x mask 0x%02x",
trace_enable, mask[3]);
return 0;
}
static int btintel_reset_debug_features(struct hci_dev *hdev,
const struct intel_debug_features *features)
{
u8 mask[11] = { 0x0a, 0x92, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00 };
u8 trace_enable = 0x00;
struct sk_buff *skb;
if (!features) {
bt_dev_warn(hdev, "Debug features not read");
return -EINVAL;
}
if (!(features->page1[0] & 0x3f)) {
bt_dev_info(hdev, "Telemetry exception format not supported");
return 0;
}
/* Should stop the trace before writing ddc event mask. */
skb = __hci_cmd_sync(hdev, 0xfca1, 1, &trace_enable, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Stop tracing of link statistics events failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
kfree_skb(skb);
skb = __hci_cmd_sync(hdev, 0xfc8b, 11, mask, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Setting Intel telemetry ddc write event mask failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
kfree_skb(skb);
bt_dev_info(hdev, "reset debug features: trace_enable 0x%02x mask 0x%02x",
trace_enable, mask[3]);
return 0;
}
int btintel_set_quality_report(struct hci_dev *hdev, bool enable)
{
struct intel_debug_features features;
int err;
bt_dev_dbg(hdev, "enable %d", enable);
/* Read the Intel supported features and if new exception formats
* supported, need to load the additional DDC config to enable.
*/
err = btintel_read_debug_features(hdev, &features);
if (err)
return err;
/* Set or reset the debug features. */
if (enable)
err = btintel_set_debug_features(hdev, &features);
else
err = btintel_reset_debug_features(hdev, &features);
return err;
}
EXPORT_SYMBOL_GPL(btintel_set_quality_report);
static const struct firmware *btintel_legacy_rom_get_fw(struct hci_dev *hdev,
struct intel_version *ver)
{
@ -1893,7 +1976,6 @@ static int btintel_bootloader_setup(struct hci_dev *hdev,
u32 boot_param;
char ddcname[64];
int err;
struct intel_debug_features features;
BT_DBG("%s", hdev->name);
@ -1934,14 +2016,7 @@ static int btintel_bootloader_setup(struct hci_dev *hdev,
btintel_load_ddc_config(hdev, ddcname);
}
/* Read the Intel supported features and if new exception formats
* supported, need to load the additional DDC config to enable.
*/
err = btintel_read_debug_features(hdev, &features);
if (!err) {
/* Set DDC mask for available debug features */
btintel_set_debug_features(hdev, &features);
}
hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT);
/* Read the Intel version information after loading the FW */
err = btintel_read_version(hdev, &new_ver);
@ -2083,13 +2158,102 @@ static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev,
return err;
}
static int btintel_get_codec_config_data(struct hci_dev *hdev,
__u8 link, struct bt_codec *codec,
__u8 *ven_len, __u8 **ven_data)
{
int err = 0;
if (!ven_data || !ven_len)
return -EINVAL;
*ven_len = 0;
*ven_data = NULL;
if (link != ESCO_LINK) {
bt_dev_err(hdev, "Invalid link type(%u)", link);
return -EINVAL;
}
*ven_data = kmalloc(sizeof(__u8), GFP_KERNEL);
if (!*ven_data) {
err = -ENOMEM;
goto error;
}
/* supports only CVSD and mSBC offload codecs */
switch (codec->id) {
case 0x02:
**ven_data = 0x00;
break;
case 0x05:
**ven_data = 0x01;
break;
default:
err = -EINVAL;
bt_dev_err(hdev, "Invalid codec id(%u)", codec->id);
goto error;
}
/* codec and its capabilities are pre-defined to ids
* preset id = 0x00 represents CVSD codec with sampling rate 8K
* preset id = 0x01 represents mSBC codec with sampling rate 16K
*/
*ven_len = sizeof(__u8);
return err;
error:
kfree(*ven_data);
*ven_data = NULL;
return err;
}
static int btintel_get_data_path_id(struct hci_dev *hdev, __u8 *data_path_id)
{
/* Intel uses 1 as data path id for all the usecases */
*data_path_id = 1;
return 0;
}
static int btintel_configure_offload(struct hci_dev *hdev)
{
struct sk_buff *skb;
int err = 0;
struct intel_offload_use_cases *use_cases;
skb = __hci_cmd_sync(hdev, 0xfc86, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading offload use cases failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
if (skb->len < sizeof(*use_cases)) {
err = -EIO;
goto error;
}
use_cases = (void *)skb->data;
if (use_cases->status) {
err = -bt_to_errno(skb->data[0]);
goto error;
}
if (use_cases->preset[0] & 0x03) {
hdev->get_data_path_id = btintel_get_data_path_id;
hdev->get_codec_config_data = btintel_get_codec_config_data;
}
error:
kfree_skb(skb);
return err;
}
static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
struct intel_version_tlv *ver)
{
u32 boot_param;
char ddcname[64];
int err;
struct intel_debug_features features;
struct intel_version_tlv new_ver;
bt_dev_dbg(hdev, "");
@ -2125,14 +2289,10 @@ static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
*/
btintel_load_ddc_config(hdev, ddcname);
/* Read the Intel supported features and if new exception formats
* supported, need to load the additional DDC config to enable.
*/
err = btintel_read_debug_features(hdev, &features);
if (!err) {
/* Set DDC mask for available debug features */
btintel_set_debug_features(hdev, &features);
}
/* Read supported use cases and set callbacks to fetch datapath id */
btintel_configure_offload(hdev);
hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT);
/* Read the Intel version information after loading the FW */
err = btintel_read_version_tlv(hdev, &new_ver);
@ -2232,6 +2392,9 @@ static int btintel_setup_combined(struct hci_dev *hdev)
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
/* Set up the quality report callback for Intel devices */
hdev->set_quality_report = btintel_set_quality_report;
/* For Legacy device, check the HW platform value and size */
if (skb->len == sizeof(ver) && skb->data[1] == 0x37) {
bt_dev_dbg(hdev, "Read the legacy Intel version information");

View file

@ -132,6 +132,11 @@ struct intel_debug_features {
__u8 page1[16];
} __packed;
struct intel_offload_use_cases {
__u8 status;
__u8 preset[8];
} __packed;
#define INTEL_HW_PLATFORM(cnvx_bt) ((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
#define INTEL_HW_VARIANT(cnvx_bt) ((u8)(((cnvx_bt) & 0x003f0000) >> 16))
#define INTEL_CNVX_TOP_TYPE(cnvx_top) ((cnvx_top) & 0x00000fff)
@ -204,6 +209,7 @@ int btintel_configure_setup(struct hci_dev *hdev);
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len);
void btintel_secure_send_result(struct hci_dev *hdev,
const void *ptr, unsigned int len);
int btintel_set_quality_report(struct hci_dev *hdev, bool enable);
#else
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
@ -294,4 +300,9 @@ static inline void btintel_secure_send_result(struct hci_dev *hdev,
const void *ptr, unsigned int len)
{
}
static inline int btintel_set_quality_report(struct hci_dev *hdev, bool enable)
{
return -ENODEV;
}
#endif

View file

@ -587,12 +587,12 @@ static int btmrvl_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
return 0;
}
static bool btmrvl_prevent_wake(struct hci_dev *hdev)
static bool btmrvl_wakeup(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
return !device_may_wakeup(&card->func->dev);
return device_may_wakeup(&card->func->dev);
}
/*
@ -696,7 +696,7 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
hdev->send = btmrvl_send_frame;
hdev->setup = btmrvl_setup;
hdev->set_bdaddr = btmrvl_set_bdaddr;
hdev->prevent_wake = btmrvl_prevent_wake;
hdev->wakeup = btmrvl_wakeup;
SET_HCIDEV_DEV(hdev, &card->func->dev);
hdev->dev_type = priv->btmrvl_dev.dev_type;

View file

@ -158,8 +158,10 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
int err;
hlen = sizeof(*hdr) + wmt_params->dlen;
if (hlen > 255)
return -EINVAL;
if (hlen > 255) {
err = -EINVAL;
goto err_free_skb;
}
hdr = (struct mtk_wmt_hdr *)&wc;
hdr->dir = 1;
@ -173,7 +175,7 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc);
if (err < 0) {
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
return err;
goto err_free_skb;
}
/* The vendor specific WMT commands are all answered by a vendor
@ -190,13 +192,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
if (err == -EINTR) {
bt_dev_err(hdev, "Execution of wmt command interrupted");
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
return err;
goto err_free_skb;
}
if (err) {
bt_dev_err(hdev, "Execution of wmt command timed out");
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
return -ETIMEDOUT;
err = -ETIMEDOUT;
goto err_free_skb;
}
/* Parse and handle the return WMT event */

View file

@ -19,7 +19,6 @@
#include <net/bluetooth/hci_core.h>
#include <asm/unaligned.h>
#include <net/rsi_91x.h>
#include <net/genetlink.h>
#define RSI_DMA_ALIGN 8
#define RSI_FRAME_DESC_SIZE 16

View file

@ -59,6 +59,7 @@ struct id_table {
__u8 hci_bus;
bool config_needed;
bool has_rom_version;
bool has_msft_ext;
char *fw_name;
char *cfg_name;
};
@ -121,6 +122,7 @@ static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8821c_fw.bin",
.cfg_name = "rtl_bt/rtl8821c_config" },
@ -135,6 +137,7 @@ static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8761b_fw.bin",
.cfg_name = "rtl_bt/rtl8761b_config" },
@ -149,6 +152,7 @@ static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8822cs_fw.bin",
.cfg_name = "rtl_bt/rtl8822cs_config" },
@ -156,6 +160,7 @@ static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8822cu_fw.bin",
.cfg_name = "rtl_bt/rtl8822cu_config" },
@ -163,6 +168,7 @@ static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8822b_fw.bin",
.cfg_name = "rtl_bt/rtl8822b_config" },
@ -170,6 +176,7 @@ static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8852au_fw.bin",
.cfg_name = "rtl_bt/rtl8852au_config" },
};
@ -594,8 +601,10 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
hci_rev = le16_to_cpu(resp->hci_rev);
lmp_subver = le16_to_cpu(resp->lmp_subver);
if (resp->hci_ver == 0x8 && le16_to_cpu(resp->hci_rev) == 0x826c &&
resp->lmp_ver == 0x8 && le16_to_cpu(resp->lmp_subver) == 0xa99e)
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
hdev->bus);
if (!btrtl_dev->ic_info)
btrtl_dev->drop_fw = true;
if (btrtl_dev->drop_fw) {
@ -634,13 +643,13 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
hci_ver = resp->hci_ver;
hci_rev = le16_to_cpu(resp->hci_rev);
lmp_subver = le16_to_cpu(resp->lmp_subver);
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
hdev->bus);
}
out_free:
kfree_skb(skb);
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
hdev->bus);
if (!btrtl_dev->ic_info) {
rtl_dev_info(hdev, "unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x",
lmp_subver, hci_rev, hci_ver);
@ -684,12 +693,8 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
/* The following chips supports the Microsoft vendor extension,
* therefore set the corresponding VsMsftOpCode.
*/
switch (lmp_subver) {
case RTL_ROM_LMP_8822B:
case RTL_ROM_LMP_8852A:
if (btrtl_dev->ic_info->has_msft_ext)
hci_set_msft_opcode(hdev, 0xFCF0);
break;
}
return btrtl_dev;
@ -746,6 +751,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
case CHIP_ID_8852A:
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
hci_set_aosp_capable(hdev);
break;
default:
rtl_dev_dbg(hdev, "Central-peripheral role not enabled.");

View file

@ -384,6 +384,12 @@ static const struct usb_device_id blacklist_table[] = {
/* Realtek 8852AE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0xc852), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0bda, 0x4852), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04c5, 0x165c), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04ca, 0x4006), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Realtek Bluetooth devices */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
@ -410,6 +416,9 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3563), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
{ USB_DEVICE(0x13d3, 0x3564), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
{ USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
@ -433,6 +442,10 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0bda, 0xb009), .driver_info = BTUSB_REALTEK },
{ USB_DEVICE(0x2ff8, 0xb011), .driver_info = BTUSB_REALTEK },
/* Additional Realtek 8761B Bluetooth devices */
{ USB_DEVICE(0x2357, 0x0604), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8761BU Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x190e), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
@ -451,10 +464,6 @@ static const struct usb_device_id blacklist_table[] = {
/* Additional Realtek 8822CE Bluetooth devices */
{ USB_DEVICE(0x04ca, 0x4005), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
/* Bluetooth component of Realtek 8852AE device */
{ USB_DEVICE(0x04ca, 0x4006), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04c5, 0x161f), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0b05, 0x18ef), .driver_info = BTUSB_REALTEK |
@ -652,11 +661,33 @@ static void btusb_rtl_cmd_timeout(struct hci_dev *hdev)
static void btusb_qca_cmd_timeout(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct gpio_desc *reset_gpio = data->reset_gpio;
int err;
if (++data->cmd_timeout_cnt < 5)
return;
if (reset_gpio) {
bt_dev_err(hdev, "Reset qca device via bt_en gpio");
/* Toggle the hard reset line. The qca bt device is going to
* yank itself off the USB and then replug. The cleanup is handled
* correctly on the way out (standard USB disconnect), and the new
* device is detected cleanly and bound to the driver again like
* it should be.
*/
if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
bt_dev_err(hdev, "last reset failed? Not resetting again");
return;
}
gpiod_set_value_cansleep(reset_gpio, 0);
msleep(200);
gpiod_set_value_cansleep(reset_gpio, 1);
return;
}
bt_dev_err(hdev, "Multiple cmd timeouts seen. Resetting usb device.");
/* This is not an unbalanced PM reference since the device will reset */
err = usb_autopm_get_interface(data->intf);
@ -2200,6 +2231,23 @@ struct btmtk_section_map {
};
} __packed;
static int btusb_set_bdaddr_mtk(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
long ret;
skb = __hci_cmd_sync(hdev, 0xfc1a, sizeof(bdaddr), bdaddr, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
bt_dev_err(hdev, "changing Mediatek device address failed (%ld)",
ret);
return ret;
}
kfree_skb(skb);
return 0;
}
static void btusb_mtk_wmt_recv(struct urb *urb)
{
struct hci_dev *hdev = urb->context;
@ -2804,6 +2852,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
case 0x7668:
fwname = FIRMWARE_MT7668;
break;
case 0x7922:
case 0x7961:
snprintf(fw_bin_name, sizeof(fw_bin_name),
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
@ -3591,11 +3640,11 @@ static void btusb_check_needs_reset_resume(struct usb_interface *intf)
interface_to_usbdev(intf)->quirks |= USB_QUIRK_RESET_RESUME;
}
static bool btusb_prevent_wake(struct hci_dev *hdev)
static bool btusb_wakeup(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
return !device_may_wakeup(&data->udev->dev);
return device_may_wakeup(&data->udev->dev);
}
static int btusb_shutdown_qca(struct hci_dev *hdev)
@ -3752,7 +3801,7 @@ static int btusb_probe(struct usb_interface *intf,
hdev->flush = btusb_flush;
hdev->send = btusb_send_frame;
hdev->notify = btusb_notify;
hdev->prevent_wake = btusb_prevent_wake;
hdev->wakeup = btusb_wakeup;
#ifdef CONFIG_PM
err = btusb_config_oob_wake(hdev);
@ -3819,6 +3868,7 @@ static int btusb_probe(struct usb_interface *intf,
hdev->shutdown = btusb_mtk_shutdown;
hdev->manufacturer = 70;
hdev->cmd_timeout = btusb_mtk_cmd_timeout;
hdev->set_bdaddr = btusb_set_bdaddr_mtk;
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
data->recv_acl = btusb_recv_acl_mtk;
}

View file

@ -587,9 +587,11 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count)
count -= processed;
}
pm_runtime_get(&hu->serdev->dev);
pm_runtime_mark_last_busy(&hu->serdev->dev);
pm_runtime_put_autosuspend(&hu->serdev->dev);
if (hu->serdev) {
pm_runtime_get(&hu->serdev->dev);
pm_runtime_mark_last_busy(&hu->serdev->dev);
pm_runtime_put_autosuspend(&hu->serdev->dev);
}
return 0;
}
@ -814,7 +816,6 @@ static int h5_serdev_probe(struct serdev_device *serdev)
struct device *dev = &serdev->dev;
struct h5 *h5;
const struct h5_device_data *data;
int err;
h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL);
if (!h5)
@ -846,6 +847,8 @@ static int h5_serdev_probe(struct serdev_device *serdev)
h5->vnd = data->vnd;
}
if (data->driver_info & H5_INFO_WAKEUP_DISABLE)
set_bit(H5_WAKEUP_DISABLE, &h5->flags);
h5->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(h5->enable_gpio))
@ -856,14 +859,7 @@ static int h5_serdev_probe(struct serdev_device *serdev)
if (IS_ERR(h5->device_wake_gpio))
return PTR_ERR(h5->device_wake_gpio);
err = hci_uart_register_device(&h5->serdev_hu, &h5p);
if (err)
return err;
if (data->driver_info & H5_INFO_WAKEUP_DISABLE)
set_bit(H5_WAKEUP_DISABLE, &h5->flags);
return 0;
return hci_uart_register_device(&h5->serdev_hu, &h5p);
}
static void h5_serdev_remove(struct serdev_device *serdev)
@ -962,11 +958,13 @@ static void h5_btrtl_open(struct h5 *h5)
serdev_device_set_parity(h5->hu->serdev, SERDEV_PARITY_EVEN);
serdev_device_set_baudrate(h5->hu->serdev, 115200);
pm_runtime_set_active(&h5->hu->serdev->dev);
pm_runtime_use_autosuspend(&h5->hu->serdev->dev);
pm_runtime_set_autosuspend_delay(&h5->hu->serdev->dev,
SUSPEND_TIMEOUT_MS);
pm_runtime_enable(&h5->hu->serdev->dev);
if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags)) {
pm_runtime_set_active(&h5->hu->serdev->dev);
pm_runtime_use_autosuspend(&h5->hu->serdev->dev);
pm_runtime_set_autosuspend_delay(&h5->hu->serdev->dev,
SUSPEND_TIMEOUT_MS);
pm_runtime_enable(&h5->hu->serdev->dev);
}
/* The controller needs up to 500ms to wakeup */
gpiod_set_value_cansleep(h5->enable_gpio, 1);
@ -976,7 +974,8 @@ static void h5_btrtl_open(struct h5 *h5)
static void h5_btrtl_close(struct h5 *h5)
{
pm_runtime_disable(&h5->hu->serdev->dev);
if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags))
pm_runtime_disable(&h5->hu->serdev->dev);
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
gpiod_set_value_cansleep(h5->enable_gpio, 0);

View file

@ -479,6 +479,9 @@ static int hci_uart_tty_open(struct tty_struct *tty)
BT_DBG("tty %p", tty);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
/* Error if the tty has no write op instead of leaving an exploitable
* hole
*/

View file

@ -1577,7 +1577,7 @@ static void qca_cmd_timeout(struct hci_dev *hdev)
mutex_unlock(&qca->hci_memdump_lock);
}
static bool qca_prevent_wake(struct hci_dev *hdev)
static bool qca_wakeup(struct hci_dev *hdev)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
bool wakeup;
@ -1730,6 +1730,7 @@ static int qca_setup(struct hci_uart *hu)
if (qca_is_wcn399x(soc_type) ||
qca_is_wcn6750(soc_type)) {
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
hci_set_aosp_capable(hdev);
ret = qca_read_soc_version(hdev, &ver, soc_type);
if (ret)
@ -1764,7 +1765,7 @@ static int qca_setup(struct hci_uart *hu)
qca_debugfs_init(hdev);
hu->hdev->hw_error = qca_hw_error;
hu->hdev->cmd_timeout = qca_cmd_timeout;
hu->hdev->prevent_wake = qca_prevent_wake;
hu->hdev->wakeup = qca_wakeup;
} else if (ret == -ENOENT) {
/* No patch/nvm-config found, run with original fw/config */
set_bit(QCA_ROM_FW, &qca->flags);

View file

@ -21,6 +21,7 @@
#include <linux/skbuff.h>
#include <linux/miscdevice.h>
#include <linux/debugfs.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@ -37,6 +38,9 @@ struct vhci_data {
struct mutex open_mutex;
struct delayed_work open_timeout;
bool suspended;
bool wakeup;
};
static int vhci_open_dev(struct hci_dev *hdev)
@ -73,6 +77,115 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}
static int vhci_get_data_path_id(struct hci_dev *hdev, u8 *data_path_id)
{
*data_path_id = 0;
return 0;
}
static int vhci_get_codec_config_data(struct hci_dev *hdev, __u8 type,
struct bt_codec *codec, __u8 *vnd_len,
__u8 **vnd_data)
{
if (type != ESCO_LINK)
return -EINVAL;
*vnd_len = 0;
*vnd_data = NULL;
return 0;
}
static bool vhci_wakeup(struct hci_dev *hdev)
{
struct vhci_data *data = hci_get_drvdata(hdev);
return data->wakeup;
}
static ssize_t force_suspend_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct vhci_data *data = file->private_data;
char buf[3];
buf[0] = data->suspended ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t force_suspend_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct vhci_data *data = file->private_data;
bool enable;
int err;
err = kstrtobool_from_user(user_buf, count, &enable);
if (err)
return err;
if (data->suspended == enable)
return -EALREADY;
if (enable)
err = hci_suspend_dev(data->hdev);
else
err = hci_resume_dev(data->hdev);
if (err)
return err;
data->suspended = enable;
return count;
}
static const struct file_operations force_suspend_fops = {
.open = simple_open,
.read = force_suspend_read,
.write = force_suspend_write,
.llseek = default_llseek,
};
static ssize_t force_wakeup_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct vhci_data *data = file->private_data;
char buf[3];
buf[0] = data->wakeup ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t force_wakeup_write(struct file *file,
const char __user *user_buf, size_t count,
loff_t *ppos)
{
struct vhci_data *data = file->private_data;
bool enable;
int err;
err = kstrtobool_from_user(user_buf, count, &enable);
if (err)
return err;
if (data->wakeup == enable)
return -EALREADY;
return count;
}
static const struct file_operations force_wakeup_fops = {
.open = simple_open,
.read = force_wakeup_read,
.write = force_wakeup_write,
.llseek = default_llseek,
};
static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
{
struct hci_dev *hdev;
@ -112,6 +225,9 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
hdev->close = vhci_close_dev;
hdev->flush = vhci_flush;
hdev->send = vhci_send_frame;
hdev->get_data_path_id = vhci_get_data_path_id;
hdev->get_codec_config_data = vhci_get_codec_config_data;
hdev->wakeup = vhci_wakeup;
/* bit 6 is for external configuration */
if (opcode & 0x40)
@ -129,6 +245,12 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
return -EBUSY;
}
debugfs_create_file("force_suspend", 0644, hdev->debugfs, data,
&force_suspend_fops);
debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data,
&force_wakeup_fops);
hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
skb_put_u8(skb, 0xff);

View file

@ -153,6 +153,30 @@ struct bt_voice {
#define BT_SCM_PKT_STATUS 0x03
#define BT_CODEC 19
struct bt_codec_caps {
__u8 len;
__u8 data[];
} __packed;
struct bt_codec {
__u8 id;
__u16 cid;
__u16 vid;
__u8 data_path;
__u8 num_caps;
} __packed;
struct bt_codecs {
__u8 num_codecs;
struct bt_codec codecs[];
} __packed;
#define BT_CODEC_CVSD 0x02
#define BT_CODEC_TRANSPARENT 0x03
#define BT_CODEC_MSBC 0x05
__printf(1, 2)
void bt_info(const char *fmt, ...);
__printf(1, 2)
@ -420,6 +444,72 @@ static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk,
return NULL;
}
/* Shall not be called with lock_sock held */
static inline struct sk_buff *bt_skb_sendmsg(struct sock *sk,
struct msghdr *msg,
size_t len, size_t mtu,
size_t headroom, size_t tailroom)
{
struct sk_buff *skb;
size_t size = min_t(size_t, len, mtu);
int err;
skb = bt_skb_send_alloc(sk, size + headroom + tailroom,
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
return ERR_PTR(err);
skb_reserve(skb, headroom);
skb_tailroom_reserve(skb, mtu, tailroom);
if (!copy_from_iter_full(skb_put(skb, size), size, &msg->msg_iter)) {
kfree_skb(skb);
return ERR_PTR(-EFAULT);
}
skb->priority = sk->sk_priority;
return skb;
}
/* Similar to bt_skb_sendmsg but can split the msg into multiple fragments
* accourding to the MTU.
*/
static inline struct sk_buff *bt_skb_sendmmsg(struct sock *sk,
struct msghdr *msg,
size_t len, size_t mtu,
size_t headroom, size_t tailroom)
{
struct sk_buff *skb, **frag;
skb = bt_skb_sendmsg(sk, msg, len, mtu, headroom, tailroom);
if (IS_ERR_OR_NULL(skb))
return skb;
len -= skb->len;
if (!len)
return skb;
/* Add remaining data over MTU as continuation fragments */
frag = &skb_shinfo(skb)->frag_list;
while (len) {
struct sk_buff *tmp;
tmp = bt_skb_sendmsg(sk, msg, len, mtu, headroom, tailroom);
if (IS_ERR(tmp)) {
kfree_skb(skb);
return tmp;
}
len -= tmp->len;
*frag = tmp;
frag = &(*frag)->next;
}
return skb;
}
int bt_to_errno(u16 code);
void hci_sock_set_flag(struct sock *sk, int nr);

View file

@ -330,6 +330,8 @@ enum {
HCI_ENABLE_LL_PRIVACY,
HCI_CMD_PENDING,
HCI_FORCE_NO_MITM,
HCI_QUALITY_REPORT,
HCI_OFFLOAD_CODECS_ENABLED,
__HCI_NUM_FLAGS,
};
@ -871,6 +873,40 @@ struct hci_cp_logical_link_cancel {
__u8 flow_spec_id;
} __packed;
#define HCI_OP_ENHANCED_SETUP_SYNC_CONN 0x043d
struct hci_coding_format {
__u8 id;
__le16 cid;
__le16 vid;
} __packed;
struct hci_cp_enhanced_setup_sync_conn {
__le16 handle;
__le32 tx_bandwidth;
__le32 rx_bandwidth;
struct hci_coding_format tx_coding_format;
struct hci_coding_format rx_coding_format;
__le16 tx_codec_frame_size;
__le16 rx_codec_frame_size;
__le32 in_bandwidth;
__le32 out_bandwidth;
struct hci_coding_format in_coding_format;
struct hci_coding_format out_coding_format;
__le16 in_coded_data_size;
__le16 out_coded_data_size;
__u8 in_pcm_data_format;
__u8 out_pcm_data_format;
__u8 in_pcm_sample_payload_msb_pos;
__u8 out_pcm_sample_payload_msb_pos;
__u8 in_data_path;
__u8 out_data_path;
__u8 in_transport_unit_size;
__u8 out_transport_unit_size;
__le16 max_latency;
__le16 pkt_type;
__u8 retrans_effort;
} __packed;
struct hci_rp_logical_link_cancel {
__u8 status;
__u8 phy_handle;
@ -1250,6 +1286,14 @@ struct hci_rp_read_local_oob_ext_data {
__u8 rand256[16];
} __packed;
#define HCI_CONFIGURE_DATA_PATH 0x0c83
struct hci_op_configure_data_path {
__u8 direction;
__u8 data_path_id;
__u8 vnd_len;
__u8 vnd_data[];
} __packed;
#define HCI_OP_READ_LOCAL_VERSION 0x1001
struct hci_rp_read_local_version {
__u8 status;
@ -1307,6 +1351,28 @@ struct hci_rp_read_data_block_size {
} __packed;
#define HCI_OP_READ_LOCAL_CODECS 0x100b
struct hci_std_codecs {
__u8 num;
__u8 codec[];
} __packed;
struct hci_vnd_codec {
/* company id */
__le16 cid;
/* vendor codec id */
__le16 vid;
} __packed;
struct hci_vnd_codecs {
__u8 num;
struct hci_vnd_codec codec[];
} __packed;
struct hci_rp_read_local_supported_codecs {
__u8 status;
struct hci_std_codecs std_codecs;
struct hci_vnd_codecs vnd_codecs;
} __packed;
#define HCI_OP_READ_LOCAL_PAIRING_OPTS 0x100c
struct hci_rp_read_local_pairing_opts {
@ -1315,6 +1381,54 @@ struct hci_rp_read_local_pairing_opts {
__u8 max_key_size;
} __packed;
#define HCI_OP_READ_LOCAL_CODECS_V2 0x100d
struct hci_std_codec_v2 {
__u8 id;
__u8 transport;
} __packed;
struct hci_std_codecs_v2 {
__u8 num;
struct hci_std_codec_v2 codec[];
} __packed;
struct hci_vnd_codec_v2 {
__u8 id;
__le16 cid;
__le16 vid;
__u8 transport;
} __packed;
struct hci_vnd_codecs_v2 {
__u8 num;
struct hci_vnd_codec_v2 codec[];
} __packed;
struct hci_rp_read_local_supported_codecs_v2 {
__u8 status;
struct hci_std_codecs_v2 std_codecs;
struct hci_vnd_codecs_v2 vendor_codecs;
} __packed;
#define HCI_OP_READ_LOCAL_CODEC_CAPS 0x100e
struct hci_op_read_local_codec_caps {
__u8 id;
__le16 cid;
__le16 vid;
__u8 transport;
__u8 direction;
} __packed;
struct hci_codec_caps {
__u8 len;
__u8 data[];
} __packed;
struct hci_rp_read_local_codec_caps {
__u8 status;
__u8 num_caps;
} __packed;
#define HCI_OP_READ_PAGE_SCAN_ACTIVITY 0x0c1b
struct hci_rp_read_page_scan_activity {
__u8 status;
@ -2551,6 +2665,9 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
#define hci_iso_data_len(h) ((h) & 0x3fff)
#define hci_iso_data_flags(h) ((h) >> 14)
/* codec transport types */
#define HCI_TRANSPORT_SCO_ESCO 0x01
/* le24 support */
static inline void hci_cpu_to_le24(__u32 val, __u8 dst[3])
{

View file

@ -131,6 +131,17 @@ struct bdaddr_list {
u8 bdaddr_type;
};
struct codec_list {
struct list_head list;
u8 id;
__u16 cid;
__u16 vid;
u8 transport;
u8 num_caps;
u32 len;
struct hci_codec_caps caps[];
};
struct bdaddr_list_with_irk {
struct list_head list;
bdaddr_t bdaddr;
@ -536,6 +547,7 @@ struct hci_dev {
struct list_head pend_le_conns;
struct list_head pend_le_reports;
struct list_head blocked_keys;
struct list_head local_codecs;
struct hci_dev_stats stat;
@ -605,7 +617,12 @@ struct hci_dev {
int (*set_diag)(struct hci_dev *hdev, bool enable);
int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
void (*cmd_timeout)(struct hci_dev *hdev);
bool (*prevent_wake)(struct hci_dev *hdev);
bool (*wakeup)(struct hci_dev *hdev);
int (*set_quality_report)(struct hci_dev *hdev, bool enable);
int (*get_data_path_id)(struct hci_dev *hdev, __u8 *data_path);
int (*get_codec_config_data)(struct hci_dev *hdev, __u8 type,
struct bt_codec *codec, __u8 *vnd_len,
__u8 **vnd_data);
};
#define HCI_PHY_HANDLE(handle) (handle & 0xff)
@ -699,6 +716,7 @@ struct hci_conn {
struct amp_mgr *amp_mgr;
struct hci_conn *link;
struct bt_codec codec;
void (*connect_cfm_cb) (struct hci_conn *conn, u8 status);
void (*security_cfm_cb) (struct hci_conn *conn, u8 status);
@ -760,6 +778,7 @@ extern struct mutex hci_cb_list_lock;
hci_dev_clear_flag(hdev, HCI_LE_ADV); \
hci_dev_clear_flag(hdev, HCI_LL_RPA_RESOLUTION);\
hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ); \
hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT); \
} while (0)
/* ----- HCI interface to upper protocols ----- */
@ -1099,13 +1118,14 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
u16 conn_timeout,
enum conn_reasons conn_reason);
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u16 conn_timeout,
u8 role, bdaddr_t *direct_rpa);
u8 dst_type, bool dst_resolved, u8 sec_level,
u16 conn_timeout, u8 role,
bdaddr_t *direct_rpa);
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
u8 sec_level, u8 auth_type,
enum conn_reasons conn_reason);
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u16 setting);
__u16 setting, struct bt_codec *codec);
int hci_conn_check_link_mode(struct hci_conn *conn);
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
@ -1360,6 +1380,8 @@ int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance,
u16 scan_rsp_len, u8 *scan_rsp_data);
int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance);
void hci_adv_instances_set_rpa_expired(struct hci_dev *hdev, bool rpa_expired);
u32 hci_adv_instance_flags(struct hci_dev *hdev, u8 instance);
bool hci_adv_instance_is_scannable(struct hci_dev *hdev, u8 instance);
void hci_adv_monitors_clear(struct hci_dev *hdev);
void hci_free_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
@ -1442,6 +1464,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
/* Use LL Privacy based address resolution if supported */
#define use_ll_privacy(dev) ((dev)->le_features[0] & HCI_LE_LL_PRIVACY)
/* Use enhanced synchronous connection if command is supported */
#define enhanced_sco_capable(dev) ((dev)->commands[29] & 0x08)
/* Use ext scanning if set ext scan param and ext scan enable is supported */
#define use_ext_scan(dev) (((dev)->commands[37] & 0x20) && \
((dev)->commands[37] & 0x40))
@ -1609,43 +1634,6 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
mutex_unlock(&hci_cb_list_lock);
}
static inline void *eir_get_data(u8 *eir, size_t eir_len, u8 type,
size_t *data_len)
{
size_t parsed = 0;
if (eir_len < 2)
return NULL;
while (parsed < eir_len - 1) {
u8 field_len = eir[0];
if (field_len == 0)
break;
parsed += field_len + 1;
if (parsed > eir_len)
break;
if (eir[1] != type) {
eir += field_len + 1;
continue;
}
/* Zero length data */
if (field_len == 1)
return NULL;
if (data_len)
*data_len = field_len - 1;
return &eir[2];
}
return NULL;
}
static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
{
if (addr_type != ADDR_LE_DEV_RANDOM)
@ -1867,4 +1855,9 @@ void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
#define SCO_AIRMODE_CVSD 0x0000
#define SCO_AIRMODE_TRANSP 0x0003
#define LOCAL_CODEC_ACL_MASK BIT(0)
#define LOCAL_CODEC_SCO_MASK BIT(1)
#define TRANSPORT_TYPE_MAX 0x04
#endif /* __HCI_CORE_H */

View file

@ -14,7 +14,8 @@ bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
ecdh_helper.o hci_request.o mgmt_util.o mgmt_config.o
ecdh_helper.o hci_request.o mgmt_util.o mgmt_config.o hci_codec.o \
eir.o
bluetooth-$(CONFIG_BT_BREDR) += sco.o
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o

335
net/bluetooth/eir.c Normal file
View file

@ -0,0 +1,335 @@
// SPDX-License-Identifier: GPL-2.0
/*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2021 Intel Corporation
*/
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>
#include "eir.h"
#define PNP_INFO_SVCLASS_ID 0x1200
u8 eir_append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
{
size_t short_len;
size_t complete_len;
/* no space left for name (+ NULL + type + len) */
if ((HCI_MAX_AD_LENGTH - ad_len) < HCI_MAX_SHORT_NAME_LENGTH + 3)
return ad_len;
/* use complete name if present and fits */
complete_len = strlen(hdev->dev_name);
if (complete_len && complete_len <= HCI_MAX_SHORT_NAME_LENGTH)
return eir_append_data(ptr, ad_len, EIR_NAME_COMPLETE,
hdev->dev_name, complete_len + 1);
/* use short name if present */
short_len = strlen(hdev->short_name);
if (short_len)
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT,
hdev->short_name, short_len + 1);
/* use shortened full name if present, we already know that name
* is longer then HCI_MAX_SHORT_NAME_LENGTH
*/
if (complete_len) {
u8 name[HCI_MAX_SHORT_NAME_LENGTH + 1];
memcpy(name, hdev->dev_name, HCI_MAX_SHORT_NAME_LENGTH);
name[HCI_MAX_SHORT_NAME_LENGTH] = '\0';
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT, name,
sizeof(name));
}
return ad_len;
}
u8 eir_append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
{
return eir_append_le16(ptr, ad_len, EIR_APPEARANCE, hdev->appearance);
}
static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
{
u8 *ptr = data, *uuids_start = NULL;
struct bt_uuid *uuid;
if (len < 4)
return ptr;
list_for_each_entry(uuid, &hdev->uuids, list) {
u16 uuid16;
if (uuid->size != 16)
continue;
uuid16 = get_unaligned_le16(&uuid->uuid[12]);
if (uuid16 < 0x1100)
continue;
if (uuid16 == PNP_INFO_SVCLASS_ID)
continue;
if (!uuids_start) {
uuids_start = ptr;
uuids_start[0] = 1;
uuids_start[1] = EIR_UUID16_ALL;
ptr += 2;
}
/* Stop if not enough space to put next UUID */
if ((ptr - data) + sizeof(u16) > len) {
uuids_start[1] = EIR_UUID16_SOME;
break;
}
*ptr++ = (uuid16 & 0x00ff);
*ptr++ = (uuid16 & 0xff00) >> 8;
uuids_start[0] += sizeof(uuid16);
}
return ptr;
}
static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
{
u8 *ptr = data, *uuids_start = NULL;
struct bt_uuid *uuid;
if (len < 6)
return ptr;
list_for_each_entry(uuid, &hdev->uuids, list) {
if (uuid->size != 32)
continue;
if (!uuids_start) {
uuids_start = ptr;
uuids_start[0] = 1;
uuids_start[1] = EIR_UUID32_ALL;
ptr += 2;
}
/* Stop if not enough space to put next UUID */
if ((ptr - data) + sizeof(u32) > len) {
uuids_start[1] = EIR_UUID32_SOME;
break;
}
memcpy(ptr, &uuid->uuid[12], sizeof(u32));
ptr += sizeof(u32);
uuids_start[0] += sizeof(u32);
}
return ptr;
}
static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
{
u8 *ptr = data, *uuids_start = NULL;
struct bt_uuid *uuid;
if (len < 18)
return ptr;
list_for_each_entry(uuid, &hdev->uuids, list) {
if (uuid->size != 128)
continue;
if (!uuids_start) {
uuids_start = ptr;
uuids_start[0] = 1;
uuids_start[1] = EIR_UUID128_ALL;
ptr += 2;
}
/* Stop if not enough space to put next UUID */
if ((ptr - data) + 16 > len) {
uuids_start[1] = EIR_UUID128_SOME;
break;
}
memcpy(ptr, uuid->uuid, 16);
ptr += 16;
uuids_start[0] += 16;
}
return ptr;
}
void eir_create(struct hci_dev *hdev, u8 *data)
{
u8 *ptr = data;
size_t name_len;
name_len = strlen(hdev->dev_name);
if (name_len > 0) {
/* EIR Data type */
if (name_len > 48) {
name_len = 48;
ptr[1] = EIR_NAME_SHORT;
} else {
ptr[1] = EIR_NAME_COMPLETE;
}
/* EIR Data length */
ptr[0] = name_len + 1;
memcpy(ptr + 2, hdev->dev_name, name_len);
ptr += (name_len + 2);
}
if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) {
ptr[0] = 2;
ptr[1] = EIR_TX_POWER;
ptr[2] = (u8)hdev->inq_tx_power;
ptr += 3;
}
if (hdev->devid_source > 0) {
ptr[0] = 9;
ptr[1] = EIR_DEVICE_ID;
put_unaligned_le16(hdev->devid_source, ptr + 2);
put_unaligned_le16(hdev->devid_vendor, ptr + 4);
put_unaligned_le16(hdev->devid_product, ptr + 6);
put_unaligned_le16(hdev->devid_version, ptr + 8);
ptr += 10;
}
ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
}
u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
{
struct adv_info *adv = NULL;
u8 ad_len = 0, flags = 0;
u32 instance_flags;
/* Return 0 when the current instance identifier is invalid. */
if (instance) {
adv = hci_find_adv_instance(hdev, instance);
if (!adv)
return 0;
}
instance_flags = hci_adv_instance_flags(hdev, instance);
/* If instance already has the flags set skip adding it once
* again.
*/
if (adv && eir_get_data(adv->adv_data, adv->adv_data_len, EIR_FLAGS,
NULL))
goto skip_flags;
/* The Add Advertising command allows userspace to set both the general
* and limited discoverable flags.
*/
if (instance_flags & MGMT_ADV_FLAG_DISCOV)
flags |= LE_AD_GENERAL;
if (instance_flags & MGMT_ADV_FLAG_LIMITED_DISCOV)
flags |= LE_AD_LIMITED;
if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
flags |= LE_AD_NO_BREDR;
if (flags || (instance_flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) {
/* If a discovery flag wasn't provided, simply use the global
* settings.
*/
if (!flags)
flags |= mgmt_get_adv_discov_flags(hdev);
/* If flags would still be empty, then there is no need to
* include the "Flags" AD field".
*/
if (flags) {
ptr[0] = 0x02;
ptr[1] = EIR_FLAGS;
ptr[2] = flags;
ad_len += 3;
ptr += 3;
}
}
skip_flags:
if (adv) {
memcpy(ptr, adv->adv_data, adv->adv_data_len);
ad_len += adv->adv_data_len;
ptr += adv->adv_data_len;
}
if (instance_flags & MGMT_ADV_FLAG_TX_POWER) {
s8 adv_tx_power;
if (ext_adv_capable(hdev)) {
if (adv)
adv_tx_power = adv->tx_power;
else
adv_tx_power = hdev->adv_tx_power;
} else {
adv_tx_power = hdev->adv_tx_power;
}
/* Provide Tx Power only if we can provide a valid value for it */
if (adv_tx_power != HCI_TX_POWER_INVALID) {
ptr[0] = 0x02;
ptr[1] = EIR_TX_POWER;
ptr[2] = (u8)adv_tx_power;
ad_len += 3;
ptr += 3;
}
}
return ad_len;
}
static u8 create_default_scan_rsp(struct hci_dev *hdev, u8 *ptr)
{
u8 scan_rsp_len = 0;
if (hdev->appearance)
scan_rsp_len = eir_append_appearance(hdev, ptr, scan_rsp_len);
return eir_append_local_name(hdev, ptr, scan_rsp_len);
}
u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr)
{
struct adv_info *adv;
u8 scan_rsp_len = 0;
if (!instance)
return create_default_scan_rsp(hdev, ptr);
adv = hci_find_adv_instance(hdev, instance);
if (!adv)
return 0;
if ((adv->flags & MGMT_ADV_FLAG_APPEARANCE) && hdev->appearance)
scan_rsp_len = eir_append_appearance(hdev, ptr, scan_rsp_len);
memcpy(&ptr[scan_rsp_len], adv->scan_rsp_data, adv->scan_rsp_len);
scan_rsp_len += adv->scan_rsp_len;
if (adv->flags & MGMT_ADV_FLAG_LOCAL_NAME)
scan_rsp_len = eir_append_local_name(hdev, ptr, scan_rsp_len);
return scan_rsp_len;
}

72
net/bluetooth/eir.h Normal file
View file

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2021 Intel Corporation
*/
void eir_create(struct hci_dev *hdev, u8 *data);
u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr);
u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr);
u8 eir_append_local_name(struct hci_dev *hdev, u8 *eir, u8 ad_len);
u8 eir_append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len);
static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type,
u8 *data, u8 data_len)
{
eir[eir_len++] = sizeof(type) + data_len;
eir[eir_len++] = type;
memcpy(&eir[eir_len], data, data_len);
eir_len += data_len;
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 inline void *eir_get_data(u8 *eir, size_t eir_len, u8 type,
size_t *data_len)
{
size_t parsed = 0;
if (eir_len < 2)
return NULL;
while (parsed < eir_len - 1) {
u8 field_len = eir[0];
if (field_len == 0)
break;
parsed += field_len + 1;
if (parsed > eir_len)
break;
if (eir[1] != type) {
eir += field_len + 1;
continue;
}
/* Zero length data */
if (field_len == 1)
return NULL;
if (data_len)
*data_len = field_len - 1;
return &eir[2];
}
return NULL;
}

238
net/bluetooth/hci_codec.c Normal file
View file

@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2021 Intel Corporation */
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_codec.h"
static int hci_codec_list_add(struct list_head *list,
struct hci_op_read_local_codec_caps *sent,
struct hci_rp_read_local_codec_caps *rp,
void *caps,
__u32 len)
{
struct codec_list *entry;
entry = kzalloc(sizeof(*entry) + len, GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->id = sent->id;
if (sent->id == 0xFF) {
entry->cid = __le16_to_cpu(sent->cid);
entry->vid = __le16_to_cpu(sent->vid);
}
entry->transport = sent->transport;
entry->len = len;
entry->num_caps = rp->num_caps;
if (rp->num_caps)
memcpy(entry->caps, caps, len);
list_add(&entry->list, list);
return 0;
}
void hci_codec_list_clear(struct list_head *codec_list)
{
struct codec_list *c, *n;
list_for_each_entry_safe(c, n, codec_list, list) {
list_del(&c->list);
kfree(c);
}
}
static void hci_read_codec_capabilities(struct hci_dev *hdev, __u8 transport,
struct hci_op_read_local_codec_caps
*cmd)
{
__u8 i;
for (i = 0; i < TRANSPORT_TYPE_MAX; i++) {
if (transport & BIT(i)) {
struct hci_rp_read_local_codec_caps *rp;
struct hci_codec_caps *caps;
struct sk_buff *skb;
__u8 j;
__u32 len;
cmd->transport = i;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_CODEC_CAPS,
sizeof(*cmd), cmd,
HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Failed to read codec capabilities (%ld)",
PTR_ERR(skb));
continue;
}
if (skb->len < sizeof(*rp))
goto error;
rp = (void *)skb->data;
if (rp->status)
goto error;
if (!rp->num_caps) {
len = 0;
/* this codec doesn't have capabilities */
goto skip_caps_parse;
}
skb_pull(skb, sizeof(*rp));
for (j = 0, len = 0; j < rp->num_caps; j++) {
caps = (void *)skb->data;
if (skb->len < sizeof(*caps))
goto error;
if (skb->len < caps->len)
goto error;
len += sizeof(caps->len) + caps->len;
skb_pull(skb, sizeof(caps->len) + caps->len);
}
skip_caps_parse:
hci_dev_lock(hdev);
hci_codec_list_add(&hdev->local_codecs, cmd, rp,
(__u8 *)rp + sizeof(*rp), len);
hci_dev_unlock(hdev);
error:
kfree_skb(skb);
}
}
}
void hci_read_supported_codecs(struct hci_dev *hdev)
{
struct sk_buff *skb;
struct hci_rp_read_local_supported_codecs *rp;
struct hci_std_codecs *std_codecs;
struct hci_vnd_codecs *vnd_codecs;
struct hci_op_read_local_codec_caps caps;
__u8 i;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_CODECS, 0, NULL,
HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Failed to read local supported codecs (%ld)",
PTR_ERR(skb));
return;
}
if (skb->len < sizeof(*rp))
goto error;
rp = (void *)skb->data;
if (rp->status)
goto error;
skb_pull(skb, sizeof(rp->status));
std_codecs = (void *)skb->data;
/* validate codecs length before accessing */
if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num)
+ sizeof(std_codecs->num))
goto error;
/* enumerate codec capabilities of standard codecs */
memset(&caps, 0, sizeof(caps));
for (i = 0; i < std_codecs->num; i++) {
caps.id = std_codecs->codec[i];
caps.direction = 0x00;
hci_read_codec_capabilities(hdev, LOCAL_CODEC_ACL_MASK, &caps);
}
skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num)
+ sizeof(std_codecs->num));
vnd_codecs = (void *)skb->data;
/* validate vendor codecs length before accessing */
if (skb->len <
flex_array_size(vnd_codecs, codec, vnd_codecs->num)
+ sizeof(vnd_codecs->num))
goto error;
/* enumerate vendor codec capabilities */
for (i = 0; i < vnd_codecs->num; i++) {
caps.id = 0xFF;
caps.cid = vnd_codecs->codec[i].cid;
caps.vid = vnd_codecs->codec[i].vid;
caps.direction = 0x00;
hci_read_codec_capabilities(hdev, LOCAL_CODEC_ACL_MASK, &caps);
}
error:
kfree_skb(skb);
}
void hci_read_supported_codecs_v2(struct hci_dev *hdev)
{
struct sk_buff *skb;
struct hci_rp_read_local_supported_codecs_v2 *rp;
struct hci_std_codecs_v2 *std_codecs;
struct hci_vnd_codecs_v2 *vnd_codecs;
struct hci_op_read_local_codec_caps caps;
__u8 i;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_CODECS_V2, 0, NULL,
HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Failed to read local supported codecs (%ld)",
PTR_ERR(skb));
return;
}
if (skb->len < sizeof(*rp))
goto error;
rp = (void *)skb->data;
if (rp->status)
goto error;
skb_pull(skb, sizeof(rp->status));
std_codecs = (void *)skb->data;
/* check for payload data length before accessing */
if (skb->len < flex_array_size(std_codecs, codec, std_codecs->num)
+ sizeof(std_codecs->num))
goto error;
memset(&caps, 0, sizeof(caps));
for (i = 0; i < std_codecs->num; i++) {
caps.id = std_codecs->codec[i].id;
hci_read_codec_capabilities(hdev, std_codecs->codec[i].transport,
&caps);
}
skb_pull(skb, flex_array_size(std_codecs, codec, std_codecs->num)
+ sizeof(std_codecs->num));
vnd_codecs = (void *)skb->data;
/* check for payload data length before accessing */
if (skb->len <
flex_array_size(vnd_codecs, codec, vnd_codecs->num)
+ sizeof(vnd_codecs->num))
goto error;
for (i = 0; i < vnd_codecs->num; i++) {
caps.id = 0xFF;
caps.cid = vnd_codecs->codec[i].cid;
caps.vid = vnd_codecs->codec[i].vid;
hci_read_codec_capabilities(hdev, vnd_codecs->codec[i].transport,
&caps);
}
error:
kfree_skb(skb);
}

View file

@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2014 Intel Corporation */
void hci_read_supported_codecs(struct hci_dev *hdev);
void hci_read_supported_codecs_v2(struct hci_dev *hdev);
void hci_codec_list_clear(struct list_head *codec_list);

View file

@ -307,13 +307,133 @@ static bool find_next_esco_param(struct hci_conn *conn,
return conn->attempt <= size;
}
bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
static bool hci_enhanced_setup_sync_conn(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_enhanced_setup_sync_conn cp;
const struct sco_param *param;
bt_dev_dbg(hdev, "hcon %p", conn);
/* for offload use case, codec needs to configured before opening SCO */
if (conn->codec.data_path)
hci_req_configure_datapath(hdev, &conn->codec);
conn->state = BT_CONNECT;
conn->out = true;
conn->attempt++;
memset(&cp, 0x00, sizeof(cp));
cp.handle = cpu_to_le16(handle);
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
switch (conn->codec.id) {
case BT_CODEC_MSBC:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
return false;
param = &esco_param_msbc[conn->attempt - 1];
cp.tx_coding_format.id = 0x05;
cp.rx_coding_format.id = 0x05;
cp.tx_codec_frame_size = __cpu_to_le16(60);
cp.rx_codec_frame_size = __cpu_to_le16(60);
cp.in_bandwidth = __cpu_to_le32(32000);
cp.out_bandwidth = __cpu_to_le32(32000);
cp.in_coding_format.id = 0x04;
cp.out_coding_format.id = 0x04;
cp.in_coded_data_size = __cpu_to_le16(16);
cp.out_coded_data_size = __cpu_to_le16(16);
cp.in_pcm_data_format = 2;
cp.out_pcm_data_format = 2;
cp.in_pcm_sample_payload_msb_pos = 0;
cp.out_pcm_sample_payload_msb_pos = 0;
cp.in_data_path = conn->codec.data_path;
cp.out_data_path = conn->codec.data_path;
cp.in_transport_unit_size = 1;
cp.out_transport_unit_size = 1;
break;
case BT_CODEC_TRANSPARENT:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
return false;
param = &esco_param_msbc[conn->attempt - 1];
cp.tx_coding_format.id = 0x03;
cp.rx_coding_format.id = 0x03;
cp.tx_codec_frame_size = __cpu_to_le16(60);
cp.rx_codec_frame_size = __cpu_to_le16(60);
cp.in_bandwidth = __cpu_to_le32(0x1f40);
cp.out_bandwidth = __cpu_to_le32(0x1f40);
cp.in_coding_format.id = 0x03;
cp.out_coding_format.id = 0x03;
cp.in_coded_data_size = __cpu_to_le16(16);
cp.out_coded_data_size = __cpu_to_le16(16);
cp.in_pcm_data_format = 2;
cp.out_pcm_data_format = 2;
cp.in_pcm_sample_payload_msb_pos = 0;
cp.out_pcm_sample_payload_msb_pos = 0;
cp.in_data_path = conn->codec.data_path;
cp.out_data_path = conn->codec.data_path;
cp.in_transport_unit_size = 1;
cp.out_transport_unit_size = 1;
break;
case BT_CODEC_CVSD:
if (lmp_esco_capable(conn->link)) {
if (!find_next_esco_param(conn, esco_param_cvsd,
ARRAY_SIZE(esco_param_cvsd)))
return false;
param = &esco_param_cvsd[conn->attempt - 1];
} else {
if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
return false;
param = &sco_param_cvsd[conn->attempt - 1];
}
cp.tx_coding_format.id = 2;
cp.rx_coding_format.id = 2;
cp.tx_codec_frame_size = __cpu_to_le16(60);
cp.rx_codec_frame_size = __cpu_to_le16(60);
cp.in_bandwidth = __cpu_to_le32(16000);
cp.out_bandwidth = __cpu_to_le32(16000);
cp.in_coding_format.id = 4;
cp.out_coding_format.id = 4;
cp.in_coded_data_size = __cpu_to_le16(16);
cp.out_coded_data_size = __cpu_to_le16(16);
cp.in_pcm_data_format = 2;
cp.out_pcm_data_format = 2;
cp.in_pcm_sample_payload_msb_pos = 0;
cp.out_pcm_sample_payload_msb_pos = 0;
cp.in_data_path = conn->codec.data_path;
cp.out_data_path = conn->codec.data_path;
cp.in_transport_unit_size = 16;
cp.out_transport_unit_size = 16;
break;
default:
return false;
}
cp.retrans_effort = param->retrans_effort;
cp.pkt_type = __cpu_to_le16(param->pkt_type);
cp.max_latency = __cpu_to_le16(param->max_latency);
if (hci_send_cmd(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0)
return false;
return true;
}
static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_setup_sync_conn cp;
const struct sco_param *param;
BT_DBG("hcon %p", conn);
bt_dev_dbg(hdev, "hcon %p", conn);
conn->state = BT_CONNECT;
conn->out = true;
@ -359,6 +479,14 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
return true;
}
bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
{
if (enhanced_sco_capable(conn->hdev))
return hci_enhanced_setup_sync_conn(conn, handle);
return hci_setup_sync_conn(conn, handle);
}
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier)
{
@ -1040,8 +1168,8 @@ static void hci_req_directed_advertising(struct hci_request *req,
}
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u16 conn_timeout,
u8 role, bdaddr_t *direct_rpa)
u8 dst_type, bool dst_resolved, u8 sec_level,
u16 conn_timeout, u8 role, bdaddr_t *direct_rpa)
{
struct hci_conn_params *params;
struct hci_conn *conn;
@ -1078,19 +1206,24 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
return ERR_PTR(-EBUSY);
}
/* When given an identity address with existing identity
* resolving key, the connection needs to be established
* to a resolvable random address.
*
* Storing the resolvable random address is required here
* to handle connection failures. The address will later
* be resolved back into the original identity address
* from the connect request.
/* Check if the destination address has been resolved by the controller
* since if it did then the identity address shall be used.
*/
irk = hci_find_irk_by_addr(hdev, dst, dst_type);
if (irk && bacmp(&irk->rpa, BDADDR_ANY)) {
dst = &irk->rpa;
dst_type = ADDR_LE_DEV_RANDOM;
if (!dst_resolved) {
/* When given an identity address with existing identity
* resolving key, the connection needs to be established
* to a resolvable random address.
*
* Storing the resolvable random address is required here
* to handle connection failures. The address will later
* be resolved back into the original identity address
* from the connect request.
*/
irk = hci_find_irk_by_addr(hdev, dst, dst_type);
if (irk && bacmp(&irk->rpa, BDADDR_ANY)) {
dst = &irk->rpa;
dst_type = ADDR_LE_DEV_RANDOM;
}
}
if (conn) {
@ -1319,7 +1452,7 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
}
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u16 setting)
__u16 setting, struct bt_codec *codec)
{
struct hci_conn *acl;
struct hci_conn *sco;
@ -1344,6 +1477,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
hci_conn_hold(sco);
sco->setting = setting;
sco->codec = *codec;
if (acl->state == BT_CONNECTED &&
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {

View file

@ -45,6 +45,7 @@
#include "leds.h"
#include "msft.h"
#include "aosp.h"
#include "hci_codec.h"
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
@ -61,130 +62,6 @@ DEFINE_MUTEX(hci_cb_list_lock);
/* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida);
/* ---- HCI debugfs entries ---- */
static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct hci_dev *hdev = file->private_data;
char buf[3];
buf[0] = hci_dev_test_flag(hdev, HCI_DUT_MODE) ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct hci_dev *hdev = file->private_data;
struct sk_buff *skb;
bool enable;
int err;
if (!test_bit(HCI_UP, &hdev->flags))
return -ENETDOWN;
err = kstrtobool_from_user(user_buf, count, &enable);
if (err)
return err;
if (enable == hci_dev_test_flag(hdev, HCI_DUT_MODE))
return -EALREADY;
hci_req_sync_lock(hdev);
if (enable)
skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
HCI_CMD_TIMEOUT);
else
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
HCI_CMD_TIMEOUT);
hci_req_sync_unlock(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
kfree_skb(skb);
hci_dev_change_flag(hdev, HCI_DUT_MODE);
return count;
}
static const struct file_operations dut_mode_fops = {
.open = simple_open,
.read = dut_mode_read,
.write = dut_mode_write,
.llseek = default_llseek,
};
static ssize_t vendor_diag_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct hci_dev *hdev = file->private_data;
char buf[3];
buf[0] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t vendor_diag_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct hci_dev *hdev = file->private_data;
bool enable;
int err;
err = kstrtobool_from_user(user_buf, count, &enable);
if (err)
return err;
/* When the diagnostic flags are not persistent and the transport
* is not active or in user channel operation, then there is no need
* for the vendor callback. Instead just store the desired value and
* the setting will be programmed when the controller gets powered on.
*/
if (test_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks) &&
(!test_bit(HCI_RUNNING, &hdev->flags) ||
hci_dev_test_flag(hdev, HCI_USER_CHANNEL)))
goto done;
hci_req_sync_lock(hdev);
err = hdev->set_diag(hdev, enable);
hci_req_sync_unlock(hdev);
if (err < 0)
return err;
done:
if (enable)
hci_dev_set_flag(hdev, HCI_VENDOR_DIAG);
else
hci_dev_clear_flag(hdev, HCI_VENDOR_DIAG);
return count;
}
static const struct file_operations vendor_diag_fops = {
.open = simple_open,
.read = vendor_diag_read,
.write = vendor_diag_write,
.llseek = default_llseek,
};
static void hci_debugfs_create_basic(struct hci_dev *hdev)
{
debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
&dut_mode_fops);
if (hdev->set_diag)
debugfs_create_file("vendor_diag", 0644, hdev->debugfs, hdev,
&vendor_diag_fops);
}
static int hci_reset_req(struct hci_request *req, unsigned long opt)
{
BT_DBG("%s %ld", req->hdev->name, opt);
@ -838,10 +715,6 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
if (hdev->commands[22] & 0x04)
hci_set_event_mask_page_2(req);
/* Read local codec list if the HCI command is supported */
if (hdev->commands[29] & 0x20)
hci_req_add(req, HCI_OP_READ_LOCAL_CODECS, 0, NULL);
/* Read local pairing options if the HCI command is supported */
if (hdev->commands[41] & 0x08)
hci_req_add(req, HCI_OP_READ_LOCAL_PAIRING_OPTS, 0, NULL);
@ -937,6 +810,12 @@ static int __hci_init(struct hci_dev *hdev)
if (err < 0)
return err;
/* Read local codec list if the HCI command is supported */
if (hdev->commands[45] & 0x04)
hci_read_supported_codecs_v2(hdev);
else if (hdev->commands[29] & 0x20)
hci_read_supported_codecs(hdev);
/* This function is only called when the controller is actually in
* configured state. When the controller is marked as unconfigured,
* this initialization procedure is not run.
@ -1848,6 +1727,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
bacpy(&hdev->random_addr, BDADDR_ANY);
hci_codec_list_clear(&hdev->local_codecs);
hci_req_sync_unlock(hdev);
@ -3080,6 +2960,60 @@ int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance,
return 0;
}
/* This function requires the caller holds hdev->lock */
u32 hci_adv_instance_flags(struct hci_dev *hdev, u8 instance)
{
u32 flags;
struct adv_info *adv;
if (instance == 0x00) {
/* Instance 0 always manages the "Tx Power" and "Flags"
* fields
*/
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
/* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting
* corresponds to the "connectable" instance flag.
*/
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
flags |= MGMT_ADV_FLAG_CONNECTABLE;
if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
else if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
flags |= MGMT_ADV_FLAG_DISCOV;
return flags;
}
adv = hci_find_adv_instance(hdev, instance);
/* Return 0 when we got an invalid instance identifier. */
if (!adv)
return 0;
return adv->flags;
}
bool hci_adv_instance_is_scannable(struct hci_dev *hdev, u8 instance)
{
struct adv_info *adv;
/* Instance 0x00 always set local name */
if (instance == 0x00)
return true;
adv = hci_find_adv_instance(hdev, instance);
if (!adv)
return false;
if (adv->flags & MGMT_ADV_FLAG_APPEARANCE ||
adv->flags & MGMT_ADV_FLAG_LOCAL_NAME)
return true;
return adv->scan_rsp_len ? true : false;
}
/* This function requires the caller holds hdev->lock */
void hci_adv_monitors_clear(struct hci_dev *hdev)
{
@ -3487,15 +3421,6 @@ struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
{
struct hci_conn_params *param;
switch (addr_type) {
case ADDR_LE_DEV_PUBLIC_RESOLVED:
addr_type = ADDR_LE_DEV_PUBLIC;
break;
case ADDR_LE_DEV_RANDOM_RESOLVED:
addr_type = ADDR_LE_DEV_RANDOM;
break;
}
list_for_each_entry(param, list, action) {
if (bacmp(&param->addr, addr) == 0 &&
param->addr_type == addr_type)
@ -3701,55 +3626,12 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
struct hci_dev *hdev =
container_of(nb, struct hci_dev, suspend_notifier);
int ret = 0;
u8 state = BT_RUNNING;
/* If powering down, wait for completion. */
if (mgmt_powering_down(hdev)) {
set_bit(SUSPEND_POWERING_DOWN, hdev->suspend_tasks);
ret = hci_suspend_wait_event(hdev);
if (ret)
goto done;
}
if (action == PM_SUSPEND_PREPARE)
ret = hci_suspend_dev(hdev);
else if (action == PM_POST_SUSPEND)
ret = hci_resume_dev(hdev);
/* Suspend notifier should only act on events when powered. */
if (!hdev_is_powered(hdev) ||
hci_dev_test_flag(hdev, HCI_UNREGISTER))
goto done;
if (action == PM_SUSPEND_PREPARE) {
/* Suspend consists of two actions:
* - First, disconnect everything and make the controller not
* connectable (disabling scanning)
* - Second, program event filter/accept list and enable scan
*/
ret = hci_change_suspend_state(hdev, BT_SUSPEND_DISCONNECT);
if (!ret)
state = BT_SUSPEND_DISCONNECT;
/* Only configure accept list if disconnect succeeded and wake
* isn't being prevented.
*/
if (!ret && !(hdev->prevent_wake && hdev->prevent_wake(hdev))) {
ret = hci_change_suspend_state(hdev,
BT_SUSPEND_CONFIGURE_WAKE);
if (!ret)
state = BT_SUSPEND_CONFIGURE_WAKE;
}
hci_clear_wake_reason(hdev);
mgmt_suspending(hdev, state);
} else if (action == PM_POST_SUSPEND) {
ret = hci_change_suspend_state(hdev, BT_RUNNING);
mgmt_resuming(hdev, hdev->wake_reason, &hdev->wake_addr,
hdev->wake_addr_type);
}
done:
/* We always allow suspend even if suspend preparation failed and
* attempt to recover in resume.
*/
if (ret)
bt_dev_err(hdev, "Suspend notifier action (%lu) failed: %d",
action, ret);
@ -3857,6 +3739,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
INIT_LIST_HEAD(&hdev->adv_instances);
INIT_LIST_HEAD(&hdev->blocked_keys);
INIT_LIST_HEAD(&hdev->local_codecs);
INIT_WORK(&hdev->rx_work, hci_rx_work);
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
INIT_WORK(&hdev->tx_work, hci_tx_work);
@ -3994,6 +3877,7 @@ int hci_register_dev(struct hci_dev *hdev)
queue_work(hdev->req_workqueue, &hdev->power_on);
idr_init(&hdev->adv_monitors_idr);
msft_register(hdev);
return id;
@ -4026,6 +3910,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
cancel_work_sync(&hdev->suspend_prepare);
}
msft_unregister(hdev);
hci_dev_do_close(hdev);
if (!test_bit(HCI_INIT, &hdev->flags) &&
@ -4088,16 +3974,78 @@ EXPORT_SYMBOL(hci_release_dev);
/* Suspend HCI device */
int hci_suspend_dev(struct hci_dev *hdev)
{
int ret;
u8 state = BT_RUNNING;
bt_dev_dbg(hdev, "");
/* Suspend should only act on when powered. */
if (!hdev_is_powered(hdev) ||
hci_dev_test_flag(hdev, HCI_UNREGISTER))
return 0;
/* If powering down, wait for completion. */
if (mgmt_powering_down(hdev)) {
set_bit(SUSPEND_POWERING_DOWN, hdev->suspend_tasks);
ret = hci_suspend_wait_event(hdev);
if (ret)
goto done;
}
/* Suspend consists of two actions:
* - First, disconnect everything and make the controller not
* connectable (disabling scanning)
* - Second, program event filter/accept list and enable scan
*/
ret = hci_change_suspend_state(hdev, BT_SUSPEND_DISCONNECT);
if (ret)
goto clear;
state = BT_SUSPEND_DISCONNECT;
/* Only configure accept list if device may wakeup. */
if (hdev->wakeup && hdev->wakeup(hdev)) {
ret = hci_change_suspend_state(hdev, BT_SUSPEND_CONFIGURE_WAKE);
if (!ret)
state = BT_SUSPEND_CONFIGURE_WAKE;
}
clear:
hci_clear_wake_reason(hdev);
mgmt_suspending(hdev, state);
done:
/* We always allow suspend even if suspend preparation failed and
* attempt to recover in resume.
*/
hci_sock_dev_event(hdev, HCI_DEV_SUSPEND);
return 0;
return ret;
}
EXPORT_SYMBOL(hci_suspend_dev);
/* Resume HCI device */
int hci_resume_dev(struct hci_dev *hdev)
{
int ret;
bt_dev_dbg(hdev, "");
/* Resume should only act on when powered. */
if (!hdev_is_powered(hdev) ||
hci_dev_test_flag(hdev, HCI_UNREGISTER))
return 0;
/* If powering down don't attempt to resume */
if (mgmt_powering_down(hdev))
return 0;
ret = hci_change_suspend_state(hdev, BT_RUNNING);
mgmt_resuming(hdev, hdev->wake_reason, &hdev->wake_addr,
hdev->wake_addr_type);
hci_sock_dev_event(hdev, HCI_DEV_RESUME);
return 0;
return ret;
}
EXPORT_SYMBOL(hci_resume_dev);

View file

@ -27,6 +27,7 @@
#include <net/bluetooth/hci_core.h>
#include "smp.h"
#include "hci_request.h"
#include "hci_debugfs.h"
#define DEFINE_QUIRK_ATTRIBUTE(__name, __quirk) \
@ -1250,3 +1251,125 @@ void hci_debugfs_create_conn(struct hci_conn *conn)
snprintf(name, sizeof(name), "%u", conn->handle);
conn->debugfs = debugfs_create_dir(name, hdev->debugfs);
}
static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct hci_dev *hdev = file->private_data;
char buf[3];
buf[0] = hci_dev_test_flag(hdev, HCI_DUT_MODE) ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct hci_dev *hdev = file->private_data;
struct sk_buff *skb;
bool enable;
int err;
if (!test_bit(HCI_UP, &hdev->flags))
return -ENETDOWN;
err = kstrtobool_from_user(user_buf, count, &enable);
if (err)
return err;
if (enable == hci_dev_test_flag(hdev, HCI_DUT_MODE))
return -EALREADY;
hci_req_sync_lock(hdev);
if (enable)
skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
HCI_CMD_TIMEOUT);
else
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
HCI_CMD_TIMEOUT);
hci_req_sync_unlock(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
kfree_skb(skb);
hci_dev_change_flag(hdev, HCI_DUT_MODE);
return count;
}
static const struct file_operations dut_mode_fops = {
.open = simple_open,
.read = dut_mode_read,
.write = dut_mode_write,
.llseek = default_llseek,
};
static ssize_t vendor_diag_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct hci_dev *hdev = file->private_data;
char buf[3];
buf[0] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t vendor_diag_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct hci_dev *hdev = file->private_data;
bool enable;
int err;
err = kstrtobool_from_user(user_buf, count, &enable);
if (err)
return err;
/* When the diagnostic flags are not persistent and the transport
* is not active or in user channel operation, then there is no need
* for the vendor callback. Instead just store the desired value and
* the setting will be programmed when the controller gets powered on.
*/
if (test_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks) &&
(!test_bit(HCI_RUNNING, &hdev->flags) ||
hci_dev_test_flag(hdev, HCI_USER_CHANNEL)))
goto done;
hci_req_sync_lock(hdev);
err = hdev->set_diag(hdev, enable);
hci_req_sync_unlock(hdev);
if (err < 0)
return err;
done:
if (enable)
hci_dev_set_flag(hdev, HCI_VENDOR_DIAG);
else
hci_dev_clear_flag(hdev, HCI_VENDOR_DIAG);
return count;
}
static const struct file_operations vendor_diag_fops = {
.open = simple_open,
.read = vendor_diag_read,
.write = vendor_diag_write,
.llseek = default_llseek,
};
void hci_debugfs_create_basic(struct hci_dev *hdev)
{
debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
&dut_mode_fops);
if (hdev->set_diag)
debugfs_create_file("vendor_diag", 0644, hdev->debugfs, hdev,
&vendor_diag_fops);
}

View file

@ -26,6 +26,7 @@ void hci_debugfs_create_common(struct hci_dev *hdev);
void hci_debugfs_create_bredr(struct hci_dev *hdev);
void hci_debugfs_create_le(struct hci_dev *hdev);
void hci_debugfs_create_conn(struct hci_conn *conn);
void hci_debugfs_create_basic(struct hci_dev *hdev);
#else
@ -45,4 +46,8 @@ static inline void hci_debugfs_create_conn(struct hci_conn *conn)
{
}
static inline void hci_debugfs_create_basic(struct hci_dev *hdev)
{
}
#endif

View file

@ -36,6 +36,7 @@
#include "amp.h"
#include "smp.h"
#include "msft.h"
#include "eir.h"
#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00"
@ -2278,6 +2279,41 @@ static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
hci_dev_unlock(hdev);
}
static void hci_cs_enhanced_setup_sync_conn(struct hci_dev *hdev, __u8 status)
{
struct hci_cp_enhanced_setup_sync_conn *cp;
struct hci_conn *acl, *sco;
__u16 handle;
bt_dev_dbg(hdev, "status 0x%2.2x", status);
if (!status)
return;
cp = hci_sent_cmd_data(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN);
if (!cp)
return;
handle = __le16_to_cpu(cp->handle);
bt_dev_dbg(hdev, "handle 0x%4.4x", handle);
hci_dev_lock(hdev);
acl = hci_conn_hash_lookup_handle(hdev, handle);
if (acl) {
sco = acl->link;
if (sco) {
sco->state = BT_CLOSED;
hci_connect_cfm(sco, status);
hci_conn_del(sco);
}
}
hci_dev_unlock(hdev);
}
static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)
{
struct hci_cp_sniff_mode *cp;
@ -2351,7 +2387,7 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
conn->dst_type, status);
if (conn->type == LE_LINK) {
if (conn->type == LE_LINK && conn->role == HCI_ROLE_SLAVE) {
hdev->cur_adv_instance = conn->adv_instance;
hci_req_reenable_advertising(hdev);
}
@ -2367,6 +2403,28 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
hci_dev_unlock(hdev);
}
static u8 ev_bdaddr_type(struct hci_dev *hdev, u8 type, bool *resolved)
{
/* When using controller based address resolution, then the new
* address types 0x02 and 0x03 are used. These types need to be
* converted back into either public address or random address type
*/
switch (type) {
case ADDR_LE_DEV_PUBLIC_RESOLVED:
if (resolved)
*resolved = true;
return ADDR_LE_DEV_PUBLIC;
case ADDR_LE_DEV_RANDOM_RESOLVED:
if (resolved)
*resolved = true;
return ADDR_LE_DEV_RANDOM;
}
if (resolved)
*resolved = false;
return type;
}
static void cs_le_create_conn(struct hci_dev *hdev, bdaddr_t *peer_addr,
u8 peer_addr_type, u8 own_address_type,
u8 filter_policy)
@ -2378,21 +2436,7 @@ static void cs_le_create_conn(struct hci_dev *hdev, bdaddr_t *peer_addr,
if (!conn)
return;
/* When using controller based address resolution, then the new
* address types 0x02 and 0x03 are used. These types need to be
* converted back into either public address or random address type
*/
if (use_ll_privacy(hdev) &&
hci_dev_test_flag(hdev, HCI_LL_RPA_RESOLUTION)) {
switch (own_address_type) {
case ADDR_LE_DEV_PUBLIC_RESOLVED:
own_address_type = ADDR_LE_DEV_PUBLIC;
break;
case ADDR_LE_DEV_RANDOM_RESOLVED:
own_address_type = ADDR_LE_DEV_RANDOM;
break;
}
}
own_address_type = ev_bdaddr_type(hdev, own_address_type, NULL);
/* Store the initiator and responder address information which
* is needed for SMP. These values will not change during the
@ -2961,7 +3005,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
* or until a connection is created or until the Advertising
* is timed out due to Directed Advertising."
*/
if (conn->type == LE_LINK) {
if (conn->type == LE_LINK && conn->role == HCI_ROLE_SLAVE) {
hdev->cur_adv_instance = conn->adv_instance;
hci_req_reenable_advertising(hdev);
}
@ -3756,6 +3800,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
hci_cs_setup_sync_conn(hdev, ev->status);
break;
case HCI_OP_ENHANCED_SETUP_SYNC_CONN:
hci_cs_enhanced_setup_sync_conn(hdev, ev->status);
break;
case HCI_OP_SNIFF_MODE:
hci_cs_sniff_mode(hdev, ev->status);
break;
@ -4397,6 +4445,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
{
struct hci_ev_sync_conn_complete *ev = (void *) skb->data;
struct hci_conn *conn;
unsigned int notify_evt;
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
@ -4471,15 +4520,21 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
switch (ev->air_mode) {
case 0x02:
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
notify_evt = HCI_NOTIFY_ENABLE_SCO_CVSD;
break;
case 0x03:
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_TRANSP);
notify_evt = HCI_NOTIFY_ENABLE_SCO_TRANSP;
break;
}
/* Notify only in case of SCO over HCI transport data path which
* is zero and non-zero value shall be non-HCI transport data path
*/
if (conn->codec.data_path == 0) {
if (hdev->notify)
hdev->notify(hdev, notify_evt);
}
hci_connect_cfm(conn, ev->status);
if (ev->status)
hci_conn_del(conn);
@ -5282,22 +5337,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
conn->dst_type = irk->addr_type;
}
/* When using controller based address resolution, then the new
* address types 0x02 and 0x03 are used. These types need to be
* converted back into either public address or random address type
*/
if (use_ll_privacy(hdev) &&
hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY) &&
hci_dev_test_flag(hdev, HCI_LL_RPA_RESOLUTION)) {
switch (conn->dst_type) {
case ADDR_LE_DEV_PUBLIC_RESOLVED:
conn->dst_type = ADDR_LE_DEV_PUBLIC;
break;
case ADDR_LE_DEV_RANDOM_RESOLVED:
conn->dst_type = ADDR_LE_DEV_RANDOM;
break;
}
}
conn->dst_type = ev_bdaddr_type(hdev, conn->dst_type, NULL);
if (status) {
hci_le_conn_failed(conn, status);
@ -5479,8 +5519,8 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev,
/* This function requires the caller holds hdev->lock */
static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
bdaddr_t *addr,
u8 addr_type, u8 adv_type,
bdaddr_t *direct_rpa)
u8 addr_type, bool addr_resolved,
u8 adv_type, bdaddr_t *direct_rpa)
{
struct hci_conn *conn;
struct hci_conn_params *params;
@ -5532,9 +5572,9 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
}
}
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
hdev->def_le_autoconnect_timeout, HCI_ROLE_MASTER,
direct_rpa);
conn = hci_connect_le(hdev, addr, addr_type, addr_resolved,
BT_SECURITY_LOW, hdev->def_le_autoconnect_timeout,
HCI_ROLE_MASTER, direct_rpa);
if (!IS_ERR(conn)) {
/* If HCI_AUTO_CONN_EXPLICIT is set, conn is already owned
* by higher layer that tried to connect, if no then
@ -5575,7 +5615,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
struct discovery_state *d = &hdev->discovery;
struct smp_irk *irk;
struct hci_conn *conn;
bool match;
bool match, bdaddr_resolved;
u32 flags;
u8 *ptr;
@ -5619,6 +5659,9 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
* controller address.
*/
if (direct_addr) {
direct_addr_type = ev_bdaddr_type(hdev, direct_addr_type,
&bdaddr_resolved);
/* Only resolvable random addresses are valid for these
* kind of reports and others can be ignored.
*/
@ -5646,13 +5689,15 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
bdaddr_type = irk->addr_type;
}
bdaddr_type = ev_bdaddr_type(hdev, bdaddr_type, &bdaddr_resolved);
/* Check if we have been requested to connect to this device.
*
* direct_addr is set only for directed advertising reports (it is NULL
* for advertising reports) and is already verified to be RPA above.
*/
conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type,
direct_addr);
conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, bdaddr_resolved,
type, direct_addr);
if (!ext_adv && conn && type == LE_ADV_IND && len <= HCI_MAX_AD_LENGTH) {
/* Store report for later inclusion by
* mgmt_device_connected

View file

@ -30,6 +30,7 @@
#include "smp.h"
#include "hci_request.h"
#include "msft.h"
#include "eir.h"
#define HCI_REQ_DONE 0
#define HCI_REQ_PEND 1
@ -521,164 +522,6 @@ void __hci_req_update_name(struct hci_request *req)
hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
}
#define PNP_INFO_SVCLASS_ID 0x1200
static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
{
u8 *ptr = data, *uuids_start = NULL;
struct bt_uuid *uuid;
if (len < 4)
return ptr;
list_for_each_entry(uuid, &hdev->uuids, list) {
u16 uuid16;
if (uuid->size != 16)
continue;
uuid16 = get_unaligned_le16(&uuid->uuid[12]);
if (uuid16 < 0x1100)
continue;
if (uuid16 == PNP_INFO_SVCLASS_ID)
continue;
if (!uuids_start) {
uuids_start = ptr;
uuids_start[0] = 1;
uuids_start[1] = EIR_UUID16_ALL;
ptr += 2;
}
/* Stop if not enough space to put next UUID */
if ((ptr - data) + sizeof(u16) > len) {
uuids_start[1] = EIR_UUID16_SOME;
break;
}
*ptr++ = (uuid16 & 0x00ff);
*ptr++ = (uuid16 & 0xff00) >> 8;
uuids_start[0] += sizeof(uuid16);
}
return ptr;
}
static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
{
u8 *ptr = data, *uuids_start = NULL;
struct bt_uuid *uuid;
if (len < 6)
return ptr;
list_for_each_entry(uuid, &hdev->uuids, list) {
if (uuid->size != 32)
continue;
if (!uuids_start) {
uuids_start = ptr;
uuids_start[0] = 1;
uuids_start[1] = EIR_UUID32_ALL;
ptr += 2;
}
/* Stop if not enough space to put next UUID */
if ((ptr - data) + sizeof(u32) > len) {
uuids_start[1] = EIR_UUID32_SOME;
break;
}
memcpy(ptr, &uuid->uuid[12], sizeof(u32));
ptr += sizeof(u32);
uuids_start[0] += sizeof(u32);
}
return ptr;
}
static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
{
u8 *ptr = data, *uuids_start = NULL;
struct bt_uuid *uuid;
if (len < 18)
return ptr;
list_for_each_entry(uuid, &hdev->uuids, list) {
if (uuid->size != 128)
continue;
if (!uuids_start) {
uuids_start = ptr;
uuids_start[0] = 1;
uuids_start[1] = EIR_UUID128_ALL;
ptr += 2;
}
/* Stop if not enough space to put next UUID */
if ((ptr - data) + 16 > len) {
uuids_start[1] = EIR_UUID128_SOME;
break;
}
memcpy(ptr, uuid->uuid, 16);
ptr += 16;
uuids_start[0] += 16;
}
return ptr;
}
static void create_eir(struct hci_dev *hdev, u8 *data)
{
u8 *ptr = data;
size_t name_len;
name_len = strlen(hdev->dev_name);
if (name_len > 0) {
/* EIR Data type */
if (name_len > 48) {
name_len = 48;
ptr[1] = EIR_NAME_SHORT;
} else
ptr[1] = EIR_NAME_COMPLETE;
/* EIR Data length */
ptr[0] = name_len + 1;
memcpy(ptr + 2, hdev->dev_name, name_len);
ptr += (name_len + 2);
}
if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) {
ptr[0] = 2;
ptr[1] = EIR_TX_POWER;
ptr[2] = (u8) hdev->inq_tx_power;
ptr += 3;
}
if (hdev->devid_source > 0) {
ptr[0] = 9;
ptr[1] = EIR_DEVICE_ID;
put_unaligned_le16(hdev->devid_source, ptr + 2);
put_unaligned_le16(hdev->devid_vendor, ptr + 4);
put_unaligned_le16(hdev->devid_product, ptr + 6);
put_unaligned_le16(hdev->devid_version, ptr + 8);
ptr += 10;
}
ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
}
void __hci_req_update_eir(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
@ -698,7 +541,7 @@ void __hci_req_update_eir(struct hci_request *req)
memset(&cp, 0, sizeof(cp));
create_eir(hdev, cp.data);
eir_create(hdev, cp.data);
if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
return;
@ -1134,25 +977,6 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
addr_resolv);
}
static bool adv_instance_is_scannable(struct hci_dev *hdev, u8 instance)
{
struct adv_info *adv_instance;
/* Instance 0x00 always set local name */
if (instance == 0x00)
return true;
adv_instance = hci_find_adv_instance(hdev, instance);
if (!adv_instance)
return false;
if (adv_instance->flags & MGMT_ADV_FLAG_APPEARANCE ||
adv_instance->flags & MGMT_ADV_FLAG_LOCAL_NAME)
return true;
return adv_instance->scan_rsp_len ? true : false;
}
static void hci_req_clear_event_filter(struct hci_request *req)
{
struct hci_cp_set_event_filter f;
@ -1281,21 +1105,24 @@ static void suspend_req_complete(struct hci_dev *hdev, u8 status, u16 opcode)
}
}
static void hci_req_add_set_adv_filter_enable(struct hci_request *req,
bool enable)
static void hci_req_prepare_adv_monitor_suspend(struct hci_request *req,
bool suspending)
{
struct hci_dev *hdev = req->hdev;
switch (hci_get_adv_monitor_offload_ext(hdev)) {
case HCI_ADV_MONITOR_EXT_MSFT:
msft_req_add_set_filter_enable(req, enable);
if (suspending)
msft_suspend(hdev);
else
msft_resume(hdev);
break;
default:
return;
}
/* No need to block when enabling since it's on resume path */
if (hdev->suspended && !enable)
if (hdev->suspended && suspending)
set_bit(SUSPEND_SET_ADV_FILTER, hdev->suspend_tasks);
}
@ -1362,7 +1189,7 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
}
/* Disable advertisement filters */
hci_req_add_set_adv_filter_enable(&req, false);
hci_req_prepare_adv_monitor_suspend(&req, true);
/* Prevent disconnects from causing scanning to be re-enabled */
hdev->scanning_paused = true;
@ -1404,7 +1231,7 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
/* Reset passive/background scanning to normal */
__hci_update_background_scan(&req);
/* Enable all of the advertisement filters */
hci_req_add_set_adv_filter_enable(&req, true);
hci_req_prepare_adv_monitor_suspend(&req, false);
/* Unpause directed advertising */
hdev->advertising_paused = false;
@ -1442,7 +1269,7 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next)
static bool adv_cur_instance_is_scannable(struct hci_dev *hdev)
{
return adv_instance_is_scannable(hdev, hdev->cur_adv_instance);
return hci_adv_instance_is_scannable(hdev, hdev->cur_adv_instance);
}
void __hci_req_disable_advertising(struct hci_request *req)
@ -1457,40 +1284,6 @@ void __hci_req_disable_advertising(struct hci_request *req)
}
}
static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
{
u32 flags;
struct adv_info *adv_instance;
if (instance == 0x00) {
/* Instance 0 always manages the "Tx Power" and "Flags"
* fields
*/
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
/* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting
* corresponds to the "connectable" instance flag.
*/
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
flags |= MGMT_ADV_FLAG_CONNECTABLE;
if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
else if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
flags |= MGMT_ADV_FLAG_DISCOV;
return flags;
}
adv_instance = hci_find_adv_instance(hdev, instance);
/* Return 0 when we got an invalid instance identifier. */
if (!adv_instance)
return 0;
return adv_instance->flags;
}
static bool adv_use_rpa(struct hci_dev *hdev, uint32_t flags)
{
/* If privacy is not enabled don't use RPA */
@ -1555,15 +1348,15 @@ static bool is_advertising_allowed(struct hci_dev *hdev, bool connectable)
void __hci_req_enable_advertising(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
struct adv_info *adv_instance;
struct adv_info *adv;
struct hci_cp_le_set_adv_param cp;
u8 own_addr_type, enable = 0x01;
bool connectable;
u16 adv_min_interval, adv_max_interval;
u32 flags;
flags = get_adv_instance_flags(hdev, hdev->cur_adv_instance);
adv_instance = hci_find_adv_instance(hdev, hdev->cur_adv_instance);
flags = hci_adv_instance_flags(hdev, hdev->cur_adv_instance);
adv = hci_find_adv_instance(hdev, hdev->cur_adv_instance);
/* If the "connectable" instance flag was not set, then choose between
* ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
@ -1595,9 +1388,9 @@ void __hci_req_enable_advertising(struct hci_request *req)
memset(&cp, 0, sizeof(cp));
if (adv_instance) {
adv_min_interval = adv_instance->min_interval;
adv_max_interval = adv_instance->max_interval;
if (adv) {
adv_min_interval = adv->min_interval;
adv_max_interval = adv->max_interval;
} else {
adv_min_interval = hdev->le_adv_min_interval;
adv_max_interval = hdev->le_adv_max_interval;
@ -1628,85 +1421,6 @@ void __hci_req_enable_advertising(struct hci_request *req)
hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
}
u8 append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
{
size_t short_len;
size_t complete_len;
/* no space left for name (+ NULL + type + len) */
if ((HCI_MAX_AD_LENGTH - ad_len) < HCI_MAX_SHORT_NAME_LENGTH + 3)
return ad_len;
/* use complete name if present and fits */
complete_len = strlen(hdev->dev_name);
if (complete_len && complete_len <= HCI_MAX_SHORT_NAME_LENGTH)
return eir_append_data(ptr, ad_len, EIR_NAME_COMPLETE,
hdev->dev_name, complete_len + 1);
/* use short name if present */
short_len = strlen(hdev->short_name);
if (short_len)
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT,
hdev->short_name, short_len + 1);
/* use shortened full name if present, we already know that name
* is longer then HCI_MAX_SHORT_NAME_LENGTH
*/
if (complete_len) {
u8 name[HCI_MAX_SHORT_NAME_LENGTH + 1];
memcpy(name, hdev->dev_name, HCI_MAX_SHORT_NAME_LENGTH);
name[HCI_MAX_SHORT_NAME_LENGTH] = '\0';
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT, name,
sizeof(name));
}
return ad_len;
}
static u8 append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
{
return eir_append_le16(ptr, ad_len, EIR_APPEARANCE, hdev->appearance);
}
static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
{
u8 scan_rsp_len = 0;
if (hdev->appearance)
scan_rsp_len = append_appearance(hdev, ptr, scan_rsp_len);
return append_local_name(hdev, ptr, scan_rsp_len);
}
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;
instance_flags = adv_instance->flags;
if ((instance_flags & MGMT_ADV_FLAG_APPEARANCE) && hdev->appearance)
scan_rsp_len = append_appearance(hdev, ptr, scan_rsp_len);
memcpy(&ptr[scan_rsp_len], adv_instance->scan_rsp_data,
adv_instance->scan_rsp_len);
scan_rsp_len += 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)
{
struct hci_dev *hdev = req->hdev;
@ -1723,11 +1437,7 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
memset(&pdu, 0, sizeof(pdu));
if (instance)
len = create_instance_scan_rsp_data(hdev, instance,
pdu.data);
else
len = create_default_scan_rsp_data(hdev, pdu.data);
len = eir_create_scan_rsp(hdev, instance, pdu.data);
if (hdev->scan_rsp_data_len == len &&
!memcmp(pdu.data, hdev->scan_rsp_data, len))
@ -1748,11 +1458,7 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
memset(&cp, 0, sizeof(cp));
if (instance)
len = create_instance_scan_rsp_data(hdev, instance,
cp.data);
else
len = create_default_scan_rsp_data(hdev, cp.data);
len = eir_create_scan_rsp(hdev, instance, cp.data);
if (hdev->scan_rsp_data_len == len &&
!memcmp(cp.data, hdev->scan_rsp_data, len))
@ -1767,95 +1473,6 @@ void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
}
}
static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
{
struct adv_info *adv_instance = NULL;
u8 ad_len = 0, flags = 0;
u32 instance_flags;
/* Return 0 when the current instance identifier is invalid. */
if (instance) {
adv_instance = hci_find_adv_instance(hdev, instance);
if (!adv_instance)
return 0;
}
instance_flags = get_adv_instance_flags(hdev, instance);
/* If instance already has the flags set skip adding it once
* again.
*/
if (adv_instance && eir_get_data(adv_instance->adv_data,
adv_instance->adv_data_len, EIR_FLAGS,
NULL))
goto skip_flags;
/* The Add Advertising command allows userspace to set both the general
* and limited discoverable flags.
*/
if (instance_flags & MGMT_ADV_FLAG_DISCOV)
flags |= LE_AD_GENERAL;
if (instance_flags & MGMT_ADV_FLAG_LIMITED_DISCOV)
flags |= LE_AD_LIMITED;
if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
flags |= LE_AD_NO_BREDR;
if (flags || (instance_flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) {
/* If a discovery flag wasn't provided, simply use the global
* settings.
*/
if (!flags)
flags |= mgmt_get_adv_discov_flags(hdev);
/* If flags would still be empty, then there is no need to
* include the "Flags" AD field".
*/
if (flags) {
ptr[0] = 0x02;
ptr[1] = EIR_FLAGS;
ptr[2] = flags;
ad_len += 3;
ptr += 3;
}
}
skip_flags:
if (adv_instance) {
memcpy(ptr, adv_instance->adv_data,
adv_instance->adv_data_len);
ad_len += adv_instance->adv_data_len;
ptr += adv_instance->adv_data_len;
}
if (instance_flags & MGMT_ADV_FLAG_TX_POWER) {
s8 adv_tx_power;
if (ext_adv_capable(hdev)) {
if (adv_instance)
adv_tx_power = adv_instance->tx_power;
else
adv_tx_power = hdev->adv_tx_power;
} else {
adv_tx_power = hdev->adv_tx_power;
}
/* Provide Tx Power only if we can provide a valid value for it */
if (adv_tx_power != HCI_TX_POWER_INVALID) {
ptr[0] = 0x02;
ptr[1] = EIR_TX_POWER;
ptr[2] = (u8)adv_tx_power;
ad_len += 3;
ptr += 3;
}
}
return ad_len;
}
void __hci_req_update_adv_data(struct hci_request *req, u8 instance)
{
struct hci_dev *hdev = req->hdev;
@ -1872,7 +1489,7 @@ void __hci_req_update_adv_data(struct hci_request *req, u8 instance)
memset(&pdu, 0, sizeof(pdu));
len = create_instance_adv_data(hdev, instance, pdu.data);
len = eir_create_adv_data(hdev, instance, pdu.data);
/* There's nothing to do if the data hasn't changed */
if (hdev->adv_data_len == len &&
@ -1894,7 +1511,7 @@ void __hci_req_update_adv_data(struct hci_request *req, u8 instance)
memset(&cp, 0, sizeof(cp));
len = create_instance_adv_data(hdev, instance, cp.data);
len = eir_create_adv_data(hdev, instance, cp.data);
/* There's nothing to do if the data hasn't changed */
if (hdev->adv_data_len == len &&
@ -2183,7 +1800,7 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
adv_instance = NULL;
}
flags = get_adv_instance_flags(hdev, instance);
flags = hci_adv_instance_flags(hdev, instance);
/* If the "connectable" instance flag was not set, then choose between
* ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
@ -2223,7 +1840,7 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
cp.evt_properties = cpu_to_le16(LE_EXT_ADV_CONN_IND);
else
cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_IND);
} else if (adv_instance_is_scannable(hdev, instance) ||
} else if (hci_adv_instance_is_scannable(hdev, instance) ||
(flags & MGMT_ADV_PARAM_SCAN_RSP)) {
if (secondary_adv)
cp.evt_properties = cpu_to_le16(LE_EXT_ADV_SCAN_IND);
@ -3327,6 +2944,53 @@ bool hci_req_stop_discovery(struct hci_request *req)
return ret;
}
static void config_data_path_complete(struct hci_dev *hdev, u8 status,
u16 opcode)
{
bt_dev_dbg(hdev, "status %u", status);
}
int hci_req_configure_datapath(struct hci_dev *hdev, struct bt_codec *codec)
{
struct hci_request req;
int err;
__u8 vnd_len, *vnd_data = NULL;
struct hci_op_configure_data_path *cmd = NULL;
hci_req_init(&req, hdev);
err = hdev->get_codec_config_data(hdev, ESCO_LINK, codec, &vnd_len,
&vnd_data);
if (err < 0)
goto error;
cmd = kzalloc(sizeof(*cmd) + vnd_len, GFP_KERNEL);
if (!cmd) {
err = -ENOMEM;
goto error;
}
err = hdev->get_data_path_id(hdev, &cmd->data_path_id);
if (err < 0)
goto error;
cmd->vnd_len = vnd_len;
memcpy(cmd->vnd_data, vnd_data, vnd_len);
cmd->direction = 0x00;
hci_req_add(&req, HCI_CONFIGURE_DATA_PATH, sizeof(*cmd) + vnd_len, cmd);
cmd->direction = 0x01;
hci_req_add(&req, HCI_CONFIGURE_DATA_PATH, sizeof(*cmd) + vnd_len, cmd);
err = hci_req_run(&req, config_data_path_complete);
error:
kfree(cmd);
kfree(vnd_data);
return err;
}
static int stop_discovery(struct hci_request *req, unsigned long opt)
{
hci_dev_lock(req->hdev);

View file

@ -101,6 +101,8 @@ void __hci_req_update_class(struct hci_request *req);
/* Returns true if HCI commands were queued */
bool hci_req_stop_discovery(struct hci_request *req);
int hci_req_configure_datapath(struct hci_dev *hdev, struct bt_codec *codec);
static inline void hci_req_update_scan(struct hci_dev *hdev)
{
queue_work(hdev->req_workqueue, &hdev->scan_update);
@ -122,26 +124,3 @@ static inline void hci_update_background_scan(struct hci_dev *hdev)
void hci_request_setup(struct hci_dev *hdev);
void hci_request_cancel_all(struct hci_dev *hdev);
u8 append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len);
static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type,
u8 *data, u8 data_len)
{
eir[eir_len++] = sizeof(type) + data_len;
eir[eir_len++] = type;
memcpy(&eir[eir_len], data, data_len);
eir_len += data_len;
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;
}

View file

@ -57,6 +57,7 @@ struct hci_pinfo {
unsigned long flags;
__u32 cookie;
char comm[TASK_COMM_LEN];
__u16 mtu;
};
static struct hci_dev *hci_hdev_from_sock(struct sock *sk)
@ -1374,6 +1375,10 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
break;
}
/* Default MTU to HCI_MAX_FRAME_SIZE if not set */
if (!hci_pi(sk)->mtu)
hci_pi(sk)->mtu = HCI_MAX_FRAME_SIZE;
sk->sk_state = BT_BOUND;
done:
@ -1506,9 +1511,8 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg,
}
static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk,
struct msghdr *msg, size_t msglen)
struct sk_buff *skb)
{
void *buf;
u8 *cp;
struct mgmt_hdr *hdr;
u16 opcode, index, len;
@ -1517,40 +1521,31 @@ static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk,
bool var_len, no_hdev;
int err;
BT_DBG("got %zu bytes", msglen);
BT_DBG("got %d bytes", skb->len);
if (msglen < sizeof(*hdr))
if (skb->len < sizeof(*hdr))
return -EINVAL;
buf = kmalloc(msglen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (memcpy_from_msg(buf, msg, msglen)) {
err = -EFAULT;
goto done;
}
hdr = buf;
hdr = (void *)skb->data;
opcode = __le16_to_cpu(hdr->opcode);
index = __le16_to_cpu(hdr->index);
len = __le16_to_cpu(hdr->len);
if (len != msglen - sizeof(*hdr)) {
if (len != skb->len - sizeof(*hdr)) {
err = -EINVAL;
goto done;
}
if (chan->channel == HCI_CHANNEL_CONTROL) {
struct sk_buff *skb;
struct sk_buff *cmd;
/* 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,
cmd = create_monitor_ctrl_command(sk, index, opcode, len,
skb->data + sizeof(*hdr));
if (cmd) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, cmd,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
kfree_skb(cmd);
}
}
@ -1615,26 +1610,25 @@ static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk,
if (hdev && chan->hdev_init)
chan->hdev_init(sk, hdev);
cp = buf + sizeof(*hdr);
cp = skb->data + sizeof(*hdr);
err = handler->func(sk, hdev, cp, len);
if (err < 0)
goto done;
err = msglen;
err = skb->len;
done:
if (hdev)
hci_dev_put(hdev);
kfree(buf);
return err;
}
static int hci_logging_frame(struct sock *sk, struct msghdr *msg, int len)
static int hci_logging_frame(struct sock *sk, struct sk_buff *skb,
unsigned int flags)
{
struct hci_mon_hdr *hdr;
struct sk_buff *skb;
struct hci_dev *hdev;
u16 index;
int err;
@ -1643,24 +1637,13 @@ static int hci_logging_frame(struct sock *sk, struct msghdr *msg, int len)
* the priority byte, the ident length byte and at least one string
* terminator NUL byte. Anything shorter are invalid packets.
*/
if (len < sizeof(*hdr) + 3)
if (skb->len < sizeof(*hdr) + 3)
return -EINVAL;
skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
return err;
if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
goto drop;
}
hdr = (void *)skb->data;
if (__le16_to_cpu(hdr->len) != len - sizeof(*hdr)) {
err = -EINVAL;
goto drop;
}
if (__le16_to_cpu(hdr->len) != skb->len - sizeof(*hdr))
return -EINVAL;
if (__le16_to_cpu(hdr->opcode) == 0x0000) {
__u8 priority = skb->data[sizeof(*hdr)];
@ -1679,25 +1662,20 @@ static int hci_logging_frame(struct sock *sk, struct msghdr *msg, int len)
* The message follows the ident string (if present) and
* must be NUL terminated. Otherwise it is not a valid packet.
*/
if (priority > 7 || skb->data[len - 1] != 0x00 ||
ident_len > len - sizeof(*hdr) - 3 ||
skb->data[sizeof(*hdr) + ident_len + 1] != 0x00) {
err = -EINVAL;
goto drop;
}
if (priority > 7 || skb->data[skb->len - 1] != 0x00 ||
ident_len > skb->len - sizeof(*hdr) - 3 ||
skb->data[sizeof(*hdr) + ident_len + 1] != 0x00)
return -EINVAL;
} else {
err = -EINVAL;
goto drop;
return -EINVAL;
}
index = __le16_to_cpu(hdr->index);
if (index != MGMT_INDEX_NONE) {
hdev = hci_dev_get(index);
if (!hdev) {
err = -ENODEV;
goto drop;
}
if (!hdev)
return -ENODEV;
} else {
hdev = NULL;
}
@ -1705,13 +1683,11 @@ static int hci_logging_frame(struct sock *sk, struct msghdr *msg, int len)
hdr->opcode = cpu_to_le16(HCI_MON_USER_LOGGING);
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, HCI_SOCK_TRUSTED, NULL);
err = len;
err = skb->len;
if (hdev)
hci_dev_put(hdev);
drop:
kfree_skb(skb);
return err;
}
@ -1723,19 +1699,23 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
struct hci_dev *hdev;
struct sk_buff *skb;
int err;
const unsigned int flags = msg->msg_flags;
BT_DBG("sock %p sk %p", sock, sk);
if (msg->msg_flags & MSG_OOB)
if (flags & MSG_OOB)
return -EOPNOTSUPP;
if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE|
MSG_CMSG_COMPAT))
if (flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE | MSG_CMSG_COMPAT))
return -EINVAL;
if (len < 4 || len > HCI_MAX_FRAME_SIZE)
if (len < 4 || len > hci_pi(sk)->mtu)
return -EINVAL;
skb = bt_skb_sendmsg(sk, msg, len, len, 0, 0);
if (IS_ERR(skb))
return PTR_ERR(skb);
lock_sock(sk);
switch (hci_pi(sk)->channel) {
@ -1744,39 +1724,30 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
break;
case HCI_CHANNEL_MONITOR:
err = -EOPNOTSUPP;
goto done;
goto drop;
case HCI_CHANNEL_LOGGING:
err = hci_logging_frame(sk, msg, len);
goto done;
err = hci_logging_frame(sk, skb, flags);
goto drop;
default:
mutex_lock(&mgmt_chan_list_lock);
chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
if (chan)
err = hci_mgmt_cmd(chan, sk, msg, len);
err = hci_mgmt_cmd(chan, sk, skb);
else
err = -EINVAL;
mutex_unlock(&mgmt_chan_list_lock);
goto done;
goto drop;
}
hdev = hci_hdev_from_sock(sk);
if (IS_ERR(hdev)) {
err = PTR_ERR(hdev);
goto done;
goto drop;
}
if (!test_bit(HCI_UP, &hdev->flags)) {
err = -ENETDOWN;
goto done;
}
skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
goto done;
if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
goto drop;
}
@ -1857,8 +1828,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
goto done;
}
static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int len)
static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int len)
{
struct hci_ufilter uf = { .opcode = 0 };
struct sock *sk = sock->sk;
@ -1866,9 +1837,6 @@ 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) {
@ -1943,8 +1911,56 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
return err;
}
static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int len)
{
struct sock *sk = sock->sk;
int err = 0, opt = 0;
BT_DBG("sk %p, opt %d", sk, optname);
if (level == SOL_HCI)
return hci_sock_setsockopt_old(sock, level, optname, optval,
len);
if (level != SOL_BLUETOOTH)
return -ENOPROTOOPT;
lock_sock(sk);
switch (optname) {
case BT_SNDMTU:
case BT_RCVMTU:
switch (hci_pi(sk)->channel) {
/* Don't allow changing MTU for channels that are meant for HCI
* traffic only.
*/
case HCI_CHANNEL_RAW:
case HCI_CHANNEL_USER:
err = -ENOPROTOOPT;
goto done;
}
if (copy_from_sockptr(&opt, optval, sizeof(u16))) {
err = -EFAULT;
break;
}
hci_pi(sk)->mtu = opt;
break;
default:
err = -ENOPROTOOPT;
break;
}
done:
release_sock(sk);
return err;
}
static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
struct hci_ufilter uf;
struct sock *sk = sock->sk;
@ -1952,9 +1968,6 @@ 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;
@ -2012,6 +2025,39 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
return err;
}
static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
int err = 0;
BT_DBG("sk %p, opt %d", sk, optname);
if (level == SOL_HCI)
return hci_sock_getsockopt_old(sock, level, optname, optval,
optlen);
if (level != SOL_BLUETOOTH)
return -ENOPROTOOPT;
lock_sock(sk);
switch (optname) {
case BT_SNDMTU:
case BT_RCVMTU:
if (put_user(hci_pi(sk)->mtu, (u16 __user *)optval))
err = -EFAULT;
break;
default:
err = -ENOPROTOOPT;
break;
}
release_sock(sk);
return err;
}
static const struct proto_ops hci_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,

View file

@ -7902,7 +7902,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
dst_type = ADDR_LE_DEV_RANDOM;
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
hcon = hci_connect_le(hdev, dst, dst_type,
hcon = hci_connect_le(hdev, dst, dst_type, false,
chan->sec_level,
HCI_LE_CONN_TIMEOUT,
HCI_ROLE_SLAVE, NULL);

View file

@ -1508,6 +1508,9 @@ static void l2cap_sock_close_cb(struct l2cap_chan *chan)
{
struct sock *sk = chan->data;
if (!sk)
return;
l2cap_sock_kill(sk);
}
@ -1516,6 +1519,9 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
struct sock *sk = chan->data;
struct sock *parent;
if (!sk)
return;
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
/* This callback can be called both for server (BT_LISTEN)
@ -1707,8 +1713,10 @@ static void l2cap_sock_destruct(struct sock *sk)
{
BT_DBG("sk %p", sk);
if (l2cap_pi(sk)->chan)
if (l2cap_pi(sk)->chan) {
l2cap_pi(sk)->chan->data = NULL;
l2cap_chan_put(l2cap_pi(sk)->chan);
}
if (l2cap_pi(sk)->rx_busy_skb) {
kfree_skb(l2cap_pi(sk)->rx_busy_skb);

View file

@ -38,6 +38,7 @@
#include "mgmt_util.h"
#include "mgmt_config.h"
#include "msft.h"
#include "eir.h"
#define MGMT_VERSION 1
#define MGMT_REVISION 21
@ -3791,6 +3792,18 @@ static const u8 debug_uuid[16] = {
};
#endif
/* 330859bc-7506-492d-9370-9a6f0614037f */
static const u8 quality_report_uuid[16] = {
0x7f, 0x03, 0x14, 0x06, 0x6f, 0x9a, 0x70, 0x93,
0x2d, 0x49, 0x06, 0x75, 0xbc, 0x59, 0x08, 0x33,
};
/* a6695ace-ee7f-4fb9-881a-5fac66c629af */
static const u8 offload_codecs_uuid[16] = {
0xaf, 0x29, 0xc6, 0x66, 0xac, 0x5f, 0x1a, 0x88,
0xb9, 0x4f, 0x7f, 0xee, 0xce, 0x5a, 0x69, 0xa6,
};
/* 671b10b5-42c0-4696-9227-eb28d1b049d6 */
static const u8 simult_central_periph_uuid[16] = {
0xd6, 0x49, 0xb0, 0xd1, 0x28, 0xeb, 0x27, 0x92,
@ -3806,7 +3819,7 @@ static const u8 rpa_resolution_uuid[16] = {
static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
char buf[62]; /* Enough space for 3 features */
char buf[102]; /* Enough space for 5 features: 2 + 20 * 5 */
struct mgmt_rp_read_exp_features_info *rp = (void *)buf;
u16 idx = 0;
u32 flags;
@ -3850,6 +3863,28 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
idx++;
}
if (hdev && hdev->set_quality_report) {
if (hci_dev_test_flag(hdev, HCI_QUALITY_REPORT))
flags = BIT(0);
else
flags = 0;
memcpy(rp->features[idx].uuid, quality_report_uuid, 16);
rp->features[idx].flags = cpu_to_le32(flags);
idx++;
}
if (hdev && hdev->get_data_path_id) {
if (hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED))
flags = BIT(0);
else
flags = 0;
memcpy(rp->features[idx].uuid, offload_codecs_uuid, 16);
rp->features[idx].flags = cpu_to_le32(flags);
idx++;
}
rp->feature_count = cpu_to_le16(idx);
/* After reading the experimental features information, enable
@ -3892,150 +3927,341 @@ static int exp_debug_feature_changed(bool enabled, struct sock *skip)
}
#endif
static int exp_quality_report_feature_changed(bool enabled, struct sock *skip)
{
struct mgmt_ev_exp_feature_changed ev;
memset(&ev, 0, sizeof(ev));
memcpy(ev.uuid, quality_report_uuid, 16);
ev.flags = cpu_to_le32(enabled ? BIT(0) : 0);
return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, NULL,
&ev, sizeof(ev),
HCI_MGMT_EXP_FEATURE_EVENTS, skip);
}
#define EXP_FEAT(_uuid, _set_func) \
{ \
.uuid = _uuid, \
.set_func = _set_func, \
}
/* The zero key uuid is special. Multiple exp features are set through it. */
static int set_zero_key_func(struct sock *sk, struct hci_dev *hdev,
struct mgmt_cp_set_exp_feature *cp, u16 data_len)
{
struct mgmt_rp_set_exp_feature rp;
memset(rp.uuid, 0, 16);
rp.flags = cpu_to_le32(0);
#ifdef CONFIG_BT_FEATURE_DEBUG
if (!hdev) {
bool changed = bt_dbg_get();
bt_dbg_set(false);
if (changed)
exp_debug_feature_changed(false, sk);
}
#endif
if (hdev && use_ll_privacy(hdev) && !hdev_is_powered(hdev)) {
bool changed = hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY);
hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY);
if (changed)
exp_ll_privacy_feature_changed(false, hdev, sk);
}
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
return mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE, 0,
&rp, sizeof(rp));
}
#ifdef CONFIG_BT_FEATURE_DEBUG
static int set_debug_func(struct sock *sk, struct hci_dev *hdev,
struct mgmt_cp_set_exp_feature *cp, u16 data_len)
{
struct mgmt_rp_set_exp_feature rp;
bool val, changed;
int err;
/* Command requires to use the non-controller index */
if (hdev)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_INDEX);
/* Parameters are limited to a single octet */
if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
/* Only boolean on/off is supported */
if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
val = !!cp->param[0];
changed = val ? !bt_dbg_get() : bt_dbg_get();
bt_dbg_set(val);
memcpy(rp.uuid, debug_uuid, 16);
rp.flags = cpu_to_le32(val ? BIT(0) : 0);
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE, 0,
&rp, sizeof(rp));
if (changed)
exp_debug_feature_changed(val, sk);
return err;
}
#endif
static int set_rpa_resolution_func(struct sock *sk, struct hci_dev *hdev,
struct mgmt_cp_set_exp_feature *cp,
u16 data_len)
{
struct mgmt_rp_set_exp_feature rp;
bool val, changed;
int err;
u32 flags;
/* Command requires to use the controller index */
if (!hdev)
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_INDEX);
/* Changes can only be made when controller is powered down */
if (hdev_is_powered(hdev))
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_REJECTED);
/* Parameters are limited to a single octet */
if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
/* Only boolean on/off is supported */
if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
val = !!cp->param[0];
if (val) {
changed = !hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY);
hci_dev_set_flag(hdev, HCI_ENABLE_LL_PRIVACY);
hci_dev_clear_flag(hdev, HCI_ADVERTISING);
/* Enable LL privacy + supported settings changed */
flags = BIT(0) | BIT(1);
} else {
changed = hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY);
hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY);
/* Disable LL privacy + supported settings changed */
flags = BIT(1);
}
memcpy(rp.uuid, rpa_resolution_uuid, 16);
rp.flags = cpu_to_le32(flags);
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
err = mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE, 0,
&rp, sizeof(rp));
if (changed)
exp_ll_privacy_feature_changed(val, hdev, sk);
return err;
}
static int set_quality_report_func(struct sock *sk, struct hci_dev *hdev,
struct mgmt_cp_set_exp_feature *cp,
u16 data_len)
{
struct mgmt_rp_set_exp_feature rp;
bool val, changed;
int err;
/* Command requires to use a valid controller index */
if (!hdev)
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_INDEX);
/* Parameters are limited to a single octet */
if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
/* Only boolean on/off is supported */
if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
hci_req_sync_lock(hdev);
val = !!cp->param[0];
changed = (val != hci_dev_test_flag(hdev, HCI_QUALITY_REPORT));
if (!hdev->set_quality_report) {
err = mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_NOT_SUPPORTED);
goto unlock_quality_report;
}
if (changed) {
err = hdev->set_quality_report(hdev, val);
if (err) {
err = mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_FAILED);
goto unlock_quality_report;
}
if (val)
hci_dev_set_flag(hdev, HCI_QUALITY_REPORT);
else
hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT);
}
bt_dev_dbg(hdev, "quality report enable %d changed %d", val, changed);
memcpy(rp.uuid, quality_report_uuid, 16);
rp.flags = cpu_to_le32(val ? BIT(0) : 0);
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
err = mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE, 0,
&rp, sizeof(rp));
if (changed)
exp_quality_report_feature_changed(val, sk);
unlock_quality_report:
hci_req_sync_unlock(hdev);
return err;
}
static int exp_offload_codec_feature_changed(bool enabled, struct sock *skip)
{
struct mgmt_ev_exp_feature_changed ev;
memset(&ev, 0, sizeof(ev));
memcpy(ev.uuid, offload_codecs_uuid, 16);
ev.flags = cpu_to_le32(enabled ? BIT(0) : 0);
return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, NULL,
&ev, sizeof(ev),
HCI_MGMT_EXP_FEATURE_EVENTS, skip);
}
static int set_offload_codec_func(struct sock *sk, struct hci_dev *hdev,
struct mgmt_cp_set_exp_feature *cp,
u16 data_len)
{
bool val, changed;
int err;
struct mgmt_rp_set_exp_feature rp;
/* Command requires to use a valid controller index */
if (!hdev)
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_INDEX);
/* Parameters are limited to a single octet */
if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
/* Only boolean on/off is supported */
if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
val = !!cp->param[0];
changed = (val != hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED));
if (!hdev->get_data_path_id) {
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_NOT_SUPPORTED);
}
if (changed) {
if (val)
hci_dev_set_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED);
else
hci_dev_clear_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED);
}
bt_dev_info(hdev, "offload codecs enable %d changed %d",
val, changed);
memcpy(rp.uuid, offload_codecs_uuid, 16);
rp.flags = cpu_to_le32(val ? BIT(0) : 0);
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
err = mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE, 0,
&rp, sizeof(rp));
if (changed)
exp_offload_codec_feature_changed(val, sk);
return err;
}
static const struct mgmt_exp_feature {
const u8 *uuid;
int (*set_func)(struct sock *sk, struct hci_dev *hdev,
struct mgmt_cp_set_exp_feature *cp, u16 data_len);
} exp_features[] = {
EXP_FEAT(ZERO_KEY, set_zero_key_func),
#ifdef CONFIG_BT_FEATURE_DEBUG
EXP_FEAT(debug_uuid, set_debug_func),
#endif
EXP_FEAT(rpa_resolution_uuid, set_rpa_resolution_func),
EXP_FEAT(quality_report_uuid, set_quality_report_func),
EXP_FEAT(offload_codecs_uuid, set_offload_codec_func),
/* end with a null feature */
EXP_FEAT(NULL, NULL)
};
static int set_exp_feature(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
struct mgmt_cp_set_exp_feature *cp = data;
struct mgmt_rp_set_exp_feature rp;
size_t i = 0;
bt_dev_dbg(hdev, "sock %p", sk);
if (!memcmp(cp->uuid, ZERO_KEY, 16)) {
memset(rp.uuid, 0, 16);
rp.flags = cpu_to_le32(0);
#ifdef CONFIG_BT_FEATURE_DEBUG
if (!hdev) {
bool changed = bt_dbg_get();
bt_dbg_set(false);
if (changed)
exp_debug_feature_changed(false, sk);
}
#endif
if (hdev && use_ll_privacy(hdev) && !hdev_is_powered(hdev)) {
bool changed = hci_dev_test_flag(hdev,
HCI_ENABLE_LL_PRIVACY);
hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY);
if (changed)
exp_ll_privacy_feature_changed(false, hdev, sk);
}
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
return mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE, 0,
&rp, sizeof(rp));
}
#ifdef CONFIG_BT_FEATURE_DEBUG
if (!memcmp(cp->uuid, debug_uuid, 16)) {
bool val, changed;
int err;
/* Command requires to use the non-controller index */
if (hdev)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_INDEX);
/* Parameters are limited to a single octet */
if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
/* Only boolean on/off is supported */
if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
val = !!cp->param[0];
changed = val ? !bt_dbg_get() : bt_dbg_get();
bt_dbg_set(val);
memcpy(rp.uuid, debug_uuid, 16);
rp.flags = cpu_to_le32(val ? BIT(0) : 0);
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE, 0,
&rp, sizeof(rp));
if (changed)
exp_debug_feature_changed(val, sk);
return err;
}
#endif
if (!memcmp(cp->uuid, rpa_resolution_uuid, 16)) {
bool val, changed;
int err;
u32 flags;
/* Command requires to use the controller index */
if (!hdev)
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_INDEX);
/* Changes can only be made when controller is powered down */
if (hdev_is_powered(hdev))
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_REJECTED);
/* Parameters are limited to a single octet */
if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
/* Only boolean on/off is supported */
if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
return mgmt_cmd_status(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_INVALID_PARAMS);
val = !!cp->param[0];
if (val) {
changed = !hci_dev_test_flag(hdev,
HCI_ENABLE_LL_PRIVACY);
hci_dev_set_flag(hdev, HCI_ENABLE_LL_PRIVACY);
hci_dev_clear_flag(hdev, HCI_ADVERTISING);
/* Enable LL privacy + supported settings changed */
flags = BIT(0) | BIT(1);
} else {
changed = hci_dev_test_flag(hdev,
HCI_ENABLE_LL_PRIVACY);
hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY);
/* Disable LL privacy + supported settings changed */
flags = BIT(1);
}
memcpy(rp.uuid, rpa_resolution_uuid, 16);
rp.flags = cpu_to_le32(flags);
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
err = mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_SET_EXP_FEATURE, 0,
&rp, sizeof(rp));
if (changed)
exp_ll_privacy_feature_changed(val, hdev, sk);
return err;
for (i = 0; exp_features[i].uuid; i++) {
if (!memcmp(cp->uuid, exp_features[i].uuid, 16))
return exp_features[i].set_func(sk, hdev, cp, data_len);
}
return mgmt_cmd_status(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
@ -7315,6 +7541,11 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
if (!rp)
return -ENOMEM;
if (!status && !lmp_ssp_capable(hdev)) {
status = MGMT_STATUS_NOT_SUPPORTED;
eir_len = 0;
}
if (status)
goto complete;
@ -7526,7 +7757,7 @@ static u8 calculate_name_len(struct hci_dev *hdev)
{
u8 buf[HCI_MAX_SHORT_NAME_LENGTH + 3];
return append_local_name(hdev, buf, 0);
return eir_append_local_name(hdev, buf, 0);
}
static u8 tlv_data_max_len(struct hci_dev *hdev, u32 adv_flags,
@ -8222,7 +8453,7 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
* advertising.
*/
if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING,
MGMT_STATUS_NOT_SUPPORTED);
hci_dev_lock(hdev);

View file

@ -94,11 +94,14 @@ struct msft_data {
__u16 pending_add_handle;
__u16 pending_remove_handle;
__u8 reregistering;
__u8 suspending;
__u8 filter_enabled;
};
static int __msft_add_monitor_pattern(struct hci_dev *hdev,
struct adv_monitor *monitor);
static int __msft_remove_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor, u16 handle);
bool msft_monitor_supported(struct hci_dev *hdev)
{
@ -154,7 +157,7 @@ static bool read_supported_features(struct hci_dev *hdev,
}
/* This function requires the caller holds hdev->lock */
static void reregister_monitor_on_restart(struct hci_dev *hdev, int handle)
static void reregister_monitor(struct hci_dev *hdev, int handle)
{
struct adv_monitor *monitor;
struct msft_data *msft = hdev->msft_data;
@ -182,31 +185,102 @@ static void reregister_monitor_on_restart(struct hci_dev *hdev, int handle)
}
}
/* This function requires the caller holds hdev->lock */
static void remove_monitor_on_suspend(struct hci_dev *hdev, int handle)
{
struct adv_monitor *monitor;
struct msft_data *msft = hdev->msft_data;
int err;
while (1) {
monitor = idr_get_next(&hdev->adv_monitors_idr, &handle);
if (!monitor) {
/* All monitors have been removed */
msft->suspending = false;
hci_update_background_scan(hdev);
return;
}
msft->pending_remove_handle = (u16)handle;
err = __msft_remove_monitor(hdev, monitor, handle);
/* If success, return and wait for monitor removed callback */
if (!err)
return;
/* Otherwise free the monitor and keep removing */
hci_free_adv_monitor(hdev, monitor);
handle++;
}
}
/* This function requires the caller holds hdev->lock */
void msft_suspend(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;
if (!msft)
return;
if (msft_monitor_supported(hdev)) {
msft->suspending = true;
/* Quitely remove all monitors on suspend to avoid waking up
* the system.
*/
remove_monitor_on_suspend(hdev, 0);
}
}
/* This function requires the caller holds hdev->lock */
void msft_resume(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;
if (!msft)
return;
if (msft_monitor_supported(hdev)) {
msft->reregistering = true;
/* Monitors are removed on suspend, so we need to add all
* monitors on resume.
*/
reregister_monitor(hdev, 0);
}
}
void msft_do_open(struct hci_dev *hdev)
{
struct msft_data *msft;
struct msft_data *msft = hdev->msft_data;
if (hdev->msft_opcode == HCI_OP_NOP)
return;
if (!msft) {
bt_dev_err(hdev, "MSFT extension not registered");
return;
}
bt_dev_dbg(hdev, "Initialize MSFT extension");
msft = kzalloc(sizeof(*msft), GFP_KERNEL);
if (!msft)
return;
/* Reset existing MSFT data before re-reading */
kfree(msft->evt_prefix);
msft->evt_prefix = NULL;
msft->evt_prefix_len = 0;
msft->features = 0;
if (!read_supported_features(hdev, msft)) {
hdev->msft_data = NULL;
kfree(msft);
return;
}
INIT_LIST_HEAD(&msft->handle_map);
hdev->msft_data = msft;
if (msft_monitor_supported(hdev)) {
msft->reregistering = true;
msft_set_filter_enable(hdev, true);
reregister_monitor_on_restart(hdev, 0);
/* Monitors get removed on power off, so we need to explicitly
* tell the controller to re-monitor.
*/
reregister_monitor(hdev, 0);
}
}
@ -221,8 +295,9 @@ void msft_do_close(struct hci_dev *hdev)
bt_dev_dbg(hdev, "Cleanup of MSFT extension");
hdev->msft_data = NULL;
/* The controller will silently remove all monitors on power off.
* Therefore, remove handle_data mapping and reset monitor state.
*/
list_for_each_entry_safe(handle_data, tmp, &msft->handle_map, list) {
monitor = idr_find(&hdev->adv_monitors_idr,
handle_data->mgmt_handle);
@ -233,6 +308,34 @@ void msft_do_close(struct hci_dev *hdev)
list_del(&handle_data->list);
kfree(handle_data);
}
}
void msft_register(struct hci_dev *hdev)
{
struct msft_data *msft = NULL;
bt_dev_dbg(hdev, "Register MSFT extension");
msft = kzalloc(sizeof(*msft), GFP_KERNEL);
if (!msft) {
bt_dev_err(hdev, "Failed to register MSFT extension");
return;
}
INIT_LIST_HEAD(&msft->handle_map);
hdev->msft_data = msft;
}
void msft_unregister(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;
if (!msft)
return;
bt_dev_dbg(hdev, "Unregister MSFT extension");
hdev->msft_data = NULL;
kfree(msft->evt_prefix);
kfree(msft);
@ -345,8 +448,7 @@ static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev,
/* If in restart/reregister sequence, keep registering. */
if (msft->reregistering)
reregister_monitor_on_restart(hdev,
msft->pending_add_handle + 1);
reregister_monitor(hdev, msft->pending_add_handle + 1);
hci_dev_unlock(hdev);
@ -383,13 +485,25 @@ static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
if (handle_data) {
monitor = idr_find(&hdev->adv_monitors_idr,
handle_data->mgmt_handle);
if (monitor)
if (monitor && monitor->state == ADV_MONITOR_STATE_OFFLOADED)
monitor->state = ADV_MONITOR_STATE_REGISTERED;
/* Do not free the monitor if it is being removed due to
* suspend. It will be re-monitored on resume.
*/
if (monitor && !msft->suspending)
hci_free_adv_monitor(hdev, monitor);
list_del(&handle_data->list);
kfree(handle_data);
}
/* If in suspend/remove sequence, keep removing. */
if (msft->suspending)
remove_monitor_on_suspend(hdev,
msft->pending_remove_handle + 1);
/* If remove all monitors is required, we need to continue the process
* here because the earlier it was paused when waiting for the
* response from controller.
@ -408,7 +522,8 @@ static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
hci_dev_unlock(hdev);
done:
hci_remove_adv_monitor_complete(hdev, status);
if (!msft->suspending)
hci_remove_adv_monitor_complete(hdev, status);
}
static void msft_le_set_advertisement_filter_enable_cb(struct hci_dev *hdev,
@ -541,15 +656,15 @@ int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor)
if (!msft)
return -EOPNOTSUPP;
if (msft->reregistering)
if (msft->reregistering || msft->suspending)
return -EBUSY;
return __msft_add_monitor_pattern(hdev, monitor);
}
/* This function requires the caller holds hdev->lock */
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
u16 handle)
static int __msft_remove_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor, u16 handle)
{
struct msft_cp_le_cancel_monitor_advertisement cp;
struct msft_monitor_advertisement_handle_data *handle_data;
@ -557,12 +672,6 @@ int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
struct msft_data *msft = hdev->msft_data;
int err = 0;
if (!msft)
return -EOPNOTSUPP;
if (msft->reregistering)
return -EBUSY;
handle_data = msft_find_handle_data(hdev, monitor->handle, true);
/* If no matched handle, just remove without telling controller */
@ -582,6 +691,21 @@ int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
return err;
}
/* This function requires the caller holds hdev->lock */
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
u16 handle)
{
struct msft_data *msft = hdev->msft_data;
if (!msft)
return -EOPNOTSUPP;
if (msft->reregistering || msft->suspending)
return -EBUSY;
return __msft_remove_monitor(hdev, monitor, handle);
}
void msft_req_add_set_filter_enable(struct hci_request *req, bool enable)
{
struct hci_dev *hdev = req->hdev;

View file

@ -13,6 +13,8 @@
#if IS_ENABLED(CONFIG_BT_MSFTEXT)
bool msft_monitor_supported(struct hci_dev *hdev);
void msft_register(struct hci_dev *hdev);
void msft_unregister(struct hci_dev *hdev);
void msft_do_open(struct hci_dev *hdev);
void msft_do_close(struct hci_dev *hdev);
void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb);
@ -22,6 +24,8 @@ int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
u16 handle);
void msft_req_add_set_filter_enable(struct hci_request *req, bool enable);
int msft_set_filter_enable(struct hci_dev *hdev, bool enable);
void msft_suspend(struct hci_dev *hdev);
void msft_resume(struct hci_dev *hdev);
bool msft_curve_validity(struct hci_dev *hdev);
#else
@ -31,6 +35,8 @@ static inline bool msft_monitor_supported(struct hci_dev *hdev)
return false;
}
static inline void msft_register(struct hci_dev *hdev) {}
static inline void msft_unregister(struct hci_dev *hdev) {}
static inline void msft_do_open(struct hci_dev *hdev) {}
static inline void msft_do_close(struct hci_dev *hdev) {}
static inline void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb) {}
@ -55,6 +61,9 @@ static inline int msft_set_filter_enable(struct hci_dev *hdev, bool enable)
return -EOPNOTSUPP;
}
static inline void msft_suspend(struct hci_dev *hdev) {}
static inline void msft_resume(struct hci_dev *hdev) {}
static inline bool msft_curve_validity(struct hci_dev *hdev)
{
return false;

View file

@ -549,22 +549,58 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel)
return dlc;
}
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
static int rfcomm_dlc_send_frag(struct rfcomm_dlc *d, struct sk_buff *frag)
{
int len = skb->len;
if (d->state != BT_CONNECTED)
return -ENOTCONN;
int len = frag->len;
BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
if (len > d->mtu)
return -EINVAL;
rfcomm_make_uih(skb, d->addr);
skb_queue_tail(&d->tx_queue, skb);
rfcomm_make_uih(frag, d->addr);
__skb_queue_tail(&d->tx_queue, frag);
if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
return len;
}
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
{
unsigned long flags;
struct sk_buff *frag, *next;
int len;
if (d->state != BT_CONNECTED)
return -ENOTCONN;
frag = skb_shinfo(skb)->frag_list;
skb_shinfo(skb)->frag_list = NULL;
/* Queue all fragments atomically. */
spin_lock_irqsave(&d->tx_queue.lock, flags);
len = rfcomm_dlc_send_frag(d, skb);
if (len < 0 || !frag)
goto unlock;
for (; frag; frag = next) {
int ret;
next = frag->next;
ret = rfcomm_dlc_send_frag(d, frag);
if (ret < 0) {
kfree_skb(frag);
goto unlock;
}
len += ret;
}
unlock:
spin_unlock_irqrestore(&d->tx_queue.lock, flags);
if (len > 0 && !test_bit(RFCOMM_TX_THROTTLED, &d->flags))
rfcomm_schedule();
return len;
}

View file

@ -575,47 +575,21 @@ static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg,
lock_sock(sk);
sent = bt_sock_wait_ready(sk, msg->msg_flags);
if (sent)
goto done;
while (len) {
size_t size = min_t(size_t, len, d->mtu);
int err;
skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb) {
if (sent == 0)
sent = err;
break;
}
skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err) {
kfree_skb(skb);
if (sent == 0)
sent = err;
break;
}
skb->priority = sk->sk_priority;
err = rfcomm_dlc_send(d, skb);
if (err < 0) {
kfree_skb(skb);
if (sent == 0)
sent = err;
break;
}
sent += size;
len -= size;
}
done:
release_sock(sk);
if (sent)
return sent;
skb = bt_skb_sendmmsg(sk, msg, len, d->mtu, RFCOMM_SKB_HEAD_RESERVE,
RFCOMM_SKB_TAIL_RESERVE);
if (IS_ERR(skb))
return PTR_ERR(skb);
sent = rfcomm_dlc_send(d, skb);
if (sent < 0)
kfree_skb(skb);
return sent;
}

View file

@ -69,6 +69,7 @@ struct sco_pinfo {
__u32 flags;
__u16 setting;
__u8 cmsg_mask;
struct bt_codec codec;
struct sco_conn *conn;
};
@ -133,6 +134,7 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
return NULL;
spin_lock_init(&conn->lock);
INIT_DELAYED_WORK(&conn->timeout_work, sco_sock_timeout);
hcon->sco_data = conn;
conn->hcon = hcon;
@ -187,20 +189,21 @@ static void sco_conn_del(struct hci_conn *hcon, int err)
/* Kill socket */
sco_conn_lock(conn);
sk = conn->sk;
if (sk)
sock_hold(sk);
sco_conn_unlock(conn);
if (sk) {
sock_hold(sk);
lock_sock(sk);
sco_sock_clear_timer(sk);
sco_chan_del(sk, err);
release_sock(sk);
sock_put(sk);
/* Ensure no more work items will run before freeing conn. */
cancel_delayed_work_sync(&conn->timeout_work);
}
/* Ensure no more work items will run before freeing conn. */
cancel_delayed_work_sync(&conn->timeout_work);
hcon->sco_data = NULL;
kfree(conn);
}
@ -213,8 +216,6 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk,
sco_pi(sk)->conn = conn;
conn->sk = sk;
INIT_DELAYED_WORK(&conn->timeout_work, sco_sock_timeout);
if (parent)
bt_accept_enqueue(parent, sk, true);
}
@ -252,7 +253,7 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk)
return -EOPNOTSUPP;
hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
sco_pi(sk)->setting);
sco_pi(sk)->setting, &sco_pi(sk)->codec);
if (IS_ERR(hcon))
return PTR_ERR(hcon);
@ -280,11 +281,10 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk)
return err;
}
static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
static int sco_send_frame(struct sock *sk, struct sk_buff *skb)
{
struct sco_conn *conn = sco_pi(sk)->conn;
struct sk_buff *skb;
int err;
int len = skb->len;
/* Check outgoing MTU */
if (len > conn->mtu)
@ -292,15 +292,6 @@ static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
BT_DBG("sk %p len %d", sk, len);
skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
return err;
if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
kfree_skb(skb);
return -EFAULT;
}
hci_send_sco(conn->hcon, skb);
return len;
@ -444,6 +435,7 @@ static void __sco_sock_close(struct sock *sk)
sock_set_flag(sk, SOCK_ZAPPED);
break;
}
}
/* Must be called on unlocked socket. */
@ -504,6 +496,10 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock,
sk->sk_state = BT_OPEN;
sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
sco_pi(sk)->codec.id = BT_CODEC_CVSD;
sco_pi(sk)->codec.cid = 0xffff;
sco_pi(sk)->codec.vid = 0xffff;
sco_pi(sk)->codec.data_path = 0x00;
bt_sock_link(&sco_sk_list, sk);
return sk;
@ -725,6 +721,7 @@ static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg,
size_t len)
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
int err;
BT_DBG("sock %p, sk %p", sock, sk);
@ -736,14 +733,21 @@ static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg,
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
skb = bt_skb_sendmsg(sk, msg, len, len, 0, 0);
if (IS_ERR(skb))
return PTR_ERR(skb);
lock_sock(sk);
if (sk->sk_state == BT_CONNECTED)
err = sco_send_frame(sk, msg, len);
err = sco_send_frame(sk, skb);
else
err = -ENOTCONN;
release_sock(sk);
if (err < 0)
kfree_skb(skb);
return err;
}
@ -825,6 +829,9 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
int len, err = 0;
struct bt_voice voice;
u32 opt;
struct bt_codecs *codecs;
struct hci_dev *hdev;
__u8 buffer[255];
BT_DBG("sk %p", sk);
@ -872,6 +879,16 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
}
sco_pi(sk)->setting = voice.setting;
hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src,
BDADDR_BREDR);
if (!hdev) {
err = -EBADFD;
break;
}
if (enhanced_sco_capable(hdev) &&
voice.setting == BT_VOICE_TRANSPARENT)
sco_pi(sk)->codec.id = BT_CODEC_TRANSPARENT;
hci_dev_put(hdev);
break;
case BT_PKT_STATUS:
@ -886,6 +903,57 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
sco_pi(sk)->cmsg_mask &= SCO_CMSG_PKT_STATUS;
break;
case BT_CODEC:
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND &&
sk->sk_state != BT_CONNECT2) {
err = -EINVAL;
break;
}
hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src,
BDADDR_BREDR);
if (!hdev) {
err = -EBADFD;
break;
}
if (!hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED)) {
hci_dev_put(hdev);
err = -EOPNOTSUPP;
break;
}
if (!hdev->get_data_path_id) {
hci_dev_put(hdev);
err = -EOPNOTSUPP;
break;
}
if (optlen < sizeof(struct bt_codecs) ||
optlen > sizeof(buffer)) {
hci_dev_put(hdev);
err = -EINVAL;
break;
}
if (copy_from_sockptr(buffer, optval, optlen)) {
hci_dev_put(hdev);
err = -EFAULT;
break;
}
codecs = (void *)buffer;
if (codecs->num_codecs > 1) {
hci_dev_put(hdev);
err = -EINVAL;
break;
}
sco_pi(sk)->codec = codecs->codecs[0];
hci_dev_put(hdev);
break;
default:
err = -ENOPROTOOPT;
break;
@ -964,6 +1032,12 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
struct bt_voice voice;
u32 phys;
int pkt_status;
int buf_len;
struct codec_list *c;
u8 num_codecs, i, __user *ptr;
struct hci_dev *hdev;
struct hci_codec_caps *caps;
struct bt_codec codec;
BT_DBG("sk %p", sk);
@ -1028,6 +1102,101 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
err = -EFAULT;
break;
case BT_CODEC:
num_codecs = 0;
buf_len = 0;
hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
if (!hdev) {
err = -EBADFD;
break;
}
if (!hci_dev_test_flag(hdev, HCI_OFFLOAD_CODECS_ENABLED)) {
hci_dev_put(hdev);
err = -EOPNOTSUPP;
break;
}
if (!hdev->get_data_path_id) {
hci_dev_put(hdev);
err = -EOPNOTSUPP;
break;
}
/* find total buffer size required to copy codec + caps */
hci_dev_lock(hdev);
list_for_each_entry(c, &hdev->local_codecs, list) {
if (c->transport != HCI_TRANSPORT_SCO_ESCO)
continue;
num_codecs++;
for (i = 0, caps = c->caps; i < c->num_caps; i++) {
buf_len += 1 + caps->len;
caps = (void *)&caps->data[caps->len];
}
buf_len += sizeof(struct bt_codec);
}
hci_dev_unlock(hdev);
buf_len += sizeof(struct bt_codecs);
if (buf_len > len) {
hci_dev_put(hdev);
err = -ENOBUFS;
break;
}
ptr = optval;
if (put_user(num_codecs, ptr)) {
hci_dev_put(hdev);
err = -EFAULT;
break;
}
ptr += sizeof(num_codecs);
/* Iterate all the codecs supported over SCO and populate
* codec data
*/
hci_dev_lock(hdev);
list_for_each_entry(c, &hdev->local_codecs, list) {
if (c->transport != HCI_TRANSPORT_SCO_ESCO)
continue;
codec.id = c->id;
codec.cid = c->cid;
codec.vid = c->vid;
err = hdev->get_data_path_id(hdev, &codec.data_path);
if (err < 0)
break;
codec.num_caps = c->num_caps;
if (copy_to_user(ptr, &codec, sizeof(codec))) {
err = -EFAULT;
break;
}
ptr += sizeof(codec);
/* find codec capabilities data length */
len = 0;
for (i = 0, caps = c->caps; i < c->num_caps; i++) {
len += 1 + caps->len;
caps = (void *)&caps->data[caps->len];
}
/* copy codec capabilities data */
if (len && copy_to_user(ptr, c->caps, len)) {
err = -EFAULT;
break;
}
ptr += len;
}
if (!err && put_user(buf_len, optlen))
err = -EFAULT;
hci_dev_unlock(hdev);
hci_dev_put(hdev);
break;
default:
err = -ENOPROTOOPT;
break;