Commit 8ef0f2c0 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Luiz Augusto von Dentz says:

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

 - btusb: Configure altsetting for HCI_USER_CHANNEL
 - hci_event: Fix enabling passive scanning
 - revert: "hci_core: Fix sleeping function called from invalid context"
 - SCO: fix sco_conn refcounting on sco_conn_ready

* tag 'for-net-2025-03-07' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Revert "Bluetooth: hci_core: Fix sleeping function called from invalid context"
  Bluetooth: hci_event: Fix enabling passive scanning
  Bluetooth: SCO: fix sco_conn refcounting on sco_conn_ready
  Bluetooth: btusb: Configure altsetting for HCI_USER_CHANNEL
====================

Link: https://patch.msgid.link/20250307181854.99433-1-luiz.dentz@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents fc14f9c0 ab6ab707
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -56,6 +56,18 @@ config BT_HCIBTUSB_POLL_SYNC
	  Say Y here to enable USB poll_sync for Bluetooth USB devices by
	  default.

config BT_HCIBTUSB_AUTO_ISOC_ALT
	bool "Automatically adjust alternate setting for Isoc endpoints"
	depends on BT_HCIBTUSB
	default y if CHROME_PLATFORMS
	help
	  Say Y here to automatically adjusting the alternate setting for
	  HCI_USER_CHANNEL whenever a SCO link is established.

	  When enabled, btusb intercepts the HCI_EV_SYNC_CONN_COMPLETE packets
	  and configures isoc endpoint alternate setting automatically when
	  HCI_USER_CHANNEL is in use.

config BT_HCIBTUSB_BCM
	bool "Broadcom protocol support"
	depends on BT_HCIBTUSB
+41 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ static bool force_scofix;
static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
static bool enable_poll_sync = IS_ENABLED(CONFIG_BT_HCIBTUSB_POLL_SYNC);
static bool reset = true;
static bool auto_isoc_alt = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTO_ISOC_ALT);

static struct usb_driver btusb_driver;

@@ -1085,6 +1086,42 @@ static inline void btusb_free_frags(struct btusb_data *data)
	spin_unlock_irqrestore(&data->rxlock, flags);
}

static void btusb_sco_connected(struct btusb_data *data, struct sk_buff *skb)
{
	struct hci_event_hdr *hdr = (void *) skb->data;
	struct hci_ev_sync_conn_complete *ev =
		(void *) skb->data + sizeof(*hdr);
	struct hci_dev *hdev = data->hdev;
	unsigned int notify_air_mode;

	if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT)
		return;

	if (skb->len < sizeof(*hdr) || hdr->evt != HCI_EV_SYNC_CONN_COMPLETE)
		return;

	if (skb->len != sizeof(*hdr) + sizeof(*ev) || ev->status)
		return;

	switch (ev->air_mode) {
	case BT_CODEC_CVSD:
		notify_air_mode = HCI_NOTIFY_ENABLE_SCO_CVSD;
		break;

	case BT_CODEC_TRANSPARENT:
		notify_air_mode = HCI_NOTIFY_ENABLE_SCO_TRANSP;
		break;

	default:
		return;
	}

	bt_dev_info(hdev, "enabling SCO with air mode %u", ev->air_mode);
	data->sco_num = 1;
	data->air_mode = notify_air_mode;
	schedule_work(&data->work);
}

static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
{
	if (data->intr_interval) {
@@ -1092,6 +1129,10 @@ static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
		schedule_delayed_work(&data->rx_work, 0);
	}

	/* Configure altsetting for HCI_USER_CHANNEL on SCO connected */
	if (auto_isoc_alt && hci_dev_test_flag(data->hdev, HCI_USER_CHANNEL))
		btusb_sco_connected(data, skb);

	return data->recv_event(data->hdev, skb);
}

+38 −70
Original line number Diff line number Diff line
@@ -804,6 +804,7 @@ struct hci_conn_params {
extern struct list_head hci_dev_list;
extern struct list_head hci_cb_list;
extern rwlock_t hci_dev_list_lock;
extern struct mutex hci_cb_list_lock;

#define hci_dev_set_flag(hdev, nr)             set_bit((nr), (hdev)->dev_flags)
#define hci_dev_clear_flag(hdev, nr)           clear_bit((nr), (hdev)->dev_flags)
@@ -2010,7 +2011,6 @@ struct hci_cb {

	char *name;

	bool (*match)		(struct hci_conn *conn);
	void (*connect_cfm)	(struct hci_conn *conn, __u8 status);
	void (*disconn_cfm)	(struct hci_conn *conn, __u8 status);
	void (*security_cfm)	(struct hci_conn *conn, __u8 status,
@@ -2019,38 +2019,16 @@ struct hci_cb {
	void (*role_switch_cfm)	(struct hci_conn *conn, __u8 status, __u8 role);
};

static inline void hci_cb_lookup(struct hci_conn *conn, struct list_head *list)
{
	struct hci_cb *cb, *cpy;

	rcu_read_lock();
	list_for_each_entry_rcu(cb, &hci_cb_list, list) {
		if (cb->match && cb->match(conn)) {
			cpy = kmalloc(sizeof(*cpy), GFP_ATOMIC);
			if (!cpy)
				break;

			*cpy = *cb;
			INIT_LIST_HEAD(&cpy->list);
			list_add_rcu(&cpy->list, list);
		}
	}
	rcu_read_unlock();
}

static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
{
	struct list_head list;
	struct hci_cb *cb, *tmp;
	struct hci_cb *cb;

	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);

	list_for_each_entry_safe(cb, tmp, &list, list) {
	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
		if (cb->connect_cfm)
			cb->connect_cfm(conn, status);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);

	if (conn->connect_cfm_cb)
		conn->connect_cfm_cb(conn, status);
@@ -2058,43 +2036,22 @@ static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)

static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
{
	struct list_head list;
	struct hci_cb *cb, *tmp;

	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);
	struct hci_cb *cb;

	list_for_each_entry_safe(cb, tmp, &list, list) {
	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
		if (cb->disconn_cfm)
			cb->disconn_cfm(conn, reason);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);

	if (conn->disconn_cfm_cb)
		conn->disconn_cfm_cb(conn, reason);
}

static inline void hci_security_cfm(struct hci_conn *conn, __u8 status,
				    __u8 encrypt)
{
	struct list_head list;
	struct hci_cb *cb, *tmp;

	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);

	list_for_each_entry_safe(cb, tmp, &list, list) {
		if (cb->security_cfm)
			cb->security_cfm(conn, status, encrypt);
		kfree(cb);
	}

	if (conn->security_cfm_cb)
		conn->security_cfm_cb(conn, status);
}

static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
{
	struct hci_cb *cb;
	__u8 encrypt;

	if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
@@ -2102,11 +2059,20 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)

	encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;

	hci_security_cfm(conn, status, encrypt);
	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
		if (cb->security_cfm)
			cb->security_cfm(conn, status, encrypt);
	}
	mutex_unlock(&hci_cb_list_lock);

	if (conn->security_cfm_cb)
		conn->security_cfm_cb(conn, status);
}

static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
{
	struct hci_cb *cb;
	__u8 encrypt;

	if (conn->state == BT_CONFIG) {
@@ -2133,38 +2099,40 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
			conn->sec_level = conn->pending_sec_level;
	}

	hci_security_cfm(conn, status, encrypt);
	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
		if (cb->security_cfm)
			cb->security_cfm(conn, status, encrypt);
	}
	mutex_unlock(&hci_cb_list_lock);

	if (conn->security_cfm_cb)
		conn->security_cfm_cb(conn, status);
}

static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
{
	struct list_head list;
	struct hci_cb *cb, *tmp;

	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);
	struct hci_cb *cb;

	list_for_each_entry_safe(cb, tmp, &list, list) {
	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
		if (cb->key_change_cfm)
			cb->key_change_cfm(conn, status);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);
}

static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
								__u8 role)
{
	struct list_head list;
	struct hci_cb *cb, *tmp;

	INIT_LIST_HEAD(&list);
	hci_cb_lookup(conn, &list);
	struct hci_cb *cb;

	list_for_each_entry_safe(cb, tmp, &list, list) {
	mutex_lock(&hci_cb_list_lock);
	list_for_each_entry(cb, &hci_cb_list, list) {
		if (cb->role_switch_cfm)
			cb->role_switch_cfm(conn, status, role);
		kfree(cb);
	}
	mutex_unlock(&hci_cb_list_lock);
}

static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
+7 −3
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ DEFINE_RWLOCK(hci_dev_list_lock);

/* HCI callback list */
LIST_HEAD(hci_cb_list);
DEFINE_MUTEX(hci_cb_list_lock);

/* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida);
@@ -2972,7 +2973,9 @@ int hci_register_cb(struct hci_cb *cb)
{
	BT_DBG("%p name %s", cb, cb->name);

	list_add_tail_rcu(&cb->list, &hci_cb_list);
	mutex_lock(&hci_cb_list_lock);
	list_add_tail(&cb->list, &hci_cb_list);
	mutex_unlock(&hci_cb_list_lock);

	return 0;
}
@@ -2982,8 +2985,9 @@ int hci_unregister_cb(struct hci_cb *cb)
{
	BT_DBG("%p name %s", cb, cb->name);

	list_del_rcu(&cb->list);
	synchronize_rcu();
	mutex_lock(&hci_cb_list_lock);
	list_del(&cb->list);
	mutex_unlock(&hci_cb_list_lock);

	return 0;
}
+22 −15
Original line number Diff line number Diff line
@@ -3391,7 +3391,12 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
		hci_update_scan(hdev);
	}

	params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
	/* Re-enable passive scanning if disconnected device is marked
	 * as auto-connectable.
	 */
	if (conn->type == LE_LINK) {
		params = hci_conn_params_lookup(hdev, &conn->dst,
						conn->dst_type);
		if (params) {
			switch (params->auto_connect) {
			case HCI_AUTO_CONN_LINK_LOSS:
@@ -3402,7 +3407,8 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
			case HCI_AUTO_CONN_DIRECT:
			case HCI_AUTO_CONN_ALWAYS:
				hci_pend_le_list_del_init(params);
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
				hci_pend_le_list_add(params,
						     &hdev->pend_le_conns);
				hci_update_passive_scan(hdev);
				break;

@@ -3410,6 +3416,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
				break;
			}
		}
	}

	hci_disconn_cfm(conn, ev->reason);

Loading