All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Add reset hci through mgmt experimental feature
@ 2021-02-09 13:36 Hui Wang
  2021-02-09 13:36 ` [PATCH 1/3] Bluetooth: btusb: Add reset_hci handler Hui Wang
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Hui Wang @ 2021-02-09 13:36 UTC (permalink / raw)
  To: linux-bluetooth, marcel, johan.hedberg, luiz.dentz

We have an annoying issue with Intel Bluetooth adapter, that is after
we update a firmware in the filessystem, and reboot the machine, the
driver will not load the new firmware since the adapter is already in
the operational mode instead of bootloader mode. we need to poweroff
the machine and cold boot it, and even on some desktops, we need to
wait for 10+ seconds between poweroff and the cold boot.

I wrote a patch for this issue last year, Marcel suggested that "we
should do it in a more generic way that resets the controller and puts
it into boot loader support if support. We can use the experimental
mgmt setting for this".

Here I wrote a new patch set according to Marcel's suggestion, a
generic reset hci method for all usb bluetooth adapters, it will reset
the adapter on the usb bus (similar to unbind the device and rebind
the device), and will call disconnect() and probe() in the btusb.c;
for Intel adapters, there is a specific method to reset the adapter
and put it into the bootloader mode.

This is the log I run reset_hci on machines with Qualcomm and CSR
adapters (generic reset hci method):
$ sudo ./btmgmt -i 0 exp-reset-hci
Reset hci feature successfully set
$ dmesg
[  110.116868] Bluetooth: hci0: urb 00000000e81d049e failed to resubmit (2)
[  110.232865] usb 3-6: reset full-speed USB device number 3 using xhci_hcd
[  110.358814] usb 3-6: device firmware changed
[  110.358949] usb 3-6: USB disconnect, device number 3
[  110.476527] usb 3-6: new full-speed USB device number 4 using xhci_hcd
[  110.603890] usb 3-6: New USB device found, idVendor=0cf3, idProduct=e005, bcdDevice= 0.01
[  110.603907] usb 3-6: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[  110.662539] usb 3-6: USB disconnect, device number 4
[  110.913411] usb 3-6: new full-speed USB device number 5 using xhci_hcd
[...]
[  116.186419] usb 3-6: New USB device found, idVendor=0cf3, idProduct=e005, bcdDevice= 0.02
[  116.186434] usb 3-6: New USB device strings: Mfr=0, Product=0, SerialNumber=0



This is the log I run reset_hci on the machien with Intel adaper, this
adapter has specific method to reset to bootloader mode:
$ sudo ./btmgmt -i 0 exp-reset-hci
Reset hci feature successfully set
$ dmesg
[  100.843848] usb 3-10: USB disconnect, device number 4
[  100.844639] Bluetooth: hci0: FW download error recovery failed (-19)
[  101.252944] usb 3-10: new full-speed USB device number 5 using xhci_hcd
[  101.380862] usb 3-10: New USB device found, idVendor=8087, idProduct=0026, bcdDevice= 0.02
[  101.380872] usb 3-10: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[  101.384448] Bluetooth: hci0: Bootloader revision 0.4 build 0 week 30 2018
[  101.385367] Bluetooth: hci0: Device revision is 2
[  101.385374] Bluetooth: hci0: Secure boot is enabled
[  101.385377] Bluetooth: hci0: OTP lock is enabled
[  101.385380] Bluetooth: hci0: API lock is enabled
[  101.385383] Bluetooth: hci0: Debug lock is disabled
[  101.385385] Bluetooth: hci0: Minimum firmware build 1 week 10 2014
[  101.385849] Bluetooth: hci0: Found device firmware: intel/ibt-19-0-4.sfi
[  103.206577] Bluetooth: hci0: Waiting for firmware download to complete
[  103.207382] Bluetooth: hci0: Firmware loaded in 1780489 usecs
[  103.207409] Bluetooth: hci0: Waiting for device to boot
[  103.222478] Bluetooth: hci0: Device booted in 14734 usecs
[  103.222595] Bluetooth: hci0: Found Intel DDC parameters: intel/ibt-19-0-4.ddc
[  103.224414] Bluetooth: hci0: Applying Intel DDC parameters completed
[  103.227429] Bluetooth: hci0: Firmware revision 0.0 build 121 week 36 2020


Hui Wang (3):
  Bluetooth: btusb: Add reset_hci handler
  Bluetooth: make experimental features expand easier
  Bluetooth: reset_hci experimental feature

 drivers/bluetooth/btusb.c        |  60 +++++
 include/net/bluetooth/hci_core.h |   1 +
 net/bluetooth/mgmt.c             | 428 +++++++++++++++++++------------
 3 files changed, 327 insertions(+), 162 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/3] Bluetooth: btusb: Add reset_hci handler
  2021-02-09 13:36 [PATCH 0/3] Add reset hci through mgmt experimental feature Hui Wang
@ 2021-02-09 13:36 ` Hui Wang
  2021-02-09 14:13   ` Add reset hci through mgmt experimental feature bluez.test.bot
  2021-02-09 13:36 ` [PATCH 2/3] Bluetooth: make experimental features expand easier Hui Wang
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Hui Wang @ 2021-02-09 13:36 UTC (permalink / raw)
  To: linux-bluetooth, marcel, johan.hedberg, luiz.dentz

After users update the bluetooth firmware, they want the driver to
load the new firmware, with the current driver, users could not make
sure the driver will load the new firmware, on some laptops, they have
to run poweroff and cold boot the machine, on some desktops, they have
to wait for a couple of seconds between poweroff and cold boot, it is
very annoying to users.

Here adding a reset_hci handler, for hci on the USB bus, we provide
a generic method, this could make the device reset over the USB bus,
it will introduce calling disconnect() and probe(), but this doesn't
guarantee the hci enters bootloader mode and load the new firmware.
For Intel_new and Intel_newgen hcis, there is a method to make them
enter bootloader mode and reset over the USB bus, so we use specific
method to overwrite the generic method for those hcis.

If we have more specific methods to make some hcis enter bootloader
mode in future, we could overwrite the generic method for them like
Intel hcis.

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 drivers/bluetooth/btusb.c        | 60 ++++++++++++++++++++++++++++++++
 include/net/bluetooth/hci_core.h |  1 +
 2 files changed, 61 insertions(+)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 20ef9377694f..79ec73e3d321 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -520,6 +520,7 @@ struct btusb_data {
 
 	struct work_struct work;
 	struct work_struct waker;
+	struct delayed_work reset_work;
 
 	struct usb_anchor deferred;
 	struct usb_anchor tx_anchor;
@@ -561,6 +562,8 @@ struct btusb_data {
 
 	int (*setup_on_usb)(struct hci_dev *hdev);
 
+	void (*btusb_reset_hci_to_bootloader)(struct btusb_data *data);
+
 	int oob_wake_irq;   /* irq for out-of-band wake-on-bt */
 	unsigned cmd_timeout_cnt;
 };
@@ -1384,6 +1387,7 @@ static int btusb_close(struct hci_dev *hdev)
 
 	cancel_work_sync(&data->work);
 	cancel_work_sync(&data->waker);
+	cancel_delayed_work(&data->reset_work);
 
 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
@@ -3105,6 +3109,43 @@ static int btusb_shutdown_intel(struct hci_dev *hdev)
 	return 0;
 }
 
+static void btusb_intel_reset_hci_to_bootloader(struct btusb_data *btdata)
+{
+	btintel_reset_to_bootloader(btdata->hdev);
+}
+
+static void btusb_generic_reset_hci(struct btusb_data *btdata)
+{
+	struct usb_device *udev = btdata->udev;
+	struct usb_interface *intf = btdata->intf;
+	int r;
+
+	/* make sure the usb device is not rt suspended */
+	r = usb_autopm_get_interface(intf);
+	if (r < 0)
+		return;
+
+	r = usb_lock_device_for_reset(udev, NULL);
+	if (r < 0) {
+		usb_autopm_put_interface(intf);
+		return;
+	}
+
+	usb_reset_device(udev);
+
+	usb_unlock_device(udev);
+
+	usb_autopm_put_interface(intf);
+}
+
+static void btusb_reset_work(struct work_struct *work)
+{
+	struct btusb_data *btdata = container_of(work, struct btusb_data,
+						 reset_work.work);
+
+	btdata->btusb_reset_hci_to_bootloader(btdata);
+}
+
 static int btusb_shutdown_intel_new(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
@@ -3123,6 +3164,19 @@ static int btusb_shutdown_intel_new(struct hci_dev *hdev)
 	return 0;
 }
 
+static int btusb_reset_controller(struct hci_dev *hdev)
+{
+	struct btusb_data *btdata = hci_get_drvdata(hdev);
+
+	/* reset_work will reset the hci on the USB bus, it will introduce
+	 * calling disconnect() and probe(), so implement it in the process
+	 * context.
+	 */
+	schedule_delayed_work(&btdata->reset_work, 0);
+
+	return 0;
+}
+
 #define FIRMWARE_MT7663		"mediatek/mt7663pr2h.bin"
 #define FIRMWARE_MT7668		"mediatek/mt7668pr2h.bin"
 
@@ -4354,6 +4408,7 @@ static int btusb_probe(struct usb_interface *intf,
 
 	INIT_WORK(&data->work, btusb_work);
 	INIT_WORK(&data->waker, btusb_waker);
+	INIT_DELAYED_WORK(&data->reset_work, btusb_reset_work);
 	init_usb_anchor(&data->deferred);
 	init_usb_anchor(&data->tx_anchor);
 	spin_lock_init(&data->txlock);
@@ -4405,6 +4460,7 @@ static int btusb_probe(struct usb_interface *intf,
 	hdev->send   = btusb_send_frame;
 	hdev->notify = btusb_notify;
 	hdev->prevent_wake = btusb_prevent_wake;
+	hdev->reset_hci = btusb_reset_controller;
 
 #ifdef CONFIG_PM
 	err = btusb_config_oob_wake(hdev);
@@ -4418,6 +4474,8 @@ static int btusb_probe(struct usb_interface *intf,
 			goto out_free_dev;
 	}
 #endif
+	data->btusb_reset_hci_to_bootloader = btusb_generic_reset_hci;
+
 	if (id->driver_info & BTUSB_CW6622)
 		set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
 
@@ -4469,6 +4527,7 @@ static int btusb_probe(struct usb_interface *intf,
 		hdev->set_diag = btintel_set_diag;
 		hdev->set_bdaddr = btintel_set_bdaddr;
 		hdev->cmd_timeout = btusb_intel_cmd_timeout;
+		data->btusb_reset_hci_to_bootloader = btusb_intel_reset_hci_to_bootloader;
 		set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
 		set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
 		set_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks);
@@ -4489,6 +4548,7 @@ static int btusb_probe(struct usb_interface *intf,
 
 		data->recv_event = btusb_recv_event_intel;
 		data->recv_bulk = btusb_recv_bulk_intel;
+		data->btusb_reset_hci_to_bootloader = btusb_intel_reset_hci_to_bootloader;
 		set_bit(BTUSB_BOOTLOADER, &data->flags);
 	}
 
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 677a8c50b2ad..8f706baf160e 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -581,6 +581,7 @@ struct hci_dev {
 	int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
 	void (*cmd_timeout)(struct hci_dev *hdev);
 	bool (*prevent_wake)(struct hci_dev *hdev);
+	int (*reset_hci)(struct hci_dev *hdev);
 };
 
 #define HCI_PHY_HANDLE(handle)	(handle & 0xff)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/3] Bluetooth: make experimental features expand easier
  2021-02-09 13:36 [PATCH 0/3] Add reset hci through mgmt experimental feature Hui Wang
  2021-02-09 13:36 ` [PATCH 1/3] Bluetooth: btusb: Add reset_hci handler Hui Wang
@ 2021-02-09 13:36 ` Hui Wang
  2021-02-09 13:36 ` [PATCH 3/3] Bluetooth: reset_hci experimental feature Hui Wang
  2021-02-09 13:36 ` [Bluez][PATCH] btmgmt: add a new command reset_hci via " Hui Wang
  3 siblings, 0 replies; 7+ messages in thread
From: Hui Wang @ 2021-02-09 13:36 UTC (permalink / raw)
  To: linux-bluetooth, marcel, johan.hedberg, luiz.dentz

So far there are 3 experimental features in total, if users want to
add new features, they need to write code in the function
read_exp_features_info() and set_exp_feature(), this will make these
2 functions larger and larger.

After using the struct exp_feature, users just need to implement
feature specific read and set function and insert an exp_feature entry
into the exp_feature_list[].

This patch converts the current 3 exp features to the exp_feature
entries, no function change for them.

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 net/bluetooth/mgmt.c | 379 ++++++++++++++++++++++++-------------------
 1 file changed, 214 insertions(+), 165 deletions(-)

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index fa0f7a4a1d2f..4ac85e24906b 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3774,12 +3774,94 @@ static int read_controller_cap(struct sock *sk, struct hci_dev *hdev,
 				 rp, sizeof(*rp) + cap_len);
 }
 
+struct exp_feature {
+	const u8 *uuid;
+	void (*read_info)(struct hci_dev *hdev, const u8 *uuid, u16 *idx,
+			  struct mgmt_rp_read_exp_features_info *rp);
+	int (*set_info)(struct sock *sk, struct hci_dev *hdev, const u8 *uuid,
+			u16 data_len, struct mgmt_cp_set_exp_feature *cp);
+};
+
+#define EXP_FEATURE_ENTRY(_uuid, _read_info_func, _set_info_func)	\
+	{ .uuid = (_uuid), .read_info = (_read_info_func), \
+	  .set_info = (_set_info_func) }
+
 #ifdef CONFIG_BT_FEATURE_DEBUG
 /* d4992530-b9ec-469f-ab01-6c481c47da1c */
 static const u8 debug_uuid[16] = {
 	0x1c, 0xda, 0x47, 0x1c, 0x48, 0x6c, 0x01, 0xab,
 	0x9f, 0x46, 0xec, 0xb9, 0x30, 0x25, 0x99, 0xd4,
 };
+
+static int exp_debug_feature_changed(bool enabled, struct sock *skip)
+{
+	struct mgmt_ev_exp_feature_changed ev;
+
+	memset(&ev, 0, sizeof(ev));
+	memcpy(ev.uuid, debug_uuid, 16);
+	ev.flags = cpu_to_le32(enabled ? BIT(0) : 0);
+
+	return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, NULL,
+				  &ev, sizeof(ev),
+				  HCI_MGMT_EXP_FEATURE_EVENTS, skip);
+}
+
+static void exp_read_debug_info(struct hci_dev *hdev, const u8 *uuid, u16 *idx,
+				struct mgmt_rp_read_exp_features_info *rp)
+{
+	u32 flags;
+
+	if (hdev)
+		return;
+
+	flags = bt_dbg_get() ? BIT(0) : 0;
+	memcpy(rp->features[*idx].uuid, uuid, 16);
+	rp->features[(*idx)++].flags = cpu_to_le32(flags);
+}
+
+static int exp_set_debug(struct sock *sk, struct hci_dev *hdev, const u8 *uuid,
+			 u16 data_len, struct mgmt_cp_set_exp_feature *cp)
+{
+	struct mgmt_rp_set_exp_feature rp;
+	bool val, changed;
+	int err;
+
+	/* Command requires to use the non-controller index */
+	if (hdev)
+		return mgmt_cmd_status(sk, hdev->id,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_INVALID_INDEX);
+
+	/* Parameters are limited to a single octet */
+	if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
+		return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_INVALID_PARAMS);
+
+	/* Only boolean on/off is supported */
+	if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
+		return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_INVALID_PARAMS);
+
+	val = !!cp->param[0];
+	changed = val ? !bt_dbg_get() : bt_dbg_get();
+	bt_dbg_set(val);
+
+	memcpy(rp.uuid, uuid, 16);
+	rp.flags = cpu_to_le32(val ? BIT(0) : 0);
+
+	hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
+
+	err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE,
+				MGMT_OP_SET_EXP_FEATURE, 0,
+				&rp, sizeof(rp));
+
+	if (changed)
+		exp_debug_feature_changed(val, sk);
+
+	return err;
+}
 #endif
 
 /* 671b10b5-42c0-4696-9227-eb28d1b049d6 */
@@ -3794,41 +3876,43 @@ static const u8 rpa_resolution_uuid[16] = {
 	0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15,
 };
 
-static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
-				  void *data, u16 data_len)
+static int exp_ll_privacy_feature_changed(bool enabled, struct hci_dev *hdev,
+					  struct sock *skip)
 {
-	char buf[62];	/* Enough space for 3 features */
-	struct mgmt_rp_read_exp_features_info *rp = (void *)buf;
-	u16 idx = 0;
-	u32 flags;
+	struct mgmt_ev_exp_feature_changed ev;
 
-	bt_dev_dbg(hdev, "sock %p", sk);
+	memset(&ev, 0, sizeof(ev));
+	memcpy(ev.uuid, rpa_resolution_uuid, 16);
+	ev.flags = cpu_to_le32((enabled ? BIT(0) : 0) | BIT(1));
 
-	memset(&buf, 0, sizeof(buf));
+	return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, hdev,
+				  &ev, sizeof(ev),
+				  HCI_MGMT_EXP_FEATURE_EVENTS, skip);
+}
 
-#ifdef CONFIG_BT_FEATURE_DEBUG
-	if (!hdev) {
-		flags = bt_dbg_get() ? BIT(0) : 0;
+static void exp_read_le_status(struct hci_dev *hdev, const u8 *uuid, u16 *idx,
+			       struct mgmt_rp_read_exp_features_info *rp)
+{
+	u32 flags;
 
-		memcpy(rp->features[idx].uuid, debug_uuid, 16);
-		rp->features[idx].flags = cpu_to_le32(flags);
-		idx++;
-	}
-#endif
+	if (!hdev)
+		return;
+	if (test_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks) &&
+	    (hdev->le_states[4] & 0x08) &&	/* Central */
+	    (hdev->le_states[4] & 0x40) &&	/* Peripheral */
+	    (hdev->le_states[3] & 0x10))	/* Simultaneous */
+		flags = BIT(0);
+	else
+		flags = 0;
 
-	if (hdev) {
-		if (test_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks) &&
-		    (hdev->le_states[4] & 0x08) &&	/* Central */
-		    (hdev->le_states[4] & 0x40) &&	/* Peripheral */
-		    (hdev->le_states[3] & 0x10))	/* Simultaneous */
-			flags = BIT(0);
-		else
-			flags = 0;
+	memcpy(rp->features[*idx].uuid, uuid, 16);
+	rp->features[(*idx)++].flags = cpu_to_le32(flags);
+}
 
-		memcpy(rp->features[idx].uuid, simult_central_periph_uuid, 16);
-		rp->features[idx].flags = cpu_to_le32(flags);
-		idx++;
-	}
+static void exp_read_rpa_resolution(struct hci_dev *hdev, const u8 *uuid, u16 *idx,
+				    struct mgmt_rp_read_exp_features_info *rp)
+{
+	u32 flags;
 
 	if (hdev && use_ll_privacy(hdev)) {
 		if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
@@ -3836,62 +3920,128 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
 		else
 			flags = BIT(1);
 
-		memcpy(rp->features[idx].uuid, rpa_resolution_uuid, 16);
-		rp->features[idx].flags = cpu_to_le32(flags);
-		idx++;
+		memcpy(rp->features[*idx].uuid, uuid, 16);
+		rp->features[(*idx)++].flags = cpu_to_le32(flags);
 	}
+}
 
-	rp->feature_count = cpu_to_le16(idx);
+static int exp_set_rpa_resolution(struct sock *sk, struct hci_dev *hdev,
+				  const u8 *uuid, u16 data_len,
+				  struct mgmt_cp_set_exp_feature *cp)
+{
+	struct mgmt_rp_set_exp_feature rp;
+	bool val, changed;
+	int err;
+	u32 flags;
 
-	/* After reading the experimental features information, enable
-	 * the events to update client on any future change.
-	 */
-	hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
+	/* Command requires to use the controller index */
+	if (!hdev)
+		return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_INVALID_INDEX);
 
-	return mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
-				 MGMT_OP_READ_EXP_FEATURES_INFO,
-				 0, rp, sizeof(*rp) + (20 * idx));
-}
+	/* Changes can only be made when controller is powered down */
+	if (hdev_is_powered(hdev))
+		return mgmt_cmd_status(sk, hdev->id,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_NOT_POWERED);
 
-static int exp_ll_privacy_feature_changed(bool enabled, struct hci_dev *hdev,
-					  struct sock *skip)
-{
-	struct mgmt_ev_exp_feature_changed ev;
+	/* Parameters are limited to a single octet */
+	if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
+		return mgmt_cmd_status(sk, hdev->id,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_INVALID_PARAMS);
 
-	memset(&ev, 0, sizeof(ev));
-	memcpy(ev.uuid, rpa_resolution_uuid, 16);
-	ev.flags = cpu_to_le32((enabled ? BIT(0) : 0) | BIT(1));
+	/* Only boolean on/off is supported */
+	if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
+		return mgmt_cmd_status(sk, hdev->id,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_INVALID_PARAMS);
 
-	return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, hdev,
-				  &ev, sizeof(ev),
-				  HCI_MGMT_EXP_FEATURE_EVENTS, skip);
+	val = !!cp->param[0];
+
+	if (val) {
+		changed = !hci_dev_test_flag(hdev,
+					     HCI_ENABLE_LL_PRIVACY);
+		hci_dev_set_flag(hdev, HCI_ENABLE_LL_PRIVACY);
+		hci_dev_clear_flag(hdev, HCI_ADVERTISING);
+
+		/* Enable LL privacy + supported settings changed */
+		flags = BIT(0) | BIT(1);
+	} else {
+		changed = hci_dev_test_flag(hdev,
+					    HCI_ENABLE_LL_PRIVACY);
+		hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY);
+
+		/* Disable LL privacy + supported settings changed */
+		flags = BIT(1);
+	}
+
+	memcpy(rp.uuid, uuid, 16);
+	rp.flags = cpu_to_le32(flags);
+
+	hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
+
+	err = mgmt_cmd_complete(sk, hdev->id,
+				MGMT_OP_SET_EXP_FEATURE, 0,
+				&rp, sizeof(rp));
 
+	if (changed)
+		exp_ll_privacy_feature_changed(val, hdev, sk);
+
+	return err;
 }
 
+static const struct exp_feature exp_feature_list[] = {
 #ifdef CONFIG_BT_FEATURE_DEBUG
-static int exp_debug_feature_changed(bool enabled, struct sock *skip)
+	EXP_FEATURE_ENTRY(debug_uuid, exp_read_debug_info,
+			  exp_set_debug),
+#endif
+	EXP_FEATURE_ENTRY(simult_central_periph_uuid, exp_read_le_status,
+			  NULL),
+	EXP_FEATURE_ENTRY(rpa_resolution_uuid, exp_read_rpa_resolution,
+			  exp_set_rpa_resolution),
+	{}
+};
+
+static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
+				  void *data, u16 data_len)
 {
-	struct mgmt_ev_exp_feature_changed ev;
+	char buf[62] = {0}; /* Enough space for 3 features */
+	struct mgmt_rp_read_exp_features_info *rp = (void *)buf;
+	u16 idx = 0;
+	int i;
 
-	memset(&ev, 0, sizeof(ev));
-	memcpy(ev.uuid, debug_uuid, 16);
-	ev.flags = cpu_to_le32(enabled ? BIT(0) : 0);
+	bt_dev_dbg(hdev, "sock %p", sk);
 
-	return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, NULL,
-				  &ev, sizeof(ev),
-				  HCI_MGMT_EXP_FEATURE_EVENTS, skip);
+	for (i = 0; exp_feature_list[i].uuid; i++)
+		if (exp_feature_list[i].read_info)
+			exp_feature_list[i].read_info(hdev,
+						      exp_feature_list[i].uuid,
+						      &idx, rp);
+	rp->feature_count = cpu_to_le16(idx);
+
+	/* After reading the experimental features information, enable
+	 * the events to update client on any future change.
+	 */
+	hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
+
+	return mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
+				 MGMT_OP_READ_EXP_FEATURES_INFO,
+				 0, rp, sizeof(*rp) + (20 * idx));
 }
-#endif
 
 static int set_exp_feature(struct sock *sk, struct hci_dev *hdev,
 			   void *data, u16 data_len)
 {
 	struct mgmt_cp_set_exp_feature *cp = data;
-	struct mgmt_rp_set_exp_feature rp;
+	int i;
 
 	bt_dev_dbg(hdev, "sock %p", sk);
 
 	if (!memcmp(cp->uuid, ZERO_KEY, 16)) {
+		struct mgmt_rp_set_exp_feature rp;
+
 		memset(rp.uuid, 0, 16);
 		rp.flags = cpu_to_le32(0);
 
@@ -3923,111 +4073,10 @@ static int set_exp_feature(struct sock *sk, struct hci_dev *hdev,
 					 &rp, sizeof(rp));
 	}
 
-#ifdef CONFIG_BT_FEATURE_DEBUG
-	if (!memcmp(cp->uuid, debug_uuid, 16)) {
-		bool val, changed;
-		int err;
-
-		/* Command requires to use the non-controller index */
-		if (hdev)
-			return mgmt_cmd_status(sk, hdev->id,
-					       MGMT_OP_SET_EXP_FEATURE,
-					       MGMT_STATUS_INVALID_INDEX);
-
-		/* Parameters are limited to a single octet */
-		if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
-			return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
-					       MGMT_OP_SET_EXP_FEATURE,
-					       MGMT_STATUS_INVALID_PARAMS);
-
-		/* Only boolean on/off is supported */
-		if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
-			return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
-					       MGMT_OP_SET_EXP_FEATURE,
-					       MGMT_STATUS_INVALID_PARAMS);
-
-		val = !!cp->param[0];
-		changed = val ? !bt_dbg_get() : bt_dbg_get();
-		bt_dbg_set(val);
-
-		memcpy(rp.uuid, debug_uuid, 16);
-		rp.flags = cpu_to_le32(val ? BIT(0) : 0);
-
-		hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
-
-		err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE,
-					MGMT_OP_SET_EXP_FEATURE, 0,
-					&rp, sizeof(rp));
-
-		if (changed)
-			exp_debug_feature_changed(val, sk);
-
-		return err;
-	}
-#endif
-
-	if (!memcmp(cp->uuid, rpa_resolution_uuid, 16)) {
-		bool val, changed;
-		int err;
-		u32 flags;
-
-		/* Command requires to use the controller index */
-		if (!hdev)
-			return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
-					       MGMT_OP_SET_EXP_FEATURE,
-					       MGMT_STATUS_INVALID_INDEX);
-
-		/* Changes can only be made when controller is powered down */
-		if (hdev_is_powered(hdev))
-			return mgmt_cmd_status(sk, hdev->id,
-					       MGMT_OP_SET_EXP_FEATURE,
-					       MGMT_STATUS_NOT_POWERED);
-
-		/* Parameters are limited to a single octet */
-		if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
-			return mgmt_cmd_status(sk, hdev->id,
-					       MGMT_OP_SET_EXP_FEATURE,
-					       MGMT_STATUS_INVALID_PARAMS);
-
-		/* Only boolean on/off is supported */
-		if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
-			return mgmt_cmd_status(sk, hdev->id,
-					       MGMT_OP_SET_EXP_FEATURE,
-					       MGMT_STATUS_INVALID_PARAMS);
-
-		val = !!cp->param[0];
-
-		if (val) {
-			changed = !hci_dev_test_flag(hdev,
-						     HCI_ENABLE_LL_PRIVACY);
-			hci_dev_set_flag(hdev, HCI_ENABLE_LL_PRIVACY);
-			hci_dev_clear_flag(hdev, HCI_ADVERTISING);
-
-			/* Enable LL privacy + supported settings changed */
-			flags = BIT(0) | BIT(1);
-		} else {
-			changed = hci_dev_test_flag(hdev,
-						    HCI_ENABLE_LL_PRIVACY);
-			hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY);
-
-			/* Disable LL privacy + supported settings changed */
-			flags = BIT(1);
-		}
-
-		memcpy(rp.uuid, rpa_resolution_uuid, 16);
-		rp.flags = cpu_to_le32(flags);
-
-		hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
-
-		err = mgmt_cmd_complete(sk, hdev->id,
-					MGMT_OP_SET_EXP_FEATURE, 0,
-					&rp, sizeof(rp));
-
-		if (changed)
-			exp_ll_privacy_feature_changed(val, hdev, sk);
-
-		return err;
-	}
+	for (i = 0; exp_feature_list[i].uuid; i++)
+		if (!memcmp(cp->uuid, exp_feature_list[i].uuid, 16))
+			return exp_feature_list[i].set_info(sk, hdev, exp_feature_list[i].uuid,
+							    data_len, cp);
 
 	return mgmt_cmd_status(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
 			       MGMT_OP_SET_EXP_FEATURE,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/3] Bluetooth: reset_hci experimental feature
  2021-02-09 13:36 [PATCH 0/3] Add reset hci through mgmt experimental feature Hui Wang
  2021-02-09 13:36 ` [PATCH 1/3] Bluetooth: btusb: Add reset_hci handler Hui Wang
  2021-02-09 13:36 ` [PATCH 2/3] Bluetooth: make experimental features expand easier Hui Wang
@ 2021-02-09 13:36 ` Hui Wang
  2021-02-09 13:36 ` [Bluez][PATCH] btmgmt: add a new command reset_hci via " Hui Wang
  3 siblings, 0 replies; 7+ messages in thread
From: Hui Wang @ 2021-02-09 13:36 UTC (permalink / raw)
  To: linux-bluetooth, marcel, johan.hedberg, luiz.dentz

If the hci_dev->reset_hci is set by the driver like btusb, users could
call reset_hci through mgmt protocol.

reset_hci will make the hci reset from the bus they are bond and
if possible make the hci enter bootloader mode, so the disconnect()
and probe() in the driver will be called, the function disconnect()
will release all resources belong to this hci, there is no need to
release resource before calling reset_hci.

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 net/bluetooth/mgmt.c | 57 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 4ac85e24906b..e16977b16bed 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3876,6 +3876,12 @@ static const u8 rpa_resolution_uuid[16] = {
 	0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15,
 };
 
+/* 3f06cef5-2b4f-4c22-b1f4-64bb4733e637 */
+static const u8 reset_hci_uuid[16] = {
+	0x37, 0xe6, 0x33, 0x47, 0xbb, 0x64, 0xf4, 0xb1,
+	0x22, 0x4c, 0x4f, 0x2b, 0xf5, 0xce, 0x06, 0x3f,
+};
+
 static int exp_ll_privacy_feature_changed(bool enabled, struct hci_dev *hdev,
 					  struct sock *skip)
 {
@@ -3992,6 +3998,53 @@ static int exp_set_rpa_resolution(struct sock *sk, struct hci_dev *hdev,
 	return err;
 }
 
+static void exp_read_reset_hci_info(struct hci_dev *hdev, const u8 *uuid, u16 *idx,
+				    struct mgmt_rp_read_exp_features_info *rp)
+{
+	u32 flags;
+
+	if (!hdev)
+		return;
+
+	if (!hdev->reset_hci)
+		return;
+
+	flags = BIT(0);
+	memcpy(rp->features[*idx].uuid, uuid, sizeof(rp->features[*idx].uuid));
+	rp->features[(*idx)++].flags = cpu_to_le32(flags);
+}
+
+static int exp_set_reset_hci(struct sock *sk, struct hci_dev *hdev,
+			     const u8 *uuid, u16 data_len,
+			     struct mgmt_cp_set_exp_feature *cp)
+{
+	struct mgmt_rp_set_exp_feature rp;
+	int err;
+
+	/* Command requires to use the controller index */
+	if (!hdev)
+		return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_INVALID_INDEX);
+
+	if (!hdev->reset_hci)
+		return mgmt_cmd_status(sk, hdev->id,
+				       MGMT_OP_SET_EXP_FEATURE,
+				       MGMT_STATUS_NOT_SUPPORTED);
+
+	hdev->reset_hci(hdev);
+
+	memcpy(rp.uuid, uuid, sizeof(rp.uuid));
+	rp.flags = cpu_to_le32(BIT(0));
+
+	hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
+
+	err = mgmt_cmd_complete(sk, hdev->id,
+				MGMT_OP_SET_EXP_FEATURE, 0,
+				&rp, sizeof(rp));
+	return err;
+}
+
 static const struct exp_feature exp_feature_list[] = {
 #ifdef CONFIG_BT_FEATURE_DEBUG
 	EXP_FEATURE_ENTRY(debug_uuid, exp_read_debug_info,
@@ -4001,13 +4054,15 @@ static const struct exp_feature exp_feature_list[] = {
 			  NULL),
 	EXP_FEATURE_ENTRY(rpa_resolution_uuid, exp_read_rpa_resolution,
 			  exp_set_rpa_resolution),
+	EXP_FEATURE_ENTRY(reset_hci_uuid, exp_read_reset_hci_info,
+			  exp_set_reset_hci),
 	{}
 };
 
 static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
 				  void *data, u16 data_len)
 {
-	char buf[62] = {0}; /* Enough space for 3 features */
+	char buf[82] = {0}; /* Enough space for 4 features */
 	struct mgmt_rp_read_exp_features_info *rp = (void *)buf;
 	u16 idx = 0;
 	int i;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Bluez][PATCH] btmgmt: add a new command reset_hci via experimental feature
  2021-02-09 13:36 [PATCH 0/3] Add reset hci through mgmt experimental feature Hui Wang
                   ` (2 preceding siblings ...)
  2021-02-09 13:36 ` [PATCH 3/3] Bluetooth: reset_hci experimental feature Hui Wang
@ 2021-02-09 13:36 ` Hui Wang
  2021-02-09 13:52   ` [Bluez] " bluez.test.bot
  3 siblings, 1 reply; 7+ messages in thread
From: Hui Wang @ 2021-02-09 13:36 UTC (permalink / raw)
  To: linux-bluetooth, marcel, johan.hedberg, luiz.dentz

Users could reset the hci through this command, and the hci will
reset to bootloader mode if it supports, then the driver will load the
firmware in the driver's probe().
---
 tools/btmgmt.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index f4eb541fa..613a1118e 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -1728,6 +1728,18 @@ static void exp_debug_rsp(uint8_t status, uint16_t len, const void *param,
 	bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
+static void exp_reset_hci_rsp(uint8_t status, uint16_t len, const void *param,
+			      void *user_data)
+{
+	if (status != 0)
+		error("Set reset hci feature failed with status 0x%02x (%s)",
+						status, mgmt_errstr(status));
+	else
+		print("Reset hci feature successfully set");
+
+	bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
 static void cmd_exp_debug(int argc, char **argv)
 {
 	/* d4992530-b9ec-469f-ab01-6c481c47da1c */
@@ -1752,6 +1764,25 @@ static void cmd_exp_debug(int argc, char **argv)
 	}
 }
 
+static void cmd_exp_reset_hci(int argc, char **argv)
+{
+	/* 3f06cef5-2b4f-4c22-b1f4-64bb4733e637 */
+	const uint8_t uuid[16] = {
+		0x37, 0xe6, 0x33, 0x47, 0xbb, 0x64, 0xf4, 0xb1,
+		0x22, 0x4c, 0x4f, 0x2b, 0xf5, 0xce, 0x06, 0x3f,
+	};
+	struct mgmt_cp_set_exp_feature cp;
+
+	memset(&cp, 0, sizeof(cp));
+	memcpy(cp.uuid, uuid, 16);
+
+	if (mgmt_send(mgmt, MGMT_OP_SET_EXP_FEATURE, mgmt_index,
+			sizeof(cp), &cp, exp_reset_hci_rsp, NULL, NULL) == 0) {
+		error("Unable to send reset hci feature cmd");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
 static void print_mgmt_tlv(void *data, void *user_data)
 {
 	const struct mgmt_tlv *entry = data;
@@ -5303,6 +5334,8 @@ static const struct bt_shell_menu main_menu = {
 		cmd_expinfo,		"Show experimental features"	},
 	{ "exp-debug",		"<on/off>",
 		cmd_exp_debug,		"Set debug feature"		},
+	{ "exp-reset-hci",	NULL,
+		cmd_exp_reset_hci,	"Reset hci"	},
 	{ "read-sysconfig",	NULL,
 		cmd_read_sysconfig,	"Read System Configuration"	},
 	{ "set-sysconfig",	"<-v|-h> [options...]",
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* RE: [Bluez] btmgmt: add a new command reset_hci via experimental feature
  2021-02-09 13:36 ` [Bluez][PATCH] btmgmt: add a new command reset_hci via " Hui Wang
@ 2021-02-09 13:52   ` bluez.test.bot
  0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2021-02-09 13:52 UTC (permalink / raw)
  To: linux-bluetooth, hui.wang

[-- Attachment #1: Type: text/plain, Size: 557 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=430721

---Test result---

##############################
Test: CheckPatch - PASS

##############################
Test: CheckGitLint - PASS

##############################
Test: CheckBuild - PASS

##############################
Test: MakeCheck - PASS



---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: Add reset hci through mgmt experimental feature
  2021-02-09 13:36 ` [PATCH 1/3] Bluetooth: btusb: Add reset_hci handler Hui Wang
@ 2021-02-09 14:13   ` bluez.test.bot
  0 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2021-02-09 14:13 UTC (permalink / raw)
  To: linux-bluetooth, hui.wang

[-- Attachment #1: Type: text/plain, Size: 1599 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=430719

---Test result---

##############################
    Test: CheckPatch - PASS
    

    ##############################
    Test: CheckGitLint - PASS
    

    ##############################
    Test: CheckBuildK - PASS
    

    ##############################
    Test: CheckTestRunner: Setup - PASS
    

    ##############################
    Test: CheckTestRunner: l2cap-tester - PASS
    Total: 40, Passed: 34 (85.0%), Failed: 0, Not Run: 6

    ##############################
    Test: CheckTestRunner: bnep-tester - PASS
    Total: 1, Passed: 1 (100.0%), Failed: 0, Not Run: 0

    ##############################
    Test: CheckTestRunner: mgmt-tester - PASS
    Total: 416, Passed: 400 (96.2%), Failed: 0, Not Run: 16

    ##############################
    Test: CheckTestRunner: rfcomm-tester - PASS
    Total: 9, Passed: 9 (100.0%), Failed: 0, Not Run: 0

    ##############################
    Test: CheckTestRunner: sco-tester - PASS
    Total: 8, Passed: 8 (100.0%), Failed: 0, Not Run: 0

    ##############################
    Test: CheckTestRunner: smp-tester - PASS
    Total: 8, Passed: 8 (100.0%), Failed: 0, Not Run: 0

    ##############################
    Test: CheckTestRunner: userchan-tester - PASS
    Total: 3, Passed: 3 (100.0%), Failed: 0, Not Run: 0

    

---
Regards,
Linux Bluetooth


[-- Attachment #2: l2cap-tester.log --]
[-- Type: application/octet-stream, Size: 43340 bytes --]

[-- Attachment #3: bnep-tester.log --]
[-- Type: application/octet-stream, Size: 3532 bytes --]

[-- Attachment #4: mgmt-tester.log --]
[-- Type: application/octet-stream, Size: 546229 bytes --]

[-- Attachment #5: rfcomm-tester.log --]
[-- Type: application/octet-stream, Size: 11652 bytes --]

[-- Attachment #6: sco-tester.log --]
[-- Type: application/octet-stream, Size: 9887 bytes --]

[-- Attachment #7: smp-tester.log --]
[-- Type: application/octet-stream, Size: 11798 bytes --]

[-- Attachment #8: userchan-tester.log --]
[-- Type: application/octet-stream, Size: 5428 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-02-09 14:14 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-09 13:36 [PATCH 0/3] Add reset hci through mgmt experimental feature Hui Wang
2021-02-09 13:36 ` [PATCH 1/3] Bluetooth: btusb: Add reset_hci handler Hui Wang
2021-02-09 14:13   ` Add reset hci through mgmt experimental feature bluez.test.bot
2021-02-09 13:36 ` [PATCH 2/3] Bluetooth: make experimental features expand easier Hui Wang
2021-02-09 13:36 ` [PATCH 3/3] Bluetooth: reset_hci experimental feature Hui Wang
2021-02-09 13:36 ` [Bluez][PATCH] btmgmt: add a new command reset_hci via " Hui Wang
2021-02-09 13:52   ` [Bluez] " bluez.test.bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.