]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: rtw89: usb: add serial_number and uuid sysfs attributes for 0x28de:0x2432
authorJohnson Tsai <wenjie.tsai@realtek.com>
Fri, 29 May 2026 07:50:32 +0000 (15:50 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Wed, 3 Jun 2026 06:48:22 +0000 (14:48 +0800)
Expose the device's Serial Number (SN) and UUID from EFUSE via two
read-only sysfs attributes, `serial_number` and `uuid`, on the ieee80211
phy device under the `rtw89_usb` attribute group.

This hardware identification information is essential for user-space
applications to uniquely identify, track, and manage specific Wi-Fi
adapters. For example, in automated factory provisioning or device
management systems, user-space tools rely on the EFUSE serial number and
UUID to bind configurations to specific physical adapters. Currently,
standard wireless APIs do not expose this low-level hardware
information, making these sysfs nodes the only viable solution for
user space to extract this data.

The attributes are gated behind a new RTW89_QUIRK_HW_INFO_SYSFS quirk,
enabled only for the VID 0x28de / PID 0x2432 device via the
dev_id_quirks field in rtw89_driver_info.

Example usage from user-space:
  $ cat /sys/class/ieee80211/phy0/rtw89_usb/serial_number
  3642000123
  $ cat /sys/class/ieee80211/phy0/rtw89_usb/uuid
  aaec2b7c-0a55-4727-8de0-b30febccbbaa

Cc: Elliot Saba <sabae@valvesoftware.com>
Cc: Charles Lohr <charlesl@valvesoftware.com>
Signed-off-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20260529075032.16807-3-pkshih@realtek.com
Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 [new file with mode: 0644]
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/rtw8852c.c
drivers/net/wireless/realtek/rtw89/rtw8852c.h
drivers/net/wireless/realtek/rtw89/rtw8852cu.c
drivers/net/wireless/realtek/rtw89/usb.c

diff --git a/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 b/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89
new file mode 100644 (file)
index 0000000..7dfdce0
--- /dev/null
@@ -0,0 +1,24 @@
+What:          /sys/class/ieee80211/phyX/rtw89_usb/serial_number
+Date:          May 2026
+Contact:       Johnson Tsai <wenjie.tsai@realtek.com>, linux-wireless@vger.kernel.org
+Description:   (Read) Serial number burned into EFUSE of the RTL8852CU-based
+               USB Wi-Fi adapter.  Only present on devices that set the
+               RTW89_QUIRK_HW_INFO_SYSFS quirk (currently VID 0x28de /
+               PID 0x2432).
+
+               Format: %10phN (5 raw bytes printed as 10 lowercase hex
+               digits, no separators).
+
+               Example: 3642000123
+
+What:          /sys/class/ieee80211/phyX/rtw89_usb/uuid
+Date:          May 2026
+Contact:       Johnson Tsai <wenjie.tsai@realtek.com>, linux-wireless@vger.kernel.org
+Description:   (Read) UUID burned into EFUSE of the RTL8852CU-based USB Wi-Fi
+               adapter.  Only present on devices that set the
+               RTW89_QUIRK_HW_INFO_SYSFS quirk (currently VID 0x28de /
+               PID 0x2432).
+
+               Format: %pUb (RFC 4122 UUID in lowercase with hyphens).
+
+               Example: aaec2b7c-0a55-4727-8de0-b30febccbbaa
index 4c638c2bdc4f7f3f529b555abbf743eba7d91dee..5547888d7e67a40c5eb9a98a5db84996e5135ebc 100644 (file)
@@ -3618,6 +3618,9 @@ struct rtw89_sta_link {
        u32 data_tx_cnt_lmt:6;
 };
 
+#define RTW89_EFUSE_SN_LEN 5
+#define RTW89_EFUSE_UUID_LEN 16
+
 struct rtw89_efuse {
        bool valid;
        bool power_k_valid;
@@ -3628,6 +3631,8 @@ struct rtw89_efuse {
        u8 adc_td;
        u8 bt_setting_2;
        u8 bt_setting_3;
+       u8 sn[RTW89_EFUSE_SN_LEN];
+       u8 uuid[RTW89_EFUSE_UUID_LEN];
 };
 
 struct rtw89_phy_rate_pattern {
@@ -5376,6 +5381,7 @@ enum rtw89_quirks {
        RTW89_QUIRK_PCI_BER,
        RTW89_QUIRK_THERMAL_PROT_120C,
        RTW89_QUIRK_THERMAL_PROT_110C,
+       RTW89_QUIRK_HW_INFO_SYSFS,
 
        NUM_OF_RTW89_QUIRKS,
 };
index 7bb1264bcaef2658a015eb9cec329b1b4d306f31..3861cce42b1ba2234243236af79f39090c2d1c86 100644 (file)
@@ -621,6 +621,15 @@ static void rtw8852c_efuse_parsing_gain_offset(struct rtw89_dev *rtwdev,
        gain->offset_valid = valid;
 }
 
+static void rtw8852c_efuse_copy_sn_uuid_usb(struct rtw89_dev *rtwdev,
+                                           const struct rtw8852c_efuse *map)
+{
+       struct rtw89_efuse *efuse = &rtwdev->efuse;
+
+       memcpy(efuse->sn, map->u.sn, sizeof(efuse->sn));
+       memcpy(efuse->uuid, map->u.uuid, sizeof(efuse->uuid));
+}
+
 static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map,
                               enum rtw89_efuse_block block)
 {
@@ -640,6 +649,7 @@ static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map,
                break;
        case RTW89_HCI_TYPE_USB:
                ether_addr_copy(efuse->addr, map->u.mac_addr);
+               rtw8852c_efuse_copy_sn_uuid_usb(rtwdev, map);
                break;
        default:
                return -ENOTSUPP;
index 8585921ac6c4a8fb0ee343ecb9b6ddb060b32d00..b1d7c354c18e5565a9b8a4a153825dda01593592 100644 (file)
 struct rtw8852c_u_efuse {
        u8 rsvd[0x88];
        u8 mac_addr[ETH_ALEN];
-};
+       u8 rsvd1[8];
+       u8 sn[RTW89_EFUSE_SN_LEN];
+       u8 rsvd2[29];
+       u8 uuid[RTW89_EFUSE_UUID_LEN];
+} __packed;
 
 struct rtw8852c_e_efuse {
        u8 mac_addr[ETH_ALEN];
index 8f89f9a3145594d64efcffd04c311c6f099219bb..81ee96b0a04873e1115d75e74f560f0cbe133605 100644 (file)
@@ -39,6 +39,16 @@ static const struct rtw89_driver_info rtw89_8852cu_info = {
        },
 };
 
+static const struct rtw89_driver_info rtw89_8852cu_valve_info = {
+       .chip = &rtw8852c_chip_info,
+       .variant = NULL,
+       .quirks = NULL,
+       .dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS),
+       .bus = {
+               .usb = &rtw8852c_usb_info,
+       },
+};
+
 static const struct usb_device_id rtw_8852cu_id_table[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03a6, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
@@ -53,7 +63,7 @@ static const struct usb_device_id rtw_8852cu_id_table[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x991d, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
        { USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff),
-         .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+         .driver_info = (kernel_ulong_t)&rtw89_8852cu_valve_info },
        { USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x8206, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
        { USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff),
index 67ebf2d9bb7d15af935e6980fd1e8e932a28bdf7..aafa8b9e93dfcdfb984db60dc1cfd4544c71d2ce 100644 (file)
@@ -1150,6 +1150,56 @@ static int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev)
        return rtw89_usb_switch_mode_be(rtwdev);
 }
 
+static ssize_t serial_number_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct wiphy *wiphy = container_of(dev, struct wiphy, dev);
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct rtw89_dev *rtwdev = hw->priv;
+       struct rtw89_efuse *efuse = &rtwdev->efuse;
+
+       return sysfs_emit(buf, "%*phN\n",
+                         (int)sizeof(efuse->sn), efuse->sn);
+}
+static DEVICE_ATTR_RO(serial_number);
+
+static ssize_t uuid_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       struct wiphy *wiphy = container_of(dev, struct wiphy, dev);
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct rtw89_dev *rtwdev = hw->priv;
+       struct rtw89_efuse *efuse = &rtwdev->efuse;
+
+       return sysfs_emit(buf, "%pUb\n", efuse->uuid);
+}
+static DEVICE_ATTR_RO(uuid);
+
+static struct attribute *rtw89_usb_attrs[] = {
+       &dev_attr_serial_number.attr,
+       &dev_attr_uuid.attr,
+       NULL,
+};
+
+static bool rtw89_usb_group_visible(struct kobject *kobj)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct wiphy *wiphy = container_of(dev, struct wiphy, dev);
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct rtw89_dev *rtwdev = hw->priv;
+
+       return test_bit(RTW89_QUIRK_HW_INFO_SYSFS, rtwdev->quirks);
+}
+
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(rtw89_usb);
+
+static const struct attribute_group rtw89_usb_group = {
+       .name           = "rtw89_usb",
+       .attrs          = rtw89_usb_attrs,
+       .is_visible     = SYSFS_GROUP_VISIBLE(rtw89_usb),
+};
+__ATTRIBUTE_GROUPS(rtw89_usb);
+
 int rtw89_usb_probe(struct usb_interface *intf,
                    const struct usb_device_id *id)
 {
@@ -1171,6 +1221,8 @@ int rtw89_usb_probe(struct usb_interface *intf,
        rtwusb->rtwdev = rtwdev;
        rtwusb->info = info->bus.usb;
 
+       rtwdev->hw->wiphy->dev.groups = rtw89_usb_groups;
+
        rtwdev->hci.ops = &rtw89_usb_ops;
        rtwdev->hci.type = RTW89_HCI_TYPE_USB;
        rtwdev->hci.tx_rpt_enabled = true;