armbian-build/patch/kernel/archive/sm8550-6.18/0015-drivers-use-soc-serial-for-wifi-and-bluetooth.patch
Alex Ling b9b5fb8b0c sm8550: Add patches for 6.18
Signed-off-by: Alex Ling <ling_kasim@hotmail.com>
2026-01-25 11:35:47 +01:00

302 lines
8.8 KiB
Diff

From 59250424d761009af70ce8079169d8e6081acf26 Mon Sep 17 00:00:00 2001
From: spycat88 <spycat88@users.noreply.github.com>
Date: Thu, 23 Jan 2025 15:18:25 +0000
Subject: [PATCH 15/18] drivers: use soc serial for wifi and bluetooth
---
drivers/bluetooth/btqca.c | 94 ++++++++++++++++++++++--
drivers/net/wireless/ath/ath12k/mac.c | 100 ++++++++++++++++++++++++--
drivers/soc/qcom/socinfo.c | 10 +++
3 files changed, 196 insertions(+), 8 deletions(-)
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 7c958d606..d71871f94 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -12,6 +12,74 @@
#include <net/bluetooth/hci_core.h>
#include "btqca.h"
+#include <linux/ctype.h>
+
+extern const char *qcom_serial_number;
+
+/**
+ * generate_bdaddr_from_serial - Generates a BD_ADDR using the serial number
+ * @bdaddr: Pointer to bdaddr_t structure to populate
+ *
+ * This function sets the first 3 bytes to 00:03:7F and the last 3 bytes
+ * are derived from the last 6 characters of qcom_serial_number in reversed order.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+static int generate_bdaddr_from_serial(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+ size_t serial_len;
+ const char *serial = qcom_serial_number;
+ char last6[7] = {0}; // 6 characters + null terminator
+ int i;
+ int ret;
+
+ if (!serial) {
+ bt_dev_err(hdev, "qcom_serial_number is NULL");
+ return -EINVAL;
+ }
+
+ serial_len = strlen(serial);
+ if (serial_len < 6) {
+ bt_dev_err(hdev, "qcom_serial_number is too short: %zu characters", serial_len);
+ return -EINVAL;
+ }
+
+ // Extract the last 6 characters
+ strncpy(last6, serial + serial_len - 6, 6);
+
+ // Initialize the first 3 bytes
+ bdaddr->b[5] = 0x00;
+ bdaddr->b[4] = 0x03;
+ bdaddr->b[3] = 0x7F;
+
+ // Convert the last 6 characters into 3 bytes in reversed order
+ for (i = 0; i < 3; i++) {
+ char byte_str[3] = {0};
+ u8 byte_val;
+
+ byte_str[0] = last6[i * 2];
+ byte_str[1] = last6[i * 2 + 1];
+
+ if (!isxdigit(byte_str[0]) || !isxdigit(byte_str[1])) {
+ bt_dev_err(hdev, "Invalid hex characters in serial number: %c%c",
+ byte_str[0], byte_str[1]);
+ return -EINVAL;
+ }
+
+ ret = kstrtou8(byte_str, 16, &byte_val);
+ if (ret < 0) {
+ bt_dev_err(hdev, "Failed to convert hex string to u8: %c%c",
+ byte_str[0], byte_str[1]);
+ return ret;
+ }
+
+ bdaddr->b[i] = byte_val; // Assign to bytes 0,1,2 (reversed order)
+ }
+
+ bt_dev_info(hdev, "Generated BD_ADDR: %pMR", bdaddr);
+
+ return 0;
+}
int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
enum qca_btsoc_type soc_type)
@@ -714,7 +782,7 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
}
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
-static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *config)
+static int __maybe_unused qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *config)
{
struct hci_rp_read_bd_addr *bda;
struct sk_buff *skb;
@@ -790,6 +858,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
u8 rom_ver = 0;
u32 soc_ver;
u16 boardid = 0;
+ bdaddr_t generated_bdaddr;
bt_dev_dbg(hdev, "QCA setup on UART");
@@ -993,9 +1062,26 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
break;
}
- err = qca_check_bdaddr(hdev, &config);
- if (err)
- return err;
+ /* Generate BD_ADDR from qcom_serial_number */
+ err = generate_bdaddr_from_serial(hdev, &generated_bdaddr);
+ if (err) {
+ bt_dev_warn(hdev, "Failed to generate BD_ADDR from serial number, falling back to NVM");
+ err = qca_check_bdaddr(hdev, &config);
+ if (err)
+ return err;
+ } else {
+ /* Set the generated BD_ADDR */
+ err = qca_set_bdaddr(hdev, &generated_bdaddr);
+ if (err) {
+ bt_dev_err(hdev, "Failed to set the generated BD_ADDR from serial number");
+ return err;
+ }
+
+ /* Update hdev->public_addr and hdev->bdaddr */
+ bacpy(&hdev->public_addr, &generated_bdaddr);
+ bacpy(&hdev->bdaddr, &generated_bdaddr);
+ bt_dev_info(hdev, "BD_ADDR set to %pMR", &hdev->public_addr);
+ }
bt_dev_info(hdev, "QCA setup on UART is completed");
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 095b49a39..45ad5cbd2 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -162,6 +162,91 @@ static const struct ieee80211_channel ath12k_6ghz_channels[] = {
CHAN6G(233, 7115, 0),
};
+extern const char *qcom_serial_number;
+
+/* Define a small struct for storing a 6-byte MAC address array */
+struct macaddr_t {
+ u8 b[ETH_ALEN];
+};
+
+/* Define a static, predefined MAC_ADDR structure */
+static const struct macaddr_t static_macaddr = {
+ .b = { 0x00, 0x03, 0x7F, 0x11, 0x22, 0x33 }
+};
+
+/**
+ * generate_macaddr_from_serial - Generates a MAC_ADDR using the serial number
+ * @macaddr: Pointer to macaddr_t structure to populate
+ *
+ * This function sets the first 3 bytes to 00:03:7F and the last 3 bytes
+ * are derived from the last 6 characters of qcom_serial_number.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+static int generate_macaddr_from_serial(struct ath12k *ar, struct macaddr_t *macaddr)
+{
+ size_t serial_len;
+ const char *serial = qcom_serial_number;
+ char last6[7] = {0}; // 6 characters + null terminator
+ int i;
+ int ret;
+
+ if (!serial) {
+ ath12k_err(ar->ab, "qcom_serial_number is NULL");
+ return -EINVAL;
+ }
+
+ serial_len = strlen(serial);
+ if (serial_len < 6) {
+ ath12k_err(ar->ab, "qcom_serial_number is too short: %zu characters", serial_len);
+ return -EINVAL;
+ }
+
+ // Extract the last 6 characters
+ strncpy(last6, serial + serial_len - 6, 6);
+
+ // Initialize the first 3 bytes
+ macaddr->b[5] = 0x00;
+ macaddr->b[4] = 0x03;
+ macaddr->b[3] = 0x7F;
+
+ // Convert the last 6 characters into 3 bytes
+ for (i = 0; i < 3; i++) {
+ char byte_str[3] = {0};
+ u8 byte_val;
+
+ byte_str[0] = last6[i * 2];
+ byte_str[1] = last6[i * 2 + 1];
+
+ if (!isxdigit(byte_str[0]) || !isxdigit(byte_str[1])) {
+ ath12k_err(ar->ab, "Invalid hex characters in serial number: %c%c",
+ byte_str[0], byte_str[1]);
+ return -EINVAL;
+ }
+
+ ret = kstrtou8(byte_str, 16, &byte_val);
+ if (ret < 0) {
+ ath12k_err(ar->ab, "Failed to convert hex string to u8: %c%c",
+ byte_str[0], byte_str[1]);
+ return ret;
+ }
+
+ macaddr->b[2 - i] = byte_val; // Assign to bytes 2,1,0
+ }
+
+ ath12k_info(ar->ab, "Generated MAC_ADDR: %pMR", macaddr);
+
+ return 0;
+}
+
+static void ath12k_reverse_mac(u8 *dst, const u8 *src)
+{
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ dst[i] = src[ETH_ALEN - 1 - i];
+}
+
static struct ieee80211_rate ath12k_legacy_rates[] = {
{ .bitrate = 10,
.hw_value = ATH12K_HW_RATE_CCK_LP_1M },
@@ -13723,6 +13808,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
struct ath12k_base *ab = ar->ab;
struct ath12k_pdev *pdev;
struct ath12k_pdev_cap *cap;
+ struct macaddr_t generated_macaddr;
static const u32 cipher_suites[] = {
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
@@ -13746,11 +13832,17 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
u32 ht_cap_info = 0;
pdev = ar->pdev;
- if (ar->ab->pdevs_macaddr_valid) {
- ether_addr_copy(ar->mac_addr, pdev->mac_addr);
+ ret = generate_macaddr_from_serial(ar, &generated_macaddr);
+ if (ret) {
+ if (ar->ab->pdevs_macaddr_valid) {
+ ether_addr_copy(ar->mac_addr, pdev->mac_addr);
+ } else {
+ ether_addr_copy(ar->mac_addr, ar->ab->mac_addr);
+ ar->mac_addr[4] += ar->pdev_idx;
+ }
} else {
- ether_addr_copy(ar->mac_addr, ar->ab->mac_addr);
- ar->mac_addr[4] += ar->pdev_idx;
+ ath12k_reverse_mac(ar->mac_addr, generated_macaddr.b);
+ ath12k_info(ab, "MAC_ADDR set to %pMR", generated_macaddr.b);
}
ret = ath12k_mac_setup_register(ar, &ht_cap_info, hw->wiphy->bands);
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index 963772f45..b69c976da 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -171,6 +171,10 @@ struct smem_image_version {
};
#endif /* CONFIG_DEBUG_FS */
+/* Global variable to hold the serial number */
+const char *qcom_serial_number;
+EXPORT_SYMBOL(qcom_serial_number);
+
struct qcom_socinfo {
struct soc_device *soc_dev;
struct soc_device_attribute attr;
@@ -817,6 +821,9 @@ static int qcom_socinfo_probe(struct platform_device *pdev)
le32_to_cpu(info->serial_num));
if (!qs->attr.serial_number)
return -ENOMEM;
+
+ /* Assign the serial number to the global variable */
+ qcom_serial_number = qs->attr.serial_number;
}
qs->soc_dev = soc_device_register(&qs->attr);
@@ -840,6 +847,9 @@ static void qcom_socinfo_remove(struct platform_device *pdev)
soc_device_unregister(qs->soc_dev);
socinfo_debugfs_exit(qs);
+
+ /* Clear the global serial number */
+ qcom_serial_number = NULL;
}
static struct platform_driver qcom_socinfo_driver = {
--
2.43.0