Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says: ==================== pull request: bluetooth-next 2020-04-17 Here's the first bluetooth-next pull request for the 5.8 kernel: - Added debugfs option to control MITM flag usage during pairing - Added new BT_MODE socket option - Added support for Qualcom QCA6390 device - Added support for Realtek RTL8761B device - Added support for mSBC audio codec over USB endpoints - Added framework for Microsoft HCI vendor extensions - Added new Read Security Information management command - Fixes/cleanup to link layer privacy related code - Various other smaller cleanups & fixes ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
513a24ffb3
@ -13,6 +13,7 @@ Required properties:
|
||||
* "qcom,wcn3990-bt"
|
||||
* "qcom,wcn3991-bt"
|
||||
* "qcom,wcn3998-bt"
|
||||
* "qcom,qca6390-bt"
|
||||
|
||||
Optional properties for compatible string qcom,qca6174-bt:
|
||||
|
||||
|
@ -32,7 +32,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
* VSE event. WCN3991 sends version command response as a payload to
|
||||
* command complete event.
|
||||
*/
|
||||
if (soc_type == QCA_WCN3991) {
|
||||
if (soc_type >= QCA_WCN3991) {
|
||||
event_type = 0;
|
||||
rlen += 1;
|
||||
rtype = EDL_PATCH_VER_REQ_CMD;
|
||||
@ -69,7 +69,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (soc_type == QCA_WCN3991)
|
||||
if (soc_type >= QCA_WCN3991)
|
||||
memmove(&edl->data, &edl->data[1], sizeof(*ver));
|
||||
|
||||
ver = (struct qca_btsoc_version *)(edl->data);
|
||||
@ -217,7 +217,7 @@ static void qca_tlv_check_data(struct qca_fw_config *config,
|
||||
tlv_nvm->data[0] |= 0x80;
|
||||
|
||||
/* UART Baud Rate */
|
||||
if (soc_type == QCA_WCN3991)
|
||||
if (soc_type >= QCA_WCN3991)
|
||||
tlv_nvm->data[1] = nvm_baud_rate;
|
||||
else
|
||||
tlv_nvm->data[2] = nvm_baud_rate;
|
||||
@ -268,7 +268,7 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
* VSE event. WCN3991 sends version command response as a payload to
|
||||
* command complete event.
|
||||
*/
|
||||
if (soc_type == QCA_WCN3991) {
|
||||
if (soc_type >= QCA_WCN3991) {
|
||||
event_type = 0;
|
||||
rlen = sizeof(*edl);
|
||||
rtype = EDL_PATCH_TLV_REQ_CMD;
|
||||
@ -301,7 +301,7 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
if (soc_type == QCA_WCN3991)
|
||||
if (soc_type >= QCA_WCN3991)
|
||||
goto out;
|
||||
|
||||
tlv_resp = (struct tlv_seg_resp *)(edl->data);
|
||||
@ -442,6 +442,11 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
(soc_ver & 0x0000000f);
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/crbtfw%02x.tlv", rom_ver);
|
||||
} else if (soc_type == QCA_QCA6390) {
|
||||
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) |
|
||||
(soc_ver & 0x0000000f);
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/htbtfw%02x.tlv", rom_ver);
|
||||
} else {
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/rampatch_%08x.bin", soc_ver);
|
||||
@ -464,6 +469,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
else if (qca_is_wcn399x(soc_type))
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/crnv%02x.bin", rom_ver);
|
||||
else if (soc_type == QCA_QCA6390)
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/htnv%02x.bin", rom_ver);
|
||||
else
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/nvm_%08x.bin", soc_ver);
|
||||
|
@ -125,8 +125,9 @@ enum qca_btsoc_type {
|
||||
QCA_AR3002,
|
||||
QCA_ROME,
|
||||
QCA_WCN3990,
|
||||
QCA_WCN3991,
|
||||
QCA_WCN3998,
|
||||
QCA_WCN3991,
|
||||
QCA_QCA6390,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_QCA)
|
||||
|
@ -130,12 +130,19 @@ static const struct id_table ic_id_table[] = {
|
||||
.cfg_name = "rtl_bt/rtl8821c_config" },
|
||||
|
||||
/* 8761A */
|
||||
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8761A, 0x0,
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xa),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8761a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761a_config" },
|
||||
|
||||
/* 8761B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8761b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761b_config" },
|
||||
|
||||
/* 8822C with UART interface */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
|
||||
IC_MATCH_FL_HCIBUS,
|
||||
@ -267,6 +274,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
||||
{ RTL_ROM_LMP_8723B, 9 }, /* 8723D */
|
||||
{ RTL_ROM_LMP_8821A, 10 }, /* 8821C */
|
||||
{ RTL_ROM_LMP_8822B, 13 }, /* 8822C */
|
||||
{ RTL_ROM_LMP_8761A, 14 }, /* 8761B */
|
||||
};
|
||||
|
||||
min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
|
||||
|
@ -492,6 +492,8 @@ struct btusb_data {
|
||||
__u8 cmdreq;
|
||||
|
||||
unsigned int sco_num;
|
||||
unsigned int air_mode;
|
||||
bool usb_alt6_packet_flow;
|
||||
int isoc_altsetting;
|
||||
int suspend_count;
|
||||
|
||||
@ -983,6 +985,42 @@ static void btusb_isoc_complete(struct urb *urb)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len,
|
||||
int mtu, struct btusb_data *data)
|
||||
{
|
||||
int i, offset = 0;
|
||||
unsigned int interval;
|
||||
|
||||
BT_DBG("len %d mtu %d", len, mtu);
|
||||
|
||||
/* For mSBC ALT 6 setting the host will send the packet at continuous
|
||||
* flow. As per core spec 5, vol 4, part B, table 2.1. For ALT setting
|
||||
* 6 the HCI PACKET INTERVAL should be 7.5ms for every usb packets.
|
||||
* To maintain the rate we send 63bytes of usb packets alternatively for
|
||||
* 7ms and 8ms to maintain the rate as 7.5ms.
|
||||
*/
|
||||
if (data->usb_alt6_packet_flow) {
|
||||
interval = 7;
|
||||
data->usb_alt6_packet_flow = false;
|
||||
} else {
|
||||
interval = 6;
|
||||
data->usb_alt6_packet_flow = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < interval; i++) {
|
||||
urb->iso_frame_desc[i].offset = offset;
|
||||
urb->iso_frame_desc[i].length = offset;
|
||||
}
|
||||
|
||||
if (len && i < BTUSB_MAX_ISOC_FRAMES) {
|
||||
urb->iso_frame_desc[i].offset = offset;
|
||||
urb->iso_frame_desc[i].length = len;
|
||||
i++;
|
||||
}
|
||||
|
||||
urb->number_of_packets = i;
|
||||
}
|
||||
|
||||
static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
|
||||
{
|
||||
int i, offset = 0;
|
||||
@ -1386,9 +1424,13 @@ static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
|
||||
__fill_isoc_descriptor(urb, skb->len,
|
||||
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
||||
|
||||
if (data->isoc_altsetting == 6)
|
||||
__fill_isoc_descriptor_msbc(urb, skb->len,
|
||||
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize),
|
||||
data);
|
||||
else
|
||||
__fill_isoc_descriptor(urb, skb->len,
|
||||
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
||||
skb->dev = (void *)hdev;
|
||||
|
||||
return urb;
|
||||
@ -1484,6 +1526,7 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
|
||||
|
||||
if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) {
|
||||
data->sco_num = hci_conn_num(hdev, SCO_LINK);
|
||||
data->air_mode = evt;
|
||||
schedule_work(&data->work);
|
||||
}
|
||||
}
|
||||
@ -1531,11 +1574,70 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_switch_alt_setting(struct hci_dev *hdev, int new_alts)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
int err;
|
||||
|
||||
if (data->isoc_altsetting != new_alts) {
|
||||
unsigned long flags;
|
||||
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||
|
||||
/* When isochronous alternate setting needs to be
|
||||
* changed, because SCO connection has been added
|
||||
* or removed, a packet fragment may be left in the
|
||||
* reassembling state. This could lead to wrongly
|
||||
* assembled fragments.
|
||||
*
|
||||
* Clear outstanding fragment when selecting a new
|
||||
* alternate setting.
|
||||
*/
|
||||
spin_lock_irqsave(&data->rxlock, flags);
|
||||
kfree_skb(data->sco_skb);
|
||||
data->sco_skb = NULL;
|
||||
spin_unlock_irqrestore(&data->rxlock, flags);
|
||||
|
||||
err = __set_isoc_interface(hdev, new_alts);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
|
||||
if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
else
|
||||
btusb_submit_isoc_urb(hdev, GFP_KERNEL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_host_interface *btusb_find_altsetting(struct btusb_data *data,
|
||||
int alt)
|
||||
{
|
||||
struct usb_interface *intf = data->isoc;
|
||||
int i;
|
||||
|
||||
BT_DBG("Looking for Alt no :%d", alt);
|
||||
|
||||
if (!intf)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < intf->num_altsetting; i++) {
|
||||
if (intf->altsetting[i].desc.bAlternateSetting == alt)
|
||||
return &intf->altsetting[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void btusb_work(struct work_struct *work)
|
||||
{
|
||||
struct btusb_data *data = container_of(work, struct btusb_data, work);
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
int new_alts;
|
||||
int new_alts = 0;
|
||||
int err;
|
||||
|
||||
if (data->sco_num > 0) {
|
||||
@ -1550,44 +1652,27 @@ static void btusb_work(struct work_struct *work)
|
||||
set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
|
||||
}
|
||||
|
||||
if (hdev->voice_setting & 0x0020) {
|
||||
static const int alts[3] = { 2, 4, 5 };
|
||||
if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_CVSD) {
|
||||
if (hdev->voice_setting & 0x0020) {
|
||||
static const int alts[3] = { 2, 4, 5 };
|
||||
|
||||
new_alts = alts[data->sco_num - 1];
|
||||
} else {
|
||||
new_alts = data->sco_num;
|
||||
}
|
||||
new_alts = alts[data->sco_num - 1];
|
||||
} else {
|
||||
new_alts = data->sco_num;
|
||||
}
|
||||
} else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) {
|
||||
|
||||
if (data->isoc_altsetting != new_alts) {
|
||||
unsigned long flags;
|
||||
data->usb_alt6_packet_flow = true;
|
||||
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||
|
||||
/* When isochronous alternate setting needs to be
|
||||
* changed, because SCO connection has been added
|
||||
* or removed, a packet fragment may be left in the
|
||||
* reassembling state. This could lead to wrongly
|
||||
* assembled fragments.
|
||||
*
|
||||
* Clear outstanding fragment when selecting a new
|
||||
* alternate setting.
|
||||
*/
|
||||
spin_lock_irqsave(&data->rxlock, flags);
|
||||
kfree_skb(data->sco_skb);
|
||||
data->sco_skb = NULL;
|
||||
spin_unlock_irqrestore(&data->rxlock, flags);
|
||||
|
||||
if (__set_isoc_interface(hdev, new_alts) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
|
||||
if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
/* Check if Alt 6 is supported for Transparent audio */
|
||||
if (btusb_find_altsetting(data, 6))
|
||||
new_alts = 6;
|
||||
else
|
||||
btusb_submit_isoc_urb(hdev, GFP_KERNEL);
|
||||
bt_dev_err(hdev, "Device does not support ALT setting 6");
|
||||
}
|
||||
|
||||
if (btusb_switch_alt_setting(hdev, new_alts) < 0)
|
||||
bt_dev_err(hdev, "set USB alt:(%d) failed!", new_alts);
|
||||
} else {
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||
@ -2252,7 +2337,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
if (ver.fw_variant == 0x23) {
|
||||
clear_bit(BTUSB_BOOTLOADER, &data->flags);
|
||||
btintel_check_bdaddr(hdev);
|
||||
return 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* If the device is not in bootloader mode, then the only possible
|
||||
@ -2452,6 +2537,23 @@ done:
|
||||
*/
|
||||
btintel_load_ddc_config(hdev, fwname);
|
||||
|
||||
/* Read the Intel version information after loading the FW */
|
||||
err = btintel_read_version(hdev, &ver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
btintel_version_info(hdev, &ver);
|
||||
|
||||
finish:
|
||||
/* All Intel controllers that support the Microsoft vendor
|
||||
* extension are using 0xFC1E for VsMsftOpCode.
|
||||
*/
|
||||
switch (ver.hw_variant) {
|
||||
case 0x12: /* ThP */
|
||||
hci_set_msft_opcode(hdev, 0xFC1E);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the event mask for Intel specific vendor events. This enables
|
||||
* a few extra events that are useful during general operation. It
|
||||
* does not enable any debugging related events.
|
||||
@ -2461,13 +2563,6 @@ done:
|
||||
*/
|
||||
btintel_set_event_mask(hdev, false);
|
||||
|
||||
/* Read the Intel version information after loading the FW */
|
||||
err = btintel_read_version(hdev, &ver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
btintel_version_info(hdev, &ver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -118,6 +118,7 @@ struct bcm_device {
|
||||
u32 oper_speed;
|
||||
int irq;
|
||||
bool irq_active_low;
|
||||
bool irq_acquired;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct hci_uart *hu;
|
||||
@ -333,6 +334,8 @@ static int bcm_request_irq(struct bcm_data *bcm)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
bdev->irq_acquired = true;
|
||||
|
||||
device_init_wakeup(bdev->dev, true);
|
||||
|
||||
pm_runtime_set_autosuspend_delay(bdev->dev,
|
||||
@ -514,7 +517,7 @@ static int bcm_close(struct hci_uart *hu)
|
||||
}
|
||||
|
||||
if (bdev) {
|
||||
if (IS_ENABLED(CONFIG_PM) && bdev->irq > 0) {
|
||||
if (IS_ENABLED(CONFIG_PM) && bdev->irq_acquired) {
|
||||
devm_free_irq(bdev->dev, bdev->irq, bdev);
|
||||
device_init_wakeup(bdev->dev, false);
|
||||
pm_runtime_disable(bdev->dev);
|
||||
@ -1153,7 +1156,8 @@ static int bcm_of_probe(struct bcm_device *bdev)
|
||||
device_property_read_u8_array(bdev->dev, "brcm,bt-pcm-int-params",
|
||||
bdev->pcm_int_params, 5);
|
||||
bdev->irq = of_irq_get_byname(bdev->dev->of_node, "host-wakeup");
|
||||
|
||||
bdev->irq_active_low = irq_get_trigger_type(bdev->irq)
|
||||
& (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/serdev.h>
|
||||
@ -1596,7 +1597,7 @@ static int qca_setup(struct hci_uart *hu)
|
||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||
|
||||
bt_dev_info(hdev, "setting up %s",
|
||||
qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME");
|
||||
qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME/QCA6390");
|
||||
|
||||
retry:
|
||||
ret = qca_power_on(hdev);
|
||||
@ -1665,10 +1666,10 @@ retry:
|
||||
}
|
||||
|
||||
/* Setup bdaddr */
|
||||
if (qca_is_wcn399x(soc_type))
|
||||
hu->hdev->set_bdaddr = qca_set_bdaddr;
|
||||
else
|
||||
if (soc_type == QCA_ROME)
|
||||
hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
|
||||
else
|
||||
hu->hdev->set_bdaddr = qca_set_bdaddr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1721,6 +1722,11 @@ static const struct qca_vreg_data qca_soc_data_wcn3998 = {
|
||||
.num_vregs = 4,
|
||||
};
|
||||
|
||||
static const struct qca_vreg_data qca_soc_data_qca6390 = {
|
||||
.soc_type = QCA_QCA6390,
|
||||
.num_vregs = 0,
|
||||
};
|
||||
|
||||
static void qca_power_shutdown(struct hci_uart *hu)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
@ -1764,7 +1770,7 @@ static int qca_power_off(struct hci_dev *hdev)
|
||||
enum qca_btsoc_type soc_type = qca_soc_type(hu);
|
||||
|
||||
/* Stop sending shutdown command if soc crashes. */
|
||||
if (qca_is_wcn399x(soc_type)
|
||||
if (soc_type != QCA_ROME
|
||||
&& qca->memdump_state == QCA_MEMDUMP_IDLE) {
|
||||
qca_send_pre_shutdown_cmd(hdev);
|
||||
usleep_range(8000, 10000);
|
||||
@ -1900,7 +1906,11 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
qcadev->btsoc_type = QCA_ROME;
|
||||
if (data)
|
||||
qcadev->btsoc_type = data->soc_type;
|
||||
else
|
||||
qcadev->btsoc_type = QCA_ROME;
|
||||
|
||||
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (!qcadev->bt_en) {
|
||||
@ -2044,21 +2054,37 @@ static int __maybe_unused qca_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id qca_bluetooth_of_match[] = {
|
||||
{ .compatible = "qcom,qca6174-bt" },
|
||||
{ .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390},
|
||||
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
|
||||
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
|
||||
{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
|
||||
{ "QCOM6390", (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
|
||||
#endif
|
||||
|
||||
|
||||
static struct serdev_device_driver qca_serdev_driver = {
|
||||
.probe = qca_serdev_probe,
|
||||
.remove = qca_serdev_remove,
|
||||
.driver = {
|
||||
.name = "hci_uart_qca",
|
||||
.of_match_table = qca_bluetooth_of_match,
|
||||
.of_match_table = of_match_ptr(qca_bluetooth_of_match),
|
||||
.acpi_match_table = ACPI_PTR(qca_bluetooth_acpi_match),
|
||||
.pm = &qca_pm_ops,
|
||||
},
|
||||
};
|
||||
|
@ -139,6 +139,14 @@ struct bt_voice {
|
||||
#define BT_PHY_LE_CODED_TX 0x00002000
|
||||
#define BT_PHY_LE_CODED_RX 0x00004000
|
||||
|
||||
#define BT_MODE 15
|
||||
|
||||
#define BT_MODE_BASIC 0x00
|
||||
#define BT_MODE_ERTM 0x01
|
||||
#define BT_MODE_STREAMING 0x02
|
||||
#define BT_MODE_LE_FLOWCTL 0x03
|
||||
#define BT_MODE_EXT_FLOWCTL 0x04
|
||||
|
||||
__printf(1, 2)
|
||||
void bt_info(const char *fmt, ...);
|
||||
__printf(1, 2)
|
||||
|
@ -53,6 +53,9 @@
|
||||
#define HCI_NOTIFY_CONN_ADD 1
|
||||
#define HCI_NOTIFY_CONN_DEL 2
|
||||
#define HCI_NOTIFY_VOICE_SETTING 3
|
||||
#define HCI_NOTIFY_ENABLE_SCO_CVSD 4
|
||||
#define HCI_NOTIFY_ENABLE_SCO_TRANSP 5
|
||||
#define HCI_NOTIFY_DISABLE_SCO 6
|
||||
|
||||
/* HCI bus types */
|
||||
#define HCI_VIRTUAL 0
|
||||
@ -65,6 +68,7 @@
|
||||
#define HCI_SPI 7
|
||||
#define HCI_I2C 8
|
||||
#define HCI_SMD 9
|
||||
#define HCI_VIRTIO 10
|
||||
|
||||
/* HCI controller types */
|
||||
#define HCI_PRIMARY 0x00
|
||||
@ -294,6 +298,7 @@ enum {
|
||||
HCI_FORCE_STATIC_ADDR,
|
||||
HCI_LL_RPA_RESOLUTION,
|
||||
HCI_CMD_PENDING,
|
||||
HCI_FORCE_NO_MITM,
|
||||
|
||||
__HCI_NUM_FLAGS,
|
||||
};
|
||||
@ -455,12 +460,11 @@ enum {
|
||||
#define HCI_LE_SLAVE_FEATURES 0x08
|
||||
#define HCI_LE_PING 0x10
|
||||
#define HCI_LE_DATA_LEN_EXT 0x20
|
||||
#define HCI_LE_PHY_2M 0x01
|
||||
#define HCI_LE_PHY_CODED 0x08
|
||||
#define HCI_LE_EXT_ADV 0x10
|
||||
#define HCI_LE_LL_PRIVACY 0x40
|
||||
#define HCI_LE_EXT_SCAN_POLICY 0x80
|
||||
#define HCI_LE_PHY_2M 0x01
|
||||
#define HCI_LE_PHY_CODED 0x08
|
||||
#define HCI_LE_EXT_ADV 0x10
|
||||
#define HCI_LE_CHAN_SEL_ALG2 0x40
|
||||
#define HCI_LE_CIS_MASTER 0x10
|
||||
#define HCI_LE_CIS_SLAVE 0x20
|
||||
@ -1272,6 +1276,13 @@ struct hci_rp_read_data_block_size {
|
||||
|
||||
#define HCI_OP_READ_LOCAL_CODECS 0x100b
|
||||
|
||||
#define HCI_OP_READ_LOCAL_PAIRING_OPTS 0x100c
|
||||
struct hci_rp_read_local_pairing_opts {
|
||||
__u8 status;
|
||||
__u8 pairing_opts;
|
||||
__u8 max_key_size;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_PAGE_SCAN_ACTIVITY 0x0c1b
|
||||
struct hci_rp_read_page_scan_activity {
|
||||
__u8 status;
|
||||
|
@ -312,6 +312,8 @@ struct hci_dev {
|
||||
__u16 conn_info_max_age;
|
||||
__u16 auth_payload_timeout;
|
||||
__u8 min_enc_key_size;
|
||||
__u8 max_enc_key_size;
|
||||
__u8 pairing_opts;
|
||||
__u8 ssp_debug_mode;
|
||||
__u8 hw_error_code;
|
||||
__u32 clock;
|
||||
@ -484,6 +486,11 @@ struct hci_dev {
|
||||
struct led_trigger *power_led;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_MSFTEXT)
|
||||
__u16 msft_opcode;
|
||||
void *msft_data;
|
||||
#endif
|
||||
|
||||
int (*open)(struct hci_dev *hdev);
|
||||
int (*close)(struct hci_dev *hdev);
|
||||
int (*flush)(struct hci_dev *hdev);
|
||||
@ -638,6 +645,7 @@ extern struct mutex hci_cb_list_lock;
|
||||
do { \
|
||||
hci_dev_clear_flag(hdev, HCI_LE_SCAN); \
|
||||
hci_dev_clear_flag(hdev, HCI_LE_ADV); \
|
||||
hci_dev_clear_flag(hdev, HCI_LL_RPA_RESOLUTION);\
|
||||
hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ); \
|
||||
} while (0)
|
||||
|
||||
@ -1116,6 +1124,14 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
__printf(2, 3) void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
|
||||
__printf(2, 3) void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);
|
||||
|
||||
static inline void hci_set_msft_opcode(struct hci_dev *hdev, __u16 opcode)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_BT_MSFTEXT)
|
||||
hdev->msft_opcode = opcode;
|
||||
#endif
|
||||
}
|
||||
|
||||
int hci_dev_open(__u16 dev);
|
||||
int hci_dev_close(__u16 dev);
|
||||
int hci_dev_do_close(struct hci_dev *hdev);
|
||||
|
@ -674,6 +674,13 @@ struct mgmt_cp_set_blocked_keys {
|
||||
|
||||
#define MGMT_OP_SET_WIDEBAND_SPEECH 0x0047
|
||||
|
||||
#define MGMT_OP_READ_SECURITY_INFO 0x0048
|
||||
#define MGMT_READ_SECURITY_INFO_SIZE 0
|
||||
struct mgmt_rp_read_security_info {
|
||||
__le16 sec_len;
|
||||
__u8 sec[0];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||
struct mgmt_ev_cmd_complete {
|
||||
__le16 opcode;
|
||||
|
@ -93,6 +93,21 @@ config BT_LEDS
|
||||
This option selects a few LED triggers for different
|
||||
Bluetooth events.
|
||||
|
||||
config BT_MSFTEXT
|
||||
bool "Enable Microsoft extensions"
|
||||
depends on BT
|
||||
help
|
||||
This options enables support for the Microsoft defined HCI
|
||||
vendor extensions.
|
||||
|
||||
config BT_DEBUGFS
|
||||
bool "Export Bluetooth internals in debugfs"
|
||||
depends on BT && DEBUG_FS
|
||||
default y
|
||||
help
|
||||
Provide extensive information about internal Bluetooth states
|
||||
in debugfs.
|
||||
|
||||
config BT_SELFTEST
|
||||
bool "Bluetooth self testing support"
|
||||
depends on BT && DEBUG_KERNEL
|
||||
@ -120,12 +135,4 @@ config BT_SELFTEST_SMP
|
||||
Run test cases for SMP cryptographic functionality, including both
|
||||
legacy SMP as well as the Secure Connections features.
|
||||
|
||||
config BT_DEBUGFS
|
||||
bool "Export Bluetooth internals in debugfs"
|
||||
depends on BT && DEBUG_FS
|
||||
default y
|
||||
help
|
||||
Provide extensive information about internal Bluetooth states
|
||||
in debugfs.
|
||||
|
||||
source "drivers/bluetooth/Kconfig"
|
||||
|
@ -19,5 +19,6 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
||||
bluetooth-$(CONFIG_BT_BREDR) += sco.o
|
||||
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
|
||||
bluetooth-$(CONFIG_BT_LEDS) += leds.o
|
||||
bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
|
||||
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
|
||||
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
|
||||
|
@ -122,8 +122,18 @@ static void hci_conn_cleanup(struct hci_conn *conn)
|
||||
|
||||
hci_conn_hash_del(hdev, conn);
|
||||
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
|
||||
if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
|
||||
switch (conn->setting & SCO_AIRMODE_MASK) {
|
||||
case SCO_AIRMODE_CVSD:
|
||||
case SCO_AIRMODE_TRANSP:
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_DISABLE_SCO);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
|
||||
}
|
||||
|
||||
hci_conn_del_sysfs(conn);
|
||||
|
||||
@ -577,8 +587,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
hci_dev_hold(hdev);
|
||||
|
||||
hci_conn_hash_add(hdev, conn);
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
|
||||
|
||||
/* The SCO and eSCO connections will only be notified when their
|
||||
* setup has been completed. This is different to ACL links which
|
||||
* can be notified right away.
|
||||
*/
|
||||
if (conn->type != SCO_LINK && conn->type != ESCO_LINK) {
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
|
||||
}
|
||||
|
||||
hci_conn_init_sysfs(conn);
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "hci_debugfs.h"
|
||||
#include "smp.h"
|
||||
#include "leds.h"
|
||||
#include "msft.h"
|
||||
|
||||
static void hci_rx_work(struct work_struct *work);
|
||||
static void hci_cmd_work(struct work_struct *work);
|
||||
@ -637,6 +638,14 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
|
||||
if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT)
|
||||
events[0] |= 0x40; /* LE Data Length Change */
|
||||
|
||||
/* If the controller supports LL Privacy feature, enable
|
||||
* the corresponding event.
|
||||
*/
|
||||
if (hdev->le_features[0] & HCI_LE_LL_PRIVACY)
|
||||
events[1] |= 0x02; /* LE Enhanced Connection
|
||||
* Complete
|
||||
*/
|
||||
|
||||
/* If the controller supports Extended Scanner Filter
|
||||
* Policies, enable the correspondig event.
|
||||
*/
|
||||
@ -710,14 +719,6 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
|
||||
* Report
|
||||
*/
|
||||
|
||||
/* If the controller supports the LE Extended Create Connection
|
||||
* command, enable the corresponding event.
|
||||
*/
|
||||
if (use_ext_conn(hdev))
|
||||
events[1] |= 0x02; /* LE Enhanced Connection
|
||||
* Complete
|
||||
*/
|
||||
|
||||
/* If the controller supports the LE Extended Advertising
|
||||
* command, enable the corresponding event.
|
||||
*/
|
||||
@ -826,6 +827,10 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
|
||||
if (hdev->commands[29] & 0x20)
|
||||
hci_req_add(req, HCI_OP_READ_LOCAL_CODECS, 0, NULL);
|
||||
|
||||
/* Read local pairing options if the HCI command is supported */
|
||||
if (hdev->commands[41] & 0x08)
|
||||
hci_req_add(req, HCI_OP_READ_LOCAL_PAIRING_OPTS, 0, NULL);
|
||||
|
||||
/* Get MWS transport configuration if the HCI command is supported */
|
||||
if (hdev->commands[30] & 0x08)
|
||||
hci_req_add(req, HCI_OP_GET_MWS_TRANSPORT_CONFIG, 0, NULL);
|
||||
@ -1563,6 +1568,8 @@ setup_failed:
|
||||
hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag)
|
||||
ret = hdev->set_diag(hdev, true);
|
||||
|
||||
msft_do_open(hdev);
|
||||
|
||||
clear_bit(HCI_INIT, &hdev->flags);
|
||||
|
||||
if (!ret) {
|
||||
@ -1758,6 +1765,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
|
||||
|
||||
hci_sock_dev_event(hdev, HCI_DEV_DOWN);
|
||||
|
||||
msft_do_close(hdev);
|
||||
|
||||
if (hdev->flush)
|
||||
hdev->flush(hdev);
|
||||
|
||||
@ -4240,6 +4249,54 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt)
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule SCO */
|
||||
static void hci_sched_sco(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct sk_buff *skb;
|
||||
int quote;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!hci_conn_num(hdev, SCO_LINK))
|
||||
return;
|
||||
|
||||
while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
hci_send_frame(hdev, skb);
|
||||
|
||||
conn->sent++;
|
||||
if (conn->sent == ~0)
|
||||
conn->sent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_sched_esco(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct sk_buff *skb;
|
||||
int quote;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!hci_conn_num(hdev, ESCO_LINK))
|
||||
return;
|
||||
|
||||
while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK,
|
||||
"e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
hci_send_frame(hdev, skb);
|
||||
|
||||
conn->sent++;
|
||||
if (conn->sent == ~0)
|
||||
conn->sent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_sched_acl_pkt(struct hci_dev *hdev)
|
||||
{
|
||||
unsigned int cnt = hdev->acl_cnt;
|
||||
@ -4271,6 +4328,10 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
|
||||
hdev->acl_cnt--;
|
||||
chan->sent++;
|
||||
chan->conn->sent++;
|
||||
|
||||
/* Send pending SCO packets right away */
|
||||
hci_sched_sco(hdev);
|
||||
hci_sched_esco(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4355,54 +4416,6 @@ static void hci_sched_acl(struct hci_dev *hdev)
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule SCO */
|
||||
static void hci_sched_sco(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct sk_buff *skb;
|
||||
int quote;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!hci_conn_num(hdev, SCO_LINK))
|
||||
return;
|
||||
|
||||
while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
hci_send_frame(hdev, skb);
|
||||
|
||||
conn->sent++;
|
||||
if (conn->sent == ~0)
|
||||
conn->sent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_sched_esco(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct sk_buff *skb;
|
||||
int quote;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!hci_conn_num(hdev, ESCO_LINK))
|
||||
return;
|
||||
|
||||
while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK,
|
||||
"e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
hci_send_frame(hdev, skb);
|
||||
|
||||
conn->sent++;
|
||||
if (conn->sent == ~0)
|
||||
conn->sent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_sched_le(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_chan *chan;
|
||||
@ -4437,6 +4450,10 @@ static void hci_sched_le(struct hci_dev *hdev)
|
||||
cnt--;
|
||||
chan->sent++;
|
||||
chan->conn->sent++;
|
||||
|
||||
/* Send pending SCO packets right away */
|
||||
hci_sched_sco(hdev);
|
||||
hci_sched_esco(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4459,9 +4476,9 @@ static void hci_tx_work(struct work_struct *work)
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
|
||||
/* Schedule queues and send stuff to HCI driver */
|
||||
hci_sched_acl(hdev);
|
||||
hci_sched_sco(hdev);
|
||||
hci_sched_esco(hdev);
|
||||
hci_sched_acl(hdev);
|
||||
hci_sched_le(hdev);
|
||||
}
|
||||
|
||||
|
@ -1075,6 +1075,50 @@ DEFINE_SIMPLE_ATTRIBUTE(auth_payload_timeout_fops,
|
||||
auth_payload_timeout_get,
|
||||
auth_payload_timeout_set, "%llu\n");
|
||||
|
||||
static ssize_t force_no_mitm_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[3];
|
||||
|
||||
buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM) ? 'Y' : 'N';
|
||||
buf[1] = '\n';
|
||||
buf[2] = '\0';
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
}
|
||||
|
||||
static ssize_t force_no_mitm_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf) - 1));
|
||||
bool enable;
|
||||
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
if (strtobool(buf, &enable))
|
||||
return -EINVAL;
|
||||
|
||||
if (enable == hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM))
|
||||
return -EALREADY;
|
||||
|
||||
hci_dev_change_flag(hdev, HCI_FORCE_NO_MITM);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations force_no_mitm_fops = {
|
||||
.open = simple_open,
|
||||
.read = force_no_mitm_read,
|
||||
.write = force_no_mitm_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter,
|
||||
HCI_QUIRK_STRICT_DUPLICATE_FILTER);
|
||||
DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery,
|
||||
@ -1134,6 +1178,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev)
|
||||
&max_key_size_fops);
|
||||
debugfs_create_file("auth_payload_timeout", 0644, hdev->debugfs, hdev,
|
||||
&auth_payload_timeout_fops);
|
||||
debugfs_create_file("force_no_mitm", 0644, hdev->debugfs, hdev,
|
||||
&force_no_mitm_fops);
|
||||
|
||||
debugfs_create_file("quirk_strict_duplicate_filter", 0644,
|
||||
hdev->debugfs, hdev,
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "a2mp.h"
|
||||
#include "amp.h"
|
||||
#include "smp.h"
|
||||
#include "msft.h"
|
||||
|
||||
#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
@ -746,6 +747,23 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
bacpy(&hdev->setup_addr, &rp->bdaddr);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_pairing_opts(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_local_pairing_opts *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_SETUP) ||
|
||||
hci_dev_test_flag(hdev, HCI_CONFIG)) {
|
||||
hdev->pairing_opts = rp->pairing_opts;
|
||||
hdev->max_enc_key_size = rp->max_key_size;
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_cc_read_page_scan_activity(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@ -2607,8 +2625,16 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
if (ev->status) {
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
} else if (ev->link_type != ACL_LINK)
|
||||
} else if (ev->link_type == SCO_LINK) {
|
||||
switch (conn->setting & SCO_AIRMODE_MASK) {
|
||||
case SCO_AIRMODE_CVSD:
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
|
||||
break;
|
||||
}
|
||||
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
@ -3334,6 +3360,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
hci_cc_read_bd_addr(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_LOCAL_PAIRING_OPTS:
|
||||
hci_cc_read_local_pairing_opts(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_PAGE_SCAN_ACTIVITY:
|
||||
hci_cc_read_page_scan_activity(hdev, skb);
|
||||
break;
|
||||
@ -4307,6 +4337,19 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
|
||||
break;
|
||||
}
|
||||
|
||||
bt_dev_dbg(hdev, "SCO connected with air mode: %02x", ev->air_mode);
|
||||
|
||||
switch (conn->setting & SCO_AIRMODE_MASK) {
|
||||
case SCO_AIRMODE_CVSD:
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
|
||||
break;
|
||||
case SCO_AIRMODE_TRANSP:
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_TRANSP);
|
||||
break;
|
||||
}
|
||||
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
if (ev->status)
|
||||
hci_conn_del(conn);
|
||||
@ -5269,7 +5312,7 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
|
||||
case HCI_AUTO_CONN_ALWAYS:
|
||||
/* Devices advertising with ADV_IND or ADV_DIRECT_IND
|
||||
* are triggering a connection attempt. This means
|
||||
* that incoming connectioms from slave device are
|
||||
* that incoming connections from slave device are
|
||||
* accepted and also outgoing connections to slave
|
||||
* devices are established when found.
|
||||
*/
|
||||
@ -5353,7 +5396,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
|
||||
/* Adjust for actual length */
|
||||
if (len != real_len) {
|
||||
bt_dev_err_ratelimited(hdev, "advertising data len corrected");
|
||||
bt_dev_err_ratelimited(hdev, "advertising data len corrected %u -> %u",
|
||||
len, real_len);
|
||||
len = real_len;
|
||||
}
|
||||
|
||||
@ -6145,6 +6189,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_num_comp_blocks_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_VENDOR:
|
||||
msft_vendor_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s event 0x%2.2x", hdev->name, event);
|
||||
break;
|
||||
|
@ -2723,6 +2723,8 @@ static int active_scan(struct hci_request *req, unsigned long opt)
|
||||
uint16_t interval = opt;
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 own_addr_type;
|
||||
/* White list is not used for discovery */
|
||||
u8 filter_policy = 0x00;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
@ -2744,7 +2746,7 @@ static int active_scan(struct hci_request *req, unsigned long opt)
|
||||
own_addr_type = ADDR_LE_DEV_PUBLIC;
|
||||
|
||||
hci_req_start_scan(req, LE_SCAN_ACTIVE, interval, DISCOV_LE_SCAN_WIN,
|
||||
own_addr_type, 0);
|
||||
own_addr_type, filter_policy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -395,6 +395,24 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||
return sizeof(struct sockaddr_l2);
|
||||
}
|
||||
|
||||
static int l2cap_get_mode(struct l2cap_chan *chan)
|
||||
{
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
return BT_MODE_BASIC;
|
||||
case L2CAP_MODE_ERTM:
|
||||
return BT_MODE_ERTM;
|
||||
case L2CAP_MODE_STREAMING:
|
||||
return BT_MODE_STREAMING;
|
||||
case L2CAP_MODE_LE_FLOWCTL:
|
||||
return BT_MODE_LE_FLOWCTL;
|
||||
case L2CAP_MODE_EXT_FLOWCTL:
|
||||
return BT_MODE_EXT_FLOWCTL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
@ -424,6 +442,20 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
/* Only BR/EDR modes are supported here */
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
case L2CAP_MODE_ERTM:
|
||||
case L2CAP_MODE_STREAMING:
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.imtu = chan->imtu;
|
||||
opts.omtu = chan->omtu;
|
||||
@ -508,7 +540,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
struct bt_security sec;
|
||||
struct bt_power pwr;
|
||||
u32 phys;
|
||||
int len, err = 0;
|
||||
int len, mode, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
@ -624,6 +656,27 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case BT_MODE:
|
||||
if (!enable_ecred) {
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mode = l2cap_get_mode(chan);
|
||||
if (mode < 0) {
|
||||
err = mode;
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(mode, (u8 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
@ -698,10 +751,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
chan->mode = opts.mode;
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_LE_FLOWCTL:
|
||||
break;
|
||||
/* Only BR/EDR modes are supported here */
|
||||
switch (opts.mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||
break;
|
||||
@ -715,6 +766,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
chan->mode = opts.mode;
|
||||
|
||||
BT_DBG("mode 0x%2.2x", chan->mode);
|
||||
|
||||
chan->imtu = opts.imtu;
|
||||
@ -763,6 +819,45 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_set_mode(struct l2cap_chan *chan, u8 mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case BT_MODE_BASIC:
|
||||
if (bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_BASIC;
|
||||
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||
break;
|
||||
case BT_MODE_ERTM:
|
||||
if (!disable_ertm || bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_ERTM;
|
||||
break;
|
||||
case BT_MODE_STREAMING:
|
||||
if (!disable_ertm || bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_STREAMING;
|
||||
break;
|
||||
case BT_MODE_LE_FLOWCTL:
|
||||
if (!bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_LE_FLOWCTL;
|
||||
break;
|
||||
case BT_MODE_EXT_FLOWCTL:
|
||||
/* TODO: Add support for ECRED PDUs to BR/EDR */
|
||||
if (!bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_EXT_FLOWCTL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan->mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
char __user *optval, unsigned int optlen)
|
||||
{
|
||||
@ -968,6 +1063,39 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
break;
|
||||
|
||||
case BT_MODE:
|
||||
if (!enable_ecred) {
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
BT_DBG("sk->sk_state %u", sk->sk_state);
|
||||
|
||||
if (sk->sk_state != BT_BOUND) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_user(opt, (u8 __user *) optval)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
BT_DBG("opt %u", opt);
|
||||
|
||||
err = l2cap_set_mode(chan, opt);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
BT_DBG("mode 0x%2.2x", chan->mode);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include "mgmt_util.h"
|
||||
|
||||
#define MGMT_VERSION 1
|
||||
#define MGMT_REVISION 16
|
||||
#define MGMT_REVISION 17
|
||||
|
||||
static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_READ_INDEX_LIST,
|
||||
@ -108,6 +108,7 @@ static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_SET_APPEARANCE,
|
||||
MGMT_OP_SET_BLOCKED_KEYS,
|
||||
MGMT_OP_SET_WIDEBAND_SPEECH,
|
||||
MGMT_OP_READ_SECURITY_INFO,
|
||||
};
|
||||
|
||||
static const u16 mgmt_events[] = {
|
||||
@ -155,6 +156,7 @@ static const u16 mgmt_untrusted_commands[] = {
|
||||
MGMT_OP_READ_CONFIG_INFO,
|
||||
MGMT_OP_READ_EXT_INDEX_LIST,
|
||||
MGMT_OP_READ_EXT_INFO,
|
||||
MGMT_OP_READ_SECURITY_INFO,
|
||||
};
|
||||
|
||||
static const u16 mgmt_untrusted_events[] = {
|
||||
@ -3659,6 +3661,55 @@ unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int read_security_info(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 data_len)
|
||||
{
|
||||
char buf[16];
|
||||
struct mgmt_rp_read_security_info *rp = (void *)buf;
|
||||
u16 sec_len = 0;
|
||||
u8 flags = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "sock %p", sk);
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* When the Read Simple Pairing Options command is supported, then
|
||||
* the remote public key validation is supported.
|
||||
*/
|
||||
if (hdev->commands[41] & 0x08)
|
||||
flags |= 0x01; /* Remote public key validation (BR/EDR) */
|
||||
|
||||
flags |= 0x02; /* Remote public key validation (LE) */
|
||||
|
||||
/* When the Read Encryption Key Size command is supported, then the
|
||||
* encryption key size is enforced.
|
||||
*/
|
||||
if (hdev->commands[20] & 0x10)
|
||||
flags |= 0x04; /* Encryption key size enforcement (BR/EDR) */
|
||||
|
||||
flags |= 0x08; /* Encryption key size enforcement (LE) */
|
||||
|
||||
sec_len = eir_append_data(rp->sec, sec_len, 0x01, &flags, 1);
|
||||
|
||||
/* When the Read Simple Pairing Options command is supported, then
|
||||
* also max encryption key size information is provided.
|
||||
*/
|
||||
if (hdev->commands[41] & 0x08)
|
||||
sec_len = eir_append_le16(rp->sec, sec_len, 0x02,
|
||||
hdev->max_enc_key_size);
|
||||
|
||||
sec_len = eir_append_le16(rp->sec, sec_len, 0x03, SMP_MAX_ENC_KEY_SIZE);
|
||||
|
||||
rp->sec_len = cpu_to_le16(sec_len);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_SECURITY_INFO, 0,
|
||||
rp, sizeof(*rp) + sec_len);
|
||||
}
|
||||
|
||||
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
@ -7099,6 +7150,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
|
||||
{ set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
{ set_wideband_speech, MGMT_SETTING_SIZE },
|
||||
{ read_security_info, MGMT_READ_SECURITY_INFO_SIZE,
|
||||
HCI_MGMT_UNTRUSTED },
|
||||
};
|
||||
|
||||
void mgmt_index_added(struct hci_dev *hdev)
|
||||
|
141
net/bluetooth/msft.c
Normal file
141
net/bluetooth/msft.c
Normal file
@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020 Google Corporation
|
||||
*/
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "msft.h"
|
||||
|
||||
#define MSFT_OP_READ_SUPPORTED_FEATURES 0x00
|
||||
struct msft_cp_read_supported_features {
|
||||
__u8 sub_opcode;
|
||||
} __packed;
|
||||
struct msft_rp_read_supported_features {
|
||||
__u8 status;
|
||||
__u8 sub_opcode;
|
||||
__le64 features;
|
||||
__u8 evt_prefix_len;
|
||||
__u8 evt_prefix[0];
|
||||
} __packed;
|
||||
|
||||
struct msft_data {
|
||||
__u64 features;
|
||||
__u8 evt_prefix_len;
|
||||
__u8 *evt_prefix;
|
||||
};
|
||||
|
||||
static bool read_supported_features(struct hci_dev *hdev,
|
||||
struct msft_data *msft)
|
||||
{
|
||||
struct msft_cp_read_supported_features cp;
|
||||
struct msft_rp_read_supported_features *rp;
|
||||
struct sk_buff *skb;
|
||||
|
||||
cp.sub_opcode = MSFT_OP_READ_SUPPORTED_FEATURES;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "Failed to read MSFT supported features (%ld)",
|
||||
PTR_ERR(skb));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (skb->len < sizeof(*rp)) {
|
||||
bt_dev_err(hdev, "MSFT supported features length mismatch");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rp = (struct msft_rp_read_supported_features *)skb->data;
|
||||
|
||||
if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES)
|
||||
goto failed;
|
||||
|
||||
if (rp->evt_prefix_len > 0) {
|
||||
msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len,
|
||||
GFP_KERNEL);
|
||||
if (!msft->evt_prefix)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
msft->evt_prefix_len = rp->evt_prefix_len;
|
||||
msft->features = __le64_to_cpu(rp->features);
|
||||
|
||||
kfree_skb(skb);
|
||||
return true;
|
||||
|
||||
failed:
|
||||
kfree_skb(skb);
|
||||
return false;
|
||||
}
|
||||
|
||||
void msft_do_open(struct hci_dev *hdev)
|
||||
{
|
||||
struct msft_data *msft;
|
||||
|
||||
if (hdev->msft_opcode == HCI_OP_NOP)
|
||||
return;
|
||||
|
||||
bt_dev_dbg(hdev, "Initialize MSFT extension");
|
||||
|
||||
msft = kzalloc(sizeof(*msft), GFP_KERNEL);
|
||||
if (!msft)
|
||||
return;
|
||||
|
||||
if (!read_supported_features(hdev, msft)) {
|
||||
kfree(msft);
|
||||
return;
|
||||
}
|
||||
|
||||
hdev->msft_data = msft;
|
||||
}
|
||||
|
||||
void msft_do_close(struct hci_dev *hdev)
|
||||
{
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
|
||||
if (!msft)
|
||||
return;
|
||||
|
||||
bt_dev_dbg(hdev, "Cleanup of MSFT extension");
|
||||
|
||||
hdev->msft_data = NULL;
|
||||
|
||||
kfree(msft->evt_prefix);
|
||||
kfree(msft);
|
||||
}
|
||||
|
||||
void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
u8 event;
|
||||
|
||||
if (!msft)
|
||||
return;
|
||||
|
||||
/* When the extension has defined an event prefix, check that it
|
||||
* matches, and otherwise just return.
|
||||
*/
|
||||
if (msft->evt_prefix_len > 0) {
|
||||
if (skb->len < msft->evt_prefix_len)
|
||||
return;
|
||||
|
||||
if (memcmp(skb->data, msft->evt_prefix, msft->evt_prefix_len))
|
||||
return;
|
||||
|
||||
skb_pull(skb, msft->evt_prefix_len);
|
||||
}
|
||||
|
||||
/* Every event starts at least with an event code and the rest of
|
||||
* the data is variable and depends on the event code.
|
||||
*/
|
||||
if (skb->len < 1)
|
||||
return;
|
||||
|
||||
event = *skb->data;
|
||||
skb_pull(skb, 1);
|
||||
|
||||
bt_dev_dbg(hdev, "MSFT vendor event %u", event);
|
||||
}
|
18
net/bluetooth/msft.h
Normal file
18
net/bluetooth/msft.h
Normal file
@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020 Google Corporation
|
||||
*/
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_MSFTEXT)
|
||||
|
||||
void msft_do_open(struct hci_dev *hdev);
|
||||
void msft_do_close(struct hci_dev *hdev);
|
||||
void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
|
||||
#else
|
||||
|
||||
static inline void msft_do_open(struct hci_dev *hdev) {}
|
||||
static inline void msft_do_close(struct hci_dev *hdev) {}
|
||||
static inline void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb) {}
|
||||
|
||||
#endif
|
@ -854,7 +854,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
|
||||
struct l2cap_chan *chan = conn->smp;
|
||||
struct smp_chan *smp = chan->data;
|
||||
u32 passkey = 0;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
/* Initialize key for JUST WORKS */
|
||||
memset(smp->tk, 0, sizeof(smp->tk));
|
||||
@ -883,9 +883,16 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
|
||||
hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
|
||||
smp->method = JUST_WORKS;
|
||||
|
||||
/* If Just Works, Continue with Zero TK */
|
||||
/* If Just Works, Continue with Zero TK and ask user-space for
|
||||
* confirmation */
|
||||
if (smp->method == JUST_WORKS) {
|
||||
set_bit(SMP_FLAG_TK_VALID, &smp->flags);
|
||||
ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
|
||||
hcon->type,
|
||||
hcon->dst_type,
|
||||
passkey, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2194,7 +2201,7 @@ mackey_and_ltk:
|
||||
if (err)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
|
||||
if (smp->method == REQ_OOB) {
|
||||
if (hcon->out) {
|
||||
sc_dhkey_check(smp);
|
||||
SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
|
||||
@ -2209,6 +2216,9 @@ mackey_and_ltk:
|
||||
confirm_hint = 0;
|
||||
|
||||
confirm:
|
||||
if (smp->method == JUST_WORKS)
|
||||
confirm_hint = 1;
|
||||
|
||||
err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
|
||||
hcon->dst_type, passkey, confirm_hint);
|
||||
if (err)
|
||||
@ -2385,12 +2395,17 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
|
||||
authreq |= SMP_AUTH_CT2;
|
||||
}
|
||||
|
||||
/* Require MITM if IO Capability allows or the security level
|
||||
* requires it.
|
||||
/* Don't attempt to set MITM if setting is overridden by debugfs
|
||||
* Needed to pass certification test SM/MAS/PKE/BV-01-C
|
||||
*/
|
||||
if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT ||
|
||||
hcon->pending_sec_level > BT_SECURITY_MEDIUM)
|
||||
authreq |= SMP_AUTH_MITM;
|
||||
if (!hci_dev_test_flag(hcon->hdev, HCI_FORCE_NO_MITM)) {
|
||||
/* Require MITM if IO Capability allows or the security level
|
||||
* requires it.
|
||||
*/
|
||||
if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT ||
|
||||
hcon->pending_sec_level > BT_SECURITY_MEDIUM)
|
||||
authreq |= SMP_AUTH_MITM;
|
||||
}
|
||||
|
||||
if (hcon->role == HCI_ROLE_MASTER) {
|
||||
struct smp_cmd_pairing cp;
|
||||
|
Loading…
x
Reference in New Issue
Block a user