bluetooth-next pull request for net-next:
- Add support for Foxconn Mediatek Chip - Add support for LG LGSBWAC92/TWCM-K505D - hci_h5 flow control fixes and suspend support - Switch to use lock_sock for SCO and RFCOMM - Various fixes for extended advertising - Reword Intel's setup on btusb unifying the supported generations -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmEe1xEZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKX/YEACMlYxmWJn2birrH5h4c+FA 6hzoDw+Kp+/Qo0FYPgWw6ady+cKuh50itKz8W050JR+n9eVdRehZ3Rlr/Yv2ol51 TSTjRKPbeDmtkGzC9h+dVBgkEERF88mF8FZiFXp+9vG/dfS4Lq2WdWzEFuYmfZyD ZMuI9PsepmprORVI37B1WjZfdUo2XeA9ZKHUVSesgarNg55mZ4T/WEFnEc8KH2rX HiqAeX+H2lt38ZEru7l5Jp6mNnzJJKLcnFjWMHXia865B8dHqC++goMXdJ8Tqcm8 NLs2W1RZgZocVwovwQ17bTiu41VnN7LdVpCig5RGcn1YtQUPcYzqBI971ixQCJUN 7vjqyMV3i+nLLD3FZmD+qYMYH/M2LaLH6fbaN0KBDlElCDHT7/Qu9N2nGreyiqKc uuEXVHbGou3sj/LkBpNKJOGtmNkUo0XN93/giu89ZHGc7BLN1tUJM9NYWaiO1TcD YiD0LO/lqmggCs9SQH0DBTUDNZ1vUDOzmVeD/tu/NqnixzSMseyqeThshZhxz6UT 7fBXvwixl+AhrN2lIxmS4WAtEwOPvaayUW8af7kESlrC4RoFvq+QaghT1D4NDpcq llYlg/gt97Wy3AnIsnvEjd0s+lxGN6byIOBgTfC4jAfPAYA4oxd7N+1vMPFyChUV MwatwE+IE1u2hjQWhMhVuA== =INb2 -----END PGP SIGNATURE----- Merge tag 'for-net-next-2021-08-19' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next Luiz Augusto von Dentz says: ==================== bluetooth-next pull request for net-next: - Add support for Foxconn Mediatek Chip - Add support for LG LGSBWAC92/TWCM-K505D - hci_h5 flow control fixes and suspend support - Switch to use lock_sock for SCO and RFCOMM - Various fixes for extended advertising - Reword Intel's setup on btusb unifying the supported generations ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
e61fbee7be
@ -387,6 +387,7 @@ struct bcm_subver_table {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct bcm_subver_table bcm_uart_subver_table[] = {
|
static const struct bcm_subver_table bcm_uart_subver_table[] = {
|
||||||
|
{ 0x1111, "BCM4362A2" }, /* 000.017.017 */
|
||||||
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
|
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
|
||||||
{ 0x410d, "BCM4334B0" }, /* 002.001.013 */
|
{ 0x410d, "BCM4334B0" }, /* 002.001.013 */
|
||||||
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
|
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -138,6 +138,49 @@ struct intel_debug_features {
|
|||||||
#define INTEL_CNVX_TOP_STEP(cnvx_top) (((cnvx_top) & 0x0f000000) >> 24)
|
#define INTEL_CNVX_TOP_STEP(cnvx_top) (((cnvx_top) & 0x0f000000) >> 24)
|
||||||
#define INTEL_CNVX_TOP_PACK_SWAB(t, s) __swab16(((__u16)(((t) << 4) | (s))))
|
#define INTEL_CNVX_TOP_PACK_SWAB(t, s) __swab16(((__u16)(((t) << 4) | (s))))
|
||||||
|
|
||||||
|
enum {
|
||||||
|
INTEL_BOOTLOADER,
|
||||||
|
INTEL_DOWNLOADING,
|
||||||
|
INTEL_FIRMWARE_LOADED,
|
||||||
|
INTEL_FIRMWARE_FAILED,
|
||||||
|
INTEL_BOOTING,
|
||||||
|
INTEL_BROKEN_INITIAL_NCMD,
|
||||||
|
INTEL_BROKEN_LED,
|
||||||
|
INTEL_ROM_LEGACY,
|
||||||
|
|
||||||
|
__INTEL_NUM_FLAGS,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct btintel_data {
|
||||||
|
DECLARE_BITMAP(flags, __INTEL_NUM_FLAGS);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define btintel_set_flag(hdev, nr) \
|
||||||
|
do { \
|
||||||
|
struct btintel_data *intel = hci_get_priv((hdev)); \
|
||||||
|
set_bit((nr), intel->flags); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define btintel_clear_flag(hdev, nr) \
|
||||||
|
do { \
|
||||||
|
struct btintel_data *intel = hci_get_priv((hdev)); \
|
||||||
|
clear_bit((nr), intel->flags); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define btintel_wake_up_flag(hdev, nr) \
|
||||||
|
do { \
|
||||||
|
struct btintel_data *intel = hci_get_priv((hdev)); \
|
||||||
|
wake_up_bit(intel->flags, (nr)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define btintel_get_flag(hdev) \
|
||||||
|
(((struct btintel_data *)hci_get_priv(hdev))->flags)
|
||||||
|
|
||||||
|
#define btintel_test_flag(hdev, nr) test_bit((nr), btintel_get_flag(hdev))
|
||||||
|
#define btintel_test_and_clear_flag(hdev, nr) test_and_clear_bit((nr), btintel_get_flag(hdev))
|
||||||
|
#define btintel_wait_on_flag_timeout(hdev, nr, m, to) \
|
||||||
|
wait_on_bit_timeout(btintel_get_flag(hdev), (nr), m, to)
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_BT_INTEL)
|
#if IS_ENABLED(CONFIG_BT_INTEL)
|
||||||
|
|
||||||
int btintel_check_bdaddr(struct hci_dev *hdev);
|
int btintel_check_bdaddr(struct hci_dev *hdev);
|
||||||
@ -145,19 +188,11 @@ int btintel_enter_mfg(struct hci_dev *hdev);
|
|||||||
int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched);
|
int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched);
|
||||||
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||||
int btintel_set_diag(struct hci_dev *hdev, bool enable);
|
int btintel_set_diag(struct hci_dev *hdev, bool enable);
|
||||||
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
|
|
||||||
void btintel_hw_error(struct hci_dev *hdev, u8 code);
|
|
||||||
|
|
||||||
int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
|
int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
|
||||||
int btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version);
|
|
||||||
int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
|
|
||||||
const void *param);
|
|
||||||
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
|
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
|
||||||
int btintel_set_event_mask(struct hci_dev *hdev, bool debug);
|
|
||||||
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
|
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
|
||||||
int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver);
|
int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver);
|
||||||
int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver);
|
|
||||||
|
|
||||||
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
|
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
|
||||||
u16 opcode_write);
|
u16 opcode_write);
|
||||||
int btintel_send_intel_reset(struct hci_dev *hdev, u32 boot_param);
|
int btintel_send_intel_reset(struct hci_dev *hdev, u32 boot_param);
|
||||||
@ -165,16 +200,10 @@ int btintel_read_boot_params(struct hci_dev *hdev,
|
|||||||
struct intel_boot_params *params);
|
struct intel_boot_params *params);
|
||||||
int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver,
|
int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver,
|
||||||
const struct firmware *fw, u32 *boot_param);
|
const struct firmware *fw, u32 *boot_param);
|
||||||
int btintel_download_firmware_newgen(struct hci_dev *hdev,
|
int btintel_configure_setup(struct hci_dev *hdev);
|
||||||
struct intel_version_tlv *ver,
|
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len);
|
||||||
const struct firmware *fw,
|
void btintel_secure_send_result(struct hci_dev *hdev,
|
||||||
u32 *boot_param, u8 hw_variant,
|
const void *ptr, unsigned int len);
|
||||||
u8 sbe_type);
|
|
||||||
void btintel_reset_to_bootloader(struct hci_dev *hdev);
|
|
||||||
int btintel_read_debug_features(struct hci_dev *hdev,
|
|
||||||
struct intel_debug_features *features);
|
|
||||||
int btintel_set_debug_features(struct hci_dev *hdev,
|
|
||||||
const struct intel_debug_features *features);
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
|
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
|
||||||
@ -202,44 +231,18 @@ static inline int btintel_set_diag(struct hci_dev *hdev, bool enable)
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int btintel_version_info(struct hci_dev *hdev,
|
static inline int btintel_version_info(struct hci_dev *hdev,
|
||||||
struct intel_version *ver)
|
struct intel_version *ver)
|
||||||
{
|
{
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int btintel_version_info_tlv(struct hci_dev *hdev,
|
|
||||||
struct intel_version_tlv *version)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type,
|
|
||||||
u32 plen, const void *param)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int btintel_load_ddc_config(struct hci_dev *hdev,
|
static inline int btintel_load_ddc_config(struct hci_dev *hdev,
|
||||||
const char *ddc_name)
|
const char *ddc_name)
|
||||||
{
|
{
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int btintel_set_event_mask(struct hci_dev *hdev, bool debug)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
|
static inline int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
|
||||||
{
|
{
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
@ -251,12 +254,6 @@ static inline int btintel_read_version(struct hci_dev *hdev,
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int btintel_read_version_tlv(struct hci_dev *hdev,
|
|
||||||
struct intel_version_tlv *ver)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
|
static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
|
||||||
u16 opcode_read,
|
u16 opcode_read,
|
||||||
u16 opcode_write)
|
u16 opcode_write)
|
||||||
@ -283,28 +280,18 @@ static inline int btintel_download_firmware(struct hci_dev *dev,
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int btintel_download_firmware_newgen(struct hci_dev *hdev,
|
static inline int btintel_configure_setup(struct hci_dev *hdev)
|
||||||
const struct firmware *fw,
|
|
||||||
u32 *boot_param,
|
|
||||||
u8 hw_variant, u8 sbe_type)
|
|
||||||
{
|
{
|
||||||
return -EOPNOTSUPP;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void btintel_reset_to_bootloader(struct hci_dev *hdev)
|
static inline void btintel_bootup(struct hci_dev *hdev,
|
||||||
|
const void *ptr, unsigned int len)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int btintel_read_debug_features(struct hci_dev *hdev,
|
static inline void btintel_secure_send_result(struct hci_dev *hdev,
|
||||||
struct intel_debug_features *features)
|
const void *ptr, unsigned int len)
|
||||||
{
|
{
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int btintel_set_debug_features(struct hci_dev *hdev,
|
|
||||||
const struct intel_debug_features *features)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1350,6 +1350,7 @@ static void btmrvl_sdio_coredump(struct device *dev)
|
|||||||
u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr;
|
u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr;
|
||||||
u8 dump_num = 0, idx, i, read_reg, doneflag = 0;
|
u8 dump_num = 0, idx, i, read_reg, doneflag = 0;
|
||||||
u32 memory_size, fw_dump_len = 0;
|
u32 memory_size, fw_dump_len = 0;
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
card = sdio_get_drvdata(func);
|
card = sdio_get_drvdata(func);
|
||||||
priv = card->priv;
|
priv = card->priv;
|
||||||
@ -1478,7 +1479,7 @@ done:
|
|||||||
if (fw_dump_len == 0)
|
if (fw_dump_len == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fw_dump_data = vzalloc(fw_dump_len+1);
|
fw_dump_data = vzalloc(fw_dump_len + 1);
|
||||||
if (!fw_dump_data) {
|
if (!fw_dump_data) {
|
||||||
BT_ERR("Vzalloc fw_dump_data fail!");
|
BT_ERR("Vzalloc fw_dump_data fail!");
|
||||||
return;
|
return;
|
||||||
@ -1493,20 +1494,18 @@ done:
|
|||||||
struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
|
struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
|
||||||
|
|
||||||
if (entry->mem_ptr) {
|
if (entry->mem_ptr) {
|
||||||
strcpy(fw_dump_ptr, "========Start dump ");
|
size += scnprintf(fw_dump_ptr + size,
|
||||||
fw_dump_ptr += strlen("========Start dump ");
|
fw_dump_len + 1 - size,
|
||||||
|
"========Start dump %s========\n",
|
||||||
|
entry->mem_name);
|
||||||
|
|
||||||
strcpy(fw_dump_ptr, entry->mem_name);
|
memcpy(fw_dump_ptr + size, entry->mem_ptr,
|
||||||
fw_dump_ptr += strlen(entry->mem_name);
|
entry->mem_size);
|
||||||
|
size += entry->mem_size;
|
||||||
|
|
||||||
strcpy(fw_dump_ptr, "========\n");
|
size += scnprintf(fw_dump_ptr + size,
|
||||||
fw_dump_ptr += strlen("========\n");
|
fw_dump_len + 1 - size,
|
||||||
|
"\n========End dump========\n");
|
||||||
memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
|
|
||||||
fw_dump_ptr += entry->mem_size;
|
|
||||||
|
|
||||||
strcpy(fw_dump_ptr, "\n========End dump========\n");
|
|
||||||
fw_dump_ptr += strlen("\n========End dump========\n");
|
|
||||||
|
|
||||||
vfree(mem_type_mapping_tbl[idx].mem_ptr);
|
vfree(mem_type_mapping_tbl[idx].mem_ptr);
|
||||||
mem_type_mapping_tbl[idx].mem_ptr = NULL;
|
mem_type_mapping_tbl[idx].mem_ptr = NULL;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* Copyright (c) 2017 Redpine Signals Inc.
|
* Copyright (c) 2017 Redpine Signals Inc.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
@ -681,11 +681,15 @@ out_free:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RTL8822CE supports the Microsoft vendor extension and uses 0xFCF0
|
/* The following chips supports the Microsoft vendor extension,
|
||||||
* for VsMsftOpCode.
|
* therefore set the corresponding VsMsftOpCode.
|
||||||
*/
|
*/
|
||||||
if (lmp_subver == RTL_ROM_LMP_8822B)
|
switch (lmp_subver) {
|
||||||
|
case RTL_ROM_LMP_8822B:
|
||||||
|
case RTL_ROM_LMP_8852A:
|
||||||
hci_set_msft_opcode(hdev, 0xFCF0);
|
hci_set_msft_opcode(hdev, 0xFCF0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return btrtl_dev;
|
return btrtl_dev;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,7 @@
|
|||||||
/**
|
/**
|
||||||
* struct bcm_device_data - device specific data
|
* struct bcm_device_data - device specific data
|
||||||
* @no_early_set_baudrate: Disallow set baudrate before driver setup()
|
* @no_early_set_baudrate: Disallow set baudrate before driver setup()
|
||||||
|
* @drive_rts_on_open: drive RTS signal on ->open() when platform requires it
|
||||||
*/
|
*/
|
||||||
struct bcm_device_data {
|
struct bcm_device_data {
|
||||||
bool no_early_set_baudrate;
|
bool no_early_set_baudrate;
|
||||||
@ -77,6 +78,8 @@ struct bcm_device_data {
|
|||||||
* @btlp: Apple ACPI method to toggle BT_WAKE pin ("Bluetooth Low Power")
|
* @btlp: Apple ACPI method to toggle BT_WAKE pin ("Bluetooth Low Power")
|
||||||
* @btpu: Apple ACPI method to drive BT_REG_ON pin high ("Bluetooth Power Up")
|
* @btpu: Apple ACPI method to drive BT_REG_ON pin high ("Bluetooth Power Up")
|
||||||
* @btpd: Apple ACPI method to drive BT_REG_ON pin low ("Bluetooth Power Down")
|
* @btpd: Apple ACPI method to drive BT_REG_ON pin low ("Bluetooth Power Down")
|
||||||
|
* @gpio_count: internal counter for GPIO resources associated with ACPI device
|
||||||
|
* @gpio_int_idx: index in _CRS for GpioInt() resource
|
||||||
* @txco_clk: external reference frequency clock used by Bluetooth device
|
* @txco_clk: external reference frequency clock used by Bluetooth device
|
||||||
* @lpo_clk: external LPO clock used by Bluetooth device
|
* @lpo_clk: external LPO clock used by Bluetooth device
|
||||||
* @supplies: VBAT and VDDIO supplies used by Bluetooth device
|
* @supplies: VBAT and VDDIO supplies used by Bluetooth device
|
||||||
@ -88,10 +91,13 @@ struct bcm_device_data {
|
|||||||
* set to 0 if @init_speed is already the preferred baudrate
|
* set to 0 if @init_speed is already the preferred baudrate
|
||||||
* @irq: interrupt triggered by HOST_WAKE_BT pin
|
* @irq: interrupt triggered by HOST_WAKE_BT pin
|
||||||
* @irq_active_low: whether @irq is active low
|
* @irq_active_low: whether @irq is active low
|
||||||
|
* @irq_acquired: flag to show if IRQ handler has been assigned
|
||||||
* @hu: pointer to HCI UART controller struct,
|
* @hu: pointer to HCI UART controller struct,
|
||||||
* used to disable flow control during runtime suspend and system sleep
|
* used to disable flow control during runtime suspend and system sleep
|
||||||
* @is_suspended: whether flow control is currently disabled
|
* @is_suspended: whether flow control is currently disabled
|
||||||
* @no_early_set_baudrate: don't set_baudrate before setup()
|
* @no_early_set_baudrate: don't set_baudrate before setup()
|
||||||
|
* @drive_rts_on_open: drive RTS signal on ->open() when platform requires it
|
||||||
|
* @pcm_int_params: keep the initial PCM configuration
|
||||||
*/
|
*/
|
||||||
struct bcm_device {
|
struct bcm_device {
|
||||||
/* Must be the first member, hci_serdev.c expects this. */
|
/* Must be the first member, hci_serdev.c expects this. */
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/serdev.h>
|
#include <linux/serdev.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
|
|
||||||
@ -21,6 +22,8 @@
|
|||||||
#include "btrtl.h"
|
#include "btrtl.h"
|
||||||
#include "hci_uart.h"
|
#include "hci_uart.h"
|
||||||
|
|
||||||
|
#define SUSPEND_TIMEOUT_MS 6000
|
||||||
|
|
||||||
#define HCI_3WIRE_ACK_PKT 0
|
#define HCI_3WIRE_ACK_PKT 0
|
||||||
#define HCI_3WIRE_LINK_PKT 15
|
#define HCI_3WIRE_LINK_PKT 15
|
||||||
|
|
||||||
@ -51,8 +54,10 @@
|
|||||||
|
|
||||||
/* H5 state flags */
|
/* H5 state flags */
|
||||||
enum {
|
enum {
|
||||||
H5_RX_ESC, /* SLIP escape mode */
|
H5_RX_ESC, /* SLIP escape mode */
|
||||||
H5_TX_ACK_REQ, /* Pending ack to send */
|
H5_TX_ACK_REQ, /* Pending ack to send */
|
||||||
|
H5_WAKEUP_DISABLE, /* Device cannot wake host */
|
||||||
|
H5_HW_FLOW_CONTROL, /* Use HW flow control */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct h5 {
|
struct h5 {
|
||||||
@ -97,6 +102,10 @@ struct h5 {
|
|||||||
struct gpio_desc *device_wake_gpio;
|
struct gpio_desc *device_wake_gpio;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum h5_driver_info {
|
||||||
|
H5_INFO_WAKEUP_DISABLE = BIT(0),
|
||||||
|
};
|
||||||
|
|
||||||
struct h5_vnd {
|
struct h5_vnd {
|
||||||
int (*setup)(struct h5 *h5);
|
int (*setup)(struct h5 *h5);
|
||||||
void (*open)(struct h5 *h5);
|
void (*open)(struct h5 *h5);
|
||||||
@ -106,6 +115,11 @@ struct h5_vnd {
|
|||||||
const struct acpi_gpio_mapping *acpi_gpio_map;
|
const struct acpi_gpio_mapping *acpi_gpio_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct h5_device_data {
|
||||||
|
uint32_t driver_info;
|
||||||
|
struct h5_vnd *vnd;
|
||||||
|
};
|
||||||
|
|
||||||
static void h5_reset_rx(struct h5 *h5);
|
static void h5_reset_rx(struct h5 *h5);
|
||||||
|
|
||||||
static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
|
static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
|
||||||
@ -573,6 +587,10 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count)
|
|||||||
count -= processed;
|
count -= processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_get(&hu->serdev->dev);
|
||||||
|
pm_runtime_mark_last_busy(&hu->serdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&hu->serdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,6 +627,10 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm_runtime_get_sync(&hu->serdev->dev);
|
||||||
|
pm_runtime_mark_last_busy(&hu->serdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&hu->serdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,6 +813,8 @@ static int h5_serdev_probe(struct serdev_device *serdev)
|
|||||||
{
|
{
|
||||||
struct device *dev = &serdev->dev;
|
struct device *dev = &serdev->dev;
|
||||||
struct h5 *h5;
|
struct h5 *h5;
|
||||||
|
const struct h5_device_data *data;
|
||||||
|
int err;
|
||||||
|
|
||||||
h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL);
|
h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL);
|
||||||
if (!h5)
|
if (!h5)
|
||||||
@ -807,20 +831,19 @@ static int h5_serdev_probe(struct serdev_device *serdev)
|
|||||||
if (!match)
|
if (!match)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
h5->vnd = (const struct h5_vnd *)match->driver_data;
|
data = (const struct h5_device_data *)match->driver_data;
|
||||||
|
h5->vnd = data->vnd;
|
||||||
h5->id = (char *)match->id;
|
h5->id = (char *)match->id;
|
||||||
|
|
||||||
if (h5->vnd->acpi_gpio_map)
|
if (h5->vnd->acpi_gpio_map)
|
||||||
devm_acpi_dev_add_driver_gpios(dev,
|
devm_acpi_dev_add_driver_gpios(dev,
|
||||||
h5->vnd->acpi_gpio_map);
|
h5->vnd->acpi_gpio_map);
|
||||||
} else {
|
} else {
|
||||||
const void *data;
|
|
||||||
|
|
||||||
data = of_device_get_match_data(dev);
|
data = of_device_get_match_data(dev);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
h5->vnd = (const struct h5_vnd *)data;
|
h5->vnd = data->vnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -833,7 +856,14 @@ static int h5_serdev_probe(struct serdev_device *serdev)
|
|||||||
if (IS_ERR(h5->device_wake_gpio))
|
if (IS_ERR(h5->device_wake_gpio))
|
||||||
return PTR_ERR(h5->device_wake_gpio);
|
return PTR_ERR(h5->device_wake_gpio);
|
||||||
|
|
||||||
return hci_uart_register_device(&h5->serdev_hu, &h5p);
|
err = hci_uart_register_device(&h5->serdev_hu, &h5p);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (data->driver_info & H5_INFO_WAKEUP_DISABLE)
|
||||||
|
set_bit(H5_WAKEUP_DISABLE, &h5->flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void h5_serdev_remove(struct serdev_device *serdev)
|
static void h5_serdev_remove(struct serdev_device *serdev)
|
||||||
@ -902,6 +932,9 @@ static int h5_btrtl_setup(struct h5 *h5)
|
|||||||
serdev_device_set_baudrate(h5->hu->serdev, controller_baudrate);
|
serdev_device_set_baudrate(h5->hu->serdev, controller_baudrate);
|
||||||
serdev_device_set_flow_control(h5->hu->serdev, flow_control);
|
serdev_device_set_flow_control(h5->hu->serdev, flow_control);
|
||||||
|
|
||||||
|
if (flow_control)
|
||||||
|
set_bit(H5_HW_FLOW_CONTROL, &h5->flags);
|
||||||
|
|
||||||
err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev);
|
err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev);
|
||||||
/* Give the device some time before the hci-core sends it a reset */
|
/* Give the device some time before the hci-core sends it a reset */
|
||||||
usleep_range(10000, 20000);
|
usleep_range(10000, 20000);
|
||||||
@ -916,11 +949,25 @@ out_free:
|
|||||||
|
|
||||||
static void h5_btrtl_open(struct h5 *h5)
|
static void h5_btrtl_open(struct h5 *h5)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Since h5_btrtl_resume() does a device_reprobe() the suspend handling
|
||||||
|
* done by the hci_suspend_notifier is not necessary; it actually causes
|
||||||
|
* delays and a bunch of errors to get logged, so disable it.
|
||||||
|
*/
|
||||||
|
if (test_bit(H5_WAKEUP_DISABLE, &h5->flags))
|
||||||
|
set_bit(HCI_UART_NO_SUSPEND_NOTIFIER, &h5->hu->flags);
|
||||||
|
|
||||||
/* Devices always start with these fixed parameters */
|
/* Devices always start with these fixed parameters */
|
||||||
serdev_device_set_flow_control(h5->hu->serdev, false);
|
serdev_device_set_flow_control(h5->hu->serdev, false);
|
||||||
serdev_device_set_parity(h5->hu->serdev, SERDEV_PARITY_EVEN);
|
serdev_device_set_parity(h5->hu->serdev, SERDEV_PARITY_EVEN);
|
||||||
serdev_device_set_baudrate(h5->hu->serdev, 115200);
|
serdev_device_set_baudrate(h5->hu->serdev, 115200);
|
||||||
|
|
||||||
|
pm_runtime_set_active(&h5->hu->serdev->dev);
|
||||||
|
pm_runtime_use_autosuspend(&h5->hu->serdev->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(&h5->hu->serdev->dev,
|
||||||
|
SUSPEND_TIMEOUT_MS);
|
||||||
|
pm_runtime_enable(&h5->hu->serdev->dev);
|
||||||
|
|
||||||
/* The controller needs up to 500ms to wakeup */
|
/* The controller needs up to 500ms to wakeup */
|
||||||
gpiod_set_value_cansleep(h5->enable_gpio, 1);
|
gpiod_set_value_cansleep(h5->enable_gpio, 1);
|
||||||
gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
|
gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
|
||||||
@ -929,21 +976,26 @@ static void h5_btrtl_open(struct h5 *h5)
|
|||||||
|
|
||||||
static void h5_btrtl_close(struct h5 *h5)
|
static void h5_btrtl_close(struct h5 *h5)
|
||||||
{
|
{
|
||||||
|
pm_runtime_disable(&h5->hu->serdev->dev);
|
||||||
|
|
||||||
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
|
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
|
||||||
gpiod_set_value_cansleep(h5->enable_gpio, 0);
|
gpiod_set_value_cansleep(h5->enable_gpio, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Suspend/resume support. On many devices the RTL BT device loses power during
|
/* Suspend/resume support. On many devices the RTL BT device loses power during
|
||||||
* suspend/resume, causing it to lose its firmware and all state. So we simply
|
* suspend/resume, causing it to lose its firmware and all state. So we simply
|
||||||
* turn it off on suspend and reprobe on resume. This mirrors how RTL devices
|
* turn it off on suspend and reprobe on resume. This mirrors how RTL devices
|
||||||
* are handled in the USB driver, where the USB_QUIRK_RESET_RESUME is used which
|
* are handled in the USB driver, where the BTUSB_WAKEUP_DISABLE is used which
|
||||||
* also causes a reprobe on resume.
|
* also causes a reprobe on resume.
|
||||||
*/
|
*/
|
||||||
static int h5_btrtl_suspend(struct h5 *h5)
|
static int h5_btrtl_suspend(struct h5 *h5)
|
||||||
{
|
{
|
||||||
serdev_device_set_flow_control(h5->hu->serdev, false);
|
serdev_device_set_flow_control(h5->hu->serdev, false);
|
||||||
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
|
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
|
||||||
gpiod_set_value_cansleep(h5->enable_gpio, 0);
|
|
||||||
|
if (test_bit(H5_WAKEUP_DISABLE, &h5->flags))
|
||||||
|
gpiod_set_value_cansleep(h5->enable_gpio, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -969,17 +1021,25 @@ static void h5_btrtl_reprobe_worker(struct work_struct *work)
|
|||||||
|
|
||||||
static int h5_btrtl_resume(struct h5 *h5)
|
static int h5_btrtl_resume(struct h5 *h5)
|
||||||
{
|
{
|
||||||
struct h5_btrtl_reprobe *reprobe;
|
if (test_bit(H5_WAKEUP_DISABLE, &h5->flags)) {
|
||||||
|
struct h5_btrtl_reprobe *reprobe;
|
||||||
|
|
||||||
reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
|
reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
|
||||||
if (!reprobe)
|
if (!reprobe)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
__module_get(THIS_MODULE);
|
__module_get(THIS_MODULE);
|
||||||
|
|
||||||
|
INIT_WORK(&reprobe->work, h5_btrtl_reprobe_worker);
|
||||||
|
reprobe->dev = get_device(&h5->hu->serdev->dev);
|
||||||
|
queue_work(system_long_wq, &reprobe->work);
|
||||||
|
} else {
|
||||||
|
gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
|
||||||
|
|
||||||
|
if (test_bit(H5_HW_FLOW_CONTROL, &h5->flags))
|
||||||
|
serdev_device_set_flow_control(h5->hu->serdev, true);
|
||||||
|
}
|
||||||
|
|
||||||
INIT_WORK(&reprobe->work, h5_btrtl_reprobe_worker);
|
|
||||||
reprobe->dev = get_device(&h5->hu->serdev->dev);
|
|
||||||
queue_work(system_long_wq, &reprobe->work);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1001,13 +1061,22 @@ static struct h5_vnd rtl_vnd = {
|
|||||||
.resume = h5_btrtl_resume,
|
.resume = h5_btrtl_resume,
|
||||||
.acpi_gpio_map = acpi_btrtl_gpios,
|
.acpi_gpio_map = acpi_btrtl_gpios,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct h5_device_data h5_data_rtl8822cs = {
|
||||||
|
.vnd = &rtl_vnd,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct h5_device_data h5_data_rtl8723bs = {
|
||||||
|
.driver_info = H5_INFO_WAKEUP_DISABLE,
|
||||||
|
.vnd = &rtl_vnd,
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
static const struct acpi_device_id h5_acpi_match[] = {
|
static const struct acpi_device_id h5_acpi_match[] = {
|
||||||
#ifdef CONFIG_BT_HCIUART_RTL
|
#ifdef CONFIG_BT_HCIUART_RTL
|
||||||
{ "OBDA0623", (kernel_ulong_t)&rtl_vnd },
|
{ "OBDA0623", (kernel_ulong_t)&h5_data_rtl8723bs },
|
||||||
{ "OBDA8723", (kernel_ulong_t)&rtl_vnd },
|
{ "OBDA8723", (kernel_ulong_t)&h5_data_rtl8723bs },
|
||||||
#endif
|
#endif
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
@ -1016,16 +1085,17 @@ MODULE_DEVICE_TABLE(acpi, h5_acpi_match);
|
|||||||
|
|
||||||
static const struct dev_pm_ops h5_serdev_pm_ops = {
|
static const struct dev_pm_ops h5_serdev_pm_ops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(h5_serdev_suspend, h5_serdev_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(h5_serdev_suspend, h5_serdev_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(h5_serdev_suspend, h5_serdev_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id rtl_bluetooth_of_match[] = {
|
static const struct of_device_id rtl_bluetooth_of_match[] = {
|
||||||
#ifdef CONFIG_BT_HCIUART_RTL
|
#ifdef CONFIG_BT_HCIUART_RTL
|
||||||
{ .compatible = "realtek,rtl8822cs-bt",
|
{ .compatible = "realtek,rtl8822cs-bt",
|
||||||
.data = (const void *)&rtl_vnd },
|
.data = (const void *)&h5_data_rtl8822cs },
|
||||||
{ .compatible = "realtek,rtl8723bs-bt",
|
{ .compatible = "realtek,rtl8723bs-bt",
|
||||||
.data = (const void *)&rtl_vnd },
|
.data = (const void *)&h5_data_rtl8723bs },
|
||||||
{ .compatible = "realtek,rtl8723ds-bt",
|
{ .compatible = "realtek,rtl8723ds-bt",
|
||||||
.data = (const void *)&rtl_vnd },
|
.data = (const void *)&h5_data_rtl8723bs },
|
||||||
#endif
|
#endif
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
@ -343,6 +343,9 @@ int hci_uart_register_device(struct hci_uart *hu,
|
|||||||
hdev->setup = hci_uart_setup;
|
hdev->setup = hci_uart_setup;
|
||||||
SET_HCIDEV_DEV(hdev, &hu->serdev->dev);
|
SET_HCIDEV_DEV(hdev, &hu->serdev->dev);
|
||||||
|
|
||||||
|
if (test_bit(HCI_UART_NO_SUSPEND_NOTIFIER, &hu->flags))
|
||||||
|
set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks);
|
||||||
|
|
||||||
if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
|
if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
|
||||||
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
||||||
|
|
||||||
|
@ -86,9 +86,10 @@ struct hci_uart {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* HCI_UART proto flag bits */
|
/* HCI_UART proto flag bits */
|
||||||
#define HCI_UART_PROTO_SET 0
|
#define HCI_UART_PROTO_SET 0
|
||||||
#define HCI_UART_REGISTERED 1
|
#define HCI_UART_REGISTERED 1
|
||||||
#define HCI_UART_PROTO_READY 2
|
#define HCI_UART_PROTO_READY 2
|
||||||
|
#define HCI_UART_NO_SUSPEND_NOTIFIER 3
|
||||||
|
|
||||||
/* TX states */
|
/* TX states */
|
||||||
#define HCI_UART_SENDING 1
|
#define HCI_UART_SENDING 1
|
||||||
|
@ -221,6 +221,7 @@ struct oob_data {
|
|||||||
|
|
||||||
struct adv_info {
|
struct adv_info {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
bool enabled;
|
||||||
bool pending;
|
bool pending;
|
||||||
__u8 instance;
|
__u8 instance;
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
@ -628,6 +629,7 @@ struct hci_conn {
|
|||||||
__u8 init_addr_type;
|
__u8 init_addr_type;
|
||||||
bdaddr_t resp_addr;
|
bdaddr_t resp_addr;
|
||||||
__u8 resp_addr_type;
|
__u8 resp_addr_type;
|
||||||
|
__u8 adv_instance;
|
||||||
__u16 handle;
|
__u16 handle;
|
||||||
__u16 state;
|
__u16 state;
|
||||||
__u8 mode;
|
__u8 mode;
|
||||||
@ -1223,14 +1225,25 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
|
|||||||
dev_set_drvdata(&hdev->dev, data);
|
dev_set_drvdata(&hdev->dev, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void *hci_get_priv(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
return (char *)hdev + sizeof(*hdev);
|
||||||
|
}
|
||||||
|
|
||||||
struct hci_dev *hci_dev_get(int index);
|
struct hci_dev *hci_dev_get(int index);
|
||||||
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, u8 src_type);
|
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src, u8 src_type);
|
||||||
|
|
||||||
struct hci_dev *hci_alloc_dev(void);
|
struct hci_dev *hci_alloc_dev_priv(int sizeof_priv);
|
||||||
|
|
||||||
|
static inline struct hci_dev *hci_alloc_dev(void)
|
||||||
|
{
|
||||||
|
return hci_alloc_dev_priv(0);
|
||||||
|
}
|
||||||
|
|
||||||
void hci_free_dev(struct hci_dev *hdev);
|
void hci_free_dev(struct hci_dev *hdev);
|
||||||
int hci_register_dev(struct hci_dev *hdev);
|
int hci_register_dev(struct hci_dev *hdev);
|
||||||
void hci_unregister_dev(struct hci_dev *hdev);
|
void hci_unregister_dev(struct hci_dev *hdev);
|
||||||
void hci_cleanup_dev(struct hci_dev *hdev);
|
void hci_release_dev(struct hci_dev *hdev);
|
||||||
int hci_suspend_dev(struct hci_dev *hdev);
|
int hci_suspend_dev(struct hci_dev *hdev);
|
||||||
int hci_resume_dev(struct hci_dev *hdev);
|
int hci_resume_dev(struct hci_dev *hdev);
|
||||||
int hci_reset_dev(struct hci_dev *hdev);
|
int hci_reset_dev(struct hci_dev *hdev);
|
||||||
@ -1412,6 +1425,10 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
|||||||
!hci_dev_test_flag(dev, HCI_AUTO_OFF))
|
!hci_dev_test_flag(dev, HCI_AUTO_OFF))
|
||||||
#define bredr_sc_enabled(dev) (lmp_sc_capable(dev) && \
|
#define bredr_sc_enabled(dev) (lmp_sc_capable(dev) && \
|
||||||
hci_dev_test_flag(dev, HCI_SC_ENABLED))
|
hci_dev_test_flag(dev, HCI_SC_ENABLED))
|
||||||
|
#define rpa_valid(dev) (bacmp(&dev->rpa, BDADDR_ANY) && \
|
||||||
|
!hci_dev_test_flag(dev, HCI_RPA_EXPIRED))
|
||||||
|
#define adv_rpa_valid(adv) (bacmp(&adv->random_addr, BDADDR_ANY) && \
|
||||||
|
!adv->rpa_expired)
|
||||||
|
|
||||||
#define scan_1m(dev) (((dev)->le_tx_def_phys & HCI_LE_SET_PHY_1M) || \
|
#define scan_1m(dev) (((dev)->le_tx_def_phys & HCI_LE_SET_PHY_1M) || \
|
||||||
((dev)->le_rx_def_phys & HCI_LE_SET_PHY_1M))
|
((dev)->le_rx_def_phys & HCI_LE_SET_PHY_1M))
|
||||||
|
@ -170,7 +170,8 @@ static void lowpan_dev_debugfs_ctx_init(struct net_device *dev,
|
|||||||
struct dentry *root;
|
struct dentry *root;
|
||||||
char buf[32];
|
char buf[32];
|
||||||
|
|
||||||
WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
|
if (WARN_ON_ONCE(id >= LOWPAN_IPHC_CTX_TABLE_SIZE))
|
||||||
|
return;
|
||||||
|
|
||||||
sprintf(buf, "%d", id);
|
sprintf(buf, "%d", id);
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
|
|
||||||
#define BTNAMSIZ 18
|
#define BTNAMSIZ 21
|
||||||
|
|
||||||
/* CMTP ioctl defines */
|
/* CMTP ioctl defines */
|
||||||
#define CMTPCONNADD _IOW('C', 200, int)
|
#define CMTPCONNADD _IOW('C', 200, int)
|
||||||
|
@ -1343,6 +1343,12 @@ int hci_inquiry(void __user *arg)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Restrict maximum inquiry length to 60 seconds */
|
||||||
|
if (ir.length > 60) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
|
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
|
||||||
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
|
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
|
||||||
@ -1718,6 +1724,7 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
|
|||||||
int hci_dev_do_close(struct hci_dev *hdev)
|
int hci_dev_do_close(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
bool auto_off;
|
bool auto_off;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("%s %p", hdev->name, hdev);
|
BT_DBG("%s %p", hdev->name, hdev);
|
||||||
|
|
||||||
@ -1727,10 +1734,18 @@ int hci_dev_do_close(struct hci_dev *hdev)
|
|||||||
hci_request_cancel_all(hdev);
|
hci_request_cancel_all(hdev);
|
||||||
hci_req_sync_lock(hdev);
|
hci_req_sync_lock(hdev);
|
||||||
|
|
||||||
|
if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
|
||||||
|
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
|
||||||
|
test_bit(HCI_UP, &hdev->flags)) {
|
||||||
|
/* Execute vendor specific shutdown routine */
|
||||||
|
if (hdev->shutdown)
|
||||||
|
err = hdev->shutdown(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
|
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
|
||||||
cancel_delayed_work_sync(&hdev->cmd_timer);
|
cancel_delayed_work_sync(&hdev->cmd_timer);
|
||||||
hci_req_sync_unlock(hdev);
|
hci_req_sync_unlock(hdev);
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
hci_leds_update_powered(hdev, false);
|
hci_leds_update_powered(hdev, false);
|
||||||
@ -1798,14 +1813,6 @@ int hci_dev_do_close(struct hci_dev *hdev)
|
|||||||
clear_bit(HCI_INIT, &hdev->flags);
|
clear_bit(HCI_INIT, &hdev->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
|
|
||||||
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
|
|
||||||
test_bit(HCI_UP, &hdev->flags)) {
|
|
||||||
/* Execute vendor specific shutdown routine */
|
|
||||||
if (hdev->shutdown)
|
|
||||||
hdev->shutdown(hdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* flush cmd work */
|
/* flush cmd work */
|
||||||
flush_work(&hdev->cmd_work);
|
flush_work(&hdev->cmd_work);
|
||||||
|
|
||||||
@ -1845,7 +1852,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
|
|||||||
hci_req_sync_unlock(hdev);
|
hci_req_sync_unlock(hdev);
|
||||||
|
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hci_dev_close(__u16 dev)
|
int hci_dev_close(__u16 dev)
|
||||||
@ -3751,11 +3758,18 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Alloc HCI device */
|
/* Alloc HCI device */
|
||||||
struct hci_dev *hci_alloc_dev(void)
|
struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
|
unsigned int alloc_size;
|
||||||
|
|
||||||
hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
|
alloc_size = sizeof(*hdev);
|
||||||
|
if (sizeof_priv) {
|
||||||
|
/* Fixme: May need ALIGN-ment? */
|
||||||
|
alloc_size += sizeof_priv;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdev = kzalloc(alloc_size, GFP_KERNEL);
|
||||||
if (!hdev)
|
if (!hdev)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -3869,7 +3883,7 @@ struct hci_dev *hci_alloc_dev(void)
|
|||||||
|
|
||||||
return hdev;
|
return hdev;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(hci_alloc_dev);
|
EXPORT_SYMBOL(hci_alloc_dev_priv);
|
||||||
|
|
||||||
/* Free HCI device */
|
/* Free HCI device */
|
||||||
void hci_free_dev(struct hci_dev *hdev)
|
void hci_free_dev(struct hci_dev *hdev)
|
||||||
@ -4034,13 +4048,13 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
device_del(&hdev->dev);
|
device_del(&hdev->dev);
|
||||||
/* Actual cleanup is deferred until hci_cleanup_dev(). */
|
/* Actual cleanup is deferred until hci_release_dev(). */
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(hci_unregister_dev);
|
EXPORT_SYMBOL(hci_unregister_dev);
|
||||||
|
|
||||||
/* Cleanup HCI device */
|
/* Release HCI device */
|
||||||
void hci_cleanup_dev(struct hci_dev *hdev)
|
void hci_release_dev(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
debugfs_remove_recursive(hdev->debugfs);
|
debugfs_remove_recursive(hdev->debugfs);
|
||||||
kfree_const(hdev->hw_info);
|
kfree_const(hdev->hw_info);
|
||||||
@ -4067,7 +4081,9 @@ void hci_cleanup_dev(struct hci_dev *hdev)
|
|||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
ida_simple_remove(&hci_index_ida, hdev->id);
|
ida_simple_remove(&hci_index_ida, hdev->id);
|
||||||
|
kfree(hdev);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(hci_release_dev);
|
||||||
|
|
||||||
/* Suspend HCI device */
|
/* Suspend HCI device */
|
||||||
int hci_suspend_dev(struct hci_dev *hdev)
|
int hci_suspend_dev(struct hci_dev *hdev)
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
|
#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
|
||||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
|
||||||
|
#define secs_to_jiffies(_secs) msecs_to_jiffies((_secs) * 1000)
|
||||||
|
|
||||||
/* Handle HCI Event packets */
|
/* Handle HCI Event packets */
|
||||||
|
|
||||||
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb,
|
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb,
|
||||||
@ -1171,6 +1173,12 @@ static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
bacpy(&hdev->random_addr, sent);
|
bacpy(&hdev->random_addr, sent);
|
||||||
|
|
||||||
|
if (!bacmp(&hdev->rpa, sent)) {
|
||||||
|
hci_dev_clear_flag(hdev, HCI_RPA_EXPIRED);
|
||||||
|
queue_delayed_work(hdev->workqueue, &hdev->rpa_expired,
|
||||||
|
secs_to_jiffies(hdev->rpa_timeout));
|
||||||
|
}
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1201,24 +1209,30 @@ static void hci_cc_le_set_adv_set_random_addr(struct hci_dev *hdev,
|
|||||||
{
|
{
|
||||||
__u8 status = *((__u8 *) skb->data);
|
__u8 status = *((__u8 *) skb->data);
|
||||||
struct hci_cp_le_set_adv_set_rand_addr *cp;
|
struct hci_cp_le_set_adv_set_rand_addr *cp;
|
||||||
struct adv_info *adv_instance;
|
struct adv_info *adv;
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_SET_RAND_ADDR);
|
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_SET_RAND_ADDR);
|
||||||
if (!cp)
|
/* Update only in case the adv instance since handle 0x00 shall be using
|
||||||
|
* HCI_OP_LE_SET_RANDOM_ADDR since that allows both extended and
|
||||||
|
* non-extended adverting.
|
||||||
|
*/
|
||||||
|
if (!cp || !cp->handle)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
if (!cp->handle) {
|
adv = hci_find_adv_instance(hdev, cp->handle);
|
||||||
/* Store in hdev for instance 0 (Set adv and Directed advs) */
|
if (adv) {
|
||||||
bacpy(&hdev->random_addr, &cp->bdaddr);
|
bacpy(&adv->random_addr, &cp->bdaddr);
|
||||||
} else {
|
if (!bacmp(&hdev->rpa, &cp->bdaddr)) {
|
||||||
adv_instance = hci_find_adv_instance(hdev, cp->handle);
|
adv->rpa_expired = false;
|
||||||
if (adv_instance)
|
queue_delayed_work(hdev->workqueue,
|
||||||
bacpy(&adv_instance->random_addr, &cp->bdaddr);
|
&adv->rpa_expired_cb,
|
||||||
|
secs_to_jiffies(hdev->rpa_timeout));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
@ -1277,7 +1291,9 @@ static void hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev,
|
|||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_cp_le_set_ext_adv_enable *cp;
|
struct hci_cp_le_set_ext_adv_enable *cp;
|
||||||
|
struct hci_cp_ext_adv_set *set;
|
||||||
__u8 status = *((__u8 *) skb->data);
|
__u8 status = *((__u8 *) skb->data);
|
||||||
|
struct adv_info *adv = NULL, *n;
|
||||||
|
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||||
|
|
||||||
@ -1288,22 +1304,48 @@ static void hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev,
|
|||||||
if (!cp)
|
if (!cp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
set = (void *)cp->data;
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (cp->num_of_sets)
|
||||||
|
adv = hci_find_adv_instance(hdev, set->handle);
|
||||||
|
|
||||||
if (cp->enable) {
|
if (cp->enable) {
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
|
|
||||||
hci_dev_set_flag(hdev, HCI_LE_ADV);
|
hci_dev_set_flag(hdev, HCI_LE_ADV);
|
||||||
|
|
||||||
|
if (adv)
|
||||||
|
adv->enabled = true;
|
||||||
|
|
||||||
conn = hci_lookup_le_connect(hdev);
|
conn = hci_lookup_le_connect(hdev);
|
||||||
if (conn)
|
if (conn)
|
||||||
queue_delayed_work(hdev->workqueue,
|
queue_delayed_work(hdev->workqueue,
|
||||||
&conn->le_conn_timeout,
|
&conn->le_conn_timeout,
|
||||||
conn->conn_timeout);
|
conn->conn_timeout);
|
||||||
} else {
|
} else {
|
||||||
|
if (adv) {
|
||||||
|
adv->enabled = false;
|
||||||
|
/* If just one instance was disabled check if there are
|
||||||
|
* any other instance enabled before clearing HCI_LE_ADV
|
||||||
|
*/
|
||||||
|
list_for_each_entry_safe(adv, n, &hdev->adv_instances,
|
||||||
|
list) {
|
||||||
|
if (adv->enabled)
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* All instances shall be considered disabled */
|
||||||
|
list_for_each_entry_safe(adv, n, &hdev->adv_instances,
|
||||||
|
list)
|
||||||
|
adv->enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
hci_dev_clear_flag(hdev, HCI_LE_ADV);
|
hci_dev_clear_flag(hdev, HCI_LE_ADV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlock:
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2306,19 +2348,20 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
|
|||||||
|
|
||||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||||
if (conn) {
|
if (conn) {
|
||||||
u8 type = conn->type;
|
|
||||||
|
|
||||||
mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
|
mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
|
||||||
conn->dst_type, status);
|
conn->dst_type, status);
|
||||||
|
|
||||||
|
if (conn->type == LE_LINK) {
|
||||||
|
hdev->cur_adv_instance = conn->adv_instance;
|
||||||
|
hci_req_reenable_advertising(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
/* If the disconnection failed for any reason, the upper layer
|
/* If the disconnection failed for any reason, the upper layer
|
||||||
* does not retry to disconnect in current implementation.
|
* does not retry to disconnect in current implementation.
|
||||||
* Hence, we need to do some basic cleanup here and re-enable
|
* Hence, we need to do some basic cleanup here and re-enable
|
||||||
* advertising if necessary.
|
* advertising if necessary.
|
||||||
*/
|
*/
|
||||||
hci_conn_del(conn);
|
hci_conn_del(conn);
|
||||||
if (type == LE_LINK)
|
|
||||||
hci_req_reenable_advertising(hdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
@ -2844,7 +2887,6 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
struct hci_conn_params *params;
|
struct hci_conn_params *params;
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
bool mgmt_connected;
|
bool mgmt_connected;
|
||||||
u8 type;
|
|
||||||
|
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
||||||
|
|
||||||
@ -2899,10 +2941,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type = conn->type;
|
|
||||||
|
|
||||||
hci_disconn_cfm(conn, ev->reason);
|
hci_disconn_cfm(conn, ev->reason);
|
||||||
hci_conn_del(conn);
|
|
||||||
|
|
||||||
/* The suspend notifier is waiting for all devices to disconnect so
|
/* The suspend notifier is waiting for all devices to disconnect so
|
||||||
* clear the bit from pending tasks and inform the wait queue.
|
* clear the bit from pending tasks and inform the wait queue.
|
||||||
@ -2922,8 +2961,12 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
* or until a connection is created or until the Advertising
|
* or until a connection is created or until the Advertising
|
||||||
* is timed out due to Directed Advertising."
|
* is timed out due to Directed Advertising."
|
||||||
*/
|
*/
|
||||||
if (type == LE_LINK)
|
if (conn->type == LE_LINK) {
|
||||||
|
hdev->cur_adv_instance = conn->adv_instance;
|
||||||
hci_req_reenable_advertising(hdev);
|
hci_req_reenable_advertising(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_conn_del(conn);
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
@ -3268,11 +3311,9 @@ unlock:
|
|||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void handle_cmd_cnt_and_timer(struct hci_dev *hdev,
|
static inline void handle_cmd_cnt_and_timer(struct hci_dev *hdev, u8 ncmd)
|
||||||
u16 opcode, u8 ncmd)
|
|
||||||
{
|
{
|
||||||
if (opcode != HCI_OP_NOP)
|
cancel_delayed_work(&hdev->cmd_timer);
|
||||||
cancel_delayed_work(&hdev->cmd_timer);
|
|
||||||
|
|
||||||
if (!test_bit(HCI_RESET, &hdev->flags)) {
|
if (!test_bit(HCI_RESET, &hdev->flags)) {
|
||||||
if (ncmd) {
|
if (ncmd) {
|
||||||
@ -3647,7 +3688,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_cmd_cnt_and_timer(hdev, *opcode, ev->ncmd);
|
handle_cmd_cnt_and_timer(hdev, ev->ncmd);
|
||||||
|
|
||||||
hci_req_cmd_complete(hdev, *opcode, *status, req_complete,
|
hci_req_cmd_complete(hdev, *opcode, *status, req_complete,
|
||||||
req_complete_skb);
|
req_complete_skb);
|
||||||
@ -3748,7 +3789,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_cmd_cnt_and_timer(hdev, *opcode, ev->ncmd);
|
handle_cmd_cnt_and_timer(hdev, ev->ncmd);
|
||||||
|
|
||||||
/* Indicate request completion if the command failed. Also, if
|
/* Indicate request completion if the command failed. Also, if
|
||||||
* we're not waiting for a special event and we get a success
|
* we're not waiting for a special event and we get a success
|
||||||
@ -4382,6 +4423,21 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
|
|||||||
|
|
||||||
switch (ev->status) {
|
switch (ev->status) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
|
/* The synchronous connection complete event should only be
|
||||||
|
* sent once per new connection. Receiving a successful
|
||||||
|
* complete event when the connection status is already
|
||||||
|
* BT_CONNECTED means that the device is misbehaving and sent
|
||||||
|
* multiple complete event packets for the same new connection.
|
||||||
|
*
|
||||||
|
* Registering the device more than once can corrupt kernel
|
||||||
|
* memory, hence upon detecting this invalid event, we report
|
||||||
|
* an error and ignore the packet.
|
||||||
|
*/
|
||||||
|
if (conn->state == BT_CONNECTED) {
|
||||||
|
bt_dev_err(hdev, "Ignoring connect complete event for existing connection");
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
conn->handle = __le16_to_cpu(ev->handle);
|
conn->handle = __le16_to_cpu(ev->handle);
|
||||||
conn->state = BT_CONNECTED;
|
conn->state = BT_CONNECTED;
|
||||||
conn->type = ev->link_type;
|
conn->type = ev->link_type;
|
||||||
@ -5104,9 +5160,64 @@ static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void le_conn_update_addr(struct hci_conn *conn, bdaddr_t *bdaddr,
|
||||||
|
u8 bdaddr_type, bdaddr_t *local_rpa)
|
||||||
|
{
|
||||||
|
if (conn->out) {
|
||||||
|
conn->dst_type = bdaddr_type;
|
||||||
|
conn->resp_addr_type = bdaddr_type;
|
||||||
|
bacpy(&conn->resp_addr, bdaddr);
|
||||||
|
|
||||||
|
/* Check if the controller has set a Local RPA then it must be
|
||||||
|
* used instead or hdev->rpa.
|
||||||
|
*/
|
||||||
|
if (local_rpa && bacmp(local_rpa, BDADDR_ANY)) {
|
||||||
|
conn->init_addr_type = ADDR_LE_DEV_RANDOM;
|
||||||
|
bacpy(&conn->init_addr, local_rpa);
|
||||||
|
} else if (hci_dev_test_flag(conn->hdev, HCI_PRIVACY)) {
|
||||||
|
conn->init_addr_type = ADDR_LE_DEV_RANDOM;
|
||||||
|
bacpy(&conn->init_addr, &conn->hdev->rpa);
|
||||||
|
} else {
|
||||||
|
hci_copy_identity_address(conn->hdev, &conn->init_addr,
|
||||||
|
&conn->init_addr_type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conn->resp_addr_type = conn->hdev->adv_addr_type;
|
||||||
|
/* Check if the controller has set a Local RPA then it must be
|
||||||
|
* used instead or hdev->rpa.
|
||||||
|
*/
|
||||||
|
if (local_rpa && bacmp(local_rpa, BDADDR_ANY)) {
|
||||||
|
conn->resp_addr_type = ADDR_LE_DEV_RANDOM;
|
||||||
|
bacpy(&conn->resp_addr, local_rpa);
|
||||||
|
} else if (conn->hdev->adv_addr_type == ADDR_LE_DEV_RANDOM) {
|
||||||
|
/* In case of ext adv, resp_addr will be updated in
|
||||||
|
* Adv Terminated event.
|
||||||
|
*/
|
||||||
|
if (!ext_adv_capable(conn->hdev))
|
||||||
|
bacpy(&conn->resp_addr,
|
||||||
|
&conn->hdev->random_addr);
|
||||||
|
} else {
|
||||||
|
bacpy(&conn->resp_addr, &conn->hdev->bdaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->init_addr_type = bdaddr_type;
|
||||||
|
bacpy(&conn->init_addr, bdaddr);
|
||||||
|
|
||||||
|
/* For incoming connections, set the default minimum
|
||||||
|
* and maximum connection interval. They will be used
|
||||||
|
* to check if the parameters are in range and if not
|
||||||
|
* trigger the connection update procedure.
|
||||||
|
*/
|
||||||
|
conn->le_conn_min_interval = conn->hdev->le_conn_min_interval;
|
||||||
|
conn->le_conn_max_interval = conn->hdev->le_conn_max_interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
||||||
bdaddr_t *bdaddr, u8 bdaddr_type, u8 role, u16 handle,
|
bdaddr_t *bdaddr, u8 bdaddr_type,
|
||||||
u16 interval, u16 latency, u16 supervision_timeout)
|
bdaddr_t *local_rpa, u8 role, u16 handle,
|
||||||
|
u16 interval, u16 latency,
|
||||||
|
u16 supervision_timeout)
|
||||||
{
|
{
|
||||||
struct hci_conn_params *params;
|
struct hci_conn_params *params;
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
@ -5154,32 +5265,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
|||||||
cancel_delayed_work(&conn->le_conn_timeout);
|
cancel_delayed_work(&conn->le_conn_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!conn->out) {
|
le_conn_update_addr(conn, bdaddr, bdaddr_type, local_rpa);
|
||||||
/* Set the responder (our side) address type based on
|
|
||||||
* the advertising address type.
|
|
||||||
*/
|
|
||||||
conn->resp_addr_type = hdev->adv_addr_type;
|
|
||||||
if (hdev->adv_addr_type == ADDR_LE_DEV_RANDOM) {
|
|
||||||
/* In case of ext adv, resp_addr will be updated in
|
|
||||||
* Adv Terminated event.
|
|
||||||
*/
|
|
||||||
if (!ext_adv_capable(hdev))
|
|
||||||
bacpy(&conn->resp_addr, &hdev->random_addr);
|
|
||||||
} else {
|
|
||||||
bacpy(&conn->resp_addr, &hdev->bdaddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
conn->init_addr_type = bdaddr_type;
|
|
||||||
bacpy(&conn->init_addr, bdaddr);
|
|
||||||
|
|
||||||
/* For incoming connections, set the default minimum
|
|
||||||
* and maximum connection interval. They will be used
|
|
||||||
* to check if the parameters are in range and if not
|
|
||||||
* trigger the connection update procedure.
|
|
||||||
*/
|
|
||||||
conn->le_conn_min_interval = hdev->le_conn_min_interval;
|
|
||||||
conn->le_conn_max_interval = hdev->le_conn_max_interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lookup the identity address from the stored connection
|
/* Lookup the identity address from the stored connection
|
||||||
* address and address type.
|
* address and address type.
|
||||||
@ -5236,6 +5322,13 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
|||||||
conn->handle = handle;
|
conn->handle = handle;
|
||||||
conn->state = BT_CONFIG;
|
conn->state = BT_CONFIG;
|
||||||
|
|
||||||
|
/* Store current advertising instance as connection advertising instance
|
||||||
|
* when sotfware rotation is in use so it can be re-enabled when
|
||||||
|
* disconnected.
|
||||||
|
*/
|
||||||
|
if (!ext_adv_capable(hdev))
|
||||||
|
conn->adv_instance = hdev->cur_adv_instance;
|
||||||
|
|
||||||
conn->le_conn_interval = interval;
|
conn->le_conn_interval = interval;
|
||||||
conn->le_conn_latency = latency;
|
conn->le_conn_latency = latency;
|
||||||
conn->le_supv_timeout = supervision_timeout;
|
conn->le_supv_timeout = supervision_timeout;
|
||||||
@ -5290,7 +5383,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
||||||
|
|
||||||
le_conn_complete_evt(hdev, ev->status, &ev->bdaddr, ev->bdaddr_type,
|
le_conn_complete_evt(hdev, ev->status, &ev->bdaddr, ev->bdaddr_type,
|
||||||
ev->role, le16_to_cpu(ev->handle),
|
NULL, ev->role, le16_to_cpu(ev->handle),
|
||||||
le16_to_cpu(ev->interval),
|
le16_to_cpu(ev->interval),
|
||||||
le16_to_cpu(ev->latency),
|
le16_to_cpu(ev->latency),
|
||||||
le16_to_cpu(ev->supervision_timeout));
|
le16_to_cpu(ev->supervision_timeout));
|
||||||
@ -5304,7 +5397,7 @@ static void hci_le_enh_conn_complete_evt(struct hci_dev *hdev,
|
|||||||
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
||||||
|
|
||||||
le_conn_complete_evt(hdev, ev->status, &ev->bdaddr, ev->bdaddr_type,
|
le_conn_complete_evt(hdev, ev->status, &ev->bdaddr, ev->bdaddr_type,
|
||||||
ev->role, le16_to_cpu(ev->handle),
|
&ev->local_rpa, ev->role, le16_to_cpu(ev->handle),
|
||||||
le16_to_cpu(ev->interval),
|
le16_to_cpu(ev->interval),
|
||||||
le16_to_cpu(ev->latency),
|
le16_to_cpu(ev->latency),
|
||||||
le16_to_cpu(ev->supervision_timeout));
|
le16_to_cpu(ev->supervision_timeout));
|
||||||
@ -5319,13 +5412,13 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
{
|
{
|
||||||
struct hci_evt_le_ext_adv_set_term *ev = (void *) skb->data;
|
struct hci_evt_le_ext_adv_set_term *ev = (void *) skb->data;
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
|
struct adv_info *adv;
|
||||||
|
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
||||||
|
|
||||||
if (ev->status) {
|
adv = hci_find_adv_instance(hdev, ev->handle);
|
||||||
struct adv_info *adv;
|
|
||||||
|
|
||||||
adv = hci_find_adv_instance(hdev, ev->handle);
|
if (ev->status) {
|
||||||
if (!adv)
|
if (!adv)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -5336,11 +5429,18 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (adv)
|
||||||
|
adv->enabled = false;
|
||||||
|
|
||||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->conn_handle));
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->conn_handle));
|
||||||
if (conn) {
|
if (conn) {
|
||||||
struct adv_info *adv_instance;
|
/* Store handle in the connection so the correct advertising
|
||||||
|
* instance can be re-enabled when disconnected.
|
||||||
|
*/
|
||||||
|
conn->adv_instance = ev->handle;
|
||||||
|
|
||||||
if (hdev->adv_addr_type != ADDR_LE_DEV_RANDOM)
|
if (hdev->adv_addr_type != ADDR_LE_DEV_RANDOM ||
|
||||||
|
bacmp(&conn->resp_addr, BDADDR_ANY))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!ev->handle) {
|
if (!ev->handle) {
|
||||||
@ -5348,9 +5448,8 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
adv_instance = hci_find_adv_instance(hdev, ev->handle);
|
if (adv)
|
||||||
if (adv_instance)
|
bacpy(&conn->resp_addr, &adv->random_addr);
|
||||||
bacpy(&conn->resp_addr, &adv_instance->random_addr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2072,8 +2072,6 @@ int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
|||||||
* current RPA has expired then generate a new one.
|
* current RPA has expired then generate a new one.
|
||||||
*/
|
*/
|
||||||
if (use_rpa) {
|
if (use_rpa) {
|
||||||
int to;
|
|
||||||
|
|
||||||
/* If Controller supports LL Privacy use own address type is
|
/* If Controller supports LL Privacy use own address type is
|
||||||
* 0x03
|
* 0x03
|
||||||
*/
|
*/
|
||||||
@ -2084,14 +2082,10 @@ int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
|||||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||||
|
|
||||||
if (adv_instance) {
|
if (adv_instance) {
|
||||||
if (!adv_instance->rpa_expired &&
|
if (adv_rpa_valid(adv_instance))
|
||||||
!bacmp(&adv_instance->random_addr, &hdev->rpa))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
adv_instance->rpa_expired = false;
|
|
||||||
} else {
|
} else {
|
||||||
if (!hci_dev_test_and_clear_flag(hdev, HCI_RPA_EXPIRED) &&
|
if (rpa_valid(hdev))
|
||||||
!bacmp(&hdev->random_addr, &hdev->rpa))
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2103,14 +2097,6 @@ int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
|||||||
|
|
||||||
bacpy(rand_addr, &hdev->rpa);
|
bacpy(rand_addr, &hdev->rpa);
|
||||||
|
|
||||||
to = msecs_to_jiffies(hdev->rpa_timeout * 1000);
|
|
||||||
if (adv_instance)
|
|
||||||
queue_delayed_work(hdev->workqueue,
|
|
||||||
&adv_instance->rpa_expired_cb, to);
|
|
||||||
else
|
|
||||||
queue_delayed_work(hdev->workqueue,
|
|
||||||
&hdev->rpa_expired, to);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2153,6 +2139,30 @@ void __hci_req_clear_ext_adv_sets(struct hci_request *req)
|
|||||||
hci_req_add(req, HCI_OP_LE_CLEAR_ADV_SETS, 0, NULL);
|
hci_req_add(req, HCI_OP_LE_CLEAR_ADV_SETS, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = req->hdev;
|
||||||
|
|
||||||
|
/* If we're advertising or initiating an LE connection we can't
|
||||||
|
* go ahead and change the random address at this time. This is
|
||||||
|
* because the eventual initiator address used for the
|
||||||
|
* subsequently created connection will be undefined (some
|
||||||
|
* controllers use the new address and others the one we had
|
||||||
|
* when the operation started).
|
||||||
|
*
|
||||||
|
* In this kind of scenario skip the update and let the random
|
||||||
|
* address be updated at the next cycle.
|
||||||
|
*/
|
||||||
|
if (hci_dev_test_flag(hdev, HCI_LE_ADV) ||
|
||||||
|
hci_lookup_le_connect(hdev)) {
|
||||||
|
bt_dev_dbg(hdev, "Deferring random address update");
|
||||||
|
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa);
|
||||||
|
}
|
||||||
|
|
||||||
int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
||||||
{
|
{
|
||||||
struct hci_cp_le_set_ext_adv_params cp;
|
struct hci_cp_le_set_ext_adv_params cp;
|
||||||
@ -2255,6 +2265,13 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
|||||||
} else {
|
} else {
|
||||||
if (!bacmp(&random_addr, &hdev->random_addr))
|
if (!bacmp(&random_addr, &hdev->random_addr))
|
||||||
return 0;
|
return 0;
|
||||||
|
/* Instance 0x00 doesn't have an adv_info, instead it
|
||||||
|
* uses hdev->random_addr to track its address so
|
||||||
|
* whenever it needs to be updated this also set the
|
||||||
|
* random address since hdev->random_addr is shared with
|
||||||
|
* scan state machine.
|
||||||
|
*/
|
||||||
|
set_random_addr(req, &random_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
memset(&cp, 0, sizeof(cp));
|
||||||
@ -2512,30 +2529,6 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
|
|||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
|
|
||||||
{
|
|
||||||
struct hci_dev *hdev = req->hdev;
|
|
||||||
|
|
||||||
/* If we're advertising or initiating an LE connection we can't
|
|
||||||
* go ahead and change the random address at this time. This is
|
|
||||||
* because the eventual initiator address used for the
|
|
||||||
* subsequently created connection will be undefined (some
|
|
||||||
* controllers use the new address and others the one we had
|
|
||||||
* when the operation started).
|
|
||||||
*
|
|
||||||
* In this kind of scenario skip the update and let the random
|
|
||||||
* address be updated at the next cycle.
|
|
||||||
*/
|
|
||||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV) ||
|
|
||||||
hci_lookup_le_connect(hdev)) {
|
|
||||||
bt_dev_dbg(hdev, "Deferring random address update");
|
|
||||||
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
||||||
bool use_rpa, u8 *own_addr_type)
|
bool use_rpa, u8 *own_addr_type)
|
||||||
{
|
{
|
||||||
@ -2547,8 +2540,6 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
|||||||
* the current RPA in use, then generate a new one.
|
* the current RPA in use, then generate a new one.
|
||||||
*/
|
*/
|
||||||
if (use_rpa) {
|
if (use_rpa) {
|
||||||
int to;
|
|
||||||
|
|
||||||
/* If Controller supports LL Privacy use own address type is
|
/* If Controller supports LL Privacy use own address type is
|
||||||
* 0x03
|
* 0x03
|
||||||
*/
|
*/
|
||||||
@ -2558,8 +2549,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
|||||||
else
|
else
|
||||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||||
|
|
||||||
if (!hci_dev_test_and_clear_flag(hdev, HCI_RPA_EXPIRED) &&
|
if (rpa_valid(hdev))
|
||||||
!bacmp(&hdev->random_addr, &hdev->rpa))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
|
err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
|
||||||
@ -2570,9 +2560,6 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
|||||||
|
|
||||||
set_random_addr(req, &hdev->rpa);
|
set_random_addr(req, &hdev->rpa);
|
||||||
|
|
||||||
to = msecs_to_jiffies(hdev->rpa_timeout * 1000);
|
|
||||||
queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +85,7 @@ static void bt_host_release(struct device *dev)
|
|||||||
struct hci_dev *hdev = to_hci_dev(dev);
|
struct hci_dev *hdev = to_hci_dev(dev);
|
||||||
|
|
||||||
if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
|
if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
|
||||||
hci_cleanup_dev(hdev);
|
hci_release_dev(hdev);
|
||||||
kfree(hdev);
|
|
||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7204,7 +7204,7 @@ static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status,
|
|||||||
if (!mgmt_rp)
|
if (!mgmt_rp)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (status)
|
if (eir_len == 0)
|
||||||
goto send_rsp;
|
goto send_rsp;
|
||||||
|
|
||||||
eir_len = eir_append_data(mgmt_rp->eir, 0, EIR_CLASS_OF_DEV,
|
eir_len = eir_append_data(mgmt_rp->eir, 0, EIR_CLASS_OF_DEV,
|
||||||
@ -7725,7 +7725,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
|||||||
* advertising.
|
* advertising.
|
||||||
*/
|
*/
|
||||||
if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
|
if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
|
||||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
|
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
|
|
||||||
if (cp->instance < 1 || cp->instance > hdev->le_num_of_adv_sets)
|
if (cp->instance < 1 || cp->instance > hdev->le_num_of_adv_sets)
|
||||||
|
@ -70,7 +70,7 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
|
|||||||
|
|
||||||
BT_DBG("dlc %p state %ld err %d", d, d->state, err);
|
BT_DBG("dlc %p state %ld err %d", d, d->state, err);
|
||||||
|
|
||||||
spin_lock_bh(&sk->sk_lock.slock);
|
lock_sock(sk);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
sk->sk_err = err;
|
sk->sk_err = err;
|
||||||
@ -91,7 +91,7 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
|
|||||||
sk->sk_state_change(sk);
|
sk->sk_state_change(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_bh(&sk->sk_lock.slock);
|
release_sock(sk);
|
||||||
|
|
||||||
if (parent && sock_flag(sk, SOCK_ZAPPED)) {
|
if (parent && sock_flag(sk, SOCK_ZAPPED)) {
|
||||||
/* We have to drop DLC lock here, otherwise
|
/* We have to drop DLC lock here, otherwise
|
||||||
@ -974,7 +974,7 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
|
|||||||
if (!parent)
|
if (!parent)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bh_lock_sock(parent);
|
lock_sock(parent);
|
||||||
|
|
||||||
/* Check for backlog size */
|
/* Check for backlog size */
|
||||||
if (sk_acceptq_is_full(parent)) {
|
if (sk_acceptq_is_full(parent)) {
|
||||||
@ -1001,7 +1001,7 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
|
|||||||
result = 1;
|
result = 1;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
bh_unlock_sock(parent);
|
release_sock(parent);
|
||||||
|
|
||||||
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
|
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
|
||||||
parent->sk_state_change(parent);
|
parent->sk_state_change(parent);
|
||||||
|
@ -48,6 +48,8 @@ struct sco_conn {
|
|||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
|
|
||||||
|
struct delayed_work timeout_work;
|
||||||
|
|
||||||
unsigned int mtu;
|
unsigned int mtu;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,31 +76,47 @@ struct sco_pinfo {
|
|||||||
#define SCO_CONN_TIMEOUT (HZ * 40)
|
#define SCO_CONN_TIMEOUT (HZ * 40)
|
||||||
#define SCO_DISCONN_TIMEOUT (HZ * 2)
|
#define SCO_DISCONN_TIMEOUT (HZ * 2)
|
||||||
|
|
||||||
static void sco_sock_timeout(struct timer_list *t)
|
static void sco_sock_timeout(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct sock *sk = from_timer(sk, t, sk_timer);
|
struct sco_conn *conn = container_of(work, struct sco_conn,
|
||||||
|
timeout_work.work);
|
||||||
|
struct sock *sk;
|
||||||
|
|
||||||
|
sco_conn_lock(conn);
|
||||||
|
sk = conn->sk;
|
||||||
|
if (sk)
|
||||||
|
sock_hold(sk);
|
||||||
|
sco_conn_unlock(conn);
|
||||||
|
|
||||||
|
if (!sk)
|
||||||
|
return;
|
||||||
|
|
||||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||||
|
|
||||||
bh_lock_sock(sk);
|
lock_sock(sk);
|
||||||
sk->sk_err = ETIMEDOUT;
|
sk->sk_err = ETIMEDOUT;
|
||||||
sk->sk_state_change(sk);
|
sk->sk_state_change(sk);
|
||||||
bh_unlock_sock(sk);
|
release_sock(sk);
|
||||||
|
|
||||||
sco_sock_kill(sk);
|
|
||||||
sock_put(sk);
|
sock_put(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sco_sock_set_timer(struct sock *sk, long timeout)
|
static void sco_sock_set_timer(struct sock *sk, long timeout)
|
||||||
{
|
{
|
||||||
|
if (!sco_pi(sk)->conn)
|
||||||
|
return;
|
||||||
|
|
||||||
BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);
|
BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);
|
||||||
sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout);
|
cancel_delayed_work(&sco_pi(sk)->conn->timeout_work);
|
||||||
|
schedule_delayed_work(&sco_pi(sk)->conn->timeout_work, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sco_sock_clear_timer(struct sock *sk)
|
static void sco_sock_clear_timer(struct sock *sk)
|
||||||
{
|
{
|
||||||
|
if (!sco_pi(sk)->conn)
|
||||||
|
return;
|
||||||
|
|
||||||
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
BT_DBG("sock %p state %d", sk, sk->sk_state);
|
||||||
sk_stop_timer(sk, &sk->sk_timer);
|
cancel_delayed_work(&sco_pi(sk)->conn->timeout_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- SCO connections ---- */
|
/* ---- SCO connections ---- */
|
||||||
@ -173,12 +191,14 @@ static void sco_conn_del(struct hci_conn *hcon, int err)
|
|||||||
|
|
||||||
if (sk) {
|
if (sk) {
|
||||||
sock_hold(sk);
|
sock_hold(sk);
|
||||||
bh_lock_sock(sk);
|
lock_sock(sk);
|
||||||
sco_sock_clear_timer(sk);
|
sco_sock_clear_timer(sk);
|
||||||
sco_chan_del(sk, err);
|
sco_chan_del(sk, err);
|
||||||
bh_unlock_sock(sk);
|
release_sock(sk);
|
||||||
sco_sock_kill(sk);
|
|
||||||
sock_put(sk);
|
sock_put(sk);
|
||||||
|
|
||||||
|
/* Ensure no more work items will run before freeing conn. */
|
||||||
|
cancel_delayed_work_sync(&conn->timeout_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
hcon->sco_data = NULL;
|
hcon->sco_data = NULL;
|
||||||
@ -193,6 +213,8 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk,
|
|||||||
sco_pi(sk)->conn = conn;
|
sco_pi(sk)->conn = conn;
|
||||||
conn->sk = sk;
|
conn->sk = sk;
|
||||||
|
|
||||||
|
INIT_DELAYED_WORK(&conn->timeout_work, sco_sock_timeout);
|
||||||
|
|
||||||
if (parent)
|
if (parent)
|
||||||
bt_accept_enqueue(parent, sk, true);
|
bt_accept_enqueue(parent, sk, true);
|
||||||
}
|
}
|
||||||
@ -212,44 +234,32 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sco_connect(struct sock *sk)
|
static int sco_connect(struct hci_dev *hdev, struct sock *sk)
|
||||||
{
|
{
|
||||||
struct sco_conn *conn;
|
struct sco_conn *conn;
|
||||||
struct hci_conn *hcon;
|
struct hci_conn *hcon;
|
||||||
struct hci_dev *hdev;
|
|
||||||
int err, type;
|
int err, type;
|
||||||
|
|
||||||
BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
|
BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
|
||||||
|
|
||||||
hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR);
|
|
||||||
if (!hdev)
|
|
||||||
return -EHOSTUNREACH;
|
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
|
|
||||||
if (lmp_esco_capable(hdev) && !disable_esco)
|
if (lmp_esco_capable(hdev) && !disable_esco)
|
||||||
type = ESCO_LINK;
|
type = ESCO_LINK;
|
||||||
else
|
else
|
||||||
type = SCO_LINK;
|
type = SCO_LINK;
|
||||||
|
|
||||||
if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT &&
|
if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT &&
|
||||||
(!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev))) {
|
(!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev)))
|
||||||
err = -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
|
hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
|
||||||
sco_pi(sk)->setting);
|
sco_pi(sk)->setting);
|
||||||
if (IS_ERR(hcon)) {
|
if (IS_ERR(hcon))
|
||||||
err = PTR_ERR(hcon);
|
return PTR_ERR(hcon);
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn = sco_conn_add(hcon);
|
conn = sco_conn_add(hcon);
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
hci_conn_drop(hcon);
|
hci_conn_drop(hcon);
|
||||||
err = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update source addr of the socket */
|
/* Update source addr of the socket */
|
||||||
@ -257,7 +267,7 @@ static int sco_connect(struct sock *sk)
|
|||||||
|
|
||||||
err = sco_chan_add(conn, sk, NULL);
|
err = sco_chan_add(conn, sk, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto done;
|
return err;
|
||||||
|
|
||||||
if (hcon->state == BT_CONNECTED) {
|
if (hcon->state == BT_CONNECTED) {
|
||||||
sco_sock_clear_timer(sk);
|
sco_sock_clear_timer(sk);
|
||||||
@ -267,9 +277,6 @@ static int sco_connect(struct sock *sk)
|
|||||||
sco_sock_set_timer(sk, sk->sk_sndtimeo);
|
sco_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
hci_dev_put(hdev);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,8 +401,7 @@ static void sco_sock_cleanup_listen(struct sock *parent)
|
|||||||
*/
|
*/
|
||||||
static void sco_sock_kill(struct sock *sk)
|
static void sco_sock_kill(struct sock *sk)
|
||||||
{
|
{
|
||||||
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
|
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
|
||||||
sock_flag(sk, SOCK_DEAD))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
||||||
@ -443,11 +449,10 @@ static void __sco_sock_close(struct sock *sk)
|
|||||||
/* Must be called on unlocked socket. */
|
/* Must be called on unlocked socket. */
|
||||||
static void sco_sock_close(struct sock *sk)
|
static void sco_sock_close(struct sock *sk)
|
||||||
{
|
{
|
||||||
sco_sock_clear_timer(sk);
|
|
||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
sco_sock_clear_timer(sk);
|
||||||
__sco_sock_close(sk);
|
__sco_sock_close(sk);
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
sco_sock_kill(sk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sco_skb_put_cmsg(struct sk_buff *skb, struct msghdr *msg,
|
static void sco_skb_put_cmsg(struct sk_buff *skb, struct msghdr *msg,
|
||||||
@ -500,8 +505,6 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock,
|
|||||||
|
|
||||||
sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
|
sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
|
||||||
|
|
||||||
timer_setup(&sk->sk_timer, sco_sock_timeout, 0);
|
|
||||||
|
|
||||||
bt_sock_link(&sco_sk_list, sk);
|
bt_sock_link(&sco_sk_list, sk);
|
||||||
return sk;
|
return sk;
|
||||||
}
|
}
|
||||||
@ -566,6 +569,7 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
|
|||||||
{
|
{
|
||||||
struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
|
struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
|
struct hci_dev *hdev;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
@ -580,12 +584,19 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
|
|||||||
if (sk->sk_type != SOCK_SEQPACKET)
|
if (sk->sk_type != SOCK_SEQPACKET)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
hdev = hci_get_route(&sa->sco_bdaddr, &sco_pi(sk)->src, BDADDR_BREDR);
|
||||||
|
if (!hdev)
|
||||||
|
return -EHOSTUNREACH;
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
/* Set destination address and psm */
|
/* Set destination address and psm */
|
||||||
bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
|
bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
|
||||||
|
|
||||||
err = sco_connect(sk);
|
err = sco_connect(hdev, sk);
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
hci_dev_put(hdev);
|
||||||
if (err)
|
if (err)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
@ -773,6 +784,11 @@ static void sco_conn_defer_accept(struct hci_conn *conn, u16 setting)
|
|||||||
cp.max_latency = cpu_to_le16(0xffff);
|
cp.max_latency = cpu_to_le16(0xffff);
|
||||||
cp.retrans_effort = 0xff;
|
cp.retrans_effort = 0xff;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
/* use CVSD settings as fallback */
|
||||||
|
cp.max_latency = cpu_to_le16(0xffff);
|
||||||
|
cp.retrans_effort = 0xff;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
|
||||||
@ -1083,11 +1099,11 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||||||
BT_DBG("conn %p", conn);
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
if (sk) {
|
if (sk) {
|
||||||
|
lock_sock(sk);
|
||||||
sco_sock_clear_timer(sk);
|
sco_sock_clear_timer(sk);
|
||||||
bh_lock_sock(sk);
|
|
||||||
sk->sk_state = BT_CONNECTED;
|
sk->sk_state = BT_CONNECTED;
|
||||||
sk->sk_state_change(sk);
|
sk->sk_state_change(sk);
|
||||||
bh_unlock_sock(sk);
|
release_sock(sk);
|
||||||
} else {
|
} else {
|
||||||
sco_conn_lock(conn);
|
sco_conn_lock(conn);
|
||||||
|
|
||||||
@ -1102,12 +1118,12 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bh_lock_sock(parent);
|
lock_sock(parent);
|
||||||
|
|
||||||
sk = sco_sock_alloc(sock_net(parent), NULL,
|
sk = sco_sock_alloc(sock_net(parent), NULL,
|
||||||
BTPROTO_SCO, GFP_ATOMIC, 0);
|
BTPROTO_SCO, GFP_ATOMIC, 0);
|
||||||
if (!sk) {
|
if (!sk) {
|
||||||
bh_unlock_sock(parent);
|
release_sock(parent);
|
||||||
sco_conn_unlock(conn);
|
sco_conn_unlock(conn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1128,7 +1144,7 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||||||
/* Wake up parent */
|
/* Wake up parent */
|
||||||
parent->sk_data_ready(parent);
|
parent->sk_data_ready(parent);
|
||||||
|
|
||||||
bh_unlock_sock(parent);
|
release_sock(parent);
|
||||||
|
|
||||||
sco_conn_unlock(conn);
|
sco_conn_unlock(conn);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user