mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 14:41:39 +00:00
bluetooth-next pull request for net-next:
- hci_conn: Only do ACL connections sequentially - hci_core: Cancel request on command timeout - Remove CONFIG_BT_HS - btrtl: Add the support for RTL8852BT/RTL8852BE-VT - btusb: Add support Mediatek MT7920 - btusb: Add new VID/PID 13d3/3602 for MT7925 - Add new quirk for broken read key length on ATS2851 -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmXrU9AZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKV4KD/9Ik0EwI4utskMShX9qxnqi 8i5LBocslSFWN3gqrNUAJTxwlSgYntRK4L4v+566/Y/DISUV7OLx9hRJ8QpzWhWl mKweR3kB2HG8/Su6E2VbzjQTriSBuwPiMIeGwP9H5d+bN+6sNLmcl+II9QjapMYQ f13ZA/zQzwDlk8A5jTw1N/cOknblvlNNYUwIPlGzJK9COtQqAVSBRz00ugXmR1LG +UZqzOgXSNFQ2m4PCsozy4fCAVk/NaXBsdnKrsQurND30MJw1jKd9lRaIkQ+eLNv phYfsYLeDDtjMei4j0t23CKOSceMdvFWLtDn3wpBmZbXs8Avd13FRYxf/U88D09g FTNhOLbVyZbWSAEqIMuWZv/EuzZvIpOZRSlCn2hJgJTRuqIi6I9mRDF0ZD4LGUzR /Es/Ozfxw9CfHFRFsiM46cGgQ01Ddq4SihZnlTQfdkBPjQcAhiJ3GbIUAZs+HHVB QFoFLAJWepInGfmyyFHngEzdh9r5zFsA/+PL6duQ1+HqJFZbhPWtgYXijrjuimZo IdcmM4KUUaRWwdDivDq9X5s9luQ1BobNxvVIPlpz61QDu2uMlrilXAgNoZJtalTU ltQxxE9oPUv5tb8xybBYklKM9keyjTGzL3Y/LluDPgzUoY+w5gTcvEqD8ByhzEw6 ouE5TO7r0k1h9BhHZSYHzw== =Jqow -----END PGP SIGNATURE----- Merge tag 'for-net-next-2024-03-08' 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: - hci_conn: Only do ACL connections sequentially - hci_core: Cancel request on command timeout - Remove CONFIG_BT_HS - btrtl: Add the support for RTL8852BT/RTL8852BE-VT - btusb: Add support Mediatek MT7920 - btusb: Add new VID/PID 13d3/3602 for MT7925 - Add new quirk for broken read key length on ATS2851 * tag 'for-net-next-2024-03-08' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next: (52 commits) Bluetooth: hci_sync: Fix UAF in hci_acl_create_conn_sync Bluetooth: Fix eir name length Bluetooth: ISO: Align broadcast sync_timeout with connection timeout Bluetooth: Add new quirk for broken read key length on ATS2851 Bluetooth: mgmt: remove NULL check in add_ext_adv_params_complete() Bluetooth: mgmt: remove NULL check in mgmt_set_connectable_complete() Bluetooth: btusb: Add support Mediatek MT7920 Bluetooth: btmtk: Add MODULE_FIRMWARE() for MT7922 Bluetooth: btnxpuart: Fix btnxpuart_close Bluetooth: ISO: Clean up returns values in iso_connect_ind() Bluetooth: fix use-after-free in accessing skb after sending it Bluetooth: af_bluetooth: Fix deadlock Bluetooth: bnep: Fix out-of-bound access Bluetooth: btusb: Fix memory leak Bluetooth: msft: Fix memory leak Bluetooth: hci_core: Fix possible buffer overflow Bluetooth: btrtl: fix out of bounds memory access Bluetooth: hci_h5: Add ability to allocate memory for private data Bluetooth: hci_sync: Fix overwriting request callback Bluetooth: hci_sync: Use QoS to determine which PHY to scan ... ==================== Link: https://lore.kernel.org/r/20240308181056.120547-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
2f901582f0
@ -11,6 +11,7 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/string.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
@ -543,8 +544,6 @@ static const char *btbcm_get_board_name(struct device *dev)
|
||||
struct device_node *root;
|
||||
char *board_type;
|
||||
const char *tmp;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
root = of_find_node_by_path("/");
|
||||
if (!root)
|
||||
@ -554,13 +553,8 @@ static const char *btbcm_get_board_name(struct device *dev)
|
||||
return NULL;
|
||||
|
||||
/* get rid of any '/' in the compatible string */
|
||||
len = strlen(tmp) + 1;
|
||||
board_type = devm_kzalloc(dev, len, GFP_KERNEL);
|
||||
strscpy(board_type, tmp, len);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (board_type[i] == '/')
|
||||
board_type[i] = '-';
|
||||
}
|
||||
board_type = devm_kstrdup(dev, tmp, GFP_KERNEL);
|
||||
strreplace(board_type, '/', '-');
|
||||
of_node_put(root);
|
||||
|
||||
return board_type;
|
||||
|
@ -441,7 +441,7 @@ int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*ver)) {
|
||||
if (!skb || skb->len != sizeof(*ver)) {
|
||||
bt_dev_err(hdev, "Intel version event size mismatch");
|
||||
kfree_skb(skb);
|
||||
return -EILSEQ;
|
||||
@ -2670,6 +2670,119 @@ static void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
|
||||
}
|
||||
}
|
||||
|
||||
static void btintel_print_fseq_info(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 *p;
|
||||
u32 val;
|
||||
const char *str;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfcb3, 0, NULL, HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_dbg(hdev, "Reading fseq status command failed (%ld)",
|
||||
PTR_ERR(skb));
|
||||
return;
|
||||
}
|
||||
|
||||
if (skb->len < (sizeof(u32) * 16 + 2)) {
|
||||
bt_dev_dbg(hdev, "Malformed packet of length %u received",
|
||||
skb->len);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
p = skb_pull_data(skb, 1);
|
||||
if (*p) {
|
||||
bt_dev_dbg(hdev, "Failed to get fseq status (0x%2.2x)", *p);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
p = skb_pull_data(skb, 1);
|
||||
switch (*p) {
|
||||
case 0:
|
||||
str = "Success";
|
||||
break;
|
||||
case 1:
|
||||
str = "Fatal error";
|
||||
break;
|
||||
case 2:
|
||||
str = "Semaphore acquire error";
|
||||
break;
|
||||
default:
|
||||
str = "Unknown error";
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p) {
|
||||
bt_dev_err(hdev, "Fseq status: %s (0x%2.2x)", str, *p);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "Fseq status: %s (0x%2.2x)", str, *p);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Reason: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Global version: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Installed version: 0x%8.8x", val);
|
||||
|
||||
p = skb->data;
|
||||
skb_pull_data(skb, 4);
|
||||
bt_dev_info(hdev, "Fseq executed: %2.2u.%2.2u.%2.2u.%2.2u", p[0], p[1],
|
||||
p[2], p[3]);
|
||||
|
||||
p = skb->data;
|
||||
skb_pull_data(skb, 4);
|
||||
bt_dev_info(hdev, "Fseq BT Top: %2.2u.%2.2u.%2.2u.%2.2u", p[0], p[1],
|
||||
p[2], p[3]);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq Top init version: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq Cnvio init version: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq MBX Wifi file version: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq BT version: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq Top reset address: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq MBX timeout: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq MBX ack: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq CNVi id: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq CNVr id: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq Error handle: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq Magic noalive indication: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq OTP version: 0x%8.8x", val);
|
||||
|
||||
val = get_unaligned_le32(skb_pull_data(skb, 4));
|
||||
bt_dev_dbg(hdev, "Fseq MBX otp version: 0x%8.8x", val);
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
{
|
||||
const u8 param[1] = { 0xFF };
|
||||
@ -2902,6 +3015,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
|
||||
err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);
|
||||
btintel_register_devcoredump_support(hdev);
|
||||
btintel_print_fseq_info(hdev);
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
|
||||
|
@ -372,8 +372,10 @@ int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
struct btmediatek_data *data = hci_get_priv(hdev);
|
||||
int err;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
|
||||
if (!IS_ENABLED(CONFIG_DEV_COREDUMP)) {
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (data->cd_info.state) {
|
||||
case HCI_DEVCOREDUMP_IDLE:
|
||||
@ -420,5 +422,6 @@ MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7622);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7663);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7668);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7922);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7961);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7925);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#define FIRMWARE_MT7622 "mediatek/mt7622pr2h.bin"
|
||||
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
|
||||
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
|
||||
#define FIRMWARE_MT7922 "mediatek/BT_RAM_CODE_MT7922_1_1_hdr.bin"
|
||||
#define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
|
||||
#define FIRMWARE_MT7925 "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
|
||||
|
||||
|
@ -126,6 +126,7 @@ struct ps_data {
|
||||
struct hci_dev *hdev;
|
||||
struct work_struct work;
|
||||
struct timer_list ps_timer;
|
||||
struct mutex ps_lock;
|
||||
};
|
||||
|
||||
struct wakeup_cmd_payload {
|
||||
@ -317,6 +318,9 @@ static void ps_start_timer(struct btnxpuart_dev *nxpdev)
|
||||
|
||||
if (psdata->cur_psmode == PS_MODE_ENABLE)
|
||||
mod_timer(&psdata->ps_timer, jiffies + msecs_to_jiffies(psdata->h2c_ps_interval));
|
||||
|
||||
if (psdata->ps_state == PS_STATE_AWAKE && psdata->ps_cmd == PS_CMD_ENTER_PS)
|
||||
cancel_work_sync(&psdata->work);
|
||||
}
|
||||
|
||||
static void ps_cancel_timer(struct btnxpuart_dev *nxpdev)
|
||||
@ -337,6 +341,7 @@ static void ps_control(struct hci_dev *hdev, u8 ps_state)
|
||||
!test_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state))
|
||||
return;
|
||||
|
||||
mutex_lock(&psdata->ps_lock);
|
||||
switch (psdata->cur_h2c_wakeupmode) {
|
||||
case WAKEUP_METHOD_DTR:
|
||||
if (ps_state == PS_STATE_AWAKE)
|
||||
@ -350,12 +355,15 @@ static void ps_control(struct hci_dev *hdev, u8 ps_state)
|
||||
status = serdev_device_break_ctl(nxpdev->serdev, 0);
|
||||
else
|
||||
status = serdev_device_break_ctl(nxpdev->serdev, -1);
|
||||
msleep(20); /* Allow chip to detect UART-break and enter sleep */
|
||||
bt_dev_dbg(hdev, "Set UART break: %s, status=%d",
|
||||
str_on_off(ps_state == PS_STATE_SLEEP), status);
|
||||
break;
|
||||
}
|
||||
if (!status)
|
||||
psdata->ps_state = ps_state;
|
||||
mutex_unlock(&psdata->ps_lock);
|
||||
|
||||
if (ps_state == PS_STATE_AWAKE)
|
||||
btnxpuart_tx_wakeup(nxpdev);
|
||||
}
|
||||
@ -391,17 +399,25 @@ static void ps_setup(struct hci_dev *hdev)
|
||||
|
||||
psdata->hdev = hdev;
|
||||
INIT_WORK(&psdata->work, ps_work_func);
|
||||
mutex_init(&psdata->ps_lock);
|
||||
timer_setup(&psdata->ps_timer, ps_timeout_func, 0);
|
||||
}
|
||||
|
||||
static void ps_wakeup(struct btnxpuart_dev *nxpdev)
|
||||
static bool ps_wakeup(struct btnxpuart_dev *nxpdev)
|
||||
{
|
||||
struct ps_data *psdata = &nxpdev->psdata;
|
||||
u8 ps_state;
|
||||
|
||||
if (psdata->ps_state != PS_STATE_AWAKE) {
|
||||
mutex_lock(&psdata->ps_lock);
|
||||
ps_state = psdata->ps_state;
|
||||
mutex_unlock(&psdata->ps_lock);
|
||||
|
||||
if (ps_state != PS_STATE_AWAKE) {
|
||||
psdata->ps_cmd = PS_CMD_EXIT_PS;
|
||||
schedule_work(&psdata->work);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int send_ps_cmd(struct hci_dev *hdev, void *data)
|
||||
@ -1171,7 +1187,6 @@ static struct sk_buff *nxp_dequeue(void *data)
|
||||
{
|
||||
struct btnxpuart_dev *nxpdev = (struct btnxpuart_dev *)data;
|
||||
|
||||
ps_wakeup(nxpdev);
|
||||
ps_start_timer(nxpdev);
|
||||
return skb_dequeue(&nxpdev->txq);
|
||||
}
|
||||
@ -1186,6 +1201,9 @@ static void btnxpuart_tx_work(struct work_struct *work)
|
||||
struct sk_buff *skb;
|
||||
int len;
|
||||
|
||||
if (ps_wakeup(nxpdev))
|
||||
return;
|
||||
|
||||
while ((skb = nxp_dequeue(nxpdev))) {
|
||||
len = serdev_device_write_buf(serdev, skb->data, skb->len);
|
||||
hdev->stat.byte_tx += len;
|
||||
@ -1234,6 +1252,9 @@ static int btnxpuart_close(struct hci_dev *hdev)
|
||||
|
||||
ps_wakeup(nxpdev);
|
||||
serdev_device_close(nxpdev->serdev);
|
||||
skb_queue_purge(&nxpdev->txq);
|
||||
kfree_skb(nxpdev->rx_skb);
|
||||
nxpdev->rx_skb = NULL;
|
||||
clear_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state);
|
||||
return 0;
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ enum btrtl_chip_id {
|
||||
CHIP_ID_8852B = 20,
|
||||
CHIP_ID_8852C = 25,
|
||||
CHIP_ID_8851B = 36,
|
||||
CHIP_ID_8852BT = 47,
|
||||
};
|
||||
|
||||
struct id_table {
|
||||
@ -307,6 +308,15 @@ static const struct id_table ic_id_table[] = {
|
||||
.fw_name = "rtl_bt/rtl8851bu_fw",
|
||||
.cfg_name = "rtl_bt/rtl8851bu_config",
|
||||
.hw_info = "rtl8851bu" },
|
||||
|
||||
/* 8852BT/8852BE-VT */
|
||||
{ IC_INFO(RTL_ROM_LMP_8852A, 0x87, 0xc, HCI_USB),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.has_msft_ext = true,
|
||||
.fw_name = "rtl_bt/rtl8852btu_fw",
|
||||
.cfg_name = "rtl_bt/rtl8852btu_config",
|
||||
.hw_info = "rtl8852btu" },
|
||||
};
|
||||
|
||||
static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
|
||||
@ -645,6 +655,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
||||
{ RTL_ROM_LMP_8852A, 20 }, /* 8852B */
|
||||
{ RTL_ROM_LMP_8852A, 25 }, /* 8852C */
|
||||
{ RTL_ROM_LMP_8851B, 36 }, /* 8851B */
|
||||
{ RTL_ROM_LMP_8852A, 47 }, /* 8852BT */
|
||||
};
|
||||
|
||||
if (btrtl_dev->fw_len <= 8)
|
||||
@ -1275,6 +1286,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
|
||||
case CHIP_ID_8852B:
|
||||
case CHIP_ID_8852C:
|
||||
case CHIP_ID_8851B:
|
||||
case CHIP_ID_8852BT:
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
|
||||
|
||||
@ -1505,6 +1517,8 @@ MODULE_FIRMWARE("rtl_bt/rtl8852bs_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852btu_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852btu_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw_v2.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin");
|
||||
|
@ -553,6 +553,9 @@ static const struct usb_device_id quirks_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3572), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek 8852BT/8852BE-VT Bluetooth devices */
|
||||
{ USB_DEVICE(0x0bda, 0x8520), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
/* Realtek Bluetooth devices */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
|
||||
.driver_info = BTUSB_REALTEK },
|
||||
@ -655,6 +658,11 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
|
||||
/* Additional MediaTek MT7925 Bluetooth devices */
|
||||
{ USB_DEVICE(0x13d3, 0x3602), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
|
||||
/* Additional Realtek 8723AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
|
||||
@ -3080,7 +3088,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
int err, status;
|
||||
u32 dev_id = 0;
|
||||
char fw_bin_name[64];
|
||||
u32 fw_version = 0;
|
||||
u32 fw_version = 0, fw_flavor = 0;
|
||||
u8 param;
|
||||
struct btmediatek_data *mediatek;
|
||||
|
||||
@ -3103,6 +3111,11 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
bt_dev_err(hdev, "Failed to get fw version (%d)", err);
|
||||
return err;
|
||||
}
|
||||
err = btusb_mtk_id_get(data, 0x70010020, &fw_flavor);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to get fw flavor (%d)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
mediatek = hci_get_priv(hdev);
|
||||
@ -3127,6 +3140,10 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
snprintf(fw_bin_name, sizeof(fw_bin_name),
|
||||
"mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
|
||||
dev_id & 0xffff, dev_id & 0xffff, (fw_version & 0xff) + 1);
|
||||
else if (dev_id == 0x7961 && fw_flavor)
|
||||
snprintf(fw_bin_name, sizeof(fw_bin_name),
|
||||
"mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
|
||||
dev_id & 0xffff, (fw_version & 0xff) + 1);
|
||||
else
|
||||
snprintf(fw_bin_name, sizeof(fw_bin_name),
|
||||
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
|
||||
@ -3273,7 +3290,6 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle);
|
||||
struct sk_buff *skb_cd;
|
||||
|
||||
switch (handle) {
|
||||
case 0xfc6f: /* Firmware dump from device */
|
||||
@ -3286,9 +3302,12 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
* for backward compatibility, so we have to clone the packet
|
||||
* extraly for the in-kernel coredump support.
|
||||
*/
|
||||
skb_cd = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb_cd)
|
||||
btmtk_process_coredump(hdev, skb_cd);
|
||||
if (IS_ENABLED(CONFIG_DEV_COREDUMP)) {
|
||||
struct sk_buff *skb_cd = skb_clone(skb, GFP_ATOMIC);
|
||||
|
||||
if (skb_cd)
|
||||
btmtk_process_coredump(hdev, skb_cd);
|
||||
}
|
||||
|
||||
fallthrough;
|
||||
case 0x05ff: /* Firmware debug logging 1 */
|
||||
@ -4481,6 +4500,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_BROKEN_READ_ENC_KEY_SIZE, &hdev->quirks);
|
||||
}
|
||||
|
||||
if (!reset)
|
||||
|
@ -113,6 +113,7 @@ struct h5_vnd {
|
||||
int (*suspend)(struct h5 *h5);
|
||||
int (*resume)(struct h5 *h5);
|
||||
const struct acpi_gpio_mapping *acpi_gpio_map;
|
||||
int sizeof_priv;
|
||||
};
|
||||
|
||||
struct h5_device_data {
|
||||
@ -863,7 +864,8 @@ static int h5_serdev_probe(struct serdev_device *serdev)
|
||||
if (IS_ERR(h5->device_wake_gpio))
|
||||
return PTR_ERR(h5->device_wake_gpio);
|
||||
|
||||
return hci_uart_register_device(&h5->serdev_hu, &h5p);
|
||||
return hci_uart_register_device_priv(&h5->serdev_hu, &h5p,
|
||||
h5->vnd->sizeof_priv);
|
||||
}
|
||||
|
||||
static void h5_serdev_remove(struct serdev_device *serdev)
|
||||
@ -1070,6 +1072,7 @@ static struct h5_vnd rtl_vnd = {
|
||||
.suspend = h5_btrtl_suspend,
|
||||
.resume = h5_btrtl_resume,
|
||||
.acpi_gpio_map = acpi_btrtl_gpios,
|
||||
.sizeof_priv = sizeof(struct btrealtek_data),
|
||||
};
|
||||
|
||||
static const struct h5_device_data h5_data_rtl8822cs = {
|
||||
|
@ -2326,7 +2326,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
|
||||
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR_OR_NULL(qcadev->bt_en) &&
|
||||
if (IS_ERR(qcadev->bt_en) &&
|
||||
(data->soc_type == QCA_WCN6750 ||
|
||||
data->soc_type == QCA_WCN6855)) {
|
||||
dev_err(&serdev->dev, "failed to acquire BT_EN gpio\n");
|
||||
@ -2335,7 +2335,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
|
||||
qcadev->sw_ctrl = devm_gpiod_get_optional(&serdev->dev, "swctrl",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR_OR_NULL(qcadev->sw_ctrl) &&
|
||||
if (IS_ERR(qcadev->sw_ctrl) &&
|
||||
(data->soc_type == QCA_WCN6750 ||
|
||||
data->soc_type == QCA_WCN6855 ||
|
||||
data->soc_type == QCA_WCN7850))
|
||||
@ -2357,7 +2357,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
default:
|
||||
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR_OR_NULL(qcadev->bt_en)) {
|
||||
if (IS_ERR(qcadev->bt_en)) {
|
||||
dev_warn(&serdev->dev, "failed to acquire enable gpio\n");
|
||||
power_ctrl_enabled = false;
|
||||
}
|
||||
|
@ -300,8 +300,9 @@ static const struct serdev_device_ops hci_serdev_client_ops = {
|
||||
.write_wakeup = hci_uart_write_wakeup,
|
||||
};
|
||||
|
||||
int hci_uart_register_device(struct hci_uart *hu,
|
||||
const struct hci_uart_proto *p)
|
||||
int hci_uart_register_device_priv(struct hci_uart *hu,
|
||||
const struct hci_uart_proto *p,
|
||||
int sizeof_priv)
|
||||
{
|
||||
int err;
|
||||
struct hci_dev *hdev;
|
||||
@ -325,7 +326,7 @@ int hci_uart_register_device(struct hci_uart *hu,
|
||||
set_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
|
||||
/* Initialize and register HCI device */
|
||||
hdev = hci_alloc_dev();
|
||||
hdev = hci_alloc_dev_priv(sizeof_priv);
|
||||
if (!hdev) {
|
||||
BT_ERR("Can't allocate HCI device");
|
||||
err = -ENOMEM;
|
||||
@ -394,7 +395,7 @@ err_rwsem:
|
||||
percpu_free_rwsem(&hu->proto_lock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hci_uart_register_device);
|
||||
EXPORT_SYMBOL_GPL(hci_uart_register_device_priv);
|
||||
|
||||
void hci_uart_unregister_device(struct hci_uart *hu)
|
||||
{
|
||||
|
@ -97,7 +97,17 @@ struct hci_uart {
|
||||
|
||||
int hci_uart_register_proto(const struct hci_uart_proto *p);
|
||||
int hci_uart_unregister_proto(const struct hci_uart_proto *p);
|
||||
int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p);
|
||||
|
||||
int hci_uart_register_device_priv(struct hci_uart *hu,
|
||||
const struct hci_uart_proto *p,
|
||||
int sizeof_priv);
|
||||
|
||||
static inline int hci_uart_register_device(struct hci_uart *hu,
|
||||
const struct hci_uart_proto *p)
|
||||
{
|
||||
return hci_uart_register_device_priv(hu, p, 0);
|
||||
}
|
||||
|
||||
void hci_uart_unregister_device(struct hci_uart *hu);
|
||||
|
||||
int hci_uart_tx_wakeup(struct hci_uart *hu);
|
||||
|
@ -164,6 +164,8 @@ struct bt_voice {
|
||||
#define BT_ISO_QOS_BIG_UNSET 0xff
|
||||
#define BT_ISO_QOS_BIS_UNSET 0xff
|
||||
|
||||
#define BT_ISO_SYNC_TIMEOUT 0x07d0 /* 20 secs */
|
||||
|
||||
struct bt_iso_io_qos {
|
||||
__u32 interval;
|
||||
__u16 latency;
|
||||
|
@ -330,6 +330,14 @@ enum {
|
||||
* during the hdev->setup vendor callback.
|
||||
*/
|
||||
HCI_QUIRK_BROKEN_LE_CODED,
|
||||
|
||||
/*
|
||||
* When this quirk is set, the HCI_OP_READ_ENC_KEY_SIZE command is
|
||||
* skipped during an HCI_EV_ENCRYPT_CHANGE event. This is required
|
||||
* for Actions Semiconductor ATS2851 based controllers, which erroneously
|
||||
* claim to support it.
|
||||
*/
|
||||
HCI_QUIRK_BROKEN_READ_ENC_KEY_SIZE,
|
||||
};
|
||||
|
||||
/* HCI device flags */
|
||||
@ -372,6 +380,7 @@ enum {
|
||||
HCI_SETUP,
|
||||
HCI_CONFIG,
|
||||
HCI_DEBUGFS_CREATED,
|
||||
HCI_POWERING_DOWN,
|
||||
HCI_AUTO_OFF,
|
||||
HCI_RFKILLED,
|
||||
HCI_MGMT,
|
||||
@ -393,7 +402,6 @@ enum {
|
||||
HCI_LIMITED_PRIVACY,
|
||||
HCI_RPA_EXPIRED,
|
||||
HCI_RPA_RESOLVING,
|
||||
HCI_HS_ENABLED,
|
||||
HCI_LE_ENABLED,
|
||||
HCI_ADVERTISING,
|
||||
HCI_ADVERTISING_CONNECTABLE,
|
||||
@ -437,7 +445,7 @@ enum {
|
||||
#define HCI_NCMD_TIMEOUT msecs_to_jiffies(4000) /* 4 seconds */
|
||||
#define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */
|
||||
#define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
|
||||
#define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */
|
||||
#define HCI_ACL_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */
|
||||
#define HCI_LE_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */
|
||||
#define HCI_LE_AUTOCONN_TIMEOUT msecs_to_jiffies(4000) /* 4 seconds */
|
||||
|
||||
@ -653,6 +661,7 @@ enum {
|
||||
#define HCI_ERROR_PIN_OR_KEY_MISSING 0x06
|
||||
#define HCI_ERROR_MEMORY_EXCEEDED 0x07
|
||||
#define HCI_ERROR_CONNECTION_TIMEOUT 0x08
|
||||
#define HCI_ERROR_COMMAND_DISALLOWED 0x0c
|
||||
#define HCI_ERROR_REJ_LIMITED_RESOURCES 0x0d
|
||||
#define HCI_ERROR_REJ_BAD_ADDR 0x0f
|
||||
#define HCI_ERROR_INVALID_PARAMETERS 0x12
|
||||
@ -661,6 +670,7 @@ enum {
|
||||
#define HCI_ERROR_REMOTE_POWER_OFF 0x15
|
||||
#define HCI_ERROR_LOCAL_HOST_TERM 0x16
|
||||
#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18
|
||||
#define HCI_ERROR_UNSUPPORTED_REMOTE_FEATURE 0x1e
|
||||
#define HCI_ERROR_INVALID_LL_PARAMS 0x1e
|
||||
#define HCI_ERROR_UNSPECIFIED 0x1f
|
||||
#define HCI_ERROR_ADVERTISING_TIMEOUT 0x3c
|
||||
@ -2035,6 +2045,7 @@ struct hci_cp_le_set_per_adv_params {
|
||||
} __packed;
|
||||
|
||||
#define HCI_MAX_PER_AD_LENGTH 252
|
||||
#define HCI_MAX_PER_AD_TOT_LEN 1650
|
||||
|
||||
#define HCI_OP_LE_SET_PER_ADV_DATA 0x203f
|
||||
struct hci_cp_le_set_per_adv_data {
|
||||
@ -2795,6 +2806,10 @@ struct hci_ev_le_per_adv_report {
|
||||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
#define LE_PA_DATA_COMPLETE 0x00
|
||||
#define LE_PA_DATA_MORE_TO_COME 0x01
|
||||
#define LE_PA_DATA_TRUNCATED 0x02
|
||||
|
||||
#define HCI_EV_LE_EXT_ADV_SET_TERM 0x12
|
||||
struct hci_evt_le_ext_adv_set_term {
|
||||
__u8 status;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
||||
Copyright 2023 NXP
|
||||
Copyright 2023-2024 NXP
|
||||
|
||||
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
|
||||
@ -552,6 +552,7 @@ struct hci_dev {
|
||||
__u32 req_status;
|
||||
__u32 req_result;
|
||||
struct sk_buff *req_skb;
|
||||
struct sk_buff *req_rsp;
|
||||
|
||||
void *smp_data;
|
||||
void *smp_bredr_data;
|
||||
@ -734,8 +735,9 @@ struct hci_conn {
|
||||
__u16 le_supv_timeout;
|
||||
__u8 le_adv_data[HCI_MAX_EXT_AD_LENGTH];
|
||||
__u8 le_adv_data_len;
|
||||
__u8 le_per_adv_data[HCI_MAX_PER_AD_LENGTH];
|
||||
__u8 le_per_adv_data_len;
|
||||
__u8 le_per_adv_data[HCI_MAX_PER_AD_TOT_LEN];
|
||||
__u16 le_per_adv_data_len;
|
||||
__u16 le_per_adv_data_offset;
|
||||
__u8 le_tx_phy;
|
||||
__u8 le_rx_phy;
|
||||
__s8 rssi;
|
||||
@ -1083,6 +1085,24 @@ static inline unsigned int hci_conn_count(struct hci_dev *hdev)
|
||||
return c->acl_num + c->amp_num + c->sco_num + c->le_num + c->iso_num;
|
||||
}
|
||||
|
||||
static inline bool hci_conn_valid(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c == conn) {
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
@ -1480,7 +1500,6 @@ struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
|
||||
bdaddr_t *dst, u8 role);
|
||||
void hci_conn_del(struct hci_conn *conn);
|
||||
void hci_conn_hash_flush(struct hci_dev *hdev);
|
||||
void hci_conn_check_pending(struct hci_dev *hdev);
|
||||
|
||||
struct hci_chan *hci_chan_create(struct hci_conn *conn);
|
||||
void hci_chan_del(struct hci_chan *chan);
|
||||
@ -1494,11 +1513,13 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
u8 dst_type, bool dst_resolved, u8 sec_level,
|
||||
u16 conn_timeout, u8 role);
|
||||
void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status);
|
||||
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
u8 sec_level, u8 auth_type,
|
||||
enum conn_reasons conn_reason);
|
||||
enum conn_reasons conn_reason, u16 timeout);
|
||||
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
__u16 setting, struct bt_codec *codec);
|
||||
__u16 setting, struct bt_codec *codec,
|
||||
u16 timeout);
|
||||
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos);
|
||||
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
@ -1509,8 +1530,8 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos,
|
||||
__u8 data_len, __u8 *data);
|
||||
int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
||||
__u8 sid, struct bt_iso_qos *qos);
|
||||
struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, __u8 sid, struct bt_iso_qos *qos);
|
||||
int hci_le_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
|
||||
struct bt_iso_qos *qos,
|
||||
__u16 sync_handle, __u8 num_bis, __u8 bis[]);
|
||||
|
@ -42,12 +42,24 @@ int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
void hci_cmd_sync_init(struct hci_dev *hdev);
|
||||
void hci_cmd_sync_clear(struct hci_dev *hdev);
|
||||
void hci_cmd_sync_cancel(struct hci_dev *hdev, int err);
|
||||
void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err);
|
||||
void hci_cmd_sync_cancel_sync(struct hci_dev *hdev, int err);
|
||||
|
||||
int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy);
|
||||
int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy);
|
||||
int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy);
|
||||
struct hci_cmd_sync_work_entry *
|
||||
hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy);
|
||||
void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
|
||||
struct hci_cmd_sync_work_entry *entry);
|
||||
bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy);
|
||||
bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
|
||||
hci_cmd_sync_work_func_t func, void *data,
|
||||
hci_cmd_sync_work_destroy_t destroy);
|
||||
|
||||
int hci_update_eir_sync(struct hci_dev *hdev);
|
||||
int hci_update_class_sync(struct hci_dev *hdev);
|
||||
@ -127,8 +139,6 @@ struct hci_conn;
|
||||
|
||||
int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason);
|
||||
|
||||
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
|
||||
int hci_le_create_cis_sync(struct hci_dev *hdev);
|
||||
|
||||
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle);
|
||||
@ -138,3 +148,9 @@ int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason);
|
||||
int hci_le_big_terminate_sync(struct hci_dev *hdev, u8 handle);
|
||||
|
||||
int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle);
|
||||
|
||||
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
|
||||
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
|
@ -59,8 +59,6 @@
|
||||
#define L2CAP_WAIT_ACK_POLL_PERIOD msecs_to_jiffies(200)
|
||||
#define L2CAP_WAIT_ACK_TIMEOUT msecs_to_jiffies(10000)
|
||||
|
||||
#define L2CAP_A2MP_DEFAULT_MTU 670
|
||||
|
||||
/* L2CAP socket address */
|
||||
struct sockaddr_l2 {
|
||||
sa_family_t l2_family;
|
||||
@ -109,12 +107,6 @@ struct l2cap_conninfo {
|
||||
#define L2CAP_ECHO_RSP 0x09
|
||||
#define L2CAP_INFO_REQ 0x0a
|
||||
#define L2CAP_INFO_RSP 0x0b
|
||||
#define L2CAP_CREATE_CHAN_REQ 0x0c
|
||||
#define L2CAP_CREATE_CHAN_RSP 0x0d
|
||||
#define L2CAP_MOVE_CHAN_REQ 0x0e
|
||||
#define L2CAP_MOVE_CHAN_RSP 0x0f
|
||||
#define L2CAP_MOVE_CHAN_CFM 0x10
|
||||
#define L2CAP_MOVE_CHAN_CFM_RSP 0x11
|
||||
#define L2CAP_CONN_PARAM_UPDATE_REQ 0x12
|
||||
#define L2CAP_CONN_PARAM_UPDATE_RSP 0x13
|
||||
#define L2CAP_LE_CONN_REQ 0x14
|
||||
@ -144,7 +136,6 @@ struct l2cap_conninfo {
|
||||
/* L2CAP fixed channels */
|
||||
#define L2CAP_FC_SIG_BREDR 0x02
|
||||
#define L2CAP_FC_CONNLESS 0x04
|
||||
#define L2CAP_FC_A2MP 0x08
|
||||
#define L2CAP_FC_ATT 0x10
|
||||
#define L2CAP_FC_SIG_LE 0x20
|
||||
#define L2CAP_FC_SMP_LE 0x40
|
||||
@ -267,7 +258,6 @@ struct l2cap_conn_rsp {
|
||||
/* channel identifier */
|
||||
#define L2CAP_CID_SIGNALING 0x0001
|
||||
#define L2CAP_CID_CONN_LESS 0x0002
|
||||
#define L2CAP_CID_A2MP 0x0003
|
||||
#define L2CAP_CID_ATT 0x0004
|
||||
#define L2CAP_CID_LE_SIGNALING 0x0005
|
||||
#define L2CAP_CID_SMP 0x0006
|
||||
@ -282,7 +272,6 @@ struct l2cap_conn_rsp {
|
||||
#define L2CAP_CR_BAD_PSM 0x0002
|
||||
#define L2CAP_CR_SEC_BLOCK 0x0003
|
||||
#define L2CAP_CR_NO_MEM 0x0004
|
||||
#define L2CAP_CR_BAD_AMP 0x0005
|
||||
#define L2CAP_CR_INVALID_SCID 0x0006
|
||||
#define L2CAP_CR_SCID_IN_USE 0x0007
|
||||
|
||||
@ -404,29 +393,6 @@ struct l2cap_info_rsp {
|
||||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct l2cap_create_chan_req {
|
||||
__le16 psm;
|
||||
__le16 scid;
|
||||
__u8 amp_id;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_create_chan_rsp {
|
||||
__le16 dcid;
|
||||
__le16 scid;
|
||||
__le16 result;
|
||||
__le16 status;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_move_chan_req {
|
||||
__le16 icid;
|
||||
__u8 dest_amp_id;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_move_chan_rsp {
|
||||
__le16 icid;
|
||||
__le16 result;
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_MR_SUCCESS 0x0000
|
||||
#define L2CAP_MR_PEND 0x0001
|
||||
#define L2CAP_MR_BAD_ID 0x0002
|
||||
@ -539,8 +505,6 @@ struct l2cap_seq_list {
|
||||
|
||||
struct l2cap_chan {
|
||||
struct l2cap_conn *conn;
|
||||
struct hci_conn *hs_hcon;
|
||||
struct hci_chan *hs_hchan;
|
||||
struct kref kref;
|
||||
atomic_t nesting;
|
||||
|
||||
@ -591,12 +555,6 @@ struct l2cap_chan {
|
||||
unsigned long conn_state;
|
||||
unsigned long flags;
|
||||
|
||||
__u8 remote_amp_id;
|
||||
__u8 local_amp_id;
|
||||
__u8 move_id;
|
||||
__u8 move_state;
|
||||
__u8 move_role;
|
||||
|
||||
__u16 next_tx_seq;
|
||||
__u16 expected_ack_seq;
|
||||
__u16 expected_tx_seq;
|
||||
@ -981,7 +939,7 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid);
|
||||
struct l2cap_chan *l2cap_chan_create(void);
|
||||
void l2cap_chan_close(struct l2cap_chan *chan, int reason);
|
||||
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
||||
bdaddr_t *dst, u8 dst_type);
|
||||
bdaddr_t *dst, u8 dst_type, u16 timeout);
|
||||
int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu);
|
||||
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);
|
||||
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
|
||||
|
@ -572,7 +572,7 @@ static void netdev_setup(struct net_device *dev)
|
||||
dev->needs_free_netdev = true;
|
||||
}
|
||||
|
||||
static struct device_type bt_type = {
|
||||
static const struct device_type bt_type = {
|
||||
.name = "bluetooth",
|
||||
};
|
||||
|
||||
@ -892,7 +892,7 @@ static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
|
||||
chan->ops = &bt_6lowpan_chan_ops;
|
||||
|
||||
err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
|
||||
addr, dst_type);
|
||||
addr, dst_type, L2CAP_CONN_TIMEOUT);
|
||||
|
||||
BT_DBG("chan %p err %d", chan, err);
|
||||
if (err < 0)
|
||||
|
@ -62,14 +62,6 @@ source "net/bluetooth/cmtp/Kconfig"
|
||||
|
||||
source "net/bluetooth/hidp/Kconfig"
|
||||
|
||||
config BT_HS
|
||||
bool "Bluetooth High Speed (HS) features"
|
||||
depends on BT_BREDR
|
||||
help
|
||||
Bluetooth High Speed includes support for off-loading
|
||||
Bluetooth connections via 802.11 (wifi) physical layer
|
||||
available with Bluetooth version 3.0 or later.
|
||||
|
||||
config BT_LE
|
||||
bool "Bluetooth Low Energy (LE) features"
|
||||
depends on BT
|
||||
|
@ -21,7 +21,6 @@ bluetooth-$(CONFIG_DEV_COREDUMP) += coredump.o
|
||||
|
||||
bluetooth-$(CONFIG_BT_BREDR) += sco.o
|
||||
bluetooth-$(CONFIG_BT_LE) += iso.o
|
||||
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
|
||||
bluetooth-$(CONFIG_BT_LEDS) += leds.o
|
||||
bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
|
||||
bluetooth-$(CONFIG_BT_AOSPEXT) += aosp.o
|
||||
|
1054
net/bluetooth/a2mp.c
1054
net/bluetooth/a2mp.c
File diff suppressed because it is too large
Load Diff
@ -1,154 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
|
||||
Copyright (c) 2011,2012 Intel Corp.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __A2MP_H
|
||||
#define __A2MP_H
|
||||
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
|
||||
enum amp_mgr_state {
|
||||
READ_LOC_AMP_INFO,
|
||||
READ_LOC_AMP_ASSOC,
|
||||
READ_LOC_AMP_ASSOC_FINAL,
|
||||
WRITE_REMOTE_AMP_ASSOC,
|
||||
};
|
||||
|
||||
struct amp_mgr {
|
||||
struct list_head list;
|
||||
struct l2cap_conn *l2cap_conn;
|
||||
struct l2cap_chan *a2mp_chan;
|
||||
struct l2cap_chan *bredr_chan;
|
||||
struct kref kref;
|
||||
__u8 ident;
|
||||
__u8 handle;
|
||||
unsigned long state;
|
||||
unsigned long flags;
|
||||
|
||||
struct list_head amp_ctrls;
|
||||
struct mutex amp_ctrls_lock;
|
||||
};
|
||||
|
||||
struct a2mp_cmd {
|
||||
__u8 code;
|
||||
__u8 ident;
|
||||
__le16 len;
|
||||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
/* A2MP command codes */
|
||||
#define A2MP_COMMAND_REJ 0x01
|
||||
struct a2mp_cmd_rej {
|
||||
__le16 reason;
|
||||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
#define A2MP_DISCOVER_REQ 0x02
|
||||
struct a2mp_discov_req {
|
||||
__le16 mtu;
|
||||
__le16 ext_feat;
|
||||
} __packed;
|
||||
|
||||
struct a2mp_cl {
|
||||
__u8 id;
|
||||
__u8 type;
|
||||
__u8 status;
|
||||
} __packed;
|
||||
|
||||
#define A2MP_DISCOVER_RSP 0x03
|
||||
struct a2mp_discov_rsp {
|
||||
__le16 mtu;
|
||||
__le16 ext_feat;
|
||||
struct a2mp_cl cl[];
|
||||
} __packed;
|
||||
|
||||
#define A2MP_CHANGE_NOTIFY 0x04
|
||||
#define A2MP_CHANGE_RSP 0x05
|
||||
|
||||
#define A2MP_GETINFO_REQ 0x06
|
||||
struct a2mp_info_req {
|
||||
__u8 id;
|
||||
} __packed;
|
||||
|
||||
#define A2MP_GETINFO_RSP 0x07
|
||||
struct a2mp_info_rsp {
|
||||
__u8 id;
|
||||
__u8 status;
|
||||
__le32 total_bw;
|
||||
__le32 max_bw;
|
||||
__le32 min_latency;
|
||||
__le16 pal_cap;
|
||||
__le16 assoc_size;
|
||||
} __packed;
|
||||
|
||||
#define A2MP_GETAMPASSOC_REQ 0x08
|
||||
struct a2mp_amp_assoc_req {
|
||||
__u8 id;
|
||||
} __packed;
|
||||
|
||||
#define A2MP_GETAMPASSOC_RSP 0x09
|
||||
struct a2mp_amp_assoc_rsp {
|
||||
__u8 id;
|
||||
__u8 status;
|
||||
__u8 amp_assoc[];
|
||||
} __packed;
|
||||
|
||||
#define A2MP_CREATEPHYSLINK_REQ 0x0A
|
||||
#define A2MP_DISCONNPHYSLINK_REQ 0x0C
|
||||
struct a2mp_physlink_req {
|
||||
__u8 local_id;
|
||||
__u8 remote_id;
|
||||
__u8 amp_assoc[];
|
||||
} __packed;
|
||||
|
||||
#define A2MP_CREATEPHYSLINK_RSP 0x0B
|
||||
#define A2MP_DISCONNPHYSLINK_RSP 0x0D
|
||||
struct a2mp_physlink_rsp {
|
||||
__u8 local_id;
|
||||
__u8 remote_id;
|
||||
__u8 status;
|
||||
} __packed;
|
||||
|
||||
/* A2MP response status */
|
||||
#define A2MP_STATUS_SUCCESS 0x00
|
||||
#define A2MP_STATUS_INVALID_CTRL_ID 0x01
|
||||
#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
|
||||
#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
|
||||
#define A2MP_STATUS_COLLISION_OCCURED 0x03
|
||||
#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
|
||||
#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
|
||||
#define A2MP_STATUS_SECURITY_VIOLATION 0x06
|
||||
|
||||
struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_HS)
|
||||
int amp_mgr_put(struct amp_mgr *mgr);
|
||||
struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
|
||||
struct sk_buff *skb);
|
||||
void a2mp_discover_amp(struct l2cap_chan *chan);
|
||||
#else
|
||||
static inline int amp_mgr_put(struct amp_mgr *mgr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void a2mp_discover_amp(struct l2cap_chan *chan)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
|
||||
void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
|
||||
void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
|
||||
void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
|
||||
|
||||
#endif /* __A2MP_H */
|
@ -309,14 +309,11 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
||||
if (flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
skb = skb_recv_datagram(sk, flags, &err);
|
||||
if (!skb) {
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
||||
err = 0;
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -346,8 +343,6 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
||||
|
||||
skb_free_datagram(sk, skb);
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
if (flags & MSG_TRUNC)
|
||||
copied = skblen;
|
||||
|
||||
@ -570,10 +565,11 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
||||
if (sk->sk_state == BT_LISTEN)
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
spin_lock(&sk->sk_receive_queue.lock);
|
||||
skb = skb_peek(&sk->sk_receive_queue);
|
||||
amount = skb ? skb->len : 0;
|
||||
release_sock(sk);
|
||||
spin_unlock(&sk->sk_receive_queue.lock);
|
||||
|
||||
err = put_user(amount, (int __user *)arg);
|
||||
break;
|
||||
|
||||
|
@ -1,590 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
Copyright (c) 2011,2012 Intel Corp.
|
||||
|
||||
*/
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <crypto/hash.h>
|
||||
|
||||
#include "hci_request.h"
|
||||
#include "a2mp.h"
|
||||
#include "amp.h"
|
||||
|
||||
/* Remote AMP Controllers interface */
|
||||
void amp_ctrl_get(struct amp_ctrl *ctrl)
|
||||
{
|
||||
BT_DBG("ctrl %p orig refcnt %d", ctrl,
|
||||
kref_read(&ctrl->kref));
|
||||
|
||||
kref_get(&ctrl->kref);
|
||||
}
|
||||
|
||||
static void amp_ctrl_destroy(struct kref *kref)
|
||||
{
|
||||
struct amp_ctrl *ctrl = container_of(kref, struct amp_ctrl, kref);
|
||||
|
||||
BT_DBG("ctrl %p", ctrl);
|
||||
|
||||
kfree(ctrl->assoc);
|
||||
kfree(ctrl);
|
||||
}
|
||||
|
||||
int amp_ctrl_put(struct amp_ctrl *ctrl)
|
||||
{
|
||||
BT_DBG("ctrl %p orig refcnt %d", ctrl,
|
||||
kref_read(&ctrl->kref));
|
||||
|
||||
return kref_put(&ctrl->kref, &_ctrl_destroy);
|
||||
}
|
||||
|
||||
struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id)
|
||||
{
|
||||
struct amp_ctrl *ctrl;
|
||||
|
||||
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
||||
if (!ctrl)
|
||||
return NULL;
|
||||
|
||||
kref_init(&ctrl->kref);
|
||||
ctrl->id = id;
|
||||
|
||||
mutex_lock(&mgr->amp_ctrls_lock);
|
||||
list_add(&ctrl->list, &mgr->amp_ctrls);
|
||||
mutex_unlock(&mgr->amp_ctrls_lock);
|
||||
|
||||
BT_DBG("mgr %p ctrl %p", mgr, ctrl);
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
void amp_ctrl_list_flush(struct amp_mgr *mgr)
|
||||
{
|
||||
struct amp_ctrl *ctrl, *n;
|
||||
|
||||
BT_DBG("mgr %p", mgr);
|
||||
|
||||
mutex_lock(&mgr->amp_ctrls_lock);
|
||||
list_for_each_entry_safe(ctrl, n, &mgr->amp_ctrls, list) {
|
||||
list_del(&ctrl->list);
|
||||
amp_ctrl_put(ctrl);
|
||||
}
|
||||
mutex_unlock(&mgr->amp_ctrls_lock);
|
||||
}
|
||||
|
||||
struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id)
|
||||
{
|
||||
struct amp_ctrl *ctrl;
|
||||
|
||||
BT_DBG("mgr %p id %u", mgr, id);
|
||||
|
||||
mutex_lock(&mgr->amp_ctrls_lock);
|
||||
list_for_each_entry(ctrl, &mgr->amp_ctrls, list) {
|
||||
if (ctrl->id == id) {
|
||||
amp_ctrl_get(ctrl);
|
||||
mutex_unlock(&mgr->amp_ctrls_lock);
|
||||
return ctrl;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&mgr->amp_ctrls_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Physical Link interface */
|
||||
static u8 __next_handle(struct amp_mgr *mgr)
|
||||
{
|
||||
if (++mgr->handle == 0)
|
||||
mgr->handle = 1;
|
||||
|
||||
return mgr->handle;
|
||||
}
|
||||
|
||||
struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
u8 remote_id, bool out)
|
||||
{
|
||||
bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst;
|
||||
struct hci_conn *hcon;
|
||||
u8 role = out ? HCI_ROLE_MASTER : HCI_ROLE_SLAVE;
|
||||
|
||||
hcon = hci_conn_add(hdev, AMP_LINK, dst, role, __next_handle(mgr));
|
||||
if (!hcon)
|
||||
return NULL;
|
||||
|
||||
BT_DBG("hcon %p dst %pMR", hcon, dst);
|
||||
|
||||
hcon->state = BT_CONNECT;
|
||||
hcon->attempt++;
|
||||
hcon->remote_id = remote_id;
|
||||
hcon->amp_mgr = amp_mgr_get(mgr);
|
||||
|
||||
return hcon;
|
||||
}
|
||||
|
||||
/* AMP crypto key generation interface */
|
||||
static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *shash;
|
||||
int ret;
|
||||
|
||||
if (!ksize)
|
||||
return -EINVAL;
|
||||
|
||||
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
BT_DBG("crypto_alloc_ahash failed: err %ld", PTR_ERR(tfm));
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
|
||||
ret = crypto_shash_setkey(tfm, key, ksize);
|
||||
if (ret) {
|
||||
BT_DBG("crypto_ahash_setkey failed: err %d", ret);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
|
||||
GFP_KERNEL);
|
||||
if (!shash) {
|
||||
ret = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
shash->tfm = tfm;
|
||||
|
||||
ret = crypto_shash_digest(shash, plaintext, psize, output);
|
||||
|
||||
kfree(shash);
|
||||
|
||||
failed:
|
||||
crypto_free_shash(tfm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct link_key *key;
|
||||
u8 keybuf[HCI_AMP_LINK_KEY_SIZE];
|
||||
u8 gamp_key[HCI_AMP_LINK_KEY_SIZE];
|
||||
int err;
|
||||
|
||||
if (!hci_conn_check_link_mode(conn))
|
||||
return -EACCES;
|
||||
|
||||
BT_DBG("conn %p key_type %d", conn, conn->key_type);
|
||||
|
||||
/* Legacy key */
|
||||
if (conn->key_type < 3) {
|
||||
bt_dev_err(hdev, "legacy key type %u", conn->key_type);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
*type = conn->key_type;
|
||||
*len = HCI_AMP_LINK_KEY_SIZE;
|
||||
|
||||
key = hci_find_link_key(hdev, &conn->dst);
|
||||
if (!key) {
|
||||
BT_DBG("No Link key for conn %p dst %pMR", conn, &conn->dst);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/* BR/EDR Link Key concatenated together with itself */
|
||||
memcpy(&keybuf[0], key->val, HCI_LINK_KEY_SIZE);
|
||||
memcpy(&keybuf[HCI_LINK_KEY_SIZE], key->val, HCI_LINK_KEY_SIZE);
|
||||
|
||||
/* Derive Generic AMP Link Key (gamp) */
|
||||
err = hmac_sha256(keybuf, HCI_AMP_LINK_KEY_SIZE, "gamp", 4, gamp_key);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "could not derive Generic AMP Key: err %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (conn->key_type == HCI_LK_DEBUG_COMBINATION) {
|
||||
BT_DBG("Use Generic AMP Key (gamp)");
|
||||
memcpy(data, gamp_key, HCI_AMP_LINK_KEY_SIZE);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Derive Dedicated AMP Link Key: "802b" is 802.11 PAL keyID */
|
||||
return hmac_sha256(gamp_key, HCI_AMP_LINK_KEY_SIZE, "802b", 4, data);
|
||||
}
|
||||
|
||||
static void read_local_amp_assoc_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_local_amp_assoc *rp = (void *)skb->data;
|
||||
struct amp_assoc *assoc = &hdev->loc_assoc;
|
||||
size_t rem_len, frag_len;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
goto send_rsp;
|
||||
|
||||
frag_len = skb->len - sizeof(*rp);
|
||||
rem_len = __le16_to_cpu(rp->rem_len);
|
||||
|
||||
if (rem_len > frag_len) {
|
||||
BT_DBG("frag_len %zu rem_len %zu", frag_len, rem_len);
|
||||
|
||||
memcpy(assoc->data + assoc->offset, rp->frag, frag_len);
|
||||
assoc->offset += frag_len;
|
||||
|
||||
/* Read other fragments */
|
||||
amp_read_loc_assoc_frag(hdev, rp->phy_handle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(assoc->data + assoc->offset, rp->frag, rem_len);
|
||||
assoc->len = assoc->offset + rem_len;
|
||||
assoc->offset = 0;
|
||||
|
||||
send_rsp:
|
||||
/* Send A2MP Rsp when all fragments are received */
|
||||
a2mp_send_getampassoc_rsp(hdev, rp->status);
|
||||
a2mp_send_create_phy_link_req(hdev, rp->status);
|
||||
}
|
||||
|
||||
void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle)
|
||||
{
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct amp_assoc *loc_assoc = &hdev->loc_assoc;
|
||||
struct hci_request req;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s handle %u", hdev->name, phy_handle);
|
||||
|
||||
cp.phy_handle = phy_handle;
|
||||
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||
cp.len_so_far = cpu_to_le16(loc_assoc->offset);
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
err = hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID);
|
||||
}
|
||||
|
||||
void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
|
||||
{
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct hci_request req;
|
||||
int err;
|
||||
|
||||
memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc));
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||
|
||||
set_bit(READ_LOC_AMP_ASSOC, &mgr->state);
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
err = hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID);
|
||||
}
|
||||
|
||||
void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||
struct hci_conn *hcon)
|
||||
{
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct amp_mgr *mgr = hcon->amp_mgr;
|
||||
struct hci_request req;
|
||||
int err;
|
||||
|
||||
if (!mgr)
|
||||
return;
|
||||
|
||||
cp.phy_handle = hcon->handle;
|
||||
cp.len_so_far = cpu_to_le16(0);
|
||||
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||
|
||||
set_bit(READ_LOC_AMP_ASSOC_FINAL, &mgr->state);
|
||||
|
||||
/* Read Local AMP Assoc final link information data */
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
err = hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID);
|
||||
}
|
||||
|
||||
static void write_remote_amp_assoc_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_write_remote_amp_assoc *rp = (void *)skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x phy_handle 0x%2.2x",
|
||||
hdev->name, rp->status, rp->phy_handle);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
amp_write_rem_assoc_continue(hdev, rp->phy_handle);
|
||||
}
|
||||
|
||||
/* Write AMP Assoc data fragments, returns true with last fragment written*/
|
||||
static bool amp_write_rem_assoc_frag(struct hci_dev *hdev,
|
||||
struct hci_conn *hcon)
|
||||
{
|
||||
struct hci_cp_write_remote_amp_assoc *cp;
|
||||
struct amp_mgr *mgr = hcon->amp_mgr;
|
||||
struct amp_ctrl *ctrl;
|
||||
struct hci_request req;
|
||||
u16 frag_len, len;
|
||||
|
||||
ctrl = amp_ctrl_lookup(mgr, hcon->remote_id);
|
||||
if (!ctrl)
|
||||
return false;
|
||||
|
||||
if (!ctrl->assoc_rem_len) {
|
||||
BT_DBG("all fragments are written");
|
||||
ctrl->assoc_rem_len = ctrl->assoc_len;
|
||||
ctrl->assoc_len_so_far = 0;
|
||||
|
||||
amp_ctrl_put(ctrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
frag_len = min_t(u16, 248, ctrl->assoc_rem_len);
|
||||
len = frag_len + sizeof(*cp);
|
||||
|
||||
cp = kzalloc(len, GFP_KERNEL);
|
||||
if (!cp) {
|
||||
amp_ctrl_put(ctrl);
|
||||
return false;
|
||||
}
|
||||
|
||||
BT_DBG("hcon %p ctrl %p frag_len %u assoc_len %u rem_len %u",
|
||||
hcon, ctrl, frag_len, ctrl->assoc_len, ctrl->assoc_rem_len);
|
||||
|
||||
cp->phy_handle = hcon->handle;
|
||||
cp->len_so_far = cpu_to_le16(ctrl->assoc_len_so_far);
|
||||
cp->rem_len = cpu_to_le16(ctrl->assoc_rem_len);
|
||||
memcpy(cp->frag, ctrl->assoc, frag_len);
|
||||
|
||||
ctrl->assoc_len_so_far += frag_len;
|
||||
ctrl->assoc_rem_len -= frag_len;
|
||||
|
||||
amp_ctrl_put(ctrl);
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_WRITE_REMOTE_AMP_ASSOC, len, cp);
|
||||
hci_req_run_skb(&req, write_remote_amp_assoc_complete);
|
||||
|
||||
kfree(cp);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle)
|
||||
{
|
||||
struct hci_conn *hcon;
|
||||
|
||||
BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle);
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!hcon)
|
||||
return;
|
||||
|
||||
/* Send A2MP create phylink rsp when all fragments are written */
|
||||
if (amp_write_rem_assoc_frag(hdev, hcon))
|
||||
a2mp_send_create_phy_link_rsp(hdev, 0);
|
||||
}
|
||||
|
||||
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle)
|
||||
{
|
||||
struct hci_conn *hcon;
|
||||
|
||||
BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle);
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!hcon)
|
||||
return;
|
||||
|
||||
BT_DBG("%s phy handle 0x%2.2x hcon %p", hdev->name, handle, hcon);
|
||||
|
||||
amp_write_rem_assoc_frag(hdev, hcon);
|
||||
}
|
||||
|
||||
static void create_phylink_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct hci_cp_create_phy_link *cp;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (status) {
|
||||
struct hci_conn *hcon;
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, cp->phy_handle);
|
||||
if (hcon)
|
||||
hci_conn_del(hcon);
|
||||
} else {
|
||||
amp_write_remote_assoc(hdev, cp->phy_handle);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
struct hci_conn *hcon)
|
||||
{
|
||||
struct hci_cp_create_phy_link cp;
|
||||
struct hci_request req;
|
||||
|
||||
cp.phy_handle = hcon->handle;
|
||||
|
||||
BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon,
|
||||
hcon->handle);
|
||||
|
||||
if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len,
|
||||
&cp.key_type)) {
|
||||
BT_DBG("Cannot create link key");
|
||||
return;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp);
|
||||
hci_req_run(&req, create_phylink_complete);
|
||||
}
|
||||
|
||||
static void accept_phylink_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct hci_cp_accept_phy_link *cp;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHY_LINK);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
amp_write_remote_assoc(hdev, cp->phy_handle);
|
||||
}
|
||||
|
||||
void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
struct hci_conn *hcon)
|
||||
{
|
||||
struct hci_cp_accept_phy_link cp;
|
||||
struct hci_request req;
|
||||
|
||||
cp.phy_handle = hcon->handle;
|
||||
|
||||
BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon,
|
||||
hcon->handle);
|
||||
|
||||
if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len,
|
||||
&cp.key_type)) {
|
||||
BT_DBG("Cannot create link key");
|
||||
return;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
|
||||
hci_req_run(&req, accept_phylink_complete);
|
||||
}
|
||||
|
||||
void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
|
||||
{
|
||||
struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
|
||||
struct amp_mgr *mgr = hs_hcon->amp_mgr;
|
||||
struct l2cap_chan *bredr_chan;
|
||||
|
||||
BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr);
|
||||
|
||||
if (!bredr_hdev || !mgr || !mgr->bredr_chan)
|
||||
return;
|
||||
|
||||
bredr_chan = mgr->bredr_chan;
|
||||
|
||||
l2cap_chan_lock(bredr_chan);
|
||||
|
||||
set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags);
|
||||
bredr_chan->remote_amp_id = hs_hcon->remote_id;
|
||||
bredr_chan->local_amp_id = hs_hcon->hdev->id;
|
||||
bredr_chan->hs_hcon = hs_hcon;
|
||||
bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu;
|
||||
|
||||
__l2cap_physical_cfm(bredr_chan, 0);
|
||||
|
||||
l2cap_chan_unlock(bredr_chan);
|
||||
|
||||
hci_dev_put(bredr_hdev);
|
||||
}
|
||||
|
||||
void amp_create_logical_link(struct l2cap_chan *chan)
|
||||
{
|
||||
struct hci_conn *hs_hcon = chan->hs_hcon;
|
||||
struct hci_cp_create_accept_logical_link cp;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon,
|
||||
&chan->conn->hcon->dst);
|
||||
|
||||
if (!hs_hcon)
|
||||
return;
|
||||
|
||||
hdev = hci_dev_hold(chan->hs_hcon->hdev);
|
||||
if (!hdev)
|
||||
return;
|
||||
|
||||
cp.phy_handle = hs_hcon->handle;
|
||||
|
||||
cp.tx_flow_spec.id = chan->local_id;
|
||||
cp.tx_flow_spec.stype = chan->local_stype;
|
||||
cp.tx_flow_spec.msdu = cpu_to_le16(chan->local_msdu);
|
||||
cp.tx_flow_spec.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
|
||||
cp.tx_flow_spec.acc_lat = cpu_to_le32(chan->local_acc_lat);
|
||||
cp.tx_flow_spec.flush_to = cpu_to_le32(chan->local_flush_to);
|
||||
|
||||
cp.rx_flow_spec.id = chan->remote_id;
|
||||
cp.rx_flow_spec.stype = chan->remote_stype;
|
||||
cp.rx_flow_spec.msdu = cpu_to_le16(chan->remote_msdu);
|
||||
cp.rx_flow_spec.sdu_itime = cpu_to_le32(chan->remote_sdu_itime);
|
||||
cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat);
|
||||
cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to);
|
||||
|
||||
if (hs_hcon->out)
|
||||
hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
|
||||
&cp);
|
||||
else
|
||||
hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
|
||||
&cp);
|
||||
|
||||
hci_dev_put(hdev);
|
||||
}
|
||||
|
||||
void amp_disconnect_logical_link(struct hci_chan *hchan)
|
||||
{
|
||||
struct hci_conn *hcon = hchan->conn;
|
||||
struct hci_cp_disconn_logical_link cp;
|
||||
|
||||
if (hcon->state != BT_CONNECTED) {
|
||||
BT_DBG("hchan %p not connected", hchan);
|
||||
return;
|
||||
}
|
||||
|
||||
cp.log_handle = cpu_to_le16(hchan->handle);
|
||||
hci_send_cmd(hcon->hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason)
|
||||
{
|
||||
BT_DBG("hchan %p", hchan);
|
||||
|
||||
hci_chan_del(hchan);
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
Copyright (c) 2011,2012 Intel Corp.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __AMP_H
|
||||
#define __AMP_H
|
||||
|
||||
struct amp_ctrl {
|
||||
struct list_head list;
|
||||
struct kref kref;
|
||||
__u8 id;
|
||||
__u16 assoc_len_so_far;
|
||||
__u16 assoc_rem_len;
|
||||
__u16 assoc_len;
|
||||
__u8 *assoc;
|
||||
};
|
||||
|
||||
int amp_ctrl_put(struct amp_ctrl *ctrl);
|
||||
void amp_ctrl_get(struct amp_ctrl *ctrl);
|
||||
struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id);
|
||||
struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id);
|
||||
void amp_ctrl_list_flush(struct amp_mgr *mgr);
|
||||
|
||||
struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
u8 remote_id, bool out);
|
||||
|
||||
int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
|
||||
|
||||
void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
|
||||
void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
|
||||
void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||
struct hci_conn *hcon);
|
||||
void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
struct hci_conn *hcon);
|
||||
void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
struct hci_conn *hcon);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_HS)
|
||||
void amp_create_logical_link(struct l2cap_chan *chan);
|
||||
void amp_disconnect_logical_link(struct hci_chan *hchan);
|
||||
#else
|
||||
static inline void amp_create_logical_link(struct l2cap_chan *chan)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void amp_disconnect_logical_link(struct hci_chan *hchan)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
|
||||
void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
|
||||
void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
|
||||
void amp_create_logical_link(struct l2cap_chan *chan);
|
||||
void amp_disconnect_logical_link(struct hci_chan *hchan);
|
||||
void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason);
|
||||
|
||||
#endif /* __AMP_H */
|
@ -385,7 +385,8 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
|
||||
|
||||
case BNEP_COMPRESSED_DST_ONLY:
|
||||
__skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN);
|
||||
__skb_put_data(nskb, s->eh.h_source, ETH_ALEN + 2);
|
||||
__skb_put_data(nskb, s->eh.h_source, ETH_ALEN);
|
||||
put_unaligned(s->eh.h_proto, (__be16 *)__skb_put(nskb, 2));
|
||||
break;
|
||||
|
||||
case BNEP_GENERAL:
|
||||
@ -549,7 +550,7 @@ static struct device *bnep_get_device(struct bnep_session *session)
|
||||
return &conn->hcon->dev;
|
||||
}
|
||||
|
||||
static struct device_type bnep_type = {
|
||||
static const struct device_type bnep_type = {
|
||||
.name = "bluetooth",
|
||||
};
|
||||
|
||||
|
@ -13,48 +13,33 @@
|
||||
|
||||
#define PNP_INFO_SVCLASS_ID 0x1200
|
||||
|
||||
static u8 eir_append_name(u8 *eir, u16 eir_len, u8 type, u8 *data, u8 data_len)
|
||||
{
|
||||
u8 name[HCI_MAX_SHORT_NAME_LENGTH + 1];
|
||||
|
||||
/* If data is already NULL terminated just pass it directly */
|
||||
if (data[data_len - 1] == '\0')
|
||||
return eir_append_data(eir, eir_len, type, data, data_len);
|
||||
|
||||
memcpy(name, data, HCI_MAX_SHORT_NAME_LENGTH);
|
||||
name[HCI_MAX_SHORT_NAME_LENGTH] = '\0';
|
||||
|
||||
return eir_append_data(eir, eir_len, type, name, sizeof(name));
|
||||
}
|
||||
|
||||
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 ((max_adv_len(hdev) - ad_len) < HCI_MAX_SHORT_NAME_LENGTH + 3)
|
||||
/* no space left for name (+ type + len) */
|
||||
if ((max_adv_len(hdev) - ad_len) < HCI_MAX_SHORT_NAME_LENGTH + 2)
|
||||
return ad_len;
|
||||
|
||||
/* use complete name if present and fits */
|
||||
complete_len = strnlen(hdev->dev_name, sizeof(hdev->dev_name));
|
||||
if (complete_len && complete_len <= HCI_MAX_SHORT_NAME_LENGTH)
|
||||
return eir_append_name(ptr, ad_len, EIR_NAME_COMPLETE,
|
||||
hdev->dev_name, complete_len + 1);
|
||||
return eir_append_data(ptr, ad_len, EIR_NAME_COMPLETE,
|
||||
hdev->dev_name, complete_len);
|
||||
|
||||
/* use short name if present */
|
||||
short_len = strnlen(hdev->short_name, sizeof(hdev->short_name));
|
||||
if (short_len)
|
||||
return eir_append_name(ptr, ad_len, EIR_NAME_SHORT,
|
||||
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT,
|
||||
hdev->short_name,
|
||||
short_len == HCI_MAX_SHORT_NAME_LENGTH ?
|
||||
short_len : short_len + 1);
|
||||
short_len);
|
||||
|
||||
/* use shortened full name if present, we already know that name
|
||||
* is longer then HCI_MAX_SHORT_NAME_LENGTH
|
||||
*/
|
||||
if (complete_len)
|
||||
return eir_append_name(ptr, ad_len, EIR_NAME_SHORT,
|
||||
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT,
|
||||
hdev->dev_name,
|
||||
HCI_MAX_SHORT_NAME_LENGTH);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
||||
Copyright 2023 NXP
|
||||
Copyright 2023-2024 NXP
|
||||
|
||||
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
|
||||
@ -36,7 +36,6 @@
|
||||
|
||||
#include "hci_request.h"
|
||||
#include "smp.h"
|
||||
#include "a2mp.h"
|
||||
#include "eir.h"
|
||||
|
||||
struct sco_param {
|
||||
@ -69,7 +68,7 @@ static const struct sco_param esco_param_msbc[] = {
|
||||
};
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
|
||||
void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
|
||||
{
|
||||
struct hci_conn_params *params;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
@ -179,64 +178,6 @@ static void hci_conn_cleanup(struct hci_conn *conn)
|
||||
hci_dev_put(hdev);
|
||||
}
|
||||
|
||||
static void hci_acl_create_connection(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct inquiry_entry *ie;
|
||||
struct hci_cp_create_conn cp;
|
||||
|
||||
BT_DBG("hcon %p", conn);
|
||||
|
||||
/* Many controllers disallow HCI Create Connection while it is doing
|
||||
* HCI Inquiry. So we cancel the Inquiry first before issuing HCI Create
|
||||
* Connection. This may cause the MGMT discovering state to become false
|
||||
* without user space's request but it is okay since the MGMT Discovery
|
||||
* APIs do not promise that discovery should be done forever. Instead,
|
||||
* the user space monitors the status of MGMT discovering and it may
|
||||
* request for discovery again when this flag becomes false.
|
||||
*/
|
||||
if (test_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||
/* Put this connection to "pending" state so that it will be
|
||||
* executed after the inquiry cancel command complete event.
|
||||
*/
|
||||
conn->state = BT_CONNECT2;
|
||||
hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = true;
|
||||
conn->role = HCI_ROLE_MASTER;
|
||||
|
||||
conn->attempt++;
|
||||
|
||||
conn->link_policy = hdev->link_policy;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
bacpy(&cp.bdaddr, &conn->dst);
|
||||
cp.pscan_rep_mode = 0x02;
|
||||
|
||||
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
|
||||
if (ie) {
|
||||
if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
|
||||
cp.pscan_rep_mode = ie->data.pscan_rep_mode;
|
||||
cp.pscan_mode = ie->data.pscan_mode;
|
||||
cp.clock_offset = ie->data.clock_offset |
|
||||
cpu_to_le16(0x8000);
|
||||
}
|
||||
|
||||
memcpy(conn->dev_class, ie->data.dev_class, 3);
|
||||
}
|
||||
|
||||
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
||||
if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
|
||||
cp.role_switch = 0x01;
|
||||
else
|
||||
cp.role_switch = 0x00;
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
int hci_disconnect(struct hci_conn *conn, __u8 reason)
|
||||
{
|
||||
BT_DBG("hcon %p", conn);
|
||||
@ -1175,9 +1116,6 @@ void hci_conn_del(struct hci_conn *conn)
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->amp_mgr)
|
||||
amp_mgr_put(conn->amp_mgr);
|
||||
|
||||
skb_queue_purge(&conn->data_q);
|
||||
|
||||
/* Remove the connection from the list and cleanup its remaining
|
||||
@ -1186,6 +1124,9 @@ void hci_conn_del(struct hci_conn *conn)
|
||||
* rest of hci_conn_del.
|
||||
*/
|
||||
hci_conn_cleanup(conn);
|
||||
|
||||
/* Dequeue callbacks using connection pointer as data */
|
||||
hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
|
||||
}
|
||||
|
||||
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, uint8_t src_type)
|
||||
@ -1320,53 +1261,6 @@ u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
u16 handle = PTR_UINT(data);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!err) {
|
||||
hci_connect_le_scan_cleanup(conn, 0x00);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if connection is still pending */
|
||||
if (conn != hci_lookup_le_connect(hdev))
|
||||
goto done;
|
||||
|
||||
/* Flush to make sure we send create conn cancel command if needed */
|
||||
flush_delayed_work(&conn->le_conn_timeout);
|
||||
hci_conn_failed(conn, bt_status(err));
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int hci_connect_le_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
u16 handle = PTR_UINT(data);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
clear_bit(HCI_CONN_SCANNING, &conn->flags);
|
||||
conn->state = BT_CONNECT;
|
||||
|
||||
return hci_le_create_conn_sync(hdev, conn);
|
||||
}
|
||||
|
||||
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
u8 dst_type, bool dst_resolved, u8 sec_level,
|
||||
u16 conn_timeout, u8 role)
|
||||
@ -1433,9 +1327,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
conn->conn_timeout = conn_timeout;
|
||||
|
||||
err = hci_cmd_sync_queue(hdev, hci_connect_le_sync,
|
||||
UINT_PTR(conn->handle),
|
||||
create_le_conn_complete);
|
||||
err = hci_connect_le_sync(hdev, conn);
|
||||
if (err) {
|
||||
hci_conn_del(conn);
|
||||
return ERR_PTR(err);
|
||||
@ -1669,7 +1561,7 @@ done:
|
||||
|
||||
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
u8 sec_level, u8 auth_type,
|
||||
enum conn_reasons conn_reason)
|
||||
enum conn_reasons conn_reason, u16 timeout)
|
||||
{
|
||||
struct hci_conn *acl;
|
||||
|
||||
@ -1700,10 +1592,18 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
|
||||
acl->conn_reason = conn_reason;
|
||||
if (acl->state == BT_OPEN || acl->state == BT_CLOSED) {
|
||||
int err;
|
||||
|
||||
acl->sec_level = BT_SECURITY_LOW;
|
||||
acl->pending_sec_level = sec_level;
|
||||
acl->auth_type = auth_type;
|
||||
hci_acl_create_connection(acl);
|
||||
acl->conn_timeout = timeout;
|
||||
|
||||
err = hci_connect_acl_sync(hdev, acl);
|
||||
if (err) {
|
||||
hci_conn_del(acl);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
}
|
||||
|
||||
return acl;
|
||||
@ -1738,14 +1638,15 @@ static struct hci_link *hci_conn_link(struct hci_conn *parent,
|
||||
}
|
||||
|
||||
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
__u16 setting, struct bt_codec *codec)
|
||||
__u16 setting, struct bt_codec *codec,
|
||||
u16 timeout)
|
||||
{
|
||||
struct hci_conn *acl;
|
||||
struct hci_conn *sco;
|
||||
struct hci_link *link;
|
||||
|
||||
acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING,
|
||||
CONN_REASON_SCO_CONNECT);
|
||||
CONN_REASON_SCO_CONNECT, timeout);
|
||||
if (IS_ERR(acl))
|
||||
return acl;
|
||||
|
||||
@ -2156,18 +2057,31 @@ static int create_pa_sync(struct hci_dev *hdev, void *data)
|
||||
return hci_update_passive_scan_sync(hdev);
|
||||
}
|
||||
|
||||
int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
||||
__u8 sid, struct bt_iso_qos *qos)
|
||||
struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, __u8 sid,
|
||||
struct bt_iso_qos *qos)
|
||||
{
|
||||
struct hci_cp_le_pa_create_sync *cp;
|
||||
struct hci_conn *conn;
|
||||
int err;
|
||||
|
||||
if (hci_dev_test_and_set_flag(hdev, HCI_PA_SYNC))
|
||||
return -EBUSY;
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_SLAVE);
|
||||
if (!conn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
conn->iso_qos = *qos;
|
||||
conn->state = BT_LISTEN;
|
||||
|
||||
hci_conn_hold(conn);
|
||||
|
||||
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
|
||||
if (!cp) {
|
||||
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
|
||||
return -ENOMEM;
|
||||
hci_conn_drop(conn);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
cp->options = qos->bcast.options;
|
||||
@ -2179,7 +2093,14 @@ int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
||||
cp->sync_cte_type = qos->bcast.sync_cte_type;
|
||||
|
||||
/* Queue start pa_create_sync and scan */
|
||||
return hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete);
|
||||
err = hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete);
|
||||
if (err < 0) {
|
||||
hci_conn_drop(conn);
|
||||
kfree(cp);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
int hci_le_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
|
||||
@ -2647,22 +2568,6 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check pending connect attempts */
|
||||
void hci_conn_check_pending(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("hdev %s", hdev->name);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2);
|
||||
if (conn)
|
||||
hci_acl_create_connection(conn);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static u32 get_link_mode(struct hci_conn *conn)
|
||||
{
|
||||
u32 link_mode = 0;
|
||||
@ -2978,12 +2883,10 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
|
||||
|
||||
static int abort_conn_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
u16 handle = PTR_UINT(data);
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
return 0;
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
return -ECANCELED;
|
||||
|
||||
return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
|
||||
}
|
||||
@ -3011,14 +2914,17 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
||||
*/
|
||||
if (conn->state == BT_CONNECT && hdev->req_status == HCI_REQ_PEND) {
|
||||
switch (hci_skb_event(hdev->sent_cmd)) {
|
||||
case HCI_EV_CONN_COMPLETE:
|
||||
case HCI_EV_LE_CONN_COMPLETE:
|
||||
case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
|
||||
case HCI_EVT_LE_CIS_ESTABLISHED:
|
||||
hci_cmd_sync_cancel(hdev, -ECANCELED);
|
||||
hci_cmd_sync_cancel(hdev, ECANCELED);
|
||||
break;
|
||||
}
|
||||
/* Cancel connect attempt if still queued/pending */
|
||||
} else if (!hci_cancel_connect_sync(hdev, conn)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hci_cmd_sync_queue(hdev, abort_conn_sync, UINT_PTR(conn->handle),
|
||||
NULL);
|
||||
return hci_cmd_sync_queue_once(hdev, abort_conn_sync, conn, NULL);
|
||||
}
|
||||
|
@ -908,7 +908,7 @@ int hci_get_dev_info(void __user *arg)
|
||||
else
|
||||
flags = hdev->flags;
|
||||
|
||||
strcpy(di.name, hdev->name);
|
||||
strscpy(di.name, hdev->name, sizeof(di.name));
|
||||
di.bdaddr = hdev->bdaddr;
|
||||
di.type = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4);
|
||||
di.flags = flags;
|
||||
@ -940,20 +940,51 @@ int hci_get_dev_info(void __user *arg)
|
||||
|
||||
/* ---- Interface to HCI drivers ---- */
|
||||
|
||||
static int hci_dev_do_poweroff(struct hci_dev *hdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
BT_DBG("%s %p", hdev->name, hdev);
|
||||
|
||||
hci_req_sync_lock(hdev);
|
||||
|
||||
err = hci_set_powered_sync(hdev, false);
|
||||
|
||||
hci_req_sync_unlock(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hci_rfkill_set_block(void *data, bool blocked)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
int err;
|
||||
|
||||
BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
|
||||
return -EBUSY;
|
||||
|
||||
if (blocked == hci_dev_test_flag(hdev, HCI_RFKILLED))
|
||||
return 0;
|
||||
|
||||
if (blocked) {
|
||||
hci_dev_set_flag(hdev, HCI_RFKILLED);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
|
||||
!hci_dev_test_flag(hdev, HCI_CONFIG))
|
||||
hci_dev_do_close(hdev);
|
||||
!hci_dev_test_flag(hdev, HCI_CONFIG)) {
|
||||
err = hci_dev_do_poweroff(hdev);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Error when powering off device on rfkill (%d)",
|
||||
err);
|
||||
|
||||
/* Make sure the device is still closed even if
|
||||
* anything during power off sequence (eg.
|
||||
* disconnecting devices) failed.
|
||||
*/
|
||||
hci_dev_do_close(hdev);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hci_dev_clear_flag(hdev, HCI_RFKILLED);
|
||||
}
|
||||
@ -1491,11 +1522,12 @@ static void hci_cmd_timeout(struct work_struct *work)
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
cmd_timer.work);
|
||||
|
||||
if (hdev->sent_cmd) {
|
||||
struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data;
|
||||
u16 opcode = __le16_to_cpu(sent->opcode);
|
||||
if (hdev->req_skb) {
|
||||
u16 opcode = hci_skb_opcode(hdev->req_skb);
|
||||
|
||||
bt_dev_err(hdev, "command 0x%4.4x tx timeout", opcode);
|
||||
|
||||
hci_cmd_sync_cancel_sync(hdev, ETIMEDOUT);
|
||||
} else {
|
||||
bt_dev_err(hdev, "command tx timeout");
|
||||
}
|
||||
@ -2608,10 +2640,11 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
*/
|
||||
switch (hdev->dev_type) {
|
||||
case HCI_PRIMARY:
|
||||
id = ida_simple_get(&hci_index_ida, 0, HCI_MAX_ID, GFP_KERNEL);
|
||||
id = ida_alloc_max(&hci_index_ida, HCI_MAX_ID - 1, GFP_KERNEL);
|
||||
break;
|
||||
case HCI_AMP:
|
||||
id = ida_simple_get(&hci_index_ida, 1, HCI_MAX_ID, GFP_KERNEL);
|
||||
id = ida_alloc_range(&hci_index_ida, 1, HCI_MAX_ID - 1,
|
||||
GFP_KERNEL);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -2710,7 +2743,7 @@ err_wqueue:
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
destroy_workqueue(hdev->req_workqueue);
|
||||
err:
|
||||
ida_simple_remove(&hci_index_ida, hdev->id);
|
||||
ida_free(&hci_index_ida, hdev->id);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -2793,8 +2826,9 @@ void hci_release_dev(struct hci_dev *hdev)
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
ida_destroy(&hdev->unset_handle_ida);
|
||||
ida_simple_remove(&hci_index_ida, hdev->id);
|
||||
ida_free(&hci_index_ida, hdev->id);
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
kfree_skb(hdev->req_skb);
|
||||
kfree_skb(hdev->recv_event);
|
||||
kfree(hdev);
|
||||
}
|
||||
@ -2826,6 +2860,23 @@ int hci_unregister_suspend_notifier(struct hci_dev *hdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Cancel ongoing command synchronously:
|
||||
*
|
||||
* - Cancel command timer
|
||||
* - Reset command counter
|
||||
* - Cancel command request
|
||||
*/
|
||||
static void hci_cancel_cmd_sync(struct hci_dev *hdev, int err)
|
||||
{
|
||||
bt_dev_dbg(hdev, "err 0x%2.2x", err);
|
||||
|
||||
cancel_delayed_work_sync(&hdev->cmd_timer);
|
||||
cancel_delayed_work_sync(&hdev->ncmd_timer);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
|
||||
hci_cmd_sync_cancel_sync(hdev, -err);
|
||||
}
|
||||
|
||||
/* Suspend HCI device */
|
||||
int hci_suspend_dev(struct hci_dev *hdev)
|
||||
{
|
||||
@ -2843,7 +2894,7 @@ int hci_suspend_dev(struct hci_dev *hdev)
|
||||
return 0;
|
||||
|
||||
/* Cancel potentially blocking sync operation before suspend */
|
||||
__hci_cmd_sync_cancel(hdev, -EHOSTDOWN);
|
||||
hci_cancel_cmd_sync(hdev, -EHOSTDOWN);
|
||||
|
||||
hci_req_sync_lock(hdev);
|
||||
ret = hci_suspend_sync(hdev);
|
||||
@ -3107,21 +3158,33 @@ int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
EXPORT_SYMBOL(__hci_cmd_send);
|
||||
|
||||
/* Get data from the previously sent command */
|
||||
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
|
||||
static void *hci_cmd_data(struct sk_buff *skb, __u16 opcode)
|
||||
{
|
||||
struct hci_command_hdr *hdr;
|
||||
|
||||
if (!hdev->sent_cmd)
|
||||
if (!skb || skb->len < HCI_COMMAND_HDR_SIZE)
|
||||
return NULL;
|
||||
|
||||
hdr = (void *) hdev->sent_cmd->data;
|
||||
hdr = (void *)skb->data;
|
||||
|
||||
if (hdr->opcode != cpu_to_le16(opcode))
|
||||
return NULL;
|
||||
|
||||
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
|
||||
return skb->data + HCI_COMMAND_HDR_SIZE;
|
||||
}
|
||||
|
||||
return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
|
||||
/* Get data from the previously sent command */
|
||||
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
|
||||
{
|
||||
void *data;
|
||||
|
||||
/* Check if opcode matches last sent command */
|
||||
data = hci_cmd_data(hdev->sent_cmd, opcode);
|
||||
if (!data)
|
||||
/* Check if opcode matches last request */
|
||||
data = hci_cmd_data(hdev->req_skb, opcode);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Get data from last received event */
|
||||
@ -4022,17 +4085,19 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
|
||||
if (!status && !hci_req_is_complete(hdev))
|
||||
return;
|
||||
|
||||
skb = hdev->req_skb;
|
||||
|
||||
/* If this was the last command in a request the complete
|
||||
* callback would be found in hdev->sent_cmd instead of the
|
||||
* callback would be found in hdev->req_skb instead of the
|
||||
* command queue (hdev->cmd_q).
|
||||
*/
|
||||
if (bt_cb(hdev->sent_cmd)->hci.req_flags & HCI_REQ_SKB) {
|
||||
*req_complete_skb = bt_cb(hdev->sent_cmd)->hci.req_complete_skb;
|
||||
if (skb && bt_cb(skb)->hci.req_flags & HCI_REQ_SKB) {
|
||||
*req_complete_skb = bt_cb(skb)->hci.req_complete_skb;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bt_cb(hdev->sent_cmd)->hci.req_complete) {
|
||||
*req_complete = bt_cb(hdev->sent_cmd)->hci.req_complete;
|
||||
if (skb && bt_cb(skb)->hci.req_complete) {
|
||||
*req_complete = bt_cb(skb)->hci.req_complete;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4128,6 +4193,36 @@ static void hci_rx_work(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
|
||||
bt_dev_dbg(hdev, "skb %p", skb);
|
||||
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
|
||||
hdev->sent_cmd = skb_clone(skb, GFP_KERNEL);
|
||||
if (!hdev->sent_cmd) {
|
||||
skb_queue_head(&hdev->cmd_q, skb);
|
||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||
return;
|
||||
}
|
||||
|
||||
err = hci_send_frame(hdev, skb);
|
||||
if (err < 0) {
|
||||
hci_cmd_sync_cancel_sync(hdev, err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hci_req_status_pend(hdev) &&
|
||||
!hci_dev_test_and_set_flag(hdev, HCI_CMD_PENDING)) {
|
||||
kfree_skb(hdev->req_skb);
|
||||
hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
|
||||
}
|
||||
|
||||
atomic_dec(&hdev->cmd_cnt);
|
||||
}
|
||||
|
||||
static void hci_cmd_work(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_work);
|
||||
@ -4142,30 +4237,15 @@ static void hci_cmd_work(struct work_struct *work)
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
hci_send_cmd_sync(hdev, skb);
|
||||
|
||||
hdev->sent_cmd = skb_clone(skb, GFP_KERNEL);
|
||||
if (hdev->sent_cmd) {
|
||||
int res;
|
||||
if (hci_req_status_pend(hdev))
|
||||
hci_dev_set_flag(hdev, HCI_CMD_PENDING);
|
||||
atomic_dec(&hdev->cmd_cnt);
|
||||
|
||||
res = hci_send_frame(hdev, skb);
|
||||
if (res < 0)
|
||||
__hci_cmd_sync_cancel(hdev, -res);
|
||||
|
||||
rcu_read_lock();
|
||||
if (test_bit(HCI_RESET, &hdev->flags) ||
|
||||
hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
|
||||
cancel_delayed_work(&hdev->cmd_timer);
|
||||
else
|
||||
queue_delayed_work(hdev->workqueue, &hdev->cmd_timer,
|
||||
HCI_CMD_TIMEOUT);
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
skb_queue_head(&hdev->cmd_q, skb);
|
||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||
}
|
||||
rcu_read_lock();
|
||||
if (test_bit(HCI_RESET, &hdev->flags) ||
|
||||
hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
|
||||
cancel_delayed_work(&hdev->cmd_timer);
|
||||
else
|
||||
queue_delayed_work(hdev->workqueue, &hdev->cmd_timer,
|
||||
HCI_CMD_TIMEOUT);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,6 @@
|
||||
#include "hci_request.h"
|
||||
#include "hci_debugfs.h"
|
||||
#include "hci_codec.h"
|
||||
#include "a2mp.h"
|
||||
#include "amp.h"
|
||||
#include "smp.h"
|
||||
#include "msft.h"
|
||||
#include "eir.h"
|
||||
@ -95,11 +93,11 @@ static u8 hci_cc_inquiry_cancel(struct hci_dev *hdev, void *data,
|
||||
/* It is possible that we receive Inquiry Complete event right
|
||||
* before we receive Inquiry Cancel Command Complete event, in
|
||||
* which case the latter event should have status of Command
|
||||
* Disallowed (0x0c). This should not be treated as error, since
|
||||
* Disallowed. This should not be treated as error, since
|
||||
* we actually achieve what Inquiry Cancel wants to achieve,
|
||||
* which is to end the last Inquiry session.
|
||||
*/
|
||||
if (rp->status == 0x0c && !test_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||
if (rp->status == HCI_ERROR_COMMAND_DISALLOWED && !test_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||
bt_dev_warn(hdev, "Ignoring error of Inquiry Cancel command");
|
||||
rp->status = 0x00;
|
||||
}
|
||||
@ -120,8 +118,6 @@ static u8 hci_cc_inquiry_cancel(struct hci_dev *hdev, void *data,
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
hci_conn_check_pending(hdev);
|
||||
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
@ -152,8 +148,6 @@ static u8 hci_cc_exit_periodic_inq(struct hci_dev *hdev, void *data,
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ);
|
||||
|
||||
hci_conn_check_pending(hdev);
|
||||
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
@ -2314,10 +2308,8 @@ static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
|
||||
if (status) {
|
||||
hci_conn_check_pending(hdev);
|
||||
if (status)
|
||||
return;
|
||||
}
|
||||
|
||||
if (hci_sent_cmd_data(hdev, HCI_OP_INQUIRY))
|
||||
set_bit(HCI_INQUIRY, &hdev->flags);
|
||||
@ -2342,12 +2334,9 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
|
||||
|
||||
if (status) {
|
||||
if (conn && conn->state == BT_CONNECT) {
|
||||
if (status != 0x0c || conn->attempt > 2) {
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_del(conn);
|
||||
} else
|
||||
conn->state = BT_CONNECT2;
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
} else {
|
||||
if (!conn) {
|
||||
@ -2526,9 +2515,7 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
* Only those in BT_CONFIG or BT_CONNECTED states can be
|
||||
* considered connected.
|
||||
*/
|
||||
if (conn &&
|
||||
(conn->state == BT_CONFIG || conn->state == BT_CONNECTED) &&
|
||||
!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
if (conn && (conn->state == BT_CONFIG || conn->state == BT_CONNECTED))
|
||||
mgmt_device_connected(hdev, conn, name, name_len);
|
||||
|
||||
if (discov->state == DISCOVERY_STOPPED)
|
||||
@ -3039,8 +3026,6 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
hci_conn_check_pending(hdev);
|
||||
|
||||
if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
|
||||
return;
|
||||
|
||||
@ -3262,8 +3247,6 @@ done:
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
hci_conn_check_pending(hdev);
|
||||
}
|
||||
|
||||
static void hci_reject_conn(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
@ -3556,8 +3539,6 @@ static void hci_remote_name_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
hci_conn_check_pending(hdev);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
||||
@ -3660,7 +3641,8 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
||||
* controller really supports it. If it doesn't, assume
|
||||
* the default size (16).
|
||||
*/
|
||||
if (!(hdev->commands[20] & 0x10)) {
|
||||
if (!(hdev->commands[20] & 0x10) ||
|
||||
test_bit(HCI_QUIRK_BROKEN_READ_ENC_KEY_SIZE, &hdev->quirks)) {
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
goto notify;
|
||||
}
|
||||
@ -3762,8 +3744,9 @@ static void hci_remote_features_evt(struct hci_dev *hdev, void *data,
|
||||
bacpy(&cp.bdaddr, &conn->dst);
|
||||
cp.pscan_rep_mode = 0x02;
|
||||
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
|
||||
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
} else {
|
||||
mgmt_device_connected(hdev, conn, NULL, 0);
|
||||
}
|
||||
|
||||
if (!hci_outgoing_auth_needed(hdev, conn)) {
|
||||
conn->state = BT_CONNECTED;
|
||||
@ -3936,6 +3919,11 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
|
||||
* last.
|
||||
*/
|
||||
hci_connect_cfm(conn, rp->status);
|
||||
|
||||
/* Notify device connected in case it is a BIG Sync */
|
||||
if (!rp->status && test_bit(HCI_CONN_BIG_SYNC, &conn->flags))
|
||||
mgmt_device_connected(hdev, conn, NULL, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -4381,7 +4369,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, void *data,
|
||||
* (since for this kind of commands there will not be a command
|
||||
* complete event).
|
||||
*/
|
||||
if (ev->status || (hdev->sent_cmd && !hci_skb_event(hdev->sent_cmd))) {
|
||||
if (ev->status || (hdev->req_skb && !hci_skb_event(hdev->req_skb))) {
|
||||
hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
|
||||
req_complete_skb);
|
||||
if (hci_dev_test_flag(hdev, HCI_CMD_PENDING)) {
|
||||
@ -5010,8 +4998,9 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, void *data,
|
||||
bacpy(&cp.bdaddr, &conn->dst);
|
||||
cp.pscan_rep_mode = 0x02;
|
||||
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
|
||||
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
} else {
|
||||
mgmt_device_connected(hdev, conn, NULL, 0);
|
||||
}
|
||||
|
||||
if (!hci_outgoing_auth_needed(hdev, conn)) {
|
||||
conn->state = BT_CONNECTED;
|
||||
@ -5675,150 +5664,6 @@ unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_HS)
|
||||
static void hci_chan_selected_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_channel_selected *ev = data;
|
||||
struct hci_conn *hcon;
|
||||
|
||||
bt_dev_dbg(hdev, "handle 0x%2.2x", ev->phy_handle);
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
||||
if (!hcon)
|
||||
return;
|
||||
|
||||
amp_read_loc_assoc_final_data(hdev, hcon);
|
||||
}
|
||||
|
||||
static void hci_phy_link_complete_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_phy_link_complete *ev = data;
|
||||
struct hci_conn *hcon, *bredr_hcon;
|
||||
|
||||
bt_dev_dbg(hdev, "handle 0x%2.2x status 0x%2.2x", ev->phy_handle,
|
||||
ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
||||
if (!hcon)
|
||||
goto unlock;
|
||||
|
||||
if (!hcon->amp_mgr)
|
||||
goto unlock;
|
||||
|
||||
if (ev->status) {
|
||||
hci_conn_del(hcon);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon;
|
||||
|
||||
hcon->state = BT_CONNECTED;
|
||||
bacpy(&hcon->dst, &bredr_hcon->dst);
|
||||
|
||||
hci_conn_hold(hcon);
|
||||
hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
hci_conn_drop(hcon);
|
||||
|
||||
hci_debugfs_create_conn(hcon);
|
||||
hci_conn_add_sysfs(hcon);
|
||||
|
||||
amp_physical_cfm(bredr_hcon, hcon);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_loglink_complete_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_logical_link_complete *ev = data;
|
||||
struct hci_conn *hcon;
|
||||
struct hci_chan *hchan;
|
||||
struct amp_mgr *mgr;
|
||||
|
||||
bt_dev_dbg(hdev, "log_handle 0x%4.4x phy_handle 0x%2.2x status 0x%2.2x",
|
||||
le16_to_cpu(ev->handle), ev->phy_handle, ev->status);
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
||||
if (!hcon)
|
||||
return;
|
||||
|
||||
/* Create AMP hchan */
|
||||
hchan = hci_chan_create(hcon);
|
||||
if (!hchan)
|
||||
return;
|
||||
|
||||
hchan->handle = le16_to_cpu(ev->handle);
|
||||
hchan->amp = true;
|
||||
|
||||
BT_DBG("hcon %p mgr %p hchan %p", hcon, hcon->amp_mgr, hchan);
|
||||
|
||||
mgr = hcon->amp_mgr;
|
||||
if (mgr && mgr->bredr_chan) {
|
||||
struct l2cap_chan *bredr_chan = mgr->bredr_chan;
|
||||
|
||||
l2cap_chan_lock(bredr_chan);
|
||||
|
||||
bredr_chan->conn->mtu = hdev->block_mtu;
|
||||
l2cap_logical_cfm(bredr_chan, hchan, 0);
|
||||
hci_conn_hold(hcon);
|
||||
|
||||
l2cap_chan_unlock(bredr_chan);
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_disconn_loglink_complete_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_disconn_logical_link_complete *ev = data;
|
||||
struct hci_chan *hchan;
|
||||
|
||||
bt_dev_dbg(hdev, "handle 0x%4.4x status 0x%2.2x",
|
||||
le16_to_cpu(ev->handle), ev->status);
|
||||
|
||||
if (ev->status)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hchan = hci_chan_lookup_handle(hdev, le16_to_cpu(ev->handle));
|
||||
if (!hchan || !hchan->amp)
|
||||
goto unlock;
|
||||
|
||||
amp_destroy_logical_link(hchan, ev->reason);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_disconn_phy_link_complete *ev = data;
|
||||
struct hci_conn *hcon;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
if (ev->status)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
||||
if (hcon && hcon->type == AMP_LINK) {
|
||||
hcon->state = BT_CLOSED;
|
||||
hci_disconn_cfm(hcon, ev->reason);
|
||||
hci_conn_del(hcon);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void le_conn_update_addr(struct hci_conn *conn, bdaddr_t *bdaddr,
|
||||
u8 bdaddr_type, bdaddr_t *local_rpa)
|
||||
{
|
||||
@ -5984,8 +5829,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
mgmt_device_connected(hdev, conn, NULL, 0);
|
||||
mgmt_device_connected(hdev, conn, NULL, 0);
|
||||
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
conn->state = BT_CONFIG;
|
||||
@ -6684,7 +6528,7 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
|
||||
* transition into connected state and mark it as
|
||||
* successful.
|
||||
*/
|
||||
if (!conn->out && ev->status == 0x1a &&
|
||||
if (!conn->out && ev->status == HCI_ERROR_UNSUPPORTED_REMOTE_FEATURE &&
|
||||
(hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
|
||||
status = 0x00;
|
||||
else
|
||||
@ -7214,6 +7058,9 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
/* Notify iso layer */
|
||||
hci_connect_cfm(pa_sync, 0x00);
|
||||
|
||||
/* Notify MGMT layer */
|
||||
mgmt_device_connected(hdev, pa_sync, NULL, 0);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
@ -7324,10 +7171,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
|
||||
bt_dev_dbg(hdev, "subevent 0x%2.2x", ev->subevent);
|
||||
|
||||
/* Only match event if command OGF is for LE */
|
||||
if (hdev->sent_cmd &&
|
||||
hci_opcode_ogf(hci_skb_opcode(hdev->sent_cmd)) == 0x08 &&
|
||||
hci_skb_event(hdev->sent_cmd) == ev->subevent) {
|
||||
*opcode = hci_skb_opcode(hdev->sent_cmd);
|
||||
if (hdev->req_skb &&
|
||||
hci_opcode_ogf(hci_skb_opcode(hdev->req_skb)) == 0x08 &&
|
||||
hci_skb_event(hdev->req_skb) == ev->subevent) {
|
||||
*opcode = hci_skb_opcode(hdev->req_skb);
|
||||
hci_req_cmd_complete(hdev, *opcode, 0x00, req_complete,
|
||||
req_complete_skb);
|
||||
}
|
||||
@ -7626,25 +7473,6 @@ static const struct hci_ev {
|
||||
/* [0x3e = HCI_EV_LE_META] */
|
||||
HCI_EV_REQ_VL(HCI_EV_LE_META, hci_le_meta_evt,
|
||||
sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_SIZE),
|
||||
#if IS_ENABLED(CONFIG_BT_HS)
|
||||
/* [0x40 = HCI_EV_PHY_LINK_COMPLETE] */
|
||||
HCI_EV(HCI_EV_PHY_LINK_COMPLETE, hci_phy_link_complete_evt,
|
||||
sizeof(struct hci_ev_phy_link_complete)),
|
||||
/* [0x41 = HCI_EV_CHANNEL_SELECTED] */
|
||||
HCI_EV(HCI_EV_CHANNEL_SELECTED, hci_chan_selected_evt,
|
||||
sizeof(struct hci_ev_channel_selected)),
|
||||
/* [0x42 = HCI_EV_DISCONN_PHY_LINK_COMPLETE] */
|
||||
HCI_EV(HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE,
|
||||
hci_disconn_loglink_complete_evt,
|
||||
sizeof(struct hci_ev_disconn_logical_link_complete)),
|
||||
/* [0x45 = HCI_EV_LOGICAL_LINK_COMPLETE] */
|
||||
HCI_EV(HCI_EV_LOGICAL_LINK_COMPLETE, hci_loglink_complete_evt,
|
||||
sizeof(struct hci_ev_logical_link_complete)),
|
||||
/* [0x46 = HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE] */
|
||||
HCI_EV(HCI_EV_DISCONN_PHY_LINK_COMPLETE,
|
||||
hci_disconn_phylink_complete_evt,
|
||||
sizeof(struct hci_ev_disconn_phy_link_complete)),
|
||||
#endif
|
||||
/* [0x48 = HCI_EV_NUM_COMP_BLOCKS] */
|
||||
HCI_EV(HCI_EV_NUM_COMP_BLOCKS, hci_num_comp_blocks_evt,
|
||||
sizeof(struct hci_ev_num_comp_blocks)),
|
||||
@ -7714,10 +7542,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
}
|
||||
|
||||
/* Only match event if command OGF is not for LE */
|
||||
if (hdev->sent_cmd &&
|
||||
hci_opcode_ogf(hci_skb_opcode(hdev->sent_cmd)) != 0x08 &&
|
||||
hci_skb_event(hdev->sent_cmd) == event) {
|
||||
hci_req_cmd_complete(hdev, hci_skb_opcode(hdev->sent_cmd),
|
||||
if (hdev->req_skb &&
|
||||
hci_opcode_ogf(hci_skb_opcode(hdev->req_skb)) != 0x08 &&
|
||||
hci_skb_event(hdev->req_skb) == event) {
|
||||
hci_req_cmd_complete(hdev, hci_skb_opcode(hdev->req_skb),
|
||||
status, &req_complete, &req_complete_skb);
|
||||
req_evt = event;
|
||||
}
|
||||
|
@ -895,7 +895,7 @@ void hci_request_setup(struct hci_dev *hdev)
|
||||
|
||||
void hci_request_cancel_all(struct hci_dev *hdev)
|
||||
{
|
||||
__hci_cmd_sync_cancel(hdev, ENODEV);
|
||||
hci_cmd_sync_cancel_sync(hdev, ENODEV);
|
||||
|
||||
cancel_interleave_scan(hdev);
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ static bool hci_sock_gen_cookie(struct sock *sk)
|
||||
int id = hci_pi(sk)->cookie;
|
||||
|
||||
if (!id) {
|
||||
id = ida_simple_get(&sock_cookie_ida, 1, 0, GFP_KERNEL);
|
||||
id = ida_alloc_min(&sock_cookie_ida, 1, GFP_KERNEL);
|
||||
if (id < 0)
|
||||
id = 0xffffffff;
|
||||
|
||||
@ -119,7 +119,7 @@ static void hci_sock_free_cookie(struct sock *sk)
|
||||
|
||||
if (id) {
|
||||
hci_pi(sk)->cookie = 0xffffffff;
|
||||
ida_simple_remove(&sock_cookie_ida, id);
|
||||
ida_free(&sock_cookie_ida, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,10 @@ static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
|
||||
hdev->req_result = result;
|
||||
hdev->req_status = HCI_REQ_DONE;
|
||||
|
||||
/* Free the request command so it is not used as response */
|
||||
kfree_skb(hdev->req_skb);
|
||||
hdev->req_skb = NULL;
|
||||
|
||||
if (skb) {
|
||||
struct sock *sk = hci_skb_sk(skb);
|
||||
|
||||
@ -39,7 +43,7 @@ static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
|
||||
if (sk)
|
||||
sock_put(sk);
|
||||
|
||||
hdev->req_skb = skb_get(skb);
|
||||
hdev->req_rsp = skb_get(skb);
|
||||
}
|
||||
|
||||
wake_up_interruptible(&hdev->req_wait_q);
|
||||
@ -187,8 +191,8 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
|
||||
hdev->req_status = 0;
|
||||
hdev->req_result = 0;
|
||||
skb = hdev->req_skb;
|
||||
hdev->req_skb = NULL;
|
||||
skb = hdev->req_rsp;
|
||||
hdev->req_rsp = NULL;
|
||||
|
||||
bt_dev_dbg(hdev, "end: err %d", err);
|
||||
|
||||
@ -566,6 +570,17 @@ void hci_cmd_sync_init(struct hci_dev *hdev)
|
||||
INIT_DELAYED_WORK(&hdev->adv_instance_expire, adv_timeout_expire);
|
||||
}
|
||||
|
||||
static void _hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
|
||||
struct hci_cmd_sync_work_entry *entry,
|
||||
int err)
|
||||
{
|
||||
if (entry->destroy)
|
||||
entry->destroy(hdev, entry->data, err);
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
void hci_cmd_sync_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cmd_sync_work_entry *entry, *tmp;
|
||||
@ -574,32 +589,11 @@ void hci_cmd_sync_clear(struct hci_dev *hdev)
|
||||
cancel_work_sync(&hdev->reenable_adv_work);
|
||||
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list) {
|
||||
if (entry->destroy)
|
||||
entry->destroy(hdev, entry->data, -ECANCELED);
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list)
|
||||
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
}
|
||||
|
||||
void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
|
||||
{
|
||||
bt_dev_dbg(hdev, "err 0x%2.2x", err);
|
||||
|
||||
if (hdev->req_status == HCI_REQ_PEND) {
|
||||
hdev->req_result = err;
|
||||
hdev->req_status = HCI_REQ_CANCELED;
|
||||
|
||||
cancel_delayed_work_sync(&hdev->cmd_timer);
|
||||
cancel_delayed_work_sync(&hdev->ncmd_timer);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
|
||||
wake_up_interruptible(&hdev->req_wait_q);
|
||||
}
|
||||
}
|
||||
|
||||
void hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
|
||||
{
|
||||
bt_dev_dbg(hdev, "err 0x%2.2x", err);
|
||||
@ -613,6 +607,24 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_cancel);
|
||||
|
||||
/* Cancel ongoing command request synchronously:
|
||||
*
|
||||
* - Set result and mark status to HCI_REQ_CANCELED
|
||||
* - Wakeup command sync thread
|
||||
*/
|
||||
void hci_cmd_sync_cancel_sync(struct hci_dev *hdev, int err)
|
||||
{
|
||||
bt_dev_dbg(hdev, "err 0x%2.2x", err);
|
||||
|
||||
if (hdev->req_status == HCI_REQ_PEND) {
|
||||
hdev->req_result = err;
|
||||
hdev->req_status = HCI_REQ_CANCELED;
|
||||
|
||||
wake_up_interruptible(&hdev->req_wait_q);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_cancel_sync);
|
||||
|
||||
/* Submit HCI command to be run in as cmd_sync_work:
|
||||
*
|
||||
* - hdev must _not_ be unregistered
|
||||
@ -667,6 +679,115 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_queue);
|
||||
|
||||
static struct hci_cmd_sync_work_entry *
|
||||
_hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy)
|
||||
{
|
||||
struct hci_cmd_sync_work_entry *entry, *tmp;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list) {
|
||||
if (func && entry->func != func)
|
||||
continue;
|
||||
|
||||
if (data && entry->data != data)
|
||||
continue;
|
||||
|
||||
if (destroy && entry->destroy != destroy)
|
||||
continue;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Queue HCI command entry once:
|
||||
*
|
||||
* - Lookup if an entry already exist and only if it doesn't creates a new entry
|
||||
* and queue it.
|
||||
*/
|
||||
int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy)
|
||||
{
|
||||
if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
|
||||
return 0;
|
||||
|
||||
return hci_cmd_sync_queue(hdev, func, data, destroy);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_queue_once);
|
||||
|
||||
/* Lookup HCI command entry:
|
||||
*
|
||||
* - Return first entry that matches by function callback or data or
|
||||
* destroy callback.
|
||||
*/
|
||||
struct hci_cmd_sync_work_entry *
|
||||
hci_cmd_sync_lookup_entry(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy)
|
||||
{
|
||||
struct hci_cmd_sync_work_entry *entry;
|
||||
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
return entry;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_lookup_entry);
|
||||
|
||||
/* Cancel HCI command entry */
|
||||
void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
|
||||
struct hci_cmd_sync_work_entry *entry)
|
||||
{
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_cancel_entry);
|
||||
|
||||
/* Dequeue one HCI command entry:
|
||||
*
|
||||
* - Lookup and cancel first entry that matches.
|
||||
*/
|
||||
bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
|
||||
hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy)
|
||||
{
|
||||
struct hci_cmd_sync_work_entry *entry;
|
||||
|
||||
entry = hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
hci_cmd_sync_cancel_entry(hdev, entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_dequeue_once);
|
||||
|
||||
/* Dequeue HCI command entry:
|
||||
*
|
||||
* - Lookup and cancel any entry that matches by function callback or data or
|
||||
* destroy callback.
|
||||
*/
|
||||
bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy)
|
||||
{
|
||||
struct hci_cmd_sync_work_entry *entry;
|
||||
bool ret = false;
|
||||
|
||||
mutex_lock(&hdev->cmd_sync_work_lock);
|
||||
while ((entry = _hci_cmd_sync_lookup_entry(hdev, func, data,
|
||||
destroy))) {
|
||||
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
|
||||
ret = true;
|
||||
}
|
||||
mutex_unlock(&hdev->cmd_sync_work_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_cmd_sync_dequeue);
|
||||
|
||||
int hci_update_eir_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_write_eir cp;
|
||||
@ -2445,6 +2566,16 @@ static struct conn_params *conn_params_copy(struct list_head *list, size_t *n)
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Clear LE Accept List */
|
||||
static int hci_le_clear_accept_list_sync(struct hci_dev *hdev)
|
||||
{
|
||||
if (!(hdev->commands[26] & 0x80))
|
||||
return 0;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CLEAR_ACCEPT_LIST, 0, NULL,
|
||||
HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Device must not be scanning when updating the accept list.
|
||||
*
|
||||
* Update is done using the following sequence:
|
||||
@ -2493,6 +2624,31 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Force address filtering if PA Sync is in progress */
|
||||
if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
|
||||
struct hci_cp_le_pa_create_sync *sent;
|
||||
|
||||
sent = hci_sent_cmd_data(hdev, HCI_OP_LE_PA_CREATE_SYNC);
|
||||
if (sent) {
|
||||
struct conn_params pa;
|
||||
|
||||
memset(&pa, 0, sizeof(pa));
|
||||
|
||||
bacpy(&pa.addr, &sent->addr);
|
||||
pa.addr_type = sent->addr_type;
|
||||
|
||||
/* Clear first since there could be addresses left
|
||||
* behind.
|
||||
*/
|
||||
hci_le_clear_accept_list_sync(hdev);
|
||||
|
||||
num_entries = 1;
|
||||
err = hci_le_add_accept_list_sync(hdev, &pa,
|
||||
&num_entries);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Go through the current accept list programmed into the
|
||||
* controller one by one and check if that address is connected or is
|
||||
* still in the list of pending connections or list of devices to
|
||||
@ -2602,6 +2758,14 @@ done:
|
||||
return filter_policy;
|
||||
}
|
||||
|
||||
static void hci_le_scan_phy_params(struct hci_cp_le_scan_phy_params *cp,
|
||||
u8 type, u16 interval, u16 window)
|
||||
{
|
||||
cp->type = type;
|
||||
cp->interval = cpu_to_le16(interval);
|
||||
cp->window = cpu_to_le16(window);
|
||||
}
|
||||
|
||||
static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
|
||||
u16 interval, u16 window,
|
||||
u8 own_addr_type, u8 filter_policy)
|
||||
@ -2609,7 +2773,7 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
|
||||
struct hci_cp_le_set_ext_scan_params *cp;
|
||||
struct hci_cp_le_scan_phy_params *phy;
|
||||
u8 data[sizeof(*cp) + sizeof(*phy) * 2];
|
||||
u8 num_phy = 0;
|
||||
u8 num_phy = 0x00;
|
||||
|
||||
cp = (void *)data;
|
||||
phy = (void *)cp->data;
|
||||
@ -2619,28 +2783,64 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
|
||||
cp->own_addr_type = own_addr_type;
|
||||
cp->filter_policy = filter_policy;
|
||||
|
||||
/* Check if PA Sync is in progress then select the PHY based on the
|
||||
* hci_conn.iso_qos.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
|
||||
struct hci_cp_le_add_to_accept_list *sent;
|
||||
|
||||
sent = hci_sent_cmd_data(hdev, HCI_OP_LE_ADD_TO_ACCEPT_LIST);
|
||||
if (sent) {
|
||||
struct hci_conn *conn;
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ISO_LINK,
|
||||
&sent->bdaddr);
|
||||
if (conn) {
|
||||
struct bt_iso_qos *qos = &conn->iso_qos;
|
||||
|
||||
if (qos->bcast.in.phy & BT_ISO_PHY_1M ||
|
||||
qos->bcast.in.phy & BT_ISO_PHY_2M) {
|
||||
cp->scanning_phys |= LE_SCAN_PHY_1M;
|
||||
hci_le_scan_phy_params(phy, type,
|
||||
interval,
|
||||
window);
|
||||
num_phy++;
|
||||
phy++;
|
||||
}
|
||||
|
||||
if (qos->bcast.in.phy & BT_ISO_PHY_CODED) {
|
||||
cp->scanning_phys |= LE_SCAN_PHY_CODED;
|
||||
hci_le_scan_phy_params(phy, type,
|
||||
interval,
|
||||
window);
|
||||
num_phy++;
|
||||
phy++;
|
||||
}
|
||||
|
||||
if (num_phy)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scan_1m(hdev) || scan_2m(hdev)) {
|
||||
cp->scanning_phys |= LE_SCAN_PHY_1M;
|
||||
|
||||
phy->type = type;
|
||||
phy->interval = cpu_to_le16(interval);
|
||||
phy->window = cpu_to_le16(window);
|
||||
|
||||
hci_le_scan_phy_params(phy, type, interval, window);
|
||||
num_phy++;
|
||||
phy++;
|
||||
}
|
||||
|
||||
if (scan_coded(hdev)) {
|
||||
cp->scanning_phys |= LE_SCAN_PHY_CODED;
|
||||
|
||||
phy->type = type;
|
||||
phy->interval = cpu_to_le16(interval);
|
||||
phy->window = cpu_to_le16(window);
|
||||
|
||||
hci_le_scan_phy_params(phy, type, interval, window);
|
||||
num_phy++;
|
||||
phy++;
|
||||
}
|
||||
|
||||
done:
|
||||
if (!num_phy)
|
||||
return -EINVAL;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_PARAMS,
|
||||
sizeof(*cp) + sizeof(*phy) * num_phy,
|
||||
data, HCI_CMD_TIMEOUT);
|
||||
@ -2879,7 +3079,8 @@ int hci_update_passive_scan(struct hci_dev *hdev)
|
||||
hci_dev_test_flag(hdev, HCI_UNREGISTER))
|
||||
return 0;
|
||||
|
||||
return hci_cmd_sync_queue(hdev, update_passive_scan_sync, NULL, NULL);
|
||||
return hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int hci_write_sc_support_sync(struct hci_dev *hdev, u8 val)
|
||||
@ -4098,16 +4299,6 @@ static int hci_le_read_accept_list_size_sync(struct hci_dev *hdev)
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Clear LE Accept List */
|
||||
static int hci_le_clear_accept_list_sync(struct hci_dev *hdev)
|
||||
{
|
||||
if (!(hdev->commands[26] & 0x80))
|
||||
return 0;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CLEAR_ACCEPT_LIST, 0, NULL,
|
||||
HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Read LE Resolving List Size */
|
||||
static int hci_le_read_resolv_list_size_sync(struct hci_dev *hdev)
|
||||
{
|
||||
@ -4834,6 +5025,11 @@ int hci_dev_open_sync(struct hci_dev *hdev)
|
||||
hdev->sent_cmd = NULL;
|
||||
}
|
||||
|
||||
if (hdev->req_skb) {
|
||||
kfree_skb(hdev->req_skb);
|
||||
hdev->req_skb = NULL;
|
||||
}
|
||||
|
||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
||||
hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
|
||||
|
||||
@ -4994,6 +5190,12 @@ int hci_dev_close_sync(struct hci_dev *hdev)
|
||||
hdev->sent_cmd = NULL;
|
||||
}
|
||||
|
||||
/* Drop last request */
|
||||
if (hdev->req_skb) {
|
||||
kfree_skb(hdev->req_skb);
|
||||
hdev->req_skb = NULL;
|
||||
}
|
||||
|
||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
||||
hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
|
||||
|
||||
@ -5403,27 +5605,33 @@ static int hci_power_off_sync(struct hci_dev *hdev)
|
||||
if (!test_bit(HCI_UP, &hdev->flags))
|
||||
return 0;
|
||||
|
||||
hci_dev_set_flag(hdev, HCI_POWERING_DOWN);
|
||||
|
||||
if (test_bit(HCI_ISCAN, &hdev->flags) ||
|
||||
test_bit(HCI_PSCAN, &hdev->flags)) {
|
||||
err = hci_write_scan_enable_sync(hdev, 0x00);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = hci_clear_adv_sync(hdev, NULL, false);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
err = hci_stop_discovery_sync(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
/* Terminated due to Power Off */
|
||||
err = hci_disconnect_all_sync(hdev, HCI_ERROR_REMOTE_POWER_OFF);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
return hci_dev_close_sync(hdev);
|
||||
err = hci_dev_close_sync(hdev);
|
||||
|
||||
out:
|
||||
hci_dev_clear_flag(hdev, HCI_POWERING_DOWN);
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_set_powered_sync(struct hci_dev *hdev, u8 val)
|
||||
@ -6161,12 +6369,21 @@ static int hci_le_ext_create_conn_sync(struct hci_dev *hdev,
|
||||
conn->conn_timeout, NULL);
|
||||
}
|
||||
|
||||
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_cp_le_create_conn cp;
|
||||
struct hci_conn_params *params;
|
||||
u8 own_addr_type;
|
||||
int err;
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
return -ECANCELED;
|
||||
|
||||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
clear_bit(HCI_CONN_SCANNING, &conn->flags);
|
||||
conn->state = BT_CONNECT;
|
||||
|
||||
/* If requested to connect as peripheral use directed advertising */
|
||||
if (conn->role == HCI_ROLE_SLAVE) {
|
||||
@ -6484,3 +6701,125 @@ int hci_update_adv_data(struct hci_dev *hdev, u8 instance)
|
||||
return hci_cmd_sync_queue(hdev, _update_adv_data_sync,
|
||||
UINT_PTR(instance), NULL);
|
||||
}
|
||||
|
||||
static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
struct inquiry_entry *ie;
|
||||
struct hci_cp_create_conn cp;
|
||||
int err;
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
return -ECANCELED;
|
||||
|
||||
/* Many controllers disallow HCI Create Connection while it is doing
|
||||
* HCI Inquiry. So we cancel the Inquiry first before issuing HCI Create
|
||||
* Connection. This may cause the MGMT discovering state to become false
|
||||
* without user space's request but it is okay since the MGMT Discovery
|
||||
* APIs do not promise that discovery should be done forever. Instead,
|
||||
* the user space monitors the status of MGMT discovering and it may
|
||||
* request for discovery again when this flag becomes false.
|
||||
*/
|
||||
if (test_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_INQUIRY_CANCEL, 0,
|
||||
NULL, HCI_CMD_TIMEOUT);
|
||||
if (err)
|
||||
bt_dev_warn(hdev, "Failed to cancel inquiry %d", err);
|
||||
}
|
||||
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = true;
|
||||
conn->role = HCI_ROLE_MASTER;
|
||||
|
||||
conn->attempt++;
|
||||
|
||||
conn->link_policy = hdev->link_policy;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
bacpy(&cp.bdaddr, &conn->dst);
|
||||
cp.pscan_rep_mode = 0x02;
|
||||
|
||||
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
|
||||
if (ie) {
|
||||
if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
|
||||
cp.pscan_rep_mode = ie->data.pscan_rep_mode;
|
||||
cp.pscan_mode = ie->data.pscan_mode;
|
||||
cp.clock_offset = ie->data.clock_offset |
|
||||
cpu_to_le16(0x8000);
|
||||
}
|
||||
|
||||
memcpy(conn->dev_class, ie->data.dev_class, 3);
|
||||
}
|
||||
|
||||
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
||||
if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
|
||||
cp.role_switch = 0x01;
|
||||
else
|
||||
cp.role_switch = 0x00;
|
||||
|
||||
return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
|
||||
sizeof(cp), &cp,
|
||||
HCI_EV_CONN_COMPLETE,
|
||||
conn->conn_timeout, NULL);
|
||||
}
|
||||
|
||||
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hci_conn_valid(hdev, conn))
|
||||
goto done;
|
||||
|
||||
if (!err) {
|
||||
hci_connect_le_scan_cleanup(conn, 0x00);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if connection is still pending */
|
||||
if (conn != hci_lookup_le_connect(hdev))
|
||||
goto done;
|
||||
|
||||
/* Flush to make sure we send create conn cancel command if needed */
|
||||
flush_delayed_work(&conn->le_conn_timeout);
|
||||
hci_conn_failed(conn, bt_status(err));
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
|
||||
create_le_conn_complete);
|
||||
}
|
||||
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
if (conn->state != BT_OPEN)
|
||||
return -EINVAL;
|
||||
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
return !hci_cmd_sync_dequeue_once(hdev,
|
||||
hci_acl_create_conn_sync,
|
||||
conn, NULL);
|
||||
case LE_LINK:
|
||||
return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
|
||||
conn, create_le_conn_complete);
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2022 Intel Corporation
|
||||
* Copyright 2023 NXP
|
||||
* Copyright 2023-2024 NXP
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -690,11 +690,8 @@ static void iso_sock_cleanup_listen(struct sock *parent)
|
||||
iso_sock_kill(sk);
|
||||
}
|
||||
|
||||
/* If listening socket stands for a PA sync connection,
|
||||
* properly disconnect the hcon and socket.
|
||||
*/
|
||||
if (iso_pi(parent)->conn && iso_pi(parent)->conn->hcon &&
|
||||
test_bit(HCI_CONN_PA_SYNC, &iso_pi(parent)->conn->hcon->flags)) {
|
||||
/* If listening socket has a hcon, properly disconnect it */
|
||||
if (iso_pi(parent)->conn && iso_pi(parent)->conn->hcon) {
|
||||
iso_sock_disconn(parent);
|
||||
return;
|
||||
}
|
||||
@ -837,10 +834,10 @@ static struct bt_iso_qos default_qos = {
|
||||
.bcode = {0x00},
|
||||
.options = 0x00,
|
||||
.skip = 0x0000,
|
||||
.sync_timeout = 0x4000,
|
||||
.sync_timeout = BT_ISO_SYNC_TIMEOUT,
|
||||
.sync_cte_type = 0x00,
|
||||
.mse = 0x00,
|
||||
.timeout = 0x4000,
|
||||
.timeout = BT_ISO_SYNC_TIMEOUT,
|
||||
},
|
||||
};
|
||||
|
||||
@ -1076,6 +1073,8 @@ static int iso_listen_bis(struct sock *sk)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
int err = 0;
|
||||
struct iso_conn *conn;
|
||||
struct hci_conn *hcon;
|
||||
|
||||
BT_DBG("%pMR -> %pMR (SID 0x%2.2x)", &iso_pi(sk)->src,
|
||||
&iso_pi(sk)->dst, iso_pi(sk)->bc_sid);
|
||||
@ -1096,18 +1095,40 @@ static int iso_listen_bis(struct sock *sk)
|
||||
if (!hdev)
|
||||
return -EHOSTUNREACH;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* Fail if user set invalid QoS */
|
||||
if (iso_pi(sk)->qos_user_set && !check_bcast_qos(&iso_pi(sk)->qos)) {
|
||||
iso_pi(sk)->qos = default_qos;
|
||||
return -EINVAL;
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = hci_pa_create_sync(hdev, &iso_pi(sk)->dst,
|
||||
le_addr_type(iso_pi(sk)->dst_type),
|
||||
iso_pi(sk)->bc_sid, &iso_pi(sk)->qos);
|
||||
hcon = hci_pa_create_sync(hdev, &iso_pi(sk)->dst,
|
||||
le_addr_type(iso_pi(sk)->dst_type),
|
||||
iso_pi(sk)->bc_sid, &iso_pi(sk)->qos);
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
conn = iso_conn_add(hcon);
|
||||
if (!conn) {
|
||||
hci_conn_drop(hcon);
|
||||
err = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = iso_chan_add(conn, sk, NULL);
|
||||
if (err) {
|
||||
hci_conn_drop(hcon);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_dev_put(hdev);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1889,7 +1910,6 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
struct hci_evt_le_big_info_adv_report *ev2;
|
||||
struct hci_ev_le_per_adv_report *ev3;
|
||||
struct sock *sk;
|
||||
int lm = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "bdaddr %pMR", bdaddr);
|
||||
|
||||
@ -1933,7 +1953,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
|
||||
if (sk && test_bit(BT_SK_PA_SYNC_TERM,
|
||||
&iso_pi(sk)->flags))
|
||||
return lm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sk) {
|
||||
@ -1961,16 +1981,58 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
|
||||
ev3 = hci_recv_event_data(hdev, HCI_EV_LE_PER_ADV_REPORT);
|
||||
if (ev3) {
|
||||
size_t base_len = ev3->length;
|
||||
size_t base_len = 0;
|
||||
u8 *base;
|
||||
struct hci_conn *hcon;
|
||||
|
||||
sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
|
||||
iso_match_sync_handle_pa_report, ev3);
|
||||
base = eir_get_service_data(ev3->data, ev3->length,
|
||||
EIR_BAA_SERVICE_UUID, &base_len);
|
||||
if (base && sk && base_len <= sizeof(iso_pi(sk)->base)) {
|
||||
if (!sk)
|
||||
goto done;
|
||||
|
||||
hcon = iso_pi(sk)->conn->hcon;
|
||||
if (!hcon)
|
||||
goto done;
|
||||
|
||||
if (ev3->data_status == LE_PA_DATA_TRUNCATED) {
|
||||
/* The controller was unable to retrieve PA data. */
|
||||
memset(hcon->le_per_adv_data, 0,
|
||||
HCI_MAX_PER_AD_TOT_LEN);
|
||||
hcon->le_per_adv_data_len = 0;
|
||||
hcon->le_per_adv_data_offset = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (hcon->le_per_adv_data_offset + ev3->length >
|
||||
HCI_MAX_PER_AD_TOT_LEN)
|
||||
goto done;
|
||||
|
||||
memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset,
|
||||
ev3->data, ev3->length);
|
||||
hcon->le_per_adv_data_offset += ev3->length;
|
||||
|
||||
if (ev3->data_status == LE_PA_DATA_COMPLETE) {
|
||||
/* All PA data has been received. */
|
||||
hcon->le_per_adv_data_len =
|
||||
hcon->le_per_adv_data_offset;
|
||||
hcon->le_per_adv_data_offset = 0;
|
||||
|
||||
/* Extract BASE */
|
||||
base = eir_get_service_data(hcon->le_per_adv_data,
|
||||
hcon->le_per_adv_data_len,
|
||||
EIR_BAA_SERVICE_UUID,
|
||||
&base_len);
|
||||
|
||||
if (!base || base_len > BASE_MAX_LENGTH)
|
||||
goto done;
|
||||
|
||||
memcpy(iso_pi(sk)->base, base, base_len);
|
||||
iso_pi(sk)->base_len = base_len;
|
||||
} else {
|
||||
/* This is a PA data fragment. Keep pa_data_len set to 0
|
||||
* until all data has been reassembled.
|
||||
*/
|
||||
hcon->le_per_adv_data_len = 0;
|
||||
}
|
||||
} else {
|
||||
sk = iso_get_sock_listen(&hdev->bdaddr, BDADDR_ANY, NULL, NULL);
|
||||
@ -1978,16 +2040,14 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
|
||||
done:
|
||||
if (!sk)
|
||||
return lm;
|
||||
|
||||
lm |= HCI_LM_ACCEPT;
|
||||
return 0;
|
||||
|
||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
|
||||
*flags |= HCI_PROTO_DEFER;
|
||||
|
||||
sock_put(sk);
|
||||
|
||||
return lm;
|
||||
return HCI_LM_ACCEPT;
|
||||
}
|
||||
|
||||
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -254,7 +254,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
|
||||
chan->mode = L2CAP_MODE_LE_FLOWCTL;
|
||||
|
||||
err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
|
||||
&la.l2_bdaddr, la.l2_bdaddr_type);
|
||||
&la.l2_bdaddr, la.l2_bdaddr_type,
|
||||
sk->sk_sndtimeo);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1027,23 +1028,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chan->mode != L2CAP_MODE_ERTM &&
|
||||
chan->mode != L2CAP_MODE_STREAMING) {
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
chan->chan_policy = (u8) opt;
|
||||
|
||||
if (sk->sk_state == BT_CONNECTED &&
|
||||
chan->move_role == L2CAP_MOVE_ROLE_NONE)
|
||||
l2cap_move_start(chan);
|
||||
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
|
||||
case BT_SNDMTU:
|
||||
|
@ -835,8 +835,6 @@ static u32 get_supported_settings(struct hci_dev *hdev)
|
||||
|
||||
if (lmp_ssp_capable(hdev)) {
|
||||
settings |= MGMT_SETTING_SSP;
|
||||
if (IS_ENABLED(CONFIG_BT_HS))
|
||||
settings |= MGMT_SETTING_HS;
|
||||
}
|
||||
|
||||
if (lmp_sc_capable(hdev))
|
||||
@ -901,9 +899,6 @@ static u32 get_current_settings(struct hci_dev *hdev)
|
||||
if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
|
||||
settings |= MGMT_SETTING_SSP;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_HS_ENABLED))
|
||||
settings |= MGMT_SETTING_HS;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
settings |= MGMT_SETTING_ADVERTISING;
|
||||
|
||||
@ -1390,6 +1385,14 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!cp->val) {
|
||||
if (hci_dev_test_flag(hdev, HCI_POWERING_DOWN)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (pending_find(MGMT_OP_SET_POWERED, hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
|
||||
MGMT_STATUS_BUSY);
|
||||
@ -1409,7 +1412,7 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
|
||||
/* Cancel potentially blocking sync operation before power off */
|
||||
if (cp->val == 0x00) {
|
||||
__hci_cmd_sync_cancel(hdev, -EHOSTDOWN);
|
||||
hci_cmd_sync_cancel_sync(hdev, -EHOSTDOWN);
|
||||
err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd,
|
||||
mgmt_set_powered_complete);
|
||||
} else {
|
||||
@ -1704,8 +1707,7 @@ static void mgmt_set_connectable_complete(struct hci_dev *hdev, void *data,
|
||||
new_settings(hdev, cmd->sk);
|
||||
|
||||
done:
|
||||
if (cmd)
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
@ -1930,7 +1932,6 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
if (enable && hci_dev_test_and_clear_flag(hdev,
|
||||
HCI_SSP_ENABLED)) {
|
||||
hci_dev_clear_flag(hdev, HCI_HS_ENABLED);
|
||||
new_settings(hdev, NULL);
|
||||
}
|
||||
|
||||
@ -1943,12 +1944,6 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
|
||||
changed = !hci_dev_test_and_set_flag(hdev, HCI_SSP_ENABLED);
|
||||
} else {
|
||||
changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED);
|
||||
|
||||
if (!changed)
|
||||
changed = hci_dev_test_and_clear_flag(hdev,
|
||||
HCI_HS_ENABLED);
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_HS_ENABLED);
|
||||
}
|
||||
|
||||
mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
|
||||
@ -2012,11 +2007,6 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
} else {
|
||||
changed = hci_dev_test_and_clear_flag(hdev,
|
||||
HCI_SSP_ENABLED);
|
||||
if (!changed)
|
||||
changed = hci_dev_test_and_clear_flag(hdev,
|
||||
HCI_HS_ENABLED);
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_HS_ENABLED);
|
||||
}
|
||||
|
||||
err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
|
||||
@ -2062,63 +2052,10 @@ failed:
|
||||
|
||||
static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
{
|
||||
struct mgmt_mode *cp = data;
|
||||
bool changed;
|
||||
u8 status;
|
||||
int err;
|
||||
|
||||
bt_dev_dbg(hdev, "sock %p", sk);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_BT_HS))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
status = mgmt_bredr_support(hdev);
|
||||
if (status)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status);
|
||||
|
||||
if (!lmp_ssp_capable(hdev))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_REJECTED);
|
||||
|
||||
if (cp->val != 0x00 && cp->val != 0x01)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (pending_find(MGMT_OP_SET_SSP, hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (cp->val) {
|
||||
changed = !hci_dev_test_and_set_flag(hdev, HCI_HS_ENABLED);
|
||||
} else {
|
||||
if (hdev_is_powered(hdev)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||
MGMT_STATUS_REJECTED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
changed = hci_dev_test_and_clear_flag(hdev, HCI_HS_ENABLED);
|
||||
}
|
||||
|
||||
err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
if (changed)
|
||||
err = new_settings(hdev, sk);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void set_le_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@ -3188,6 +3125,7 @@ failed:
|
||||
static u8 link_to_bdaddr(u8 link_type, u8 addr_type)
|
||||
{
|
||||
switch (link_type) {
|
||||
case ISO_LINK:
|
||||
case LE_LINK:
|
||||
switch (addr_type) {
|
||||
case ADDR_LE_DEV_PUBLIC:
|
||||
@ -3505,7 +3443,8 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
|
||||
if (cp->addr.type == BDADDR_BREDR) {
|
||||
conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level,
|
||||
auth_type, CONN_REASON_PAIR_DEVICE);
|
||||
auth_type, CONN_REASON_PAIR_DEVICE,
|
||||
HCI_ACL_CONN_TIMEOUT);
|
||||
} else {
|
||||
u8 addr_type = le_addr_type(cp->addr.type);
|
||||
struct hci_conn_params *p;
|
||||
@ -6766,7 +6705,6 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
hci_dev_clear_flag(hdev, HCI_SSP_ENABLED);
|
||||
hci_dev_clear_flag(hdev, HCI_LINK_SECURITY);
|
||||
hci_dev_clear_flag(hdev, HCI_FAST_CONNECTABLE);
|
||||
hci_dev_clear_flag(hdev, HCI_HS_ENABLED);
|
||||
}
|
||||
|
||||
hci_dev_change_flag(hdev, HCI_BREDR_ENABLED);
|
||||
@ -8470,7 +8408,7 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
static u8 calculate_name_len(struct hci_dev *hdev)
|
||||
{
|
||||
u8 buf[HCI_MAX_SHORT_NAME_LENGTH + 3];
|
||||
u8 buf[HCI_MAX_SHORT_NAME_LENGTH + 2]; /* len + type + name */
|
||||
|
||||
return eir_append_local_name(hdev, buf, 0);
|
||||
}
|
||||
@ -8829,8 +8767,7 @@ static void add_ext_adv_params_complete(struct hci_dev *hdev, void *data,
|
||||
}
|
||||
|
||||
unlock:
|
||||
if (cmd)
|
||||
mgmt_pending_free(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
@ -9681,6 +9618,9 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
u16 eir_len = 0;
|
||||
u32 flags = 0;
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
return;
|
||||
|
||||
/* allocate buff for LE or BR/EDR adv */
|
||||
if (conn->le_adv_data_len > 0)
|
||||
skb = mgmt_alloc_skb(hdev, MGMT_EV_DEVICE_CONNECTED,
|
||||
@ -9748,6 +9688,9 @@ bool mgmt_powering_down(struct hci_dev *hdev)
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct mgmt_mode *cp;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_POWERING_DOWN))
|
||||
return true;
|
||||
|
||||
cmd = pending_find(MGMT_OP_SET_POWERED, hdev);
|
||||
if (!cmd)
|
||||
return false;
|
||||
@ -9766,14 +9709,6 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
struct mgmt_ev_device_disconnected ev;
|
||||
struct sock *sk = NULL;
|
||||
|
||||
/* The connection is still in hci_conn_hash so test for 1
|
||||
* instead of 0 to know if this is the last one.
|
||||
*/
|
||||
if (mgmt_powering_down(hdev) && hci_conn_count(hdev) == 1) {
|
||||
cancel_delayed_work(&hdev->power_off);
|
||||
queue_work(hdev->req_workqueue, &hdev->power_off.work);
|
||||
}
|
||||
|
||||
if (!mgmt_connected)
|
||||
return;
|
||||
|
||||
@ -9830,14 +9765,6 @@ void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||
{
|
||||
struct mgmt_ev_connect_failed ev;
|
||||
|
||||
/* The connection is still in hci_conn_hash so test for 1
|
||||
* instead of 0 to know if this is the last one.
|
||||
*/
|
||||
if (mgmt_powering_down(hdev) && hci_conn_count(hdev) == 1) {
|
||||
cancel_delayed_work(&hdev->power_off);
|
||||
queue_work(hdev->req_workqueue, &hdev->power_off.work);
|
||||
}
|
||||
|
||||
bacpy(&ev.addr.bdaddr, bdaddr);
|
||||
ev.addr.type = link_to_bdaddr(link_type, addr_type);
|
||||
ev.status = mgmt_status(status);
|
||||
@ -10071,6 +9998,9 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
|
||||
/* If this is a HCI command related to powering on the
|
||||
* HCI dev don't send any mgmt signals.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_POWERING_DOWN))
|
||||
return;
|
||||
|
||||
if (pending_find(MGMT_OP_SET_POWERED, hdev))
|
||||
return;
|
||||
}
|
||||
|
@ -875,6 +875,7 @@ static int msft_add_address_filter_sync(struct hci_dev *hdev, void *data)
|
||||
remove = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cp->sub_opcode = MSFT_OP_LE_MONITOR_ADVERTISEMENT;
|
||||
cp->rssi_high = address_filter->rssi_high;
|
||||
cp->rssi_low = address_filter->rssi_low;
|
||||
@ -887,6 +888,8 @@ static int msft_add_address_filter_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
skb = __hci_cmd_sync(hdev, hdev->msft_opcode, size, cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
kfree(cp);
|
||||
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "Failed to enable address %pMR filter",
|
||||
&address_filter->bdaddr);
|
||||
|
@ -264,7 +264,8 @@ static int sco_connect(struct sock *sk)
|
||||
}
|
||||
|
||||
hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
|
||||
sco_pi(sk)->setting, &sco_pi(sk)->codec);
|
||||
sco_pi(sk)->setting, &sco_pi(sk)->codec,
|
||||
sk->sk_sndtimeo);
|
||||
if (IS_ERR(hcon)) {
|
||||
err = PTR_ERR(hcon);
|
||||
goto unlock;
|
||||
|
Loading…
Reference in New Issue
Block a user