Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 28dc4b92 authored by Loic Poulain's avatar Loic Poulain Committed by Marcel Holtmann
Browse files

Bluetooth: btintel: Add manufacturing enter/exit helpers



Older Intel controllers need to enter manufacturing mode to perform
some vendor specific operations (patching, configuration...).
Add enter/exit manufaturing methods and refactor existing
manufacturing code.
Exit can be configured to perform a reset. Reset can be performed
either with patches activated or deactivated.

Signed-off-by: default avatarLoic Poulain <loic.poulain@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 1623d0bf
Loading
Loading
Loading
Loading
+60 −54
Original line number Original line Diff line number Diff line
@@ -73,6 +73,48 @@ int btintel_check_bdaddr(struct hci_dev *hdev)
}
}
EXPORT_SYMBOL_GPL(btintel_check_bdaddr);
EXPORT_SYMBOL_GPL(btintel_check_bdaddr);


int btintel_enter_mfg(struct hci_dev *hdev)
{
	const u8 param[] = { 0x01, 0x00 };
	struct sk_buff *skb;

	skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT);
	if (IS_ERR(skb)) {
		bt_dev_err(hdev, "Entering manufacturer mode failed (%ld)",
			   PTR_ERR(skb));
		return PTR_ERR(skb);
	}
	kfree_skb(skb);

	return 0;
}
EXPORT_SYMBOL_GPL(btintel_enter_mfg);

int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched)
{
	u8 param[] = { 0x00, 0x00 };
	struct sk_buff *skb;

	/* The 2nd command parameter specifies the manufacturing exit method:
	 * 0x00: Just disable the manufacturing mode (0x00).
	 * 0x01: Disable manufacturing mode and reset with patches deactivated.
	 * 0x02: Disable manufacturing mode and reset with patches activated.
	 */
	if (reset)
		param[1] |= patched ? 0x02 : 0x01;

	skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT);
	if (IS_ERR(skb)) {
		bt_dev_err(hdev, "Exiting manufacturer mode failed (%ld)",
			   PTR_ERR(skb));
		return PTR_ERR(skb);
	}
	kfree_skb(skb);

	return 0;
}
EXPORT_SYMBOL_GPL(btintel_exit_mfg);

int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
{
	struct sk_buff *skb;
	struct sk_buff *skb;
@@ -126,37 +168,19 @@ EXPORT_SYMBOL_GPL(btintel_set_diag);


int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
{
{
	struct sk_buff *skb;
	int err, ret;
	u8 param[2];
	int err;

	param[0] = 0x01;
	param[1] = 0x00;

	skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
	if (IS_ERR(skb)) {
		err = PTR_ERR(skb);
		BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
		       hdev->name, err);
		return PTR_ERR(skb);
	}
	kfree_skb(skb);

	err = btintel_set_diag(hdev, enable);


	param[0] = 0x00;
	err = btintel_enter_mfg(hdev);
	param[1] = 0x00;
	if (err)
		return err;


	skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
	ret = btintel_set_diag(hdev, enable);
	if (IS_ERR(skb)) {
		err = PTR_ERR(skb);
		BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
		       hdev->name, err);
		return PTR_ERR(skb);
	}
	kfree_skb(skb);


	err = btintel_exit_mfg(hdev, false, false);
	if (err)
		return err;
		return err;

	return ret;
}
}
EXPORT_SYMBOL_GPL(btintel_set_diag_mfg);
EXPORT_SYMBOL_GPL(btintel_set_diag_mfg);


@@ -309,37 +333,19 @@ EXPORT_SYMBOL_GPL(btintel_set_event_mask);


int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
{
{
	struct sk_buff *skb;
	int err, ret;
	u8 param[2];
	int err;

	param[0] = 0x01;
	param[1] = 0x00;


	skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
	err = btintel_enter_mfg(hdev);
	if (IS_ERR(skb)) {
	if (err)
		err = PTR_ERR(skb);
		return err;
		BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
		       hdev->name, err);
		return PTR_ERR(skb);
	}
	kfree_skb(skb);

	err = btintel_set_event_mask(hdev, debug);

	param[0] = 0x00;
	param[1] = 0x00;


	skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
	ret = btintel_set_event_mask(hdev, debug);
	if (IS_ERR(skb)) {
		err = PTR_ERR(skb);
		BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
		       hdev->name, err);
		return PTR_ERR(skb);
	}
	kfree_skb(skb);


	err = btintel_exit_mfg(hdev, false, false);
	if (err)
		return err;
		return err;

	return ret;
}
}
EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);
EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);


+12 −0
Original line number Original line Diff line number Diff line
@@ -72,6 +72,8 @@ struct intel_secure_send_result {
#if IS_ENABLED(CONFIG_BT_INTEL)
#if IS_ENABLED(CONFIG_BT_INTEL)


int btintel_check_bdaddr(struct hci_dev *hdev);
int btintel_check_bdaddr(struct hci_dev *hdev);
int btintel_enter_mfg(struct hci_dev *hdev);
int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched);
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int btintel_set_diag(struct hci_dev *hdev, bool enable);
int btintel_set_diag(struct hci_dev *hdev, bool enable);
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
@@ -94,6 +96,16 @@ static inline int btintel_check_bdaddr(struct hci_dev *hdev)
	return -EOPNOTSUPP;
	return -EOPNOTSUPP;
}
}


static inline int btintel_enter_mfg(struct hci_dev *hdev)
{
	return -EOPNOTSUPP;
}

static inline int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched)
{
	return -EOPNOTSUPP;
}

static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
{
	return -EOPNOTSUPP;
	return -EOPNOTSUPP;
+14 −40
Original line number Original line Diff line number Diff line
@@ -1646,14 +1646,9 @@ static int btusb_setup_intel(struct hci_dev *hdev)
	struct sk_buff *skb;
	struct sk_buff *skb;
	const struct firmware *fw;
	const struct firmware *fw;
	const u8 *fw_ptr;
	const u8 *fw_ptr;
	int disable_patch;
	int disable_patch, err;
	struct intel_version *ver;
	struct intel_version *ver;


	const u8 mfg_enable[] = { 0x01, 0x00 };
	const u8 mfg_disable[] = { 0x00, 0x00 };
	const u8 mfg_reset_deactivate[] = { 0x00, 0x01 };
	const u8 mfg_reset_activate[] = { 0x00, 0x02 };

	BT_DBG("%s", hdev->name);
	BT_DBG("%s", hdev->name);


	/* The controller has a bug with the first HCI command sent to it
	/* The controller has a bug with the first HCI command sent to it
@@ -1725,22 +1720,16 @@ static int btusb_setup_intel(struct hci_dev *hdev)


	kfree_skb(skb);
	kfree_skb(skb);


	/* This Intel specific command enables the manufacturer mode of the
	/* Enable the manufacturer mode of the controller.
	 * controller.
	 *
	 * Only while this mode is enabled, the driver can download the
	 * Only while this mode is enabled, the driver can download the
	 * firmware patch data and configuration parameters.
	 * firmware patch data and configuration parameters.
	 */
	 */
	skb = __hci_cmd_sync(hdev, 0xfc11, 2, mfg_enable, HCI_INIT_TIMEOUT);
	err = btintel_enter_mfg(hdev);
	if (IS_ERR(skb)) {
	if (err) {
		BT_ERR("%s entering Intel manufacturer mode failed (%ld)",
		       hdev->name, PTR_ERR(skb));
		release_firmware(fw);
		release_firmware(fw);
		return PTR_ERR(skb);
		return err;
	}
	}


	kfree_skb(skb);

	disable_patch = 1;
	disable_patch = 1;


	/* The firmware data file consists of list of Intel specific HCI
	/* The firmware data file consists of list of Intel specific HCI
@@ -1780,14 +1769,9 @@ static int btusb_setup_intel(struct hci_dev *hdev)
	/* Patching completed successfully and disable the manufacturer mode
	/* Patching completed successfully and disable the manufacturer mode
	 * with reset and activate the downloaded firmware patches.
	 * with reset and activate the downloaded firmware patches.
	 */
	 */
	skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_activate),
	err = btintel_exit_mfg(hdev, true, true);
			     mfg_reset_activate, HCI_INIT_TIMEOUT);
	if (err)
	if (IS_ERR(skb)) {
		return err;
		BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
		       hdev->name, PTR_ERR(skb));
		return PTR_ERR(skb);
	}
	kfree_skb(skb);


	BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
	BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
		hdev->name);
		hdev->name);
@@ -1796,14 +1780,9 @@ static int btusb_setup_intel(struct hci_dev *hdev)


exit_mfg_disable:
exit_mfg_disable:
	/* Disable the manufacturer mode without reset */
	/* Disable the manufacturer mode without reset */
	skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_disable), mfg_disable,
	err = btintel_exit_mfg(hdev, false, false);
			     HCI_INIT_TIMEOUT);
	if (err)
	if (IS_ERR(skb)) {
		return err;
		BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
		       hdev->name, PTR_ERR(skb));
		return PTR_ERR(skb);
	}
	kfree_skb(skb);


	BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
	BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);


@@ -1815,14 +1794,9 @@ static int btusb_setup_intel(struct hci_dev *hdev)
	/* Patching failed. Disable the manufacturer mode with reset and
	/* Patching failed. Disable the manufacturer mode with reset and
	 * deactivate the downloaded firmware patches.
	 * deactivate the downloaded firmware patches.
	 */
	 */
	skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_deactivate),
	err = btintel_exit_mfg(hdev, true, false);
			     mfg_reset_deactivate, HCI_INIT_TIMEOUT);
	if (err)
	if (IS_ERR(skb)) {
		return err;
		BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
		       hdev->name, PTR_ERR(skb));
		return PTR_ERR(skb);
	}
	kfree_skb(skb);


	BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
	BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
		hdev->name);
		hdev->name);