Backport a USB WiFi 6 Driver in Linux Kernel

Posted by shengliangd's blog on January 1, 2023

This blog introduces how to backport a driver (mt7921u) for a USB WiFi 6 adapter from Linux 5.19 to Linux 5.10 (finally got ~600Mbps with a WiFi 6 router). The motivation was described in my previous blog. Check out my repo if you need it. This workflow also applies to other device drivers in Linux kernel.

It’s worth noting that, although I only need the mt7921u driver, this driver shares many code with other Mediatek wireless drivers under drivers/net/wireless/mediatek/mt76 in the kernel source tree. These code often goes through major changes across kernel versions. So, if we only backport the mt7921u part, there will be a lot of things to do. Fortunately, Linux kernel’s wireless API does not change much cross versions, so it will be much easier to backport the whole directory of drivers/net/wireless/mediatek/mt76, and we can just skip other Mediatek drivers such as mt7915 by disabling them in the kernel config.

Preparation: Download Linux kernel sources of both the new version and the old version (5.19 and 5.10 for this). Note that for Jetson boards it is recommended to follow NVIDIA’s guides about building kernel on your own. Then replace the driver’s folder (drivers/net/wireless/mediatek/mt76 in this case) of the old kernel tree with that of the new kernel tree.

The main process is to compile the kernel, read the compiler error message, and fix errors one by one. These errors are often due to kernel APIs changed name, macros that were not defined in older version, or even just typos (AMSDU mistaken as AMDSU in one of the macros in Linux 5.10). Sometimes you need to carefully read and compare the driver code in both versions to figure out if some pieces of code are necessary and what changes should be made. Have fun! The following are some example changes I made in mt76 driver:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
diff --git a/mt76x02_mmio.c b/mt76x02_mmio.c
index 96ec96d..0fa3c7c 100644
--- a/mt76x02_mmio.c
+++ b/mt76x02_mmio.c
@@ -230,8 +230,8 @@ int mt76x02_dma_init(struct mt76x02_dev *dev)
        if (ret)
                return ret;
 
-       netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
-                         mt76x02_poll_tx);
+       netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+                         mt76x02_poll_tx, NAPI_POLL_WEIGHT);
        napi_enable(&dev->mt76.tx_napi);
 
        return 0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
diff --git a/eeprom.c b/eeprom.c
index 9bc8758..2c6d845 100644
--- a/eeprom.c
+++ b/eeprom.c
@@ -107,8 +107,12 @@ mt76_eeprom_override(struct mt76_phy *phy)
 {
        struct mt76_dev *dev = phy->dev;
        struct device_node *np = dev->dev->of_node;
+       const u8 *mac = NULL;
 
-       of_get_mac_address(np, phy->macaddr);
+       if (np)
+               mac = of_get_mac_address(np);
+       if (!IS_ERR_OR_NULL(mac))
+               ether_addr_copy(phy->macaddr, mac);
 
        if (!is_valid_ether_addr(phy->macaddr)) {
                eth_random_addr(phy->macaddr);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
diff --git a/mt7921/main.c b/mt7921/main.c
index 94dd0c1..6857756 100644
--- a/mt7921/main.c
+++ b/mt7921/main.c
@@ -67,9 +67,9 @@ mt7921_init_he_caps(struct mt7921_phy *phy, enum nl80211_band band,
                        IEEE80211_HE_MAC_CAP0_HTC_HE;
                he_cap_elem->mac_cap_info[3] =
                        IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
-                       IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3;
+                       IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK;
                he_cap_elem->mac_cap_info[4] =
-                       IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU;
+                       IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU;
 
                if (band == NL80211_BAND_2GHZ)
                        he_cap_elem->phy_cap_info[0] =
@@ -135,7 +135,7 @@ mt7921_init_he_caps(struct mt7921_phy *phy, enum nl80211_band band,
                                IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE |
                                IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT;
                        he_cap_elem->phy_cap_info[7] |=
-                               IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP |
+                               IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR |
                                IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
                        he_cap_elem->phy_cap_info[8] |=
                                IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
@@ -171,8 +171,8 @@ mt7921_init_he_caps(struct mt7921_phy *phy, enum nl80211_band band,
                        mt7921_gen_ppe_thresh(he_cap->ppe_thres, nss);
                } else {
                        he_cap_elem->phy_cap_info[9] |=
-                               u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,
-                                              IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
+                               u8_encode_bits(0x2,
+                                              IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK);
                }
 
                if (band == NL80211_BAND_6GHZ) {

It’s easy, isn’t it?