# Linux 核心專題: 虛擬無線網路裝置驅動程式 > 執行人: horseface1110, dingsen-Greenhorn > [Dingsen-Greenhorn 解說影片(可調整的 MCS)](https://youtu.be/Z1twsLcdhi8) > [horseface1110 解說影片(模擬延遲)](https://youtu.be/s-zPecXAWPs) ### Reviewed by `charliechiou` 部分敘述會有全形半形的差異,如 : MCS 及 MCS 圖片的使用上可以去除原先來源的`Figure 1` 等 caption 並附上來源,建議可做修改。 >Response by 'dingsen-Greenhorn' 感謝建議,修改標題全形 MCS 統一半形。 ### Reviewed by `salmoniscute` 僅有在解說影片說明使用 Long GI 與 Short GI 的分別考量,建議可以整理在筆記 `擴展 USER 手動調整 MCS 的範圍並且同時可以調整 GI(Guard Interval)` 此 section 的開頭。 > Response by 'dingsen-Greenhorn' 感謝建議,在開頭寫出。 ### Reviewed by `liangchingyun` 是 bit rate 還是 bitrate? 須整篇統一。 > Response by 'dingsen-Greenhorn' 感謝建議,已更正。 ### Reviewed by `horseface1110` > 用簡單的方式,依照訊號強度區間來動態自動選擇 MCS 請問這之後的程式碼中,-50、-70、-90的區段劃分是有什麼理論的依據或是白皮書的指示嗎?還是單純就想這樣設定 另外,commit連結無法看到了 >Response by 'dingsen-Greenhorn' > >IEEE 802.11 標準文件不會明確指出「RSSI 到某值時應用哪個 MCS」,因為這涉及裝置廠商對 RF 硬體、通道狀況預測、錯誤率容忍度、使用情境等多種因素的調整。因此,此部分通常是個廠商演算法與競爭力核心,不會有公開文件。 你所提出的理想情況下理論依據是有的 optimal solution 是 [Water-Filling Algorithm](https://en.wikipedia.org/wiki/Water_filling_algorithm),但這裡為了實現將功能簡化,未來可以考慮。(邏輯上粗淺推論,通道增益是與功率相乘,當通道增益過小,功率必須極大,不划算) ## TODO: 重現去年實驗並確保在 Linux v6.11+ 運作 > [2024 年報告](https://hackmd.io/@sysprog/rJ-LF4nNC) > commit [800ad32](https://github.com/horseface1110/vwifi/commit/800ad3246f800839b63a256f35f605b8ec19889f) / Support Linux v6.11 - [ ] Replace legacy virtio_find_vqs() interface with struct virtqueue_info based version - [ ] Explicitly set `ctx = false` to maintain behavior of legacy API > commit [c502eb8](https://github.com/torvalds/linux/commit/c502eb85c34e29bb185e3d15dd9b8fce8a74f303) "virtio: Introduce struct virtqueue_info and find_vqs_info() config op" kernel version : v6.11-rc1 ~ date : Jul 17, 2024 > Using separate arrays for names, callbacks, and contexts when setting up virtqueues is error-prone and not extensible. To simplify and consolidate queue configuration, a new struct virtqueue_info was introduced. This structure bundles name, callback, and ctx together into a single unit. ```diff + struct virtqueue_info {Add commentMore actions + const char *name; + vq_callback_t *callback; + bool ctx; + }; + int virtio_find_vqs_info(struct virtio_device *vdev, unsigned int nvqs,Add commentMore actions + struct virtqueue *vqs[], + struct virtqueue_info vqs_info[], + struct irq_affinity *desc) + { + return vdev->config->find_vqs_info(vdev, nvqs, vqs, vqs_info, desc); + } ``` > commit [6c85d6b](https://github.com/torvalds/linux/commit/6c85d6b653caeba2ef982925703cbb4f2b3b3163)/Support Linux 6.15 kernel version : v6.11-rc1 ~ Jul 17, 2024 >Since the original virtio_find_vqs() is no longer present, rename virtio_find_vqs_info() back to virtio_find_vqs(). ```diff - int virtio_find_vqs_info(struct virtio_device *vdev, unsigned int nvqs,Add commentMore actions - struct virtqueue *vqs[], - struct virtqueue_info vqs_info[], - struct irq_affinity *desc) +int virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, + struct virtqueue *vqs[], + struct virtqueue_info vqs_info[], + struct irq_affinity *desc) ``` > commit [a50af43](https://github.com/horseface1110/vwifi/commit/a50af436163553b653da5d2d4a7118bf87c79c8e)/Support Linux 6.15 - [ ] Replace legacy hrtimer_init() with the new hrtimer_setup() API > commit [908a1d7](https://github.com/torvalds/linux/commit/908a1d775422ba2e27a5e33d0c130b522419e121) "hrtimer: Introduce hrtimer_setup() to replace hrtimer_init()" kernel version : v6.13-rc1 ~ date : Nov 7, 2024 >The legacy hrtimer_init() was replaced by hrtimer_setup() to provide a safer, clearer API. The new interface requires the callback function to be explicitly passed during initialization, avoiding assignment errors. ```diff +extern void hrtimer_setup(struct hrtimer *timer, enum hrtimer_restart (*function)(struct hrtimer *),Add commentMore actions + clockid_t clock_id, enum hrtimer_mode mode); ``` > commit [9779489](https://github.com/torvalds/linux/commit/9779489a31d77a7b9cb6f20d2d2caced4e29dbe6#diff-9e21884045055dedb4ace4d7734bf7d1369ed58c691090051bde7fb0c84172ad)"hrtimers: Delete hrtimer_init()" kernel version : v6.15-rc1 ~ date : Apr 5, 2025 > hrtimer_init() is now unused. Delete it. ```diff - extern void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,Add commentMore actions - enum hrtimer_mode mode); ``` > commit[75f36f5](https://github.com/sysprog21/vwifi/commit/75f36f59e56c9d9b8a8752eb3e37ba1568fa0983)/Support Linux v6.1.84 - [ ] Replace deprecated del_timer_sync() with timer_delete_sync() > commit [9b13df3f](https://github.com/torvalds/linux/commit/9b13df3fb64ee95e2397585404e442afee2c7d4f) "timers: Rename del_timer_sync() to timer_delete_sync()" kernel version: v6.1.84 ~ date: Nov 24, 2022 > del_timer_sync() was renamed to timer_delete_sync() for naming consistency. The old name remains as a wrapper but is discouraged in new code. ```diff - extern int del_timer_sync(struct timer_list *timer); + extern int timer_delete_sync(struct timer_list *timer);Add commentMore actions + /** + * del_timer_sync - Delete a pending timer and wait for a running callback + * @timer: The timer to be deleted + * + * See timer_delete_sync() for detailed explanation. + * + * Do not use in new code. Use timer_delete_sync() instead. + */ + static inline int del_timer_sync(struct timer_list *timer) + { + return timer_delete_sync(timer); + } ``` > commit [f82e7df](https://github.com/horseface1110/vwifi/commit/f82e7df62282fcbb043a023ed00786ecae5d2d03)/ Support Linux v6.14 - [ ] Update vwifi_get_tx_power() signature to match new MLO-compatible prototype > commit [7a53af8](https://github.com/torvalds/linux/commit/7a53af85d3bbdbe06cd47b81a6d99a04dc0a3963)"wifi: cfg80211: send MLO links tx power info in GET_INTERFACE" kernel version: v6.14-rc1 ~ date: Dec 4, 2024 > To support Multi-Link Operation (MLO), the get_tx_power callback now includes a link_id parameter. TX power can be reported per link via NL80211_ATTR_MLO_LINKS in GET_INTERFACE. For non-MLO interfaces, behavior remains unchanged. ```diff int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,Add commentMore actions - int *dbm); + unsigned int link_id, int *dbm); ``` ## TODO: 傳輸延遲應與目前 bitrate 成反比,自動或手動調整 > 傳輸延遲應能根據目前的 bitrate 動態調整,以模擬不同傳輸速率下的封包傳輸行為。 ### 改動動機與目標 該 TODO: 傳輸延遲應與目前 bitrate 成反比,自動或手動調整 由於原始的 `vwifi` 驅動將傳輸速率(bitrate)寫死為固定值(MCS 31,260 Mbps),這導致在測試環境中無法模擬不同 bitrate 對網路行為(如延遲)的影響。因此,我的目標是: 1. 支援 `iw dev set bitrates` 指令,讓使用者能透過 CLI 設定速率。 2. 將使用者設定的 bitrate mask 反映到 `station dump` 顯示與實際傳輸行為中。 3. 導入延遲模擬邏輯,讓不同的 bitrate 設定產生符合預期的延遲變化(例如傳輸速率越慢,延遲越高)。 ### 參考架構選擇與設計依據 為了找出一個實作 `set_bitrate_mask` 的標準範例,我閱讀了多個已有驅動的實作邏輯,包括: - `ath9k_htc_set_bitrate_mask`(有實作) - `ieee80211_set_bitrate_mask`(有實作) - `drv_set_bitrate_mask` - `rdev_set_bitrate_mask` 其中我選擇主要參考 **mac80211 子系統下的 `ieee80211_set_bitrate_mask()`**,原因如下: - 該實作與 `vwifi` 虛擬設備所在層級相同,需要的參數也相同,更適合作為設計參考。 - 它處理了許多重要檢查,包括: - 網卡是否啟動 (`ieee80211_sdata_running`) - 使用者設定的速率是否涵蓋基本速率(basic rates) - 是否需要委託驅動進行速率控制(`HAS_RATE_CONTROL`) - 針對他的實作內容,我擬出了三項應該復刻的功能 - 這張網卡介面有打開嗎 - 檢查使用者設定的速率會不會讓大家都收不到訊號 - 把使用者的設定記下來 我將這些核心邏輯濃縮後,實現在 `vwifi_set_bitrate_mask()` 中,並以 `bitmask` 判斷使用者是否設定了合理的速率範圍(以 `vwifi_supported_rates[]` 為依據定義 2.4GHz / 5GHz 的合法範圍)。 ### mask 儲存 為了讓 `bitrate_mask` 的設定不只是一個瞬時指令,而是能影響整體傳輸與查詢行為,我在 `vwifi` 的結構中新增儲存欄位: ```c // 在 struct vwifi_vif 中新增一個欄位 struct cfg80211_bitrate_mask bitrate_mask; // 在 vwifi_set_bitrate_mask 中保存使用者設定的值 memcpy(&vif->bitrate_mask, mask, sizeof(*mask)); pr_info("vwifi: bitrate mask saved. legacy 2.4GHz = 0x%x\n", mask->control[NL80211_BAND_2GHZ].legacy); ``` 這段實作有兩個目的: 1. **在 `vwifi_vif` 中預留空間保存設定值**,避免設定後在後續流程中失效。 2. **讓其他模組(如 `get_station()` 與封包傳輸)能夠引用相同設定**,確保查詢與行為一致。 接著,在 `get_station` 中我從 `vif->bitrate_mask` 推算出應顯示的傳輸速率;而在傳輸封包時,則根據這個速率計算應延遲的時間,並使用 `udelay()` 模擬出來。 不過在實作延遲模擬的過程中,我發現無法在 `__vwifi_ndo_start_xmit()` 中直接呼叫 `vwifi_get_station()`,查明後發現`vwifi_get_station` 是專為 cfg80211 框架設計的接口函數,他複雜的參數結構和錯誤處理機制使其不適合作為內部函數直接呼叫 ### 單位處理與延遲模擬設計 在實作延遲模擬(於 `__vwifi_ndo_start_xmit()` 函式中)時,我遇到一個問題是「如何從 `bitrate_mask` 中取出正確的傳輸速率」。最初我考慮是否需要進行單位換算,但後來深入閱讀了 [iw](https://github.com/drygdryg/iw) 的原始碼與 Linux 的 `nl80211.h` 定義,發現: > `NL80211_RATE_INFO_BITRATE` 的單位為 **100 kbps**,而 `vwifi_supported_rates[].bitrate` 的單位亦相同。 這讓我確認可以直接使用 `vwifi_supported_rates[i].bitrate`(不需再乘上 10 或 1000),並應用於延遲計算公式: ```c int delay_us = (datalen * 8 * 1000) / bitrate_kbps; ``` 此公式的邏輯為:將資料長度轉為 bit,再除以 kbps 速率,得出理論延遲(單位為 microseconds),透過 `udelay()` 注入延遲模擬。 這樣的設計讓系統可以根據設定的 bitrate 自動對應不同的延遲行為,並在 `station dump` 中正確顯示對應速率。 --- ### 測試結果與延遲圖表 為驗證我的 `bitrate_mask` 實作是否實際影響傳輸延遲,我設計了一組系統性實驗,模擬多組不同速率設定下的實際傳輸表現。具體流程如下: 1. 1. **使用 `sudo ip netns exec ns0 iw dev vw0 set bitrates legacy-2.4 <bitrate_list>` 指令設定 `bitmask`**。 - `bitrate_list` 的內容依據 `vwifi_supported_rates[]` 所定義的速率順序進行選擇。 - 2.4GHz 頻段支援的速率為全部(1、2、5.5、11 Mbps)。 - 5GHz 頻段則對應後面 8 項(6~54 Mbps)。 2. 對每組設定執行 10 次 ping 測試,每次傳送 5 個 ICMP 封包。 3. 每次 ping 結果中移除單次最大值,保留其餘 4 筆 time 數據,減少 outlier 影響。 4. 將每組設定產生的 10 次 × 4 筆 latency 值,共 40 筆資料記錄下來。 5. 針對每個 bitrate mask 分別計算其平均延遲(avg latency),並將所有結果彙整輸出成統一的統計檔案與圖表。 目前的行為設計如下: - 當使用者設定多個合法的傳輸速率(例如 `1 2 11`),驅動會從 bitmask 中選取最低的速率作為該連線實際使用的 `txrate` / `rxrate`。 - 這個選擇邏輯對應實作中的 `for (int i = 0; i < 32; i++)`,會從 bitmask 的最低有效位元開始找第一個被設定的速率。 根據這些測試,我繪製出下方圖表,可明顯看出 **bitrate 越低,平均延遲越高** ![image](https://hackmd.io/_uploads/SJ36fgeEgx.png) 如圖所示,bitrate 數值越低時,平均延遲明顯上升;當 bitrate 提升至一定程度後,延遲曲線趨於平緩,呈現合理的反比趨勢,驗證模擬行為符合預期。 而這是沒有依照bitrate調整延遲的情況: ```shell udo ip netns exec ns0 ping -c 5 10.0.0.2 PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data. 64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.664 ms 64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.118 ms 64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.130 ms 64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.127 ms 64 bytes from 10.0.0.2: icmp_seq=5 ttl=64 time=0.128 ms --- 10.0.0.2 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4133ms rtt min/avg/max/mdev = 0.118/0.233/0.664/0.215 ms ``` 此時的vwifi沒有實作延遲模擬,這些延遲是來自於系統的實際處理時間 ### 新增MCS table #### 動機 為支援動態環境下的 MCS 選擇邏輯,需實作 MCS 查表功能以便快速對應 MCS index 至實際資料速率與 GI 組合。此查表亦用於 station_dump 與傳輸時間模擬中,並支援 iw dev set bitrates 指令後的即時速率查詢與驗證,故將 MCS 表建置為全域結構以提升查詢效率與可維護性。 #### 表格與struct設計 為支援 HT 模式(802.11n)在 2.4 GHz 頻段下的傳輸速率計算與模擬,新增靜態 `ht_mcs_table`,涵蓋 MCS index 0–31,對應 20 MHz 頻寬下 0.8 µs 及 0.4 µs GI(Guard Interval)所對應的 bitrate(Kbps)。 ```c struct ht_mcs_entry { int mcs_index; int bitrate_20mhz_gi08; // in Mbps int bitrate_20mhz_gi04; // in Mbps }; ``` 該表格作為全域結構,支援快速查詢速率,便於未來支援: - 傳輸時間模擬使用(tx delay simulation) - station_dump 顯示當前速率 - `iw dev set bitrates ht-mcs-2.4` 指令執行後之速率驗證 另提供 `get_ht_bitrate_kbps()` 輔助函式,可依據 `vwifi_vif` 中當前的 MCS index 與 GI 設定,查詢實際資料速率(單位:Kbps): ```c static inline int get_ht_bitrate_kbps(const struct vwifi_vif *vif, int mcs_index) { ... } ``` ### 結語 目前整體流程已可完成: - 使用者設定速率 → 被驅動層解析並保存 → 顯示與傳輸同步更新 → 傳輸時延遲自動模擬。 - MCS to bitrate查詢表格 :::warning TODO: 提交延遲測試和分析的自動化腳本 TODO: 針對應用場景去提供延遲模擬 ::: --- ## TODO: 可調整的 MCS (dingsen-Greenhorn) > 調變與編碼組合 (Modulation and Coding Scheme, MCS) 應允許程式化控制與調整,以符合各種測試場景中對於 PHY 層吞吐量模擬的需求。 > 在 IEEE 802.11 無線網路架構中,MCS 決定了所使用的調變方式(如 BPSK、QPSK、16-QAM 等)以及 FEC 編碼率 (如 1/2、3/4),因此對 bitrate 與傳輸延遲具直接影響。現代無線網路裝置通常支援根據訊號品質進行動態 MCS 調整 (即 rate adaptation),而在虛擬裝置中,若能以使用者空間介面(例如 netlink 或 debugfs)手動調整 MCS,將有助於研究 WiFi stack 在不同傳輸品質下的行為。 ### 根據通道強度,動態自動調整 MCS #### 想法與目標 由於前一版本 vwifi driver,是無法調整調變與編碼(MCS fixed 31, lgi, 260 Mpbs),進而調整其傳輸速率,這對於許多設備,像是邊緣裝置而言,會有許多的資源浪費。舉手機為例,通常的使用習慣是少數時間下載與看影片時,有大量傳輸的需求,其他大多數時間,只需要少數傳輸需求。然而固定 64-QAM 的調變會大幅增加功率消耗,對於邊緣裝置而言,是非常致命的,會大幅降低裝置使用時間。 以下,大略粗估 QPSK (MCS 1) 與 64-QAM (MCS 31) 在相同錯誤率下所需的功率比較。(不考慮多天線與編碼) [錯誤率估計相關文獻](https://www.researchgate.net/publication/270274327_A_Systematic_Study_of_Coding_Performance_in_a_MIMO-STBC-OFDM_Link) ![截圖 2025-06-27 下午4.21.58](https://hackmd.io/_uploads/B1lGH0sNge.png) 從以上這張圖可以看到,在 Rayleigh fading 通道下(無線中裝置在典型的城市中通道)錯誤率與功率比較。 可以看到在固定(uncoded)錯誤率 10^-4 下,QPSK 與 64-QAM 功率差距超過 26dB。(功率差距超過 398 倍)(圖中黑線) 這裡只單純考慮傳送功率,現實環境中 High-QAM 還會進一步增加耗能元件功率,使用者之間的干擾,裝置發熱所造成的效能降低等等...。 因此,現代無線通訊裝置,通常會動態調整 MCS,來大幅降低功率消耗,這也是我實作的最初動機。 > commit [0c6c24b](https://github.com/sysprog21/vwifi/pull/80/commits/0c6c24b50f5e1b79981772a317958091d764d64f) 這個 commit 主要依照以下模擬的通道訊號強度來決定所使用的 MCS。 ```c /* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in dBm */ sinfo->signal = rand_int_smooth(-100, -30, jiffies); ``` 用簡單的方式,依照訊號強度區間來動態自動選擇 MCS ```c /* Dynamic modulation based on signal strength */ int mcs_index; const char *modulation; if (sinfo->signal > -50) { /* Strong signal: 64-QAM, MCS 31 */ mcs_index = 31; modulation = "64-QAM"; } else if (sinfo->signal > -70 && sinfo->signal <= -50) { /* Medium signal: 16-QAM, MCS 23 */ mcs_index = 23; modulation = "16-QAM"; } else if (sinfo->signal > -90 && sinfo->signal <= -70) { /* Weak signal: QPSK, MCS 15 */ mcs_index = 15; modulation = "QPSK"; } else { /* Very weak signal: BPSK, MCS 7 */ mcs_index = 7; modulation = "BPSK"; } ``` > commit [0c6c24b](https://github.com/sysprog21/vwifi/pull/80/commits/0c6c24b50f5e1b79981772a317958091d764d64f) --- ### User 根據自身需求手動調整 MCS #### 想法與目標 此想法主要來自我的 reviewer `@jychen0611` 他提出一個有趣的想法,就是使用者可以根據自身需求來調整 MCS 。 #### 初步架構 實現的方法架構為: ```graphviz digraph mcs_setting_flow { rankdir=TB; node [shape=box, style=rounded, fontsize=10, fontname="Arial"]; userspace [label="Userspace\n(iw tool)\n (set bitrates) 手動輸入 MCS " ]; cfg80211 [label="Kernel: cfg80211_ops\n set_bitrate_mask(vif, mask)\n← 儲存手動設定的 MCS"]; vwifi_struct [label="vwifi.c\n vif->manual_mcs\n vif->manual_mcs_set = true"]; get_station [label="vwifi_get_station()\n if manual_mcs_set:\n use manual_mcs (設定輸入的 MCS)\n else:\n use signal strength to select MCS (auto)"]; userspace -> cfg80211 -> vwifi_struct -> get_station; } ``` 依照上述架構實現出可以手動調整的 MCS #### 實現講解 實作 `cfg80211_ops` 中的 `set_bitrate_mask` 回呼函式以支援手動 MCS 設定 >功能說明 > 本次實作在 Linux 無線網路架構中的 `cfg80211_ops` 結構體中,加入 `set_bitrate_mask` 回呼函式,用於支援使用者透過 `iw` 工具手動設定 Modulation and Coding Scheme(MCS) 指數。這項功能允許開發者或進階使用者在測試環境中精確控制 Wi-Fi 傳輸的調變與編碼方式。 >入口點:使用者設定 > 透過 `iw dev <dev> set bitrates ht-mcs-2.4 <mcs>`,由 `cfg80211` 呼叫驅動提供的 `set_bitrate_mask()`。 驅動在 `vwifi_cfg_ops` 中註冊了此 callback: ```c .set_bitrate_mask = vwifi_set_bitrate_mask, ``` >MCS 解析與驗證 > 驅動從 `mask->control[NL80211_BAND_2GHZ].ht_mcs[i]` 提取 MCS bitmask。 搜尋前 32 個 MCS index(4 個 byte × 8 bit)並取第一個有效的 index。 僅允許 index 為 {7, 15, 23, 31},否則拒絕設定。 >儲存使用者設定 > 將選定的 MCS index 存入 vwifi_vif 結構: ```c vif->manual_mcs = mcs_index; vif->manual_mcs_set = true; ``` 若無法取得合法 MCS 或介面未連線(`SME_CONNECTED`),則記錄錯誤並返回 `-EINVAL`。 跨模組共享使用者設定,未來當 `vwifi_get_station()` 被呼叫查詢速率資訊時,若 `manual_mcs_set`為 `true`,即回傳 `manual_mcs` 對應速率。 否則落回自動 MCS 選擇策略(如 RSSI-based selection),也就是 commit [0c6c24b](https://github.com/sysprog21/vwifi/pull/80/commits/0c6c24b50f5e1b79981772a317958091d764d64f) 實現的功能。 >儲存欄位擴充說明 > 在 vwifi_vif 結構中新增欄位,以保存完整的 bitrate 設定,支援未來查詢或封包模擬: `struct cfg80211_bitrate_mask bitrate_mask`; 並於 callback 函式中保存使用者設定: ```c memcpy(&vif->bitrate_mask, mask, sizeof(*mask)); ``` #### 測試 Test commands format ```c $sudo ip netns exec ns1 iw dev vw1 link $sudo ip netns exec ns1 iw dev vw1 set bitrates ht-mcs-2.4 31(changable) $sudo ip netns exec ns1 iw dev vw1 link ``` result ```clike $ sudo ip netns exec ns1 iw dev vw1 link $ sudo ip netns exec ns1 iw dev vw1 set bitrates ht-mcs-2.4 31 $ sudo ip netns exec ns1 iw dev vw1 link Connected to 00:76:77:30:00:00 (on vw1) SSID: test freq: 2437.0 RX: 282 bytes (2 packets) TX: 904 bytes (10 packets) signal: -67 dBm rx bitrate: 78.0 MBit/s MCS 26 tx bitrate: 78.0 MBit/s MCS 26 Connected to 00:76:77:30:00:00 (on vw1) SSID: test freq: 2437.0 RX: 282 bytes (2 packets) TX: 904 bytes (10 packets) signal: -57 dBm rx bitrate: 260.0 MBit/s MCS 31 tx bitrate: 260.0 MBit/s MCS 31 ``` > commit [a9b4edd](https://github.com/sysprog21/vwifi/pull/80/commits/a9b4edd9cd1b0add006aba5314d5501dfdf629f0) --- ### 將手動與自動 MCS 擴充至 24-31 #### 開發背景與目標 為什麼現代無線通訊系統要用多天線 (spatial stream) 呢?有什麼好處? 由於基地台會同時服務多個用戶,也因此需要多個 RF-Chain 來分別傳輸不同資料流,當有 N 個用戶時,RF-Chain 必須至少 N 個才能滿足不同用戶同時傳輸。 並且就算在一個用戶的情形下,多天線也有極大好處。 由 [Shannon's Channel Capacity theorem 相關文獻](https://mail.sharetechnote.com/html/Handbook_Communication_ChannelCapacity.html) 。我們可以用數學非常清楚證明出的通道中的 Upper bond 的極限 Capacity,也就是可以傳輸的最大速率。 如以下圖片: ![截圖 2025-06-28 凌晨3.14.26](https://hackmd.io/_uploads/S1Ls6v3Ele.png) 此數學式為: ![截圖 2025-06-28 凌晨3.09.38](https://hackmd.io/_uploads/SJxpTawhNeg.png) 從上圖中可以看到,當我增加我的傳輸功率(可以粗略的想成增加 QAM ) 我所得到的傳輸速率會在我逐漸增加功率時,被一個緩慢遞增的 log2 函數限制,也就是說當增加功率時,所能得到的 Capacity(頻譜使用效率)越來越少。 當然從這個公式中的 B 可以突破限制,使得 Capacity 線性成長。然而,現實中, 頻譜是一個非常寶貴的資源,需要與政府購買。甚至像是 Wi-Fi 使用的是 ISM band 為免費資源。有此可知增加頻段很難實現。 那解決的方法就是 MIMO 多天線系統,他會使得上面數學式變成以下情況。 ![截圖 2025-06-28 凌晨3.09.22](https://hackmd.io/_uploads/BJ5Ye_3Elg.png) M 就是我的天線數量,當我增加天線時,也可以線性增加 Capacity,避免了 log2 的速率成長,也同時避免增加昂貴的頻譜資源。 因此,在通訊的技術發展上,增加天線數,成為關鍵技術,也是現代無線通訊裝置的必要技術之一。 由上可以知道,我們可以進一步將多天線考慮進 vwifi driver 實現的方向。 #### 擴展功能 原始 vwifi 驅動程式中,手動 MCS 僅支援部分固定 index(如 7、15、23、31),對於 4 spatial streams 對應的 MCS 24–31 並未完整支援。 本次開發的目標為: 擴展 `vwifi_set_bitrate_mask()` 的驗證邏輯以允許 MCS 24–31。 在 `vwifi_get_station()` 中增加對 MCS 24–31 對應的 modulation 和 coding scheme(例如 BPSK、QPSK、16-QAM、64-QAM)。 | MCS | Modulation | Coding Rate | Spatial Streams | | --- | ---------- | ----------- | --------------- | | 24 | BPSK | 1/2 | 4 | | 25 | QPSK | 1/2 | 4 | | 26 | QPSK | 3/4 | 4 | | 27 | 16-QAM | 1/2 | 4 | | 28 | 16-QAM | 3/4 | 4 | | 29 | 64-QAM | 2/3 | 4 | | 30 | 64-QAM | 3/4 | 4 | | 31 | 64-QAM | 5/6 | 4 | 並考慮編碼與資訊流(也就是 最少 4 個 RF-Chain ,與最少 4 隻天線) 以下為 commit code 參考。 > commit [9f8dfc2](https://github.com/sysprog21/vwifi/pull/80/commits/9f8dfc2accdf87ae4a8bd814d0ff532e38733ce8) > > commit [fc181b9](https://github.com/sysprog21/vwifi/pull/80/commits/fc181b9a0963238c5bf7cb583994f39202464472) --- ### 擴展 USER 手動調整 MCS 的範圍並且同時可以調整 GI(Guard Interval) 擴展手動調整功能並考量同時可調整 GI (guard Interval) >先說結論,城市中適合 long GI ,鄉村適合 short GI。 GI(Guard Interval,保護間隔)是現代 OFDM(正交分頻多工)系統中的一項關鍵技術,特別在 Wi-Fi(如 802.11n/ac/ax)或 LTE/5G 中扮演抑制干擾與增進系統穩定性的角色。以下我們詳細說明它所解決的三大問題與其相關背景: 1. ISI(Inter-Symbol Interference,符號間干擾) 問題: ISI 是當前一個符號還未完全接收完畢時,下一個符號已經開始進入接收器所造成的干擾。在多路徑傳播環境中,前一個符號的尾端會以延遲形式(multipath delay)重疊到下一個符號的開頭,導致接收錯誤。 GI 的解決方法: 為了防止多路徑延遲干擾下一個符號,GI 被插入於每個 OFDM 符號之間,其長度 至少與最大多徑延遲相當,這樣即使有延遲,重疊的訊號也只會落在 GI 範圍中,而不會影響下一個有效符號。 成效: * 減少 BER(Bit Error Rate) * 增強穩定性與通訊可靠性 2. ICI(Inter-Carrier Interference,子載波間干擾) 問題描述: OFDM 利用「正交」子載波傳送不同數據。然而,由於多路徑或頻率偏移(如 Doppler shift),這些子載波可能不再完全正交,導致頻率重疊與干擾,也就是 ICI。 GI 的解決方法: 雖然 GI 本身不是直接為 ICI 設計,但搭配「循環前綴」(cyclic prefix)可以使子載波保持正交性,進一步降低 ICI。 成效: * 保持子載波正交性,減少頻域間干擾 * 穩定 FFT 解調過程,維持頻率解析度 3. Prefix 與 Circular Convolution(循環卷積) 為什麼要使用 Cyclic Prefix(CP)? 在離散傅立葉轉換(DFT)領域,卷積對應到頻域乘法。但若接收器採用 FFT(有限長度 DFT),則「線性卷積」會產生邊界效應。 GI 作為 Cyclic Prefix: GI 的內容是 將 OFDM 符號尾端的一段資料複製到開頭,這稱為「循環前綴」。這麼做的好處是: * 能夠將通道效應視為「循環卷積」 * FFT 解調後可直接使用頻域等化(例如單一子載波頻率上的簡單除法) 好處與優點: * 減少通道估計與等化的複雜度 * 提升解調準確率 * 與 ISI/ICI 抑制聯合作用 實現講解 `gi_mode` 模組參數支援 你加入了新的模組參數 `gi_mode`,可讓使用者透過 `modprobe` 或 `echo` 寫入來切換 Long GI(0.8 µs)與 Short GI(0.4 µs),取代原本寫死的: ```C vif->gi = VWIFI_TXRATE_GI_800NS; ``` 在 `vwifi_set_bitrate_mask()` 函數中,根據 `bitrate_mask.control[].gi` 值解析 GI 模式,並記錄 log: ```C switch (vif->bitrate_mask.control[NL80211_BAND_2GHZ].gi) { case NL80211_TXRATE_FORCE_SGI: gi_str = "short (0.4µs)"; break; case NL80211_TXRATE_FORCE_LGI: gi_str = "long (0.8µs)"; break; default: gi_str = "default (0.8µs)"; break; } pr_info("vwifi: Set GI to %s\n", gi_str); ``` 這樣在實驗時能透過 GI 選擇參數靈活調整傳輸效率與抗干擾能力。 2. MCS 解析與手動選擇 你透過 `bitrate_mask.control[].ht_mcs[]` 的 bitmask 判斷啟用的 MCS 指數,新增 loop 遍歷並找出最高有效的 MCS: ```C for (i = 31; i >= 0; i--) { if (vif->bitrate_mask.control[NL80211_BAND_2GHZ].ht_mcs[i / 8] & (1 << (i % 8))) { vif->manual_mcs = i; pr_info("vwifi: Selected highest MCS %d for configuration\n", i); break; } } ``` 此段確保使用者在啟用多個 MCS 時,優先採用最高者,兼顧性能與測試效率。若沒設定,預設回退為 MCS 0。 3. 調變與編碼率解析 你加入 MCS 對應到 modulation 與 coding rate 的 mapping 表,在如 `vwifi_get_station()`報告時印出對應資訊: ```C case 0, 8, 16, 24 → BPSK 1/2 case 1, 9, 17, 25 → QPSK 1/2 ... case 7, 15, 23, 31 → 64-QAM 5/6 ``` 這對 debug 及測試時了解每個傳輸模式的實際調變與編碼非常重要,並符合 802.11n 規格。 4. GI 標記反饋至 `get_station` 在 `vwifi_get_station()` 中新增回報 `txrate.flags`: ``` if (vif->gi == VWIFI_TXRATE_GI_400NS) sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; ``` 讓 `iw dev link` 可正確反映使用的是 Short GI 或 Long GI,達成用戶層觀察傳輸行為的能力。 詳細參考此 commit 部分 > commit [acc1bd1](https://github.com/sysprog21/vwifi/pull/80/commits/acc1bd147c739816f8c2993de9cce0324371c4a5) > > commit [75fc59c](https://github.com/sysprog21/vwifi/pull/80/commits/75fc59c8bd32b9436aef1225d804bd6a9eb70dfe) --- ## 最終 MCS 詳細的測試結果與方法 `Verify.sh` 為了自動化測試所有實作的 MCS table 與不同 GI (guard interval) 寫出 [Verify.sh](https://github.com/sysprog21/vwifi/pull/80/files來測試) 詳細參考此 commit script 部分 > commit [75fc59c](https://github.com/sysprog21/vwifi/pull/80/commits/75fc59c8bd32b9436aef1225d804bd6a9eb70dfe) Result:(part of the log) ```c Successfully initialized wpa_supplicant Successfully initialized wpa_supplicant ---------------------------------------- Testing MCS 0 with lgi-2.4 on vw1 Set GI to long (0.8 µs) Connected to 00:76:77:30:00:00 (on vw1) SSID: test freq: 2437.0 RX: 282 bytes (2 packets) TX: 764 bytes (8 packets) signal: -54 dBm rx bitrate: 6.5 MBit/s MCS 0 tx bitrate: 6.5 MBit/s MCS 0 Success: MCS 0 lgi-2.4 bitrate 6.5 Mbps matches expected 6.5 Mbps ---------------------------------------- ---------------------------------------- Testing MCS 1 with lgi-2.4 on vw1 Set GI to long (0.8 µs) Connected to 00:76:77:30:00:00 (on vw1) SSID: test freq: 2437.0 RX: 282 bytes (2 packets) TX: 764 bytes (8 packets) signal: -53 dBm rx bitrate: 13.0 MBit/s MCS 1 tx bitrate: 13.0 MBit/s MCS 1 Success: MCS 1 lgi-2.4 bitrate 13.0 Mbps matches expected 13.0 Mbps ---------------------------------------- ---------------------------------------- Testing MCS 0 with sgi-2.4 on vw1 Set GI to short (0.4 µs) Connected to 00:76:77:30:00:00 (on vw1) SSID: test freq: 2437.0 RX: 282 bytes (2 packets) TX: 974 bytes (11 packets) signal: -91 dBm rx bitrate: 7.2 MBit/s MCS 0 short GI tx bitrate: 7.2 MBit/s MCS 0 short GI Success: MCS 0 sgi-2.4 bitrate 7.2 Mbps matches expected 7.2 Mbps ---------------------------------------- ---------------------------------------- Testing MCS 1 with sgi-2.4 on vw1 Set GI to short (0.4 µs) Connected to 00:76:77:30:00:00 (on vw1) SSID: test freq: 2437.0 RX: 282 bytes (2 packets) TX: 974 bytes (11 packets) signal: -91 dBm rx bitrate: 14.4 MBit/s MCS 1 short GI tx bitrate: 14.4 MBit/s MCS 1 short GI Success: MCS 1 sgi-2.4 bitrate 14.4 Mbps matches expected 14.4 Mbps ---------------------------------------- ```