Commit f2d85904 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'for-net-2024-05-03' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - mediatek: mt8183-pico6: Fix bluetooth node
 - sco: Fix use-after-free bugs caused by sco_sock_timeout
 - l2cap: fix null-ptr-deref in l2cap_chan_timeout
 - qca: Various fixes
 - l2cap: Fix slab-use-after-free in l2cap_connect()
 - msft: fix slab-use-after-free in msft_do_close()
 - HCI: Fix potential null-ptr-deref

* tag 'for-net-2024-05-03' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: qca: fix firmware check error path
  Bluetooth: l2cap: fix null-ptr-deref in l2cap_chan_timeout
  Bluetooth: HCI: Fix potential null-ptr-deref
  arm64: dts: mediatek: mt8183-pico6: Fix bluetooth node
  Bluetooth: qca: fix info leak when fetching board id
  Bluetooth: qca: fix info leak when fetching fw build id
  Bluetooth: qca: generalise device address check
  Bluetooth: qca: fix NVM configuration parsing
  Bluetooth: qca: add missing firmware sanity checks
  Bluetooth: msft: fix slab-use-after-free in msft_do_close()
  Bluetooth: L2CAP: Fix slab-use-after-free in l2cap_connect()
  Bluetooth: qca: fix wcn3991 device address check
  Bluetooth: Fix use-after-free bugs caused by sco_sock_timeout
====================

Link: https://lore.kernel.org/r/20240503171933.3851244-1-luiz.dentz@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents e0863634 40d442f9
...@@ -82,7 +82,8 @@ pins-clk { ...@@ -82,7 +82,8 @@ pins-clk {
}; };
&mmc1 { &mmc1 {
bt_reset: bt-reset { bluetooth@2 {
reg = <2>;
compatible = "mediatek,mt7921s-bluetooth"; compatible = "mediatek,mt7921s-bluetooth";
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&bt_pins_reset>; pinctrl-0 = <&bt_pins_reset>;
......
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
#define VERSION "0.1" #define VERSION "0.1"
#define QCA_BDADDR_DEFAULT (&(bdaddr_t) {{ 0xad, 0x5a, 0x00, 0x00, 0x00, 0x00 }})
int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver, int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
enum qca_btsoc_type soc_type) enum qca_btsoc_type soc_type)
{ {
...@@ -101,7 +99,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev) ...@@ -101,7 +99,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct edl_event_hdr *edl; struct edl_event_hdr *edl;
char cmd, build_label[QCA_FW_BUILD_VER_LEN]; char *build_label;
char cmd;
int build_lbl_len, err = 0; int build_lbl_len, err = 0;
bt_dev_dbg(hdev, "QCA read fw build info"); bt_dev_dbg(hdev, "QCA read fw build info");
...@@ -116,6 +115,11 @@ static int qca_read_fw_build_info(struct hci_dev *hdev) ...@@ -116,6 +115,11 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
return err; return err;
} }
if (skb->len < sizeof(*edl)) {
err = -EILSEQ;
goto out;
}
edl = (struct edl_event_hdr *)(skb->data); edl = (struct edl_event_hdr *)(skb->data);
if (!edl) { if (!edl) {
bt_dev_err(hdev, "QCA read fw build info with no header"); bt_dev_err(hdev, "QCA read fw build info with no header");
...@@ -131,14 +135,25 @@ static int qca_read_fw_build_info(struct hci_dev *hdev) ...@@ -131,14 +135,25 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
goto out; goto out;
} }
if (skb->len < sizeof(*edl) + 1) {
err = -EILSEQ;
goto out;
}
build_lbl_len = edl->data[0]; build_lbl_len = edl->data[0];
if (build_lbl_len <= QCA_FW_BUILD_VER_LEN - 1) {
memcpy(build_label, edl->data + 1, build_lbl_len); if (skb->len < sizeof(*edl) + 1 + build_lbl_len) {
*(build_label + build_lbl_len) = '\0'; err = -EILSEQ;
goto out;
} }
build_label = kstrndup(&edl->data[1], build_lbl_len, GFP_KERNEL);
if (!build_label)
goto out;
hci_set_fw_info(hdev, "%s", build_label); hci_set_fw_info(hdev, "%s", build_label);
kfree(build_label);
out: out:
kfree_skb(skb); kfree_skb(skb);
return err; return err;
...@@ -237,6 +252,11 @@ static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid) ...@@ -237,6 +252,11 @@ static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
goto out; goto out;
} }
if (skb->len < 3) {
err = -EILSEQ;
goto out;
}
*bid = (edl->data[1] << 8) + edl->data[2]; *bid = (edl->data[1] << 8) + edl->data[2];
bt_dev_dbg(hdev, "%s: bid = %x", __func__, *bid); bt_dev_dbg(hdev, "%s: bid = %x", __func__, *bid);
...@@ -267,9 +287,10 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev) ...@@ -267,9 +287,10 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
} }
EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd); EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);
static void qca_tlv_check_data(struct hci_dev *hdev, static int qca_tlv_check_data(struct hci_dev *hdev,
struct qca_fw_config *config, struct qca_fw_config *config,
u8 *fw_data, enum qca_btsoc_type soc_type) u8 *fw_data, size_t fw_size,
enum qca_btsoc_type soc_type)
{ {
const u8 *data; const u8 *data;
u32 type_len; u32 type_len;
...@@ -279,12 +300,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev, ...@@ -279,12 +300,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
struct tlv_type_patch *tlv_patch; struct tlv_type_patch *tlv_patch;
struct tlv_type_nvm *tlv_nvm; struct tlv_type_nvm *tlv_nvm;
uint8_t nvm_baud_rate = config->user_baud_rate; uint8_t nvm_baud_rate = config->user_baud_rate;
u8 type;
config->dnld_mode = QCA_SKIP_EVT_NONE; config->dnld_mode = QCA_SKIP_EVT_NONE;
config->dnld_type = QCA_SKIP_EVT_NONE; config->dnld_type = QCA_SKIP_EVT_NONE;
switch (config->type) { switch (config->type) {
case ELF_TYPE_PATCH: case ELF_TYPE_PATCH:
if (fw_size < 7)
return -EINVAL;
config->dnld_mode = QCA_SKIP_EVT_VSE_CC; config->dnld_mode = QCA_SKIP_EVT_VSE_CC;
config->dnld_type = QCA_SKIP_EVT_VSE_CC; config->dnld_type = QCA_SKIP_EVT_VSE_CC;
...@@ -293,6 +318,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev, ...@@ -293,6 +318,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
bt_dev_dbg(hdev, "File version : 0x%x", fw_data[6]); bt_dev_dbg(hdev, "File version : 0x%x", fw_data[6]);
break; break;
case TLV_TYPE_PATCH: case TLV_TYPE_PATCH:
if (fw_size < sizeof(struct tlv_type_hdr) + sizeof(struct tlv_type_patch))
return -EINVAL;
tlv = (struct tlv_type_hdr *)fw_data; tlv = (struct tlv_type_hdr *)fw_data;
type_len = le32_to_cpu(tlv->type_len); type_len = le32_to_cpu(tlv->type_len);
tlv_patch = (struct tlv_type_patch *)tlv->data; tlv_patch = (struct tlv_type_patch *)tlv->data;
...@@ -332,25 +360,64 @@ static void qca_tlv_check_data(struct hci_dev *hdev, ...@@ -332,25 +360,64 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break; break;
case TLV_TYPE_NVM: case TLV_TYPE_NVM:
if (fw_size < sizeof(struct tlv_type_hdr))
return -EINVAL;
tlv = (struct tlv_type_hdr *)fw_data; tlv = (struct tlv_type_hdr *)fw_data;
type_len = le32_to_cpu(tlv->type_len); type_len = le32_to_cpu(tlv->type_len);
length = (type_len >> 8) & 0x00ffffff; length = type_len >> 8;
type = type_len & 0xff;
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff); /* Some NVM files have more than one set of tags, only parse
* the first set when it has type 2 for now. When there is
* more than one set there is an enclosing header of type 4.
*/
if (type == 4) {
if (fw_size < 2 * sizeof(struct tlv_type_hdr))
return -EINVAL;
tlv++;
type_len = le32_to_cpu(tlv->type_len);
length = type_len >> 8;
type = type_len & 0xff;
}
BT_DBG("TLV Type\t\t : 0x%x", type);
BT_DBG("Length\t\t : %d bytes", length); BT_DBG("Length\t\t : %d bytes", length);
if (type != 2)
break;
if (fw_size < length + (tlv->data - fw_data))
return -EINVAL;
idx = 0; idx = 0;
data = tlv->data; data = tlv->data;
while (idx < length) { while (idx < length - sizeof(struct tlv_type_nvm)) {
tlv_nvm = (struct tlv_type_nvm *)(data + idx); tlv_nvm = (struct tlv_type_nvm *)(data + idx);
tag_id = le16_to_cpu(tlv_nvm->tag_id); tag_id = le16_to_cpu(tlv_nvm->tag_id);
tag_len = le16_to_cpu(tlv_nvm->tag_len); tag_len = le16_to_cpu(tlv_nvm->tag_len);
if (length < idx + sizeof(struct tlv_type_nvm) + tag_len)
return -EINVAL;
/* Update NVM tags as needed */ /* Update NVM tags as needed */
switch (tag_id) { switch (tag_id) {
case EDL_TAG_ID_BD_ADDR:
if (tag_len != sizeof(bdaddr_t))
return -EINVAL;
memcpy(&config->bdaddr, tlv_nvm->data, sizeof(bdaddr_t));
break;
case EDL_TAG_ID_HCI: case EDL_TAG_ID_HCI:
if (tag_len < 3)
return -EINVAL;
/* HCI transport layer parameters /* HCI transport layer parameters
* enabling software inband sleep * enabling software inband sleep
* onto controller side. * onto controller side.
...@@ -366,6 +433,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev, ...@@ -366,6 +433,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break; break;
case EDL_TAG_ID_DEEP_SLEEP: case EDL_TAG_ID_DEEP_SLEEP:
if (tag_len < 1)
return -EINVAL;
/* Sleep enable mask /* Sleep enable mask
* enabling deep sleep feature on controller. * enabling deep sleep feature on controller.
*/ */
...@@ -374,14 +444,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev, ...@@ -374,14 +444,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break; break;
} }
idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len); idx += sizeof(struct tlv_type_nvm) + tag_len;
} }
break; break;
default: default:
BT_ERR("Unknown TLV type %d", config->type); BT_ERR("Unknown TLV type %d", config->type);
break; return -EINVAL;
} }
return 0;
} }
static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size, static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
...@@ -531,7 +603,9 @@ static int qca_download_firmware(struct hci_dev *hdev, ...@@ -531,7 +603,9 @@ static int qca_download_firmware(struct hci_dev *hdev,
memcpy(data, fw->data, size); memcpy(data, fw->data, size);
release_firmware(fw); release_firmware(fw);
qca_tlv_check_data(hdev, config, data, soc_type); ret = qca_tlv_check_data(hdev, config, data, size, soc_type);
if (ret)
goto out;
segment = data; segment = data;
remain = size; remain = size;
...@@ -614,7 +688,7 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) ...@@ -614,7 +688,7 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
} }
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome); EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
static int qca_check_bdaddr(struct hci_dev *hdev) static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *config)
{ {
struct hci_rp_read_bd_addr *bda; struct hci_rp_read_bd_addr *bda;
struct sk_buff *skb; struct sk_buff *skb;
...@@ -638,7 +712,7 @@ static int qca_check_bdaddr(struct hci_dev *hdev) ...@@ -638,7 +712,7 @@ static int qca_check_bdaddr(struct hci_dev *hdev)
} }
bda = (struct hci_rp_read_bd_addr *)skb->data; bda = (struct hci_rp_read_bd_addr *)skb->data;
if (!bacmp(&bda->bdaddr, QCA_BDADDR_DEFAULT)) if (!bacmp(&bda->bdaddr, &config->bdaddr))
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
kfree_skb(skb); kfree_skb(skb);
...@@ -667,7 +741,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, ...@@ -667,7 +741,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, struct qca_btsoc_version ver, enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
const char *firmware_name) const char *firmware_name)
{ {
struct qca_fw_config config; struct qca_fw_config config = {};
int err; int err;
u8 rom_ver = 0; u8 rom_ver = 0;
u32 soc_ver; u32 soc_ver;
...@@ -852,7 +926,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, ...@@ -852,7 +926,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
break; break;
} }
err = qca_check_bdaddr(hdev); err = qca_check_bdaddr(hdev, &config);
if (err) if (err)
return err; return err;
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define EDL_PATCH_CONFIG_RES_EVT (0x00) #define EDL_PATCH_CONFIG_RES_EVT (0x00)
#define QCA_DISABLE_LOGGING_SUB_OP (0x14) #define QCA_DISABLE_LOGGING_SUB_OP (0x14)
#define EDL_TAG_ID_BD_ADDR 2
#define EDL_TAG_ID_HCI (17) #define EDL_TAG_ID_HCI (17)
#define EDL_TAG_ID_DEEP_SLEEP (27) #define EDL_TAG_ID_DEEP_SLEEP (27)
...@@ -47,7 +48,6 @@ ...@@ -47,7 +48,6 @@
#define get_soc_ver(soc_id, rom_ver) \ #define get_soc_ver(soc_id, rom_ver) \
((le32_to_cpu(soc_id) << 16) | (le16_to_cpu(rom_ver))) ((le32_to_cpu(soc_id) << 16) | (le16_to_cpu(rom_ver)))
#define QCA_FW_BUILD_VER_LEN 255
#define QCA_HSP_GF_SOC_ID 0x1200 #define QCA_HSP_GF_SOC_ID 0x1200
#define QCA_HSP_GF_SOC_MASK 0x0000ff00 #define QCA_HSP_GF_SOC_MASK 0x0000ff00
...@@ -94,6 +94,7 @@ struct qca_fw_config { ...@@ -94,6 +94,7 @@ struct qca_fw_config {
uint8_t user_baud_rate; uint8_t user_baud_rate;
enum qca_tlv_dnld_mode dnld_mode; enum qca_tlv_dnld_mode dnld_mode;
enum qca_tlv_dnld_mode dnld_type; enum qca_tlv_dnld_mode dnld_type;
bdaddr_t bdaddr;
}; };
struct edl_event_hdr { struct edl_event_hdr {
......
...@@ -2768,8 +2768,6 @@ void hci_unregister_dev(struct hci_dev *hdev) ...@@ -2768,8 +2768,6 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_unregister_suspend_notifier(hdev); hci_unregister_suspend_notifier(hdev);
msft_unregister(hdev);
hci_dev_do_close(hdev); hci_dev_do_close(hdev);
if (!test_bit(HCI_INIT, &hdev->flags) && if (!test_bit(HCI_INIT, &hdev->flags) &&
...@@ -2823,6 +2821,7 @@ void hci_release_dev(struct hci_dev *hdev) ...@@ -2823,6 +2821,7 @@ void hci_release_dev(struct hci_dev *hdev)
hci_discovery_filter_clear(hdev); hci_discovery_filter_clear(hdev);
hci_blocked_keys_clear(hdev); hci_blocked_keys_clear(hdev);
hci_codec_list_clear(&hdev->local_codecs); hci_codec_list_clear(&hdev->local_codecs);
msft_release(hdev);
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
ida_destroy(&hdev->unset_handle_ida); ida_destroy(&hdev->unset_handle_ida);
......
...@@ -7037,6 +7037,8 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data, ...@@ -7037,6 +7037,8 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
u16 handle = le16_to_cpu(ev->bis[i]); u16 handle = le16_to_cpu(ev->bis[i]);
bis = hci_conn_hash_lookup_handle(hdev, handle); bis = hci_conn_hash_lookup_handle(hdev, handle);
if (!bis)
continue;
set_bit(HCI_CONN_BIG_SYNC_FAILED, &bis->flags); set_bit(HCI_CONN_BIG_SYNC_FAILED, &bis->flags);
hci_connect_cfm(bis, ev->status); hci_connect_cfm(bis, ev->status);
......
...@@ -415,6 +415,9 @@ static void l2cap_chan_timeout(struct work_struct *work) ...@@ -415,6 +415,9 @@ static void l2cap_chan_timeout(struct work_struct *work)
BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
if (!conn)
return;
mutex_lock(&conn->chan_lock); mutex_lock(&conn->chan_lock);
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling /* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
* this work. No need to call l2cap_chan_hold(chan) here again. * this work. No need to call l2cap_chan_hold(chan) here again.
...@@ -3902,13 +3905,12 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, ...@@ -3902,13 +3905,12 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
return 0; return 0;
} }
static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
struct l2cap_cmd_hdr *cmd, u8 *data, u8 rsp_code, u8 amp_id)
u8 *data, u8 rsp_code, u8 amp_id)
{ {
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp; struct l2cap_conn_rsp rsp;
struct l2cap_chan *chan = NULL, *pchan; struct l2cap_chan *chan = NULL, *pchan = NULL;
int result, status = L2CAP_CS_NO_INFO; int result, status = L2CAP_CS_NO_INFO;
u16 dcid = 0, scid = __le16_to_cpu(req->scid); u16 dcid = 0, scid = __le16_to_cpu(req->scid);
...@@ -3921,7 +3923,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, ...@@ -3921,7 +3923,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
&conn->hcon->dst, ACL_LINK); &conn->hcon->dst, ACL_LINK);
if (!pchan) { if (!pchan) {
result = L2CAP_CR_BAD_PSM; result = L2CAP_CR_BAD_PSM;
goto sendresp; goto response;
} }
mutex_lock(&conn->chan_lock); mutex_lock(&conn->chan_lock);
...@@ -4008,17 +4010,15 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, ...@@ -4008,17 +4010,15 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
} }
response: response:
l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
l2cap_chan_put(pchan);
sendresp:
rsp.scid = cpu_to_le16(scid); rsp.scid = cpu_to_le16(scid);
rsp.dcid = cpu_to_le16(dcid); rsp.dcid = cpu_to_le16(dcid);
rsp.result = cpu_to_le16(result); rsp.result = cpu_to_le16(result);
rsp.status = cpu_to_le16(status); rsp.status = cpu_to_le16(status);
l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp); l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp);
if (!pchan)
return;
if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) { if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
struct l2cap_info_req info; struct l2cap_info_req info;
info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
...@@ -4041,7 +4041,9 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, ...@@ -4041,7 +4041,9 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
chan->num_conf_req++; chan->num_conf_req++;
} }
return chan; l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
l2cap_chan_put(pchan);
} }
static int l2cap_connect_req(struct l2cap_conn *conn, static int l2cap_connect_req(struct l2cap_conn *conn,
......
...@@ -769,7 +769,7 @@ void msft_register(struct hci_dev *hdev) ...@@ -769,7 +769,7 @@ void msft_register(struct hci_dev *hdev)
mutex_init(&msft->filter_lock); mutex_init(&msft->filter_lock);
} }
void msft_unregister(struct hci_dev *hdev) void msft_release(struct hci_dev *hdev)
{ {
struct msft_data *msft = hdev->msft_data; struct msft_data *msft = hdev->msft_data;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
bool msft_monitor_supported(struct hci_dev *hdev); bool msft_monitor_supported(struct hci_dev *hdev);
void msft_register(struct hci_dev *hdev); void msft_register(struct hci_dev *hdev);
void msft_unregister(struct hci_dev *hdev); void msft_release(struct hci_dev *hdev);
void msft_do_open(struct hci_dev *hdev); void msft_do_open(struct hci_dev *hdev);
void msft_do_close(struct hci_dev *hdev); void msft_do_close(struct hci_dev *hdev);
void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb); void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb);
...@@ -35,7 +35,7 @@ static inline bool msft_monitor_supported(struct hci_dev *hdev) ...@@ -35,7 +35,7 @@ static inline bool msft_monitor_supported(struct hci_dev *hdev)
} }
static inline void msft_register(struct hci_dev *hdev) {} static inline void msft_register(struct hci_dev *hdev) {}
static inline void msft_unregister(struct hci_dev *hdev) {} static inline void msft_release(struct hci_dev *hdev) {}
static inline void msft_do_open(struct hci_dev *hdev) {} static inline void msft_do_open(struct hci_dev *hdev) {}
static inline void msft_do_close(struct hci_dev *hdev) {} static inline void msft_do_close(struct hci_dev *hdev) {}
static inline void msft_vendor_evt(struct hci_dev *hdev, void *data, static inline void msft_vendor_evt(struct hci_dev *hdev, void *data,
......
...@@ -83,6 +83,10 @@ static void sco_sock_timeout(struct work_struct *work) ...@@ -83,6 +83,10 @@ static void sco_sock_timeout(struct work_struct *work)
struct sock *sk; struct sock *sk;
sco_conn_lock(conn); sco_conn_lock(conn);
if (!conn->hcon) {
sco_conn_unlock(conn);
return;
}
sk = conn->sk; sk = conn->sk;
if (sk) if (sk)
sock_hold(sk); sock_hold(sk);
......
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