Commit 513a24ff authored by David S. Miller's avatar David S. Miller

Merge branch 'for-upstream' of...

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

Johan Hedberg says:

====================
pull request: bluetooth-next 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: default avatarDavid S. Miller <davem@davemloft.net>
parents 2fcd8014 7edc9079
...@@ -13,6 +13,7 @@ Required properties: ...@@ -13,6 +13,7 @@ Required properties:
* "qcom,wcn3990-bt" * "qcom,wcn3990-bt"
* "qcom,wcn3991-bt" * "qcom,wcn3991-bt"
* "qcom,wcn3998-bt" * "qcom,wcn3998-bt"
* "qcom,qca6390-bt"
Optional properties for compatible string qcom,qca6174-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, ...@@ -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 * VSE event. WCN3991 sends version command response as a payload to
* command complete event. * command complete event.
*/ */
if (soc_type == QCA_WCN3991) { if (soc_type >= QCA_WCN3991) {
event_type = 0; event_type = 0;
rlen += 1; rlen += 1;
rtype = EDL_PATCH_VER_REQ_CMD; rtype = EDL_PATCH_VER_REQ_CMD;
...@@ -69,7 +69,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version, ...@@ -69,7 +69,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
goto out; goto out;
} }
if (soc_type == QCA_WCN3991) if (soc_type >= QCA_WCN3991)
memmove(&edl->data, &edl->data[1], sizeof(*ver)); memmove(&edl->data, &edl->data[1], sizeof(*ver));
ver = (struct qca_btsoc_version *)(edl->data); ver = (struct qca_btsoc_version *)(edl->data);
...@@ -217,7 +217,7 @@ static void qca_tlv_check_data(struct qca_fw_config *config, ...@@ -217,7 +217,7 @@ static void qca_tlv_check_data(struct qca_fw_config *config,
tlv_nvm->data[0] |= 0x80; tlv_nvm->data[0] |= 0x80;
/* UART Baud Rate */ /* UART Baud Rate */
if (soc_type == QCA_WCN3991) if (soc_type >= QCA_WCN3991)
tlv_nvm->data[1] = nvm_baud_rate; tlv_nvm->data[1] = nvm_baud_rate;
else else
tlv_nvm->data[2] = nvm_baud_rate; tlv_nvm->data[2] = nvm_baud_rate;
...@@ -268,7 +268,7 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size, ...@@ -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 * VSE event. WCN3991 sends version command response as a payload to
* command complete event. * command complete event.
*/ */
if (soc_type == QCA_WCN3991) { if (soc_type >= QCA_WCN3991) {
event_type = 0; event_type = 0;
rlen = sizeof(*edl); rlen = sizeof(*edl);
rtype = EDL_PATCH_TLV_REQ_CMD; rtype = EDL_PATCH_TLV_REQ_CMD;
...@@ -301,7 +301,7 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size, ...@@ -301,7 +301,7 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
err = -EIO; err = -EIO;
} }
if (soc_type == QCA_WCN3991) if (soc_type >= QCA_WCN3991)
goto out; goto out;
tlv_resp = (struct tlv_seg_resp *)(edl->data); tlv_resp = (struct tlv_seg_resp *)(edl->data);
...@@ -442,6 +442,11 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, ...@@ -442,6 +442,11 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
(soc_ver & 0x0000000f); (soc_ver & 0x0000000f);
snprintf(config.fwname, sizeof(config.fwname), snprintf(config.fwname, sizeof(config.fwname),
"qca/crbtfw%02x.tlv", rom_ver); "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 { } else {
snprintf(config.fwname, sizeof(config.fwname), snprintf(config.fwname, sizeof(config.fwname),
"qca/rampatch_%08x.bin", soc_ver); "qca/rampatch_%08x.bin", soc_ver);
...@@ -464,6 +469,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, ...@@ -464,6 +469,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
else if (qca_is_wcn399x(soc_type)) else if (qca_is_wcn399x(soc_type))
snprintf(config.fwname, sizeof(config.fwname), snprintf(config.fwname, sizeof(config.fwname),
"qca/crnv%02x.bin", rom_ver); "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 else
snprintf(config.fwname, sizeof(config.fwname), snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver); "qca/nvm_%08x.bin", soc_ver);
......
...@@ -125,8 +125,9 @@ enum qca_btsoc_type { ...@@ -125,8 +125,9 @@ enum qca_btsoc_type {
QCA_AR3002, QCA_AR3002,
QCA_ROME, QCA_ROME,
QCA_WCN3990, QCA_WCN3990,
QCA_WCN3991,
QCA_WCN3998, QCA_WCN3998,
QCA_WCN3991,
QCA_QCA6390,
}; };
#if IS_ENABLED(CONFIG_BT_QCA) #if IS_ENABLED(CONFIG_BT_QCA)
......
...@@ -130,12 +130,19 @@ static const struct id_table ic_id_table[] = { ...@@ -130,12 +130,19 @@ static const struct id_table ic_id_table[] = {
.cfg_name = "rtl_bt/rtl8821c_config" }, .cfg_name = "rtl_bt/rtl8821c_config" },
/* 8761A */ /* 8761A */
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8761A, 0x0, { IC_INFO(RTL_ROM_LMP_8761A, 0xa),
.config_needed = false, .config_needed = false,
.has_rom_version = true, .has_rom_version = true,
.fw_name = "rtl_bt/rtl8761a_fw.bin", .fw_name = "rtl_bt/rtl8761a_fw.bin",
.cfg_name = "rtl_bt/rtl8761a_config" }, .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 */ /* 8822C with UART interface */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
IC_MATCH_FL_HCIBUS, IC_MATCH_FL_HCIBUS,
...@@ -267,6 +274,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, ...@@ -267,6 +274,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
{ RTL_ROM_LMP_8723B, 9 }, /* 8723D */ { RTL_ROM_LMP_8723B, 9 }, /* 8723D */
{ RTL_ROM_LMP_8821A, 10 }, /* 8821C */ { RTL_ROM_LMP_8821A, 10 }, /* 8821C */
{ RTL_ROM_LMP_8822B, 13 }, /* 8822C */ { RTL_ROM_LMP_8822B, 13 }, /* 8822C */
{ RTL_ROM_LMP_8761A, 14 }, /* 8761B */
}; };
min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3; min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
......
...@@ -492,6 +492,8 @@ struct btusb_data { ...@@ -492,6 +492,8 @@ struct btusb_data {
__u8 cmdreq; __u8 cmdreq;
unsigned int sco_num; unsigned int sco_num;
unsigned int air_mode;
bool usb_alt6_packet_flow;
int isoc_altsetting; int isoc_altsetting;
int suspend_count; int suspend_count;
...@@ -983,6 +985,42 @@ static void btusb_isoc_complete(struct urb *urb) ...@@ -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) static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
{ {
int i, offset = 0; int i, offset = 0;
...@@ -1386,9 +1424,13 @@ static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -1386,9 +1424,13 @@ static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
urb->transfer_flags = URB_ISO_ASAP; urb->transfer_flags = URB_ISO_ASAP;
__fill_isoc_descriptor(urb, skb->len, if (data->isoc_altsetting == 6)
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize)); __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; skb->dev = (void *)hdev;
return urb; return urb;
...@@ -1484,6 +1526,7 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt) ...@@ -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) { if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) {
data->sco_num = hci_conn_num(hdev, SCO_LINK); data->sco_num = hci_conn_num(hdev, SCO_LINK);
data->air_mode = evt;
schedule_work(&data->work); schedule_work(&data->work);
} }
} }
...@@ -1531,11 +1574,70 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting) ...@@ -1531,11 +1574,70 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
return 0; 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) static void btusb_work(struct work_struct *work)
{ {
struct btusb_data *data = container_of(work, struct btusb_data, work); struct btusb_data *data = container_of(work, struct btusb_data, work);
struct hci_dev *hdev = data->hdev; struct hci_dev *hdev = data->hdev;
int new_alts; int new_alts = 0;
int err; int err;
if (data->sco_num > 0) { if (data->sco_num > 0) {
...@@ -1550,44 +1652,27 @@ static void btusb_work(struct work_struct *work) ...@@ -1550,44 +1652,27 @@ static void btusb_work(struct work_struct *work)
set_bit(BTUSB_DID_ISO_RESUME, &data->flags); set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
} }
if (hdev->voice_setting & 0x0020) { if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_CVSD) {
static const int alts[3] = { 2, 4, 5 }; if (hdev->voice_setting & 0x0020) {
static const int alts[3] = { 2, 4, 5 };
new_alts = alts[data->sco_num - 1]; new_alts = alts[data->sco_num - 1];
} else { } else {
new_alts = data->sco_num; new_alts = data->sco_num;
} }
} else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) {
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);
if (__set_isoc_interface(hdev, new_alts) < 0) data->usb_alt6_packet_flow = true;
return;
}
if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) { /* Check if Alt 6 is supported for Transparent audio */
if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0) if (btusb_find_altsetting(data, 6))
clear_bit(BTUSB_ISOC_RUNNING, &data->flags); new_alts = 6;
else 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 { } else {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags); clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor); usb_kill_anchored_urbs(&data->isoc_anchor);
...@@ -2252,7 +2337,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) ...@@ -2252,7 +2337,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
if (ver.fw_variant == 0x23) { if (ver.fw_variant == 0x23) {
clear_bit(BTUSB_BOOTLOADER, &data->flags); clear_bit(BTUSB_BOOTLOADER, &data->flags);
btintel_check_bdaddr(hdev); btintel_check_bdaddr(hdev);
return 0; goto finish;
} }
/* If the device is not in bootloader mode, then the only possible /* If the device is not in bootloader mode, then the only possible
...@@ -2452,6 +2537,23 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) ...@@ -2452,6 +2537,23 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
*/ */
btintel_load_ddc_config(hdev, fwname); 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 /* Set the event mask for Intel specific vendor events. This enables
* a few extra events that are useful during general operation. It * a few extra events that are useful during general operation. It
* does not enable any debugging related events. * does not enable any debugging related events.
...@@ -2461,13 +2563,6 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) ...@@ -2461,13 +2563,6 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
*/ */
btintel_set_event_mask(hdev, false); 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; return 0;
} }
......
...@@ -118,6 +118,7 @@ struct bcm_device { ...@@ -118,6 +118,7 @@ struct bcm_device {
u32 oper_speed; u32 oper_speed;
int irq; int irq;
bool irq_active_low; bool irq_active_low;
bool irq_acquired;
#ifdef CONFIG_PM #ifdef CONFIG_PM
struct hci_uart *hu; struct hci_uart *hu;
...@@ -333,6 +334,8 @@ static int bcm_request_irq(struct bcm_data *bcm) ...@@ -333,6 +334,8 @@ static int bcm_request_irq(struct bcm_data *bcm)
goto unlock; goto unlock;
} }
bdev->irq_acquired = true;
device_init_wakeup(bdev->dev, true); device_init_wakeup(bdev->dev, true);
pm_runtime_set_autosuspend_delay(bdev->dev, pm_runtime_set_autosuspend_delay(bdev->dev,
...@@ -514,7 +517,7 @@ static int bcm_close(struct hci_uart *hu) ...@@ -514,7 +517,7 @@ static int bcm_close(struct hci_uart *hu)
} }
if (bdev) { 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); devm_free_irq(bdev->dev, bdev->irq, bdev);
device_init_wakeup(bdev->dev, false); device_init_wakeup(bdev->dev, false);
pm_runtime_disable(bdev->dev); pm_runtime_disable(bdev->dev);
...@@ -1153,7 +1156,8 @@ static int bcm_of_probe(struct bcm_device *bdev) ...@@ -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", device_property_read_u8_array(bdev->dev, "brcm,bt-pcm-int-params",
bdev->pcm_int_params, 5); bdev->pcm_int_params, 5);
bdev->irq = of_irq_get_byname(bdev->dev->of_node, "host-wakeup"); 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; return 0;
} }
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/acpi.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/serdev.h> #include <linux/serdev.h>
...@@ -1596,7 +1597,7 @@ static int qca_setup(struct hci_uart *hu) ...@@ -1596,7 +1597,7 @@ static int qca_setup(struct hci_uart *hu)
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
bt_dev_info(hdev, "setting up %s", bt_dev_info(hdev, "setting up %s",
qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME"); qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME/QCA6390");
retry: retry:
ret = qca_power_on(hdev); ret = qca_power_on(hdev);
...@@ -1665,10 +1666,10 @@ static int qca_setup(struct hci_uart *hu) ...@@ -1665,10 +1666,10 @@ static int qca_setup(struct hci_uart *hu)
} }
/* Setup bdaddr */ /* Setup bdaddr */
if (qca_is_wcn399x(soc_type)) if (soc_type == QCA_ROME)
hu->hdev->set_bdaddr = qca_set_bdaddr;
else
hu->hdev->set_bdaddr = qca_set_bdaddr_rome; hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
else
hu->hdev->set_bdaddr = qca_set_bdaddr;
return ret; return ret;
} }
...@@ -1721,6 +1722,11 @@ static const struct qca_vreg_data qca_soc_data_wcn3998 = { ...@@ -1721,6 +1722,11 @@ static const struct qca_vreg_data qca_soc_data_wcn3998 = {
.num_vregs = 4, .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) static void qca_power_shutdown(struct hci_uart *hu)
{ {
struct qca_serdev *qcadev; struct qca_serdev *qcadev;
...@@ -1764,7 +1770,7 @@ static int qca_power_off(struct hci_dev *hdev) ...@@ -1764,7 +1770,7 @@ static int qca_power_off(struct hci_dev *hdev)
enum qca_btsoc_type soc_type = qca_soc_type(hu); enum qca_btsoc_type soc_type = qca_soc_type(hu);
/* Stop sending shutdown command if soc crashes. */ /* 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->memdump_state == QCA_MEMDUMP_IDLE) {
qca_send_pre_shutdown_cmd(hdev); qca_send_pre_shutdown_cmd(hdev);
usleep_range(8000, 10000); usleep_range(8000, 10000);
...@@ -1900,7 +1906,11 @@ static int qca_serdev_probe(struct serdev_device *serdev) ...@@ -1900,7 +1906,11 @@ static int qca_serdev_probe(struct serdev_device *serdev)
return err; return err;
} }
} else { } 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", qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
GPIOD_OUT_LOW); GPIOD_OUT_LOW);
if (!qcadev->bt_en) { if (!qcadev->bt_en) {
...@@ -2044,21 +2054,37 @@ static int __maybe_unused qca_resume(struct device *dev) ...@@ -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); 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[] = { static const struct of_device_id qca_bluetooth_of_match[] = {
{ .compatible = "qcom,qca6174-bt" }, { .compatible = "qcom,qca6174-bt" },
{ .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390},
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990}, { .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991}, { .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998}, { .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match); 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 = { static struct serdev_device_driver qca_serdev_driver = {
.probe = qca_serdev_probe, .probe = qca_serdev_probe,
.remove = qca_serdev_remove, .remove = qca_serdev_remove,
.driver = { .driver = {
.name = "hci_uart_qca", .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, .pm = &qca_pm_ops,
}, },
}; };
......
...@@ -139,6 +139,14 @@ struct bt_voice { ...@@ -139,6 +139,14 @@ struct bt_voice {
#define BT_PHY_LE_CODED_TX 0x00002000 #define BT_PHY_LE_CODED_TX 0x00002000
#define BT_PHY_LE_CODED_RX 0x00004000 #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) __printf(1, 2)
void bt_info(const char *fmt, ...); void bt_info(const char *fmt, ...);
__printf(1, 2) __printf(1, 2)
......
...@@ -53,6 +53,9 @@ ...@@ -53,6 +53,9 @@
#define HCI_NOTIFY_CONN_ADD 1 #define HCI_NOTIFY_CONN_ADD 1
#define HCI_NOTIFY_CONN_DEL 2 #define HCI_NOTIFY_CONN_DEL 2
#define HCI_NOTIFY_VOICE_SETTING 3 #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 */ /* HCI bus types */
#define HCI_VIRTUAL 0 #define HCI_VIRTUAL 0
...@@ -65,6 +68,7 @@ ...@@ -65,6 +68,7 @@
#define HCI_SPI 7 #define HCI_SPI 7
#define HCI_I2C 8 #define HCI_I2C 8
#define HCI_SMD 9 #define HCI_SMD 9
#define HCI_VIRTIO 10
/* HCI controller types */ /* HCI controller types */
#define HCI_PRIMARY 0x00 #define HCI_PRIMARY 0x00
...@@ -294,6 +298,7 @@ enum { ...@@ -294,6 +298,7 @@ enum {
HCI_FORCE_STATIC_ADDR, HCI_FORCE_STATIC_ADDR,
HCI_LL_RPA_RESOLUTION, HCI_LL_RPA_RESOLUTION,
HCI_CMD_PENDING, HCI_CMD_PENDING,
HCI_FORCE_NO_MITM,
__HCI_NUM_FLAGS, __HCI_NUM_FLAGS,
}; };
...@@ -455,12 +460,11 @@ enum { ...@@ -455,12 +460,11 @@ enum {
#define HCI_LE_SLAVE_FEATURES 0x08 #define HCI_LE_SLAVE_FEATURES 0x08
#define HCI_LE_PING 0x10 #define HCI_LE_PING 0x10
#define HCI_LE_DATA_LEN_EXT 0x20 #define HCI_LE_DATA_LEN_EXT 0x20
#define HCI_LE_PHY_2M 0x01 #define HCI_LE_LL_PRIVACY 0x40
#define HCI_LE_PHY_CODED 0x08
#define HCI_LE_EXT_ADV 0x10
#define HCI_LE_EXT_SCAN_POLICY 0x80 #define HCI_LE_EXT_SCAN_POLICY 0x80
#define HCI_LE_PHY_2M 0x01 #define HCI_LE_PHY_2M 0x01
#define HCI_LE_PHY_CODED 0x08 #define HCI_LE_PHY_CODED 0x08
#define HCI_LE_EXT_ADV 0x10
#define HCI_LE_CHAN_SEL_ALG2 0x40 #define HCI_LE_CHAN_SEL_ALG2 0x40
#define HCI_LE_CIS_MASTER 0x10 #define HCI_LE_CIS_MASTER 0x10
#define HCI_LE_CIS_SLAVE 0x20 #define HCI_LE_CIS_SLAVE 0x20
...@@ -1272,6 +1276,13 @@ struct hci_rp_read_data_block_size { ...@@ -1272,6 +1276,13 @@ struct hci_rp_read_data_block_size {
#define HCI_OP_READ_LOCAL_CODECS 0x100b #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 #define HCI_OP_READ_PAGE_SCAN_ACTIVITY 0x0c1b
struct hci_rp_read_page_scan_activity { struct hci_rp_read_page_scan_activity {
__u8 status; __u8 status;
......
...@@ -312,6 +312,8 @@ struct hci_dev { ...@@ -312,6 +312,8 @@ struct hci_dev {
__u16 conn_info_max_age; __u16 conn_info_max_age;
__u16 auth_payload_timeout; __u16 auth_payload_timeout;
__u8 min_enc_key_size; __u8 min_enc_key_size;
__u8 max_enc_key_size;
__u8 pairing_opts;
__u8 ssp_debug_mode; __u8 ssp_debug_mode;
__u8 hw_error_code; __u8 hw_error_code;
__u32 clock; __u32 clock;
...@@ -484,6 +486,11 @@ struct hci_dev { ...@@ -484,6 +486,11 @@ struct hci_dev {
struct led_trigger *power_led; struct led_trigger *power_led;
#endif #endif
#if IS_ENABLED(CONFIG_BT_MSFTEXT)
__u16 msft_opcode;
void *msft_data;
#endif
int (*open)(struct hci_dev *hdev); int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev);
...@@ -638,6 +645,7 @@ extern struct mutex hci_cb_list_lock; ...@@ -638,6 +645,7 @@ extern struct mutex hci_cb_list_lock;
do { \ do { \
hci_dev_clear_flag(hdev, HCI_LE_SCAN); \ hci_dev_clear_flag(hdev, HCI_LE_SCAN); \
hci_dev_clear_flag(hdev, HCI_LE_ADV); \ hci_dev_clear_flag(hdev, HCI_LE_ADV); \
hci_dev_clear_flag(hdev, HCI_LL_RPA_RESOLUTION);\
hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ); \ hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ); \
} while (0) } while (0)
...@@ -1116,6 +1124,14 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); ...@@ -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); 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_hw_info(struct hci_dev *hdev, const char *fmt, ...);
__printf(2, 3) void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...); __printf(2, 3) void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);
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_open(__u16 dev);
int hci_dev_close(__u16 dev); int hci_dev_close(__u16 dev);
int hci_dev_do_close(struct hci_dev *hdev); int hci_dev_do_close(struct hci_dev *hdev);
......
...@@ -674,6 +674,13 @@ struct mgmt_cp_set_blocked_keys { ...@@ -674,6 +674,13 @@ struct mgmt_cp_set_blocked_keys {
#define MGMT_OP_SET_WIDEBAND_SPEECH 0x0047 #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 #define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete { struct mgmt_ev_cmd_complete {
__le16 opcode; __le16 opcode;
......
...@@ -93,6 +93,21 @@ config BT_LEDS ...@@ -93,6 +93,21 @@ config BT_LEDS
This option selects a few LED triggers for different This option selects a few LED triggers for different
Bluetooth events. 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 config BT_SELFTEST
bool "Bluetooth self testing support" bool "Bluetooth self testing support"
depends on BT && DEBUG_KERNEL depends on BT && DEBUG_KERNEL
...@@ -120,12 +135,4 @@ config BT_SELFTEST_SMP ...@@ -120,12 +135,4 @@ config BT_SELFTEST_SMP
Run test cases for SMP cryptographic functionality, including both Run test cases for SMP cryptographic functionality, including both
legacy SMP as well as the Secure Connections features. 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" source "drivers/bluetooth/Kconfig"
...@@ -19,5 +19,6 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ ...@@ -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_BREDR) += sco.o
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
bluetooth-$(CONFIG_BT_LEDS) += leds.o bluetooth-$(CONFIG_BT_LEDS) += leds.o
bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
...@@ -122,8 +122,18 @@ static void hci_conn_cleanup(struct hci_conn *conn) ...@@ -122,8 +122,18 @@ static void hci_conn_cleanup(struct hci_conn *conn)
hci_conn_hash_del(hdev, conn); hci_conn_hash_del(hdev, conn);
if (hdev->notify) if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); 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); hci_conn_del_sysfs(conn);
...@@ -577,8 +587,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, ...@@ -577,8 +587,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
hci_dev_hold(hdev); hci_dev_hold(hdev);
hci_conn_hash_add(hdev, conn); 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); hci_conn_init_sysfs(conn);
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include "hci_debugfs.h" #include "hci_debugfs.h"
#include "smp.h" #include "smp.h"
#include "leds.h" #include "leds.h"
#include "msft.h"
static void hci_rx_work(struct work_struct *work); static void hci_rx_work(struct work_struct *work);
static void hci_cmd_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) ...@@ -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) if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT)
events[0] |= 0x40; /* LE Data Length Change */ 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 /* If the controller supports Extended Scanner Filter
* Policies, enable the correspondig event. * Policies, enable the correspondig event.
*/ */
...@@ -710,14 +719,6 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt) ...@@ -710,14 +719,6 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
* Report * 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 /* If the controller supports the LE Extended Advertising
* command, enable the corresponding event. * command, enable the corresponding event.
*/ */
...@@ -826,6 +827,10 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt) ...@@ -826,6 +827,10 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
if (hdev->commands[29] & 0x20) if (hdev->commands[29] & 0x20)
hci_req_add(req, HCI_OP_READ_LOCAL_CODECS, 0, NULL); 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 */ /* Get MWS transport configuration if the HCI command is supported */
if (hdev->commands[30] & 0x08) if (hdev->commands[30] & 0x08)
hci_req_add(req, HCI_OP_GET_MWS_TRANSPORT_CONFIG, 0, NULL); hci_req_add(req, HCI_OP_GET_MWS_TRANSPORT_CONFIG, 0, NULL);
...@@ -1563,6 +1568,8 @@ static int hci_dev_do_open(struct hci_dev *hdev) ...@@ -1563,6 +1568,8 @@ static int hci_dev_do_open(struct hci_dev *hdev)
hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag) hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag)
ret = hdev->set_diag(hdev, true); ret = hdev->set_diag(hdev, true);
msft_do_open(hdev);
clear_bit(HCI_INIT, &hdev->flags); clear_bit(HCI_INIT, &hdev->flags);
if (!ret) { if (!ret) {
...@@ -1758,6 +1765,8 @@ int hci_dev_do_close(struct hci_dev *hdev) ...@@ -1758,6 +1765,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
hci_sock_dev_event(hdev, HCI_DEV_DOWN); hci_sock_dev_event(hdev, HCI_DEV_DOWN);
msft_do_close(hdev);
if (hdev->flush) if (hdev->flush)
hdev->flush(hdev); hdev->flush(hdev);
...@@ -4240,6 +4249,54 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt) ...@@ -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, &quote))) {
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,
&quote))) {
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) static void hci_sched_acl_pkt(struct hci_dev *hdev)
{ {
unsigned int cnt = hdev->acl_cnt; unsigned int cnt = hdev->acl_cnt;
...@@ -4271,6 +4328,10 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) ...@@ -4271,6 +4328,10 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
hdev->acl_cnt--; hdev->acl_cnt--;
chan->sent++; chan->sent++;
chan->conn->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) ...@@ -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, &quote))) {
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,
&quote))) {
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) static void hci_sched_le(struct hci_dev *hdev)
{ {
struct hci_chan *chan; struct hci_chan *chan;
...@@ -4437,6 +4450,10 @@ static void hci_sched_le(struct hci_dev *hdev) ...@@ -4437,6 +4450,10 @@ static void hci_sched_le(struct hci_dev *hdev)
cnt--; cnt--;
chan->sent++; chan->sent++;
chan->conn->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) ...@@ -4459,9 +4476,9 @@ static void hci_tx_work(struct work_struct *work)
if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
/* Schedule queues and send stuff to HCI driver */ /* Schedule queues and send stuff to HCI driver */
hci_sched_acl(hdev);
hci_sched_sco(hdev); hci_sched_sco(hdev);
hci_sched_esco(hdev); hci_sched_esco(hdev);
hci_sched_acl(hdev);
hci_sched_le(hdev); hci_sched_le(hdev);
} }
......
...@@ -1075,6 +1075,50 @@ DEFINE_SIMPLE_ATTRIBUTE(auth_payload_timeout_fops, ...@@ -1075,6 +1075,50 @@ DEFINE_SIMPLE_ATTRIBUTE(auth_payload_timeout_fops,
auth_payload_timeout_get, auth_payload_timeout_get,
auth_payload_timeout_set, "%llu\n"); 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, DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter,
HCI_QUIRK_STRICT_DUPLICATE_FILTER); HCI_QUIRK_STRICT_DUPLICATE_FILTER);
DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery, DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery,
...@@ -1134,6 +1178,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev) ...@@ -1134,6 +1178,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev)
&max_key_size_fops); &max_key_size_fops);
debugfs_create_file("auth_payload_timeout", 0644, hdev->debugfs, hdev, debugfs_create_file("auth_payload_timeout", 0644, hdev->debugfs, hdev,
&auth_payload_timeout_fops); &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, debugfs_create_file("quirk_strict_duplicate_filter", 0644,
hdev->debugfs, hdev, hdev->debugfs, hdev,
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "a2mp.h" #include "a2mp.h"
#include "amp.h" #include "amp.h"
#include "smp.h" #include "smp.h"
#include "msft.h"
#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"
...@@ -746,6 +747,23 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -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); 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, static void hci_cc_read_page_scan_activity(struct hci_dev *hdev,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -2607,8 +2625,16 @@ static void hci_conn_complete_evt(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) { if (ev->status) {
hci_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
hci_conn_del(conn); 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); hci_connect_cfm(conn, ev->status);
}
unlock: unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
...@@ -3334,6 +3360,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb, ...@@ -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); hci_cc_read_bd_addr(hdev, skb);
break; break;
case HCI_OP_READ_LOCAL_PAIRING_OPTS:
hci_cc_read_local_pairing_opts(hdev, skb);
break;
case HCI_OP_READ_PAGE_SCAN_ACTIVITY: case HCI_OP_READ_PAGE_SCAN_ACTIVITY:
hci_cc_read_page_scan_activity(hdev, skb); hci_cc_read_page_scan_activity(hdev, skb);
break; break;
...@@ -4307,6 +4337,19 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, ...@@ -4307,6 +4337,19 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
break; 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); hci_connect_cfm(conn, ev->status);
if (ev->status) if (ev->status)
hci_conn_del(conn); hci_conn_del(conn);
...@@ -5269,7 +5312,7 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev, ...@@ -5269,7 +5312,7 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
case HCI_AUTO_CONN_ALWAYS: case HCI_AUTO_CONN_ALWAYS:
/* Devices advertising with ADV_IND or ADV_DIRECT_IND /* Devices advertising with ADV_IND or ADV_DIRECT_IND
* are triggering a connection attempt. This means * 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 * accepted and also outgoing connections to slave
* devices are established when found. * devices are established when found.
*/ */
...@@ -5353,7 +5396,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, ...@@ -5353,7 +5396,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
/* Adjust for actual length */ /* Adjust for actual length */
if (len != real_len) { 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; len = real_len;
} }
...@@ -6145,6 +6189,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -6145,6 +6189,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_num_comp_blocks_evt(hdev, skb); hci_num_comp_blocks_evt(hdev, skb);
break; break;
case HCI_EV_VENDOR:
msft_vendor_evt(hdev, skb);
break;
default: default:
BT_DBG("%s event 0x%2.2x", hdev->name, event); BT_DBG("%s event 0x%2.2x", hdev->name, event);
break; break;
......
...@@ -2723,6 +2723,8 @@ static int active_scan(struct hci_request *req, unsigned long opt) ...@@ -2723,6 +2723,8 @@ static int active_scan(struct hci_request *req, unsigned long opt)
uint16_t interval = opt; uint16_t interval = opt;
struct hci_dev *hdev = req->hdev; struct hci_dev *hdev = req->hdev;
u8 own_addr_type; u8 own_addr_type;
/* White list is not used for discovery */
u8 filter_policy = 0x00;
int err; int err;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
...@@ -2744,7 +2746,7 @@ static int active_scan(struct hci_request *req, unsigned long opt) ...@@ -2744,7 +2746,7 @@ static int active_scan(struct hci_request *req, unsigned long opt)
own_addr_type = ADDR_LE_DEV_PUBLIC; own_addr_type = ADDR_LE_DEV_PUBLIC;
hci_req_start_scan(req, LE_SCAN_ACTIVE, interval, DISCOV_LE_SCAN_WIN, hci_req_start_scan(req, LE_SCAN_ACTIVE, interval, DISCOV_LE_SCAN_WIN,
own_addr_type, 0); own_addr_type, filter_policy);
return 0; return 0;
} }
......
...@@ -395,6 +395,24 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, ...@@ -395,6 +395,24 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
return sizeof(struct sockaddr_l2); 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, static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
char __user *optval, int __user *optlen) char __user *optval, int __user *optlen)
{ {
...@@ -424,6 +442,20 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, ...@@ -424,6 +442,20 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
break; 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)); memset(&opts, 0, sizeof(opts));
opts.imtu = chan->imtu; opts.imtu = chan->imtu;
opts.omtu = chan->omtu; opts.omtu = chan->omtu;
...@@ -508,7 +540,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ...@@ -508,7 +540,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
struct bt_security sec; struct bt_security sec;
struct bt_power pwr; struct bt_power pwr;
u32 phys; u32 phys;
int len, err = 0; int len, mode, err = 0;
BT_DBG("sk %p", sk); BT_DBG("sk %p", sk);
...@@ -624,6 +656,27 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ...@@ -624,6 +656,27 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
err = -EFAULT; err = -EFAULT;
break; 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: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
...@@ -698,10 +751,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, ...@@ -698,10 +751,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
break; break;
} }
chan->mode = opts.mode; /* Only BR/EDR modes are supported here */
switch (chan->mode) { switch (opts.mode) {
case L2CAP_MODE_LE_FLOWCTL:
break;
case L2CAP_MODE_BASIC: case L2CAP_MODE_BASIC:
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
break; break;
...@@ -715,6 +766,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, ...@@ -715,6 +766,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
break; break;
} }
if (err < 0)
break;
chan->mode = opts.mode;
BT_DBG("mode 0x%2.2x", chan->mode); BT_DBG("mode 0x%2.2x", chan->mode);
chan->imtu = opts.imtu; chan->imtu = opts.imtu;
...@@ -763,6 +819,45 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, ...@@ -763,6 +819,45 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
return err; 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, static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen) char __user *optval, unsigned int optlen)
{ {
...@@ -968,6 +1063,39 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ...@@ -968,6 +1063,39 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break; 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: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
#include "mgmt_util.h" #include "mgmt_util.h"
#define MGMT_VERSION 1 #define MGMT_VERSION 1
#define MGMT_REVISION 16 #define MGMT_REVISION 17
static const u16 mgmt_commands[] = { static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST, MGMT_OP_READ_INDEX_LIST,
...@@ -108,6 +108,7 @@ static const u16 mgmt_commands[] = { ...@@ -108,6 +108,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_SET_APPEARANCE, MGMT_OP_SET_APPEARANCE,
MGMT_OP_SET_BLOCKED_KEYS, MGMT_OP_SET_BLOCKED_KEYS,
MGMT_OP_SET_WIDEBAND_SPEECH, MGMT_OP_SET_WIDEBAND_SPEECH,
MGMT_OP_READ_SECURITY_INFO,
}; };
static const u16 mgmt_events[] = { static const u16 mgmt_events[] = {
...@@ -155,6 +156,7 @@ static const u16 mgmt_untrusted_commands[] = { ...@@ -155,6 +156,7 @@ static const u16 mgmt_untrusted_commands[] = {
MGMT_OP_READ_CONFIG_INFO, MGMT_OP_READ_CONFIG_INFO,
MGMT_OP_READ_EXT_INDEX_LIST, MGMT_OP_READ_EXT_INDEX_LIST,
MGMT_OP_READ_EXT_INFO, MGMT_OP_READ_EXT_INFO,
MGMT_OP_READ_SECURITY_INFO,
}; };
static const u16 mgmt_untrusted_events[] = { static const u16 mgmt_untrusted_events[] = {
...@@ -3659,6 +3661,55 @@ static int set_wideband_speech(struct sock *sk, struct hci_dev *hdev, ...@@ -3659,6 +3661,55 @@ static int set_wideband_speech(struct sock *sk, struct hci_dev *hdev,
return err; 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, static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb) u16 opcode, struct sk_buff *skb)
{ {
...@@ -7099,6 +7150,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { ...@@ -7099,6 +7150,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE, { set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE,
HCI_MGMT_VAR_LEN }, HCI_MGMT_VAR_LEN },
{ set_wideband_speech, MGMT_SETTING_SIZE }, { 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) void mgmt_index_added(struct hci_dev *hdev)
......
// 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);
}
// 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, ...@@ -854,7 +854,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
struct l2cap_chan *chan = conn->smp; struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data; struct smp_chan *smp = chan->data;
u32 passkey = 0; u32 passkey = 0;
int ret = 0; int ret;
/* Initialize key for JUST WORKS */ /* Initialize key for JUST WORKS */
memset(smp->tk, 0, sizeof(smp->tk)); memset(smp->tk, 0, sizeof(smp->tk));
...@@ -883,9 +883,16 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, ...@@ -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) hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
smp->method = JUST_WORKS; 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) { 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; return 0;
} }
...@@ -2194,7 +2201,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -2194,7 +2201,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
if (err) if (err)
return SMP_UNSPECIFIED; return SMP_UNSPECIFIED;
if (smp->method == JUST_WORKS || smp->method == REQ_OOB) { if (smp->method == REQ_OOB) {
if (hcon->out) { if (hcon->out) {
sc_dhkey_check(smp); sc_dhkey_check(smp);
SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK); SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
...@@ -2209,6 +2216,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -2209,6 +2216,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
confirm_hint = 0; confirm_hint = 0;
confirm: confirm:
if (smp->method == JUST_WORKS)
confirm_hint = 1;
err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type, err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
hcon->dst_type, passkey, confirm_hint); hcon->dst_type, passkey, confirm_hint);
if (err) if (err)
...@@ -2385,12 +2395,17 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) ...@@ -2385,12 +2395,17 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
authreq |= SMP_AUTH_CT2; authreq |= SMP_AUTH_CT2;
} }
/* Require MITM if IO Capability allows or the security level /* Don't attempt to set MITM if setting is overridden by debugfs
* requires it. * Needed to pass certification test SM/MAS/PKE/BV-01-C
*/ */
if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT || if (!hci_dev_test_flag(hcon->hdev, HCI_FORCE_NO_MITM)) {
hcon->pending_sec_level > BT_SECURITY_MEDIUM) /* Require MITM if IO Capability allows or the security level
authreq |= SMP_AUTH_MITM; * 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) { if (hcon->role == HCI_ROLE_MASTER) {
struct smp_cmd_pairing cp; struct smp_cmd_pairing cp;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment