diff --git a/package/kernel/mac80211/patches/rtl/001-rtw88-Call-rtw_fw_beacon_filter_config-with-rtwdev--mutex-held.patch b/package/kernel/mac80211/patches/rtl/001-rtw88-Call-rtw_fw_beacon_filter_config-with-rtwdev--mutex-held.patch new file mode 100644 index 000000000..45d35574c --- /dev/null +++ b/package/kernel/mac80211/patches/rtl/001-rtw88-Call-rtw_fw_beacon_filter_config-with-rtwdev--mutex-held.patch @@ -0,0 +1,14 @@ +diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c +index 5cdc54c9a9aae..3c07485d6ba47 100644 +--- a/drivers/net/wireless/realtek/rtw88/mac80211.c ++++ b/drivers/net/wireless/realtek/rtw88/mac80211.c +@@ -466,8 +466,8 @@ static int rtw_ops_sta_remove(struct ieee80211_hw *hw, + { + struct rtw_dev *rtwdev = hw->priv; + +- rtw_fw_beacon_filter_config(rtwdev, false, vif); + mutex_lock(&rtwdev->mutex); ++ rtw_fw_beacon_filter_config(rtwdev, false, vif); + rtw_sta_remove(rtwdev, sta, true); + mutex_unlock(&rtwdev->mutex); + diff --git a/package/kernel/mac80211/patches/rtl/002-rtw88-Drop-rf_lock.patch b/package/kernel/mac80211/patches/rtl/002-rtw88-Drop-rf_lock.patch new file mode 100644 index 000000000..b5060739b --- /dev/null +++ b/package/kernel/mac80211/patches/rtl/002-rtw88-Drop-rf_lock.patch @@ -0,0 +1,117 @@ +diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c +index 1a52ff585fbc7..ba5ba852efb8c 100644 +--- a/drivers/net/wireless/realtek/rtw88/debug.c ++++ b/drivers/net/wireless/realtek/rtw88/debug.c +@@ -144,7 +144,9 @@ static int rtw_debugfs_get_rf_read(struct seq_file *m, void *v) + addr = debugfs_priv->rf_addr; + mask = debugfs_priv->rf_mask; + ++ mutex_lock(&rtwdev->mutex); + val = rtw_read_rf(rtwdev, path, addr, mask); ++ mutex_unlock(&rtwdev->mutex); + + seq_printf(m, "rf_read path:%d addr:0x%08x mask:0x%08x val=0x%08x\n", + path, addr, mask, val); +@@ -418,7 +420,9 @@ static ssize_t rtw_debugfs_set_rf_write(struct file *filp, + return count; + } + ++ mutex_lock(&rtwdev->mutex); + rtw_write_rf(rtwdev, path, addr, mask, val); ++ mutex_unlock(&rtwdev->mutex); + rtw_dbg(rtwdev, RTW_DBG_DEBUGFS, + "write_rf path:%d addr:0x%08x mask:0x%08x, val:0x%08x\n", + path, addr, mask, val); +@@ -523,6 +527,8 @@ static int rtw_debug_get_rf_dump(struct seq_file *m, void *v) + u32 addr, offset, data; + u8 path; + ++ mutex_lock(&rtwdev->mutex); ++ + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { + seq_printf(m, "RF path:%d\n", path); + for (addr = 0; addr < 0x100; addr += 4) { +@@ -537,6 +543,8 @@ static int rtw_debug_get_rf_dump(struct seq_file *m, void *v) + seq_puts(m, "\n"); + } + ++ mutex_unlock(&rtwdev->mutex); ++ + return 0; + } + +@@ -1027,6 +1035,8 @@ static void dump_gapk_status(struct rtw_dev *rtwdev, struct seq_file *m) + dm_info->dm_flags & BIT(RTW_DM_CAP_TXGAPK) ? '-' : '+', + rtw_dm_cap_strs[RTW_DM_CAP_TXGAPK]); + ++ mutex_lock(&rtwdev->mutex); ++ + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { + val = rtw_read_rf(rtwdev, path, RF_GAINTX, RFREG_MASK); + seq_printf(m, "path %d:\n0x%x = 0x%x\n", path, RF_GAINTX, val); +@@ -1036,6 +1046,7 @@ static void dump_gapk_status(struct rtw_dev *rtwdev, struct seq_file *m) + txgapk->rf3f_fs[path][i], i); + seq_puts(m, "\n"); + } ++ mutex_unlock(&rtwdev->mutex); + } + + static int rtw_debugfs_get_dm_cap(struct seq_file *m, void *v) +diff --git a/drivers/net/wireless/realtek/rtw88/hci.h b/drivers/net/wireless/realtek/rtw88/hci.h +index 4c6fc6fb3f83b..830d7532f2a35 100644 +--- a/drivers/net/wireless/realtek/rtw88/hci.h ++++ b/drivers/net/wireless/realtek/rtw88/hci.h +@@ -166,12 +166,11 @@ static inline u32 + rtw_read_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, + u32 addr, u32 mask) + { +- unsigned long flags; + u32 val; + +- spin_lock_irqsave(&rtwdev->rf_lock, flags); ++ lockdep_assert_held(&rtwdev->mutex); ++ + val = rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask); +- spin_unlock_irqrestore(&rtwdev->rf_lock, flags); + + return val; + } +@@ -180,11 +179,9 @@ static inline void + rtw_write_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, + u32 addr, u32 mask, u32 data) + { +- unsigned long flags; ++ lockdep_assert_held(&rtwdev->mutex); + +- spin_lock_irqsave(&rtwdev->rf_lock, flags); + rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); +- spin_unlock_irqrestore(&rtwdev->rf_lock, flags); + } + + static inline u32 +diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c +index 8b9899e41b0bb..f9864840ffd9c 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.c ++++ b/drivers/net/wireless/realtek/rtw88/main.c +@@ -1994,7 +1994,6 @@ int rtw_core_init(struct rtw_dev *rtwdev) + skb_queue_head_init(&rtwdev->coex.queue); + skb_queue_head_init(&rtwdev->tx_report.queue); + +- spin_lock_init(&rtwdev->rf_lock); + spin_lock_init(&rtwdev->h2c.lock); + spin_lock_init(&rtwdev->txq_lock); + spin_lock_init(&rtwdev->tx_report.q_lock); +diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h +index 17815af9dd4ea..df6c6032bbd3b 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.h ++++ b/drivers/net/wireless/realtek/rtw88/main.h +@@ -1994,9 +1994,6 @@ struct rtw_dev { + /* ensures exclusive access from mac80211 callbacks */ + struct mutex mutex; + +- /* read/write rf register */ +- spinlock_t rf_lock; +- + /* watch dog every 2 sec */ + struct delayed_work watch_dog_work; + u32 watch_dog_cnt; diff --git a/package/kernel/mac80211/patches/rtl/003-rtw88-Drop-h2c.lock.patch b/package/kernel/mac80211/patches/rtl/003-rtw88-Drop-h2c.lock.patch new file mode 100644 index 000000000..a753e2d33 --- /dev/null +++ b/package/kernel/mac80211/patches/rtl/003-rtw88-Drop-h2c.lock.patch @@ -0,0 +1,97 @@ +diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c +index ba5ba852efb8c..79939aa6b752c 100644 +--- a/drivers/net/wireless/realtek/rtw88/debug.c ++++ b/drivers/net/wireless/realtek/rtw88/debug.c +@@ -396,7 +396,9 @@ static ssize_t rtw_debugfs_set_h2c(struct file *filp, + return -EINVAL; + } + ++ mutex_lock(&rtwdev->mutex); + rtw_fw_h2c_cmd_dbg(rtwdev, param); ++ mutex_unlock(&rtwdev->mutex); + + return count; + } +diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c +index aa2aeb5fb2ccd..c3ad2a1b47212 100644 +--- a/drivers/net/wireless/realtek/rtw88/fw.c ++++ b/drivers/net/wireless/realtek/rtw88/fw.c +@@ -320,7 +320,7 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, + h2c[3], h2c[2], h2c[1], h2c[0], + h2c[7], h2c[6], h2c[5], h2c[4]); + +- spin_lock(&rtwdev->h2c.lock); ++ lockdep_assert_held(&rtwdev->mutex); + + box = rtwdev->h2c.last_box_num; + switch (box) { +@@ -342,7 +342,7 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, + break; + default: + WARN(1, "invalid h2c mail box number\n"); +- goto out; ++ return; + } + + ret = read_poll_timeout_atomic(rtw_read8, box_state, +@@ -351,7 +351,7 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, + + if (ret) { + rtw_err(rtwdev, "failed to send h2c command\n"); +- goto out; ++ return; + } + + for (idx = 0; idx < 4; idx++) +@@ -361,9 +361,6 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, + + if (++rtwdev->h2c.last_box_num >= 4) + rtwdev->h2c.last_box_num = 0; +- +-out: +- spin_unlock(&rtwdev->h2c.lock); + } + + void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c) +@@ -375,15 +372,13 @@ static void rtw_fw_send_h2c_packet(struct rtw_dev *rtwdev, u8 *h2c_pkt) + { + int ret; + +- spin_lock(&rtwdev->h2c.lock); ++ lockdep_assert_held(&rtwdev->mutex); + + FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c_pkt, rtwdev->h2c.seq); + ret = rtw_hci_write_data_h2c(rtwdev, h2c_pkt, H2C_PKT_SIZE); + if (ret) + rtw_err(rtwdev, "failed to send h2c packet\n"); + rtwdev->h2c.seq++; +- +- spin_unlock(&rtwdev->h2c.lock); + } + + void +diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c +index f9864840ffd9c..baf4d29fde678 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.c ++++ b/drivers/net/wireless/realtek/rtw88/main.c +@@ -1994,7 +1994,6 @@ int rtw_core_init(struct rtw_dev *rtwdev) + skb_queue_head_init(&rtwdev->coex.queue); + skb_queue_head_init(&rtwdev->tx_report.queue); + +- spin_lock_init(&rtwdev->h2c.lock); + spin_lock_init(&rtwdev->txq_lock); + spin_lock_init(&rtwdev->tx_report.q_lock); + +diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h +index df6c6032bbd3b..619ee6e8d2807 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.h ++++ b/drivers/net/wireless/realtek/rtw88/main.h +@@ -2018,8 +2018,6 @@ struct rtw_dev { + struct { + /* incicate the mail box to use with fw */ + u8 last_box_num; +- /* protect to send h2c to fw */ +- spinlock_t lock; + u32 seq; + } h2c; + diff --git a/package/kernel/mac80211/patches/rtl/004-rtw88-Drop-coex-mutex.patch b/package/kernel/mac80211/patches/rtl/004-rtw88-Drop-coex-mutex.patch new file mode 100644 index 000000000..c888c9aa8 --- /dev/null +++ b/package/kernel/mac80211/patches/rtl/004-rtw88-Drop-coex-mutex.patch @@ -0,0 +1,68 @@ +diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c +index cac053f485c3b..b156f8c48ffbb 100644 +--- a/drivers/net/wireless/realtek/rtw88/coex.c ++++ b/drivers/net/wireless/realtek/rtw88/coex.c +@@ -633,7 +633,7 @@ static struct sk_buff *rtw_coex_info_request(struct rtw_dev *rtwdev, + struct rtw_coex *coex = &rtwdev->coex; + struct sk_buff *skb_resp = NULL; + +- mutex_lock(&coex->mutex); ++ lockdep_assert_held(&rtwdev->mutex); + + rtw_fw_query_bt_mp_info(rtwdev, req); + +@@ -650,7 +650,6 @@ static struct sk_buff *rtw_coex_info_request(struct rtw_dev *rtwdev, + } + + out: +- mutex_unlock(&coex->mutex); + return skb_resp; + } + +diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c +index 79939aa6b752c..1453a32ea3ef0 100644 +--- a/drivers/net/wireless/realtek/rtw88/debug.c ++++ b/drivers/net/wireless/realtek/rtw88/debug.c +@@ -842,7 +842,9 @@ static int rtw_debugfs_get_coex_info(struct seq_file *m, void *v) + struct rtw_debugfs_priv *debugfs_priv = m->private; + struct rtw_dev *rtwdev = debugfs_priv->rtwdev; + ++ mutex_lock(&rtwdev->mutex); + rtw_coex_display_coex_info(rtwdev, m); ++ mutex_unlock(&rtwdev->mutex); + + return 0; + } +diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c +index baf4d29fde678..5afb8bef9696a 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.c ++++ b/drivers/net/wireless/realtek/rtw88/main.c +@@ -1998,7 +1998,6 @@ int rtw_core_init(struct rtw_dev *rtwdev) + spin_lock_init(&rtwdev->tx_report.q_lock); + + mutex_init(&rtwdev->mutex); +- mutex_init(&rtwdev->coex.mutex); + mutex_init(&rtwdev->hal.tx_power_mutex); + + init_waitqueue_head(&rtwdev->coex.wait); +@@ -2066,7 +2065,6 @@ void rtw_core_deinit(struct rtw_dev *rtwdev) + } + + mutex_destroy(&rtwdev->mutex); +- mutex_destroy(&rtwdev->coex.mutex); + mutex_destroy(&rtwdev->hal.tx_power_mutex); + } + EXPORT_SYMBOL(rtw_core_deinit); +diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h +index 619ee6e8d2807..fc27066a67a72 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.h ++++ b/drivers/net/wireless/realtek/rtw88/main.h +@@ -1507,8 +1507,6 @@ struct rtw_coex_stat { + }; + + struct rtw_coex { +- /* protects coex info request section */ +- struct mutex mutex; + struct sk_buff_head queue; + wait_queue_head_t wait; + diff --git a/package/kernel/mac80211/patches/rtl/005-rtw88-iterate-over-vif-sta-list-non-atomically.patch b/package/kernel/mac80211/patches/rtl/005-rtw88-iterate-over-vif-sta-list-non-atomically.patch new file mode 100644 index 000000000..529677c41 --- /dev/null +++ b/package/kernel/mac80211/patches/rtl/005-rtw88-iterate-over-vif-sta-list-non-atomically.patch @@ -0,0 +1,185 @@ +diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c +index e505d17f107e4..5521a7c2c1afe 100644 +--- a/drivers/net/wireless/realtek/rtw88/phy.c ++++ b/drivers/net/wireless/realtek/rtw88/phy.c +@@ -300,7 +300,7 @@ static void rtw_phy_stat_rssi(struct rtw_dev *rtwdev) + + data.rtwdev = rtwdev; + data.min_rssi = U8_MAX; +- rtw_iterate_stas_atomic(rtwdev, rtw_phy_stat_rssi_iter, &data); ++ rtw_iterate_stas(rtwdev, rtw_phy_stat_rssi_iter, &data); + + dm_info->pre_min_rssi = dm_info->min_rssi; + dm_info->min_rssi = data.min_rssi; +@@ -544,7 +544,7 @@ static void rtw_phy_ra_info_update(struct rtw_dev *rtwdev) + if (rtwdev->watch_dog_cnt & 0x3) + return; + +- rtw_iterate_stas_atomic(rtwdev, rtw_phy_ra_info_update_iter, rtwdev); ++ rtw_iterate_stas(rtwdev, rtw_phy_ra_info_update_iter, rtwdev); + } + + static u32 rtw_phy_get_rrsr_mask(struct rtw_dev *rtwdev, u8 rate_idx) +@@ -597,7 +597,7 @@ static void rtw_phy_rrsr_update(struct rtw_dev *rtwdev) + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + + dm_info->rrsr_mask_min = RRSR_RATE_ORDER_MAX; +- rtw_iterate_stas_atomic(rtwdev, rtw_phy_rrsr_mask_min_iter, rtwdev); ++ rtw_iterate_stas(rtwdev, rtw_phy_rrsr_mask_min_iter, rtwdev); + rtw_write32(rtwdev, REG_RRSR, dm_info->rrsr_val_init & dm_info->rrsr_mask_min); + } + +diff --git a/drivers/net/wireless/realtek/rtw88/ps.c b/drivers/net/wireless/realtek/rtw88/ps.c +index bfa64c038f5f0..a7213ff2c2244 100644 +--- a/drivers/net/wireless/realtek/rtw88/ps.c ++++ b/drivers/net/wireless/realtek/rtw88/ps.c +@@ -58,7 +58,7 @@ int rtw_leave_ips(struct rtw_dev *rtwdev) + return ret; + } + +- rtw_iterate_vifs_atomic(rtwdev, rtw_restore_port_cfg_iter, rtwdev); ++ rtw_iterate_vifs(rtwdev, rtw_restore_port_cfg_iter, rtwdev); + + rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE); + +diff --git a/drivers/net/wireless/realtek/rtw88/util.c b/drivers/net/wireless/realtek/rtw88/util.c +index 2c515af214e76..ed61c962fa27f 100644 +--- a/drivers/net/wireless/realtek/rtw88/util.c ++++ b/drivers/net/wireless/realtek/rtw88/util.c +@@ -105,3 +105,106 @@ void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss) + *mcs = rate - DESC_RATEMCS0; + } + } ++ ++struct rtw_stas_entry { ++ struct list_head list; ++ struct ieee80211_sta *sta; ++}; ++ ++struct rtw_iter_stas_data { ++ struct rtw_dev *rtwdev; ++ struct list_head list; ++}; ++ ++static void rtw_collect_sta_iter(void *data, struct ieee80211_sta *sta) ++{ ++ struct rtw_iter_stas_data *iter_stas = data; ++ struct rtw_stas_entry *stas_entry; ++ ++ stas_entry = kmalloc(sizeof(*stas_entry), GFP_ATOMIC); ++ if (!stas_entry) ++ return; ++ ++ stas_entry->sta = sta; ++ list_add_tail(&stas_entry->list, &iter_stas->list); ++} ++ ++void rtw_iterate_stas(struct rtw_dev *rtwdev, ++ void (*iterator)(void *data, ++ struct ieee80211_sta *sta), ++ void *data) ++{ ++ struct rtw_iter_stas_data iter_data; ++ struct rtw_stas_entry *sta_entry, *tmp; ++ ++ /* &rtwdev->mutex makes sure no stations can be removed between ++ * collecting the stations and iterating over them. ++ */ ++ lockdep_assert_held(&rtwdev->mutex); ++ ++ iter_data.rtwdev = rtwdev; ++ INIT_LIST_HEAD(&iter_data.list); ++ ++ ieee80211_iterate_stations_atomic(rtwdev->hw, rtw_collect_sta_iter, ++ &iter_data); ++ ++ list_for_each_entry_safe(sta_entry, tmp, &iter_data.list, ++ list) { ++ list_del_init(&sta_entry->list); ++ iterator(data, sta_entry->sta); ++ kfree(sta_entry); ++ } ++} ++ ++struct rtw_vifs_entry { ++ struct list_head list; ++ struct ieee80211_vif *vif; ++ u8 mac[ETH_ALEN]; ++}; ++ ++struct rtw_iter_vifs_data { ++ struct rtw_dev *rtwdev; ++ struct list_head list; ++}; ++ ++void rtw_collect_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ++{ ++ struct rtw_iter_vifs_data *iter_stas = data; ++ struct rtw_vifs_entry *vifs_entry; ++ ++ vifs_entry = kmalloc(sizeof(*vifs_entry), GFP_ATOMIC); ++ if (!vifs_entry) ++ return; ++ ++ vifs_entry->vif = vif; ++ ether_addr_copy(vifs_entry->mac, mac); ++ list_add_tail(&vifs_entry->list, &iter_stas->list); ++} ++ ++void rtw_iterate_vifs(struct rtw_dev *rtwdev, ++ void (*iterator)(void *data, u8 *mac, ++ struct ieee80211_vif *vif), ++ void *data) ++{ ++ struct rtw_iter_vifs_data iter_data; ++ struct rtw_vifs_entry *vif_entry, *tmp; ++ ++ /* &rtwdev->mutex makes sure no interfaces can be removed between ++ * collecting the interfaces and iterating over them. ++ */ ++ lockdep_assert_held(&rtwdev->mutex); ++ ++ iter_data.rtwdev = rtwdev; ++ INIT_LIST_HEAD(&iter_data.list); ++ ++ ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, ++ IEEE80211_IFACE_ITER_NORMAL, ++ rtw_collect_vif_iter, &iter_data); ++ ++ list_for_each_entry_safe(vif_entry, tmp, &iter_data.list, ++ list) { ++ list_del_init(&vif_entry->list); ++ iterator(data, vif_entry->mac, vif_entry->vif); ++ kfree(vif_entry); ++ } ++} +diff --git a/drivers/net/wireless/realtek/rtw88/util.h b/drivers/net/wireless/realtek/rtw88/util.h +index 0c23b5069be0b..dc89655254002 100644 +--- a/drivers/net/wireless/realtek/rtw88/util.h ++++ b/drivers/net/wireless/realtek/rtw88/util.h +@@ -7,9 +7,6 @@ + + struct rtw_dev; + +-#define rtw_iterate_vifs(rtwdev, iterator, data) \ +- ieee80211_iterate_active_interfaces(rtwdev->hw, \ +- IEEE80211_IFACE_ITER_NORMAL, iterator, data) + #define rtw_iterate_vifs_atomic(rtwdev, iterator, data) \ + ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, \ + IEEE80211_IFACE_ITER_NORMAL, iterator, data) +@@ -20,6 +17,15 @@ struct rtw_dev; + #define rtw_iterate_keys_rcu(rtwdev, vif, iterator, data) \ + ieee80211_iter_keys_rcu((rtwdev)->hw, vif, iterator, data) + ++void rtw_iterate_vifs(struct rtw_dev *rtwdev, ++ void (*iterator)(void *data, u8 *mac, ++ struct ieee80211_vif *vif), ++ void *data); ++void rtw_iterate_stas(struct rtw_dev *rtwdev, ++ void (*iterator)(void *data, ++ struct ieee80211_sta *sta), ++ void *data); ++ + static inline u8 *get_hdr_bssid(struct ieee80211_hdr *hdr) + { + __le16 fc = hdr->frame_control; diff --git a/package/kernel/mac80211/patches/rtl/006-rtw88-Add-common-USB-chip-support.patch b/package/kernel/mac80211/patches/rtl/006-rtw88-Add-common-USB-chip-support.patch new file mode 100644 index 000000000..68bfbd302 --- /dev/null +++ b/package/kernel/mac80211/patches/rtl/006-rtw88-Add-common-USB-chip-support.patch @@ -0,0 +1,1305 @@ +diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig +index e3d7cb6c12902..1624c5db69bac 100644 +--- a/drivers/net/wireless/realtek/rtw88/Kconfig ++++ b/drivers/net/wireless/realtek/rtw88/Kconfig +@@ -19,6 +19,10 @@ + tristate + depends on m + ++config RTW88_USB ++ tristate ++ depends on m ++ + config RTW88_8822B + tristate + depends on m +diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile +index 834c66ec0af9e..9e095f8181483 100644 +--- a/drivers/net/wireless/realtek/rtw88/Makefile ++++ b/drivers/net/wireless/realtek/rtw88/Makefile +@@ -45,3 +45,6 @@ + + obj-$(CPTCFG_RTW88_PCI) += rtw88_pci.o + rtw88_pci-objs := pci.o ++ ++obj-$(CPTCFG_RTW88_USB) += rtw88_usb.o ++rtw88_usb-objs := usb.o +diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c +index d1678aed9d9cb..19728c705eaa9 100644 +--- a/drivers/net/wireless/realtek/rtw88/mac.c ++++ b/drivers/net/wireless/realtek/rtw88/mac.c +@@ -1032,6 +1032,9 @@ static int txdma_queue_mapping(struct rtw_dev *rtwdev) + if (rtw_chip_wcpu_11ac(rtwdev)) + rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL); + ++ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) ++ rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_ARBBW_EN); ++ + return 0; + } + +diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c +index 5afb8bef9696a..c191260e67a79 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.c ++++ b/drivers/net/wireless/realtek/rtw88/main.c +@@ -1715,6 +1715,10 @@ static int rtw_chip_parameter_setup(struct rtw_dev *rtwdev) + rtwdev->hci.rpwm_addr = 0x03d9; + rtwdev->hci.cpwm_addr = 0x03da; + break; ++ case RTW_HCI_TYPE_USB: ++ rtwdev->hci.rpwm_addr = 0xfe58; ++ rtwdev->hci.cpwm_addr = 0xfe57; ++ break; + default: + rtw_err(rtwdev, "unsupported hci type\n"); + return -EINVAL; +diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h +index fc27066a67a72..007da6df088a3 100644 +--- a/drivers/net/wireless/realtek/rtw88/main.h ++++ b/drivers/net/wireless/realtek/rtw88/main.h +@@ -845,6 +845,10 @@ + enum rtw_bb_path tx_path_1ss, + enum rtw_bb_path tx_path_cck, + bool is_tx2_path); ++ /* for USB/SDIO only */ ++ void (*fill_txdesc_checksum)(struct rtw_dev *rtwdev, ++ struct rtw_tx_pkt_info *pkt_info, ++ u8 *txdesc); + + /* for coex */ + void (*coex_set_init)(struct rtw_dev *rtwdev); +diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h +index 84ba9ec489c37..a928899030863 100644 +--- a/drivers/net/wireless/realtek/rtw88/reg.h ++++ b/drivers/net/wireless/realtek/rtw88/reg.h +@@ -184,6 +184,7 @@ + #define BIT_TXDMA_VIQ_MAP(x) \ + (((x) & BIT_MASK_TXDMA_VIQ_MAP) << BIT_SHIFT_TXDMA_VIQ_MAP) + #define REG_TXDMA_PQ_MAP 0x010C ++#define BIT_RXDMA_ARBBW_EN BIT(0) + #define BIT_SHIFT_TXDMA_BEQ_MAP 8 + #define BIT_MASK_TXDMA_BEQ_MAP 0x3 + #define BIT_TXDMA_BEQ_MAP(x) \ +diff --git a/drivers/net/wireless/realtek/rtw88/tx.h b/drivers/net/wireless/realtek/rtw88/tx.h +index 56371eff9f7ff..f900d7c08c84c 100644 +--- a/drivers/net/wireless/realtek/rtw88/tx.h ++++ b/drivers/net/wireless/realtek/rtw88/tx.h +@@ -67,6 +67,14 @@ + le32p_replace_bits((__le32 *)(txdesc) + 0x03, value, BIT(15)) + #define SET_TX_DESC_BT_NULL(txdesc, value) \ + le32p_replace_bits((__le32 *)(txdesc) + 0x02, value, BIT(23)) ++#define SET_TX_DESC_TXDESC_CHECKSUM(txdesc, value) \ ++ le32p_replace_bits((__le32 *)(txdesc) + 0x07, value, GENMASK(15, 0)) ++#define SET_TX_DESC_DMA_TXAGG_NUM(txdesc, value) \ ++ le32p_replace_bits((__le32 *)(txdesc) + 0x07, value, GENMASK(31, 24)) ++#define GET_TX_DESC_PKT_OFFSET(txdesc) \ ++ le32_get_bits(*((__le32 *)(txdesc) + 0x01), GENMASK(28, 24)) ++#define GET_TX_DESC_QSEL(txdesc) \ ++ le32_get_bits(*((__le32 *)(txdesc) + 0x01), GENMASK(12, 8)) + + enum rtw_tx_desc_queue_select { + TX_DESC_QSEL_TID0 = 0, +@@ -119,4 +127,27 @@ rtw_tx_write_data_h2c_get(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *buf, u32 size); + ++static inline ++void fill_txdesc_checksum_common(u8 *txdesc, size_t words) ++{ ++ __le16 chksum = 0; ++ __le16 *data = (__le16 *)(txdesc); ++ ++ SET_TX_DESC_TXDESC_CHECKSUM(txdesc, 0x0000); ++ ++ while (words--) ++ chksum ^= *data++; ++ ++ SET_TX_DESC_TXDESC_CHECKSUM(txdesc, __le16_to_cpu(chksum)); ++} ++ ++static inline void rtw_tx_fill_txdesc_checksum(struct rtw_dev *rtwdev, ++ struct rtw_tx_pkt_info *pkt_info, ++ u8 *txdesc) ++{ ++ struct rtw_chip_info *chip = rtwdev->chip; ++ ++ chip->ops->fill_txdesc_checksum(rtwdev, pkt_info, txdesc); ++} ++ + #endif +diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c +new file mode 100644 +index 0000000000000..500d5208086dd +--- /dev/null ++++ b/drivers/net/wireless/realtek/rtw88/usb.c +@@ -0,0 +1,1037 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* Copyright(c) 2018-2019 Realtek Corporation ++ */ ++ ++#include ++#include ++#include ++#include "main.h" ++#include "debug.h" ++#include "reg.h" ++#include "tx.h" ++#include "rx.h" ++#include "fw.h" ++#include "ps.h" ++#include "usb.h" ++ ++#define RTW_USB_MAX_RXQ_LEN 128 ++ ++struct rtw_usb_txcb { ++ struct rtw_dev *rtwdev; ++ struct sk_buff_head tx_ack_queue; ++}; ++ ++static void rtw_usb_fill_tx_checksum(struct rtw_usb *rtwusb, ++ struct sk_buff *skb, int agg_num) ++{ ++ struct rtw_dev *rtwdev = rtwusb->rtwdev; ++ struct rtw_tx_pkt_info pkt_info; ++ ++ SET_TX_DESC_DMA_TXAGG_NUM(skb->data, agg_num); ++ pkt_info.pkt_offset = GET_TX_DESC_PKT_OFFSET(skb->data); ++ rtw_tx_fill_txdesc_checksum(rtwdev, &pkt_info, skb->data); ++} ++ ++static void usbctrl_async_callback(struct urb *urb) ++{ ++ /* free dr */ ++ kfree(urb->setup_packet); ++ /* free databuf */ ++ kfree(urb->transfer_buffer); ++} ++ ++static int usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request, ++ u16 value, u16 index, void *pdata, ++ u16 len) ++{ ++ const u16 databuf_maxlen = RTW_USB_VENQT_MAX_BUF_SIZE; ++ struct usb_ctrlrequest *dr; ++ unsigned int pipe; ++ struct urb *urb; ++ u8 *databuf; ++ u8 reqtype; ++ int ret; ++ ++ if (WARN_ON_ONCE(len > databuf_maxlen)) ++ len = databuf_maxlen; ++ ++ pipe = usb_sndctrlpipe(udev, 0); /* write_out */ ++ reqtype = RTW_USB_CMD_WRITE; ++ ++ dr = kzalloc(sizeof(*dr), GFP_ATOMIC); ++ if (!dr) ++ return -ENOMEM; ++ ++ databuf = kmemdup(pdata, len, GFP_ATOMIC); ++ if (!databuf) { ++ kfree(dr); ++ return -ENOMEM; ++ } ++ ++ urb = usb_alloc_urb(0, GFP_ATOMIC); ++ if (!urb) { ++ kfree(databuf); ++ kfree(dr); ++ return -ENOMEM; ++ } ++ ++ dr->bRequestType = reqtype; ++ dr->bRequest = request; ++ dr->wValue = cpu_to_le16(value); ++ dr->wIndex = cpu_to_le16(index); ++ dr->wLength = cpu_to_le16(len); ++ ++ usb_fill_control_urb(urb, udev, pipe, ++ (unsigned char *)dr, databuf, len, ++ usbctrl_async_callback, NULL); ++ ret = usb_submit_urb(urb, GFP_ATOMIC); ++ if (ret < 0) { ++ kfree(databuf); ++ kfree(dr); ++ } ++ ++ usb_free_urb(urb); ++ ++ return ret; ++} ++ ++static u32 rtw_usb_read_sync(struct rtw_dev *rtwdev, u32 addr, u16 len) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ struct usb_device *udev = rtwusb->udev; ++ __le32 *data; ++ unsigned long flags; ++ int ret; ++ static int count; ++ ++ spin_lock_irqsave(&rtwusb->usb_lock, flags); ++ ++ if (++rtwusb->usb_data_index >= RTW_USB_MAX_RX_COUNT) ++ rtwusb->usb_data_index = 0; ++ data = &rtwusb->usb_data[rtwusb->usb_data_index]; ++ ++ spin_unlock_irqrestore(&rtwusb->usb_lock, flags); ++ ++ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), ++ RTW_USB_CMD_REQ, RTW_USB_CMD_READ, addr, ++ RTW_USB_VENQT_CMD_IDX, data, len, 1000); ++ if (ret < 0 && ret != -ENODEV && count++ < 4) ++ rtw_err(rtwdev, "reg 0x%x, usbctrl_vendorreq failed with %d\n", ++ addr, ret); ++ ++ return le32_to_cpu(*data); ++} ++ ++static u8 rtw_usb_read8_sync(struct rtw_dev *rtwdev, u32 addr) ++{ ++ return (u8)rtw_usb_read_sync(rtwdev, addr, 1); ++} ++ ++static u16 rtw_usb_read16_sync(struct rtw_dev *rtwdev, u32 addr) ++{ ++ return (u16)rtw_usb_read_sync(rtwdev, addr, 2); ++} ++ ++static u32 rtw_usb_read32_sync(struct rtw_dev *rtwdev, u32 addr) ++{ ++ return (u32)rtw_usb_read_sync(rtwdev, addr, 4); ++} ++ ++static void rtw_usb_write_async(struct rtw_dev *rtwdev, u32 addr, u32 val, ++ u16 len) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ struct usb_device *udev = rtwusb->udev; ++ u8 request; ++ u16 wvalue; ++ u16 index; ++ __le32 data; ++ ++ request = RTW_USB_CMD_REQ; ++ index = RTW_USB_VENQT_CMD_IDX; /* n/a */ ++ wvalue = (u16)(addr & 0x0000ffff); ++ data = cpu_to_le32(val); ++ usbctrl_vendorreq_async_write(udev, request, wvalue, index, &data, len); ++} ++ ++static void rtw_usb_write8_async(struct rtw_dev *rtwdev, u32 addr, u8 val) ++{ ++ rtw_usb_write_async(rtwdev, addr, val, 1); ++} ++ ++static void rtw_usb_write16_async(struct rtw_dev *rtwdev, u32 addr, u16 val) ++{ ++ rtw_usb_write_async(rtwdev, addr, val, 2); ++} ++ ++static void rtw_usb_write32_async(struct rtw_dev *rtwdev, u32 addr, u32 val) ++{ ++ rtw_usb_write_async(rtwdev, addr, val, 4); ++} ++ ++static int rtw_usb_parse(struct rtw_dev *rtwdev, ++ struct usb_interface *interface) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ struct usb_host_interface *host_interface = &interface->altsetting[0]; ++ struct usb_interface_descriptor *interface_desc = &host_interface->desc; ++ struct usb_endpoint_descriptor *endpoint; ++ struct usb_device *usbd = interface_to_usbdev(interface); ++ int num_out_pipes = 0; ++ int i; ++ u8 num; ++ ++ for (i = 0; i < interface_desc->bNumEndpoints; i++) { ++ endpoint = &host_interface->endpoint[i].desc; ++ num = usb_endpoint_num(endpoint); ++ ++ if (usb_endpoint_dir_in(endpoint) && ++ usb_endpoint_xfer_bulk(endpoint)) { ++ if (rtwusb->pipe_in) { ++ rtw_err(rtwdev, "IN pipes overflow\n"); ++ return -EINVAL; ++ } ++ ++ rtwusb->pipe_in = num; ++ } ++ ++ if (usb_endpoint_dir_in(endpoint) && ++ usb_endpoint_xfer_int(endpoint)) { ++ if (rtwusb->pipe_interrupt) { ++ rtw_err(rtwdev, "INT pipes overflow\n"); ++ return -EINVAL; ++ } ++ ++ rtwusb->pipe_interrupt = num; ++ } ++ ++ if (usb_endpoint_dir_out(endpoint) && ++ usb_endpoint_xfer_bulk(endpoint)) { ++ if (num_out_pipes >= ARRAY_SIZE(rtwusb->out_ep)) { ++ rtw_err(rtwdev, "OUT pipes overflow\n"); ++ return -EINVAL; ++ } ++ ++ rtwusb->out_ep[num_out_pipes++] = num; ++ } ++ } ++ ++ switch (usbd->speed) { ++ case USB_SPEED_LOW: ++ case USB_SPEED_FULL: ++ rtwusb->bulkout_size = RTW_USB_FULL_SPEED_BULK_SIZE; ++ break; ++ case USB_SPEED_HIGH: ++ rtwusb->bulkout_size = RTW_USB_HIGH_SPEED_BULK_SIZE; ++ break; ++ case USB_SPEED_SUPER: ++ rtwusb->bulkout_size = RTW_USB_SUPER_SPEED_BULK_SIZE; ++ break; ++ default: ++ rtw_err(rtwdev, "failed to detect usb speed\n"); ++ return -EINVAL; ++ } ++ ++ rtwdev->hci.bulkout_num = num_out_pipes; ++ ++ switch (num_out_pipes) { ++ case 4: ++ case 3: ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID0] = 2; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID1] = 2; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID2] = 2; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID3] = 2; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID4] = 1; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID5] = 1; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID6] = 1; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID7] = 1; ++ break; ++ case 2: ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID0] = 1; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID1] = 1; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID2] = 1; ++ rtwusb->qsel_to_ep[TX_DESC_QSEL_TID3] = 1; ++ break; ++ case 1: ++ break; ++ default: ++ rtw_err(rtwdev, "failed to get out_pipes(%d)\n", num_out_pipes); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void rtw_usb_txcb_enqueue(struct rtw_usb_txcb *txcb, struct sk_buff *skb) ++{ ++ skb_queue_tail(&txcb->tx_ack_queue, skb); ++} ++ ++static void rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list, ++ struct sk_buff *skb_head, struct sk_buff *skb, ++ struct rtw_usb_txcb *txcb) ++{ ++ struct sk_buff *skb_iter; ++ unsigned long flags; ++ u8 *data_ptr; ++ int agg_num = 0, len, max_len; ++ ++ data_ptr = skb_head->data; ++ skb_iter = skb; ++ ++ while (skb_iter) { ++ memcpy(data_ptr, skb_iter->data, skb_iter->len); ++ len = ALIGN(skb_iter->len, 8); ++ skb_put(skb_head, len); ++ data_ptr += len; ++ agg_num++; ++ ++ rtw_usb_txcb_enqueue(txcb, skb_iter); ++ ++ spin_lock_irqsave(&list->lock, flags); ++ ++ skb_iter = skb_peek(list); ++ max_len = RTW_USB_MAX_XMITBUF_SZ - skb_head->len; ++ ++ if (skb_iter && skb_iter->len < max_len) ++ __skb_unlink(skb_iter, list); ++ else ++ skb_iter = NULL; ++ spin_unlock_irqrestore(&list->lock, flags); ++ } ++ ++ if (agg_num > 1) ++ rtw_usb_fill_tx_checksum(rtwusb, skb_head, agg_num); ++} ++ ++static void rtw_usb_indicate_tx_status(struct rtw_dev *rtwdev, ++ struct sk_buff *skb) ++{ ++ struct ieee80211_hw *hw = rtwdev->hw; ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ++ struct rtw_usb_tx_data *tx_data = rtw_usb_get_tx_data(skb); ++ ++ /* enqueue to wait for tx report */ ++ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) { ++ rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn); ++ return; ++ } ++ ++ /* always ACK for others, then they won't be marked as drop */ ++ ieee80211_tx_info_clear_status(info); ++ if (info->flags & IEEE80211_TX_CTL_NO_ACK) ++ info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; ++ else ++ info->flags |= IEEE80211_TX_STAT_ACK; ++ ++ ieee80211_tx_status_irqsafe(hw, skb); ++} ++ ++static void rtw_usb_write_port_tx_complete(struct urb *urb) ++{ ++ struct rtw_usb_txcb *txcb = urb->context; ++ struct rtw_dev *rtwdev = txcb->rtwdev; ++ ++ while (true) { ++ struct sk_buff *skb = skb_dequeue(&txcb->tx_ack_queue); ++ ++ if (!skb) ++ break; ++ ++ if (GET_TX_DESC_QSEL(skb->data) <= TX_DESC_QSEL_TID7) ++ rtw_usb_indicate_tx_status(rtwdev, skb); ++ else ++ dev_kfree_skb_any(skb); ++ } ++ ++ kfree(txcb); ++} ++ ++static int rtw_usb_write_port(struct rtw_dev *rtwdev, u8 qsel, struct sk_buff *skb, ++ usb_complete_t cb, void *context) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ struct usb_device *usbd = rtwusb->udev; ++ struct urb *urb; ++ unsigned int pipe; ++ int ret; ++ int ep = rtwusb->qsel_to_ep[qsel]; ++ ++ pipe = usb_sndbulkpipe(usbd, rtwusb->out_ep[ep]); ++ urb = usb_alloc_urb(0, GFP_ATOMIC); ++ if (!urb) ++ return -ENOMEM; ++ ++ usb_fill_bulk_urb(urb, usbd, pipe, skb->data, skb->len, cb, context); ++ ret = usb_submit_urb(urb, GFP_ATOMIC); ++ ++ usb_free_urb(urb); ++ ++ return ret; ++} ++ ++static struct sk_buff *rtw_usb_tx_agg_check(struct rtw_usb *rtwusb, ++ struct sk_buff *skb, ++ int index, ++ struct rtw_usb_txcb *txcb) ++{ ++ struct sk_buff_head *list; ++ struct sk_buff *skb_head; ++ ++ list = &rtwusb->tx_queue[index]; ++ if (skb_queue_empty(list)) ++ return NULL; ++ ++ skb_head = dev_alloc_skb(RTW_USB_MAX_XMITBUF_SZ); ++ if (!skb_head) ++ return NULL; ++ ++ rtw_usb_tx_agg_skb(rtwusb, list, skb_head, skb, txcb); ++ ++ return skb_head; ++} ++ ++static void rtw_usb_tx_agg(struct rtw_usb *rtwusb, struct sk_buff *skb, int index) ++{ ++ struct rtw_dev *rtwdev = rtwusb->rtwdev; ++ struct sk_buff *skb_head; ++ struct rtw_usb_txcb *txcb; ++ u8 qsel; ++ ++ txcb = kmalloc(sizeof(*txcb), GFP_ATOMIC); ++ if (!txcb) ++ return; ++ ++ txcb->rtwdev = rtwdev; ++ skb_queue_head_init(&txcb->tx_ack_queue); ++ ++ skb_head = rtw_usb_tx_agg_check(rtwusb, skb, index, txcb); ++ if (!skb_head) { ++ skb_head = skb; ++ rtw_usb_txcb_enqueue(txcb, skb); ++ } ++ ++ qsel = GET_TX_DESC_QSEL(skb->data); ++ ++ rtw_usb_write_port(rtwdev, qsel, skb_head, ++ rtw_usb_write_port_tx_complete, txcb); ++ ++ if (skb_head != skb) ++ dev_kfree_skb(skb_head); ++} ++ ++static void rtw_usb_tx_handler(struct work_struct *work) ++{ ++ struct rtw_usb *rtwusb = container_of(work, struct rtw_usb, tx_work); ++ struct sk_buff *skb; ++ int index, limit; ++ ++ for (index = ARRAY_SIZE(rtwusb->tx_queue) - 1; index >= 0; index--) { ++ for (limit = 0; limit < 200; limit++) { ++ skb = skb_dequeue(&rtwusb->tx_queue[index]); ++ if (skb) ++ rtw_usb_tx_agg(rtwusb, skb, index); ++ else ++ break; ++ } ++ } ++} ++ ++static void rtw_usb_tx_queue_purge(struct rtw_usb *rtwusb) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) ++ skb_queue_purge(&rtwusb->tx_queue[i]); ++} ++ ++static void rtw_usb_write_port_complete(struct urb *urb) ++{ ++ struct sk_buff *skb = urb->context; ++ ++ dev_kfree_skb_any(skb); ++} ++ ++static int rtw_usb_write_data(struct rtw_dev *rtwdev, ++ struct rtw_tx_pkt_info *pkt_info, ++ u8 *buf) ++{ ++ struct rtw_chip_info *chip = rtwdev->chip; ++ struct sk_buff *skb; ++ unsigned int desclen, headsize, size; ++ u8 qsel; ++ int ret = 0; ++ ++ size = pkt_info->tx_pkt_size; ++ qsel = pkt_info->qsel; ++ desclen = chip->tx_pkt_desc_sz; ++ headsize = pkt_info->offset ? pkt_info->offset : desclen; ++ ++ skb = dev_alloc_skb(headsize + size); ++ if (unlikely(!skb)) ++ return -ENOMEM; ++ ++ skb_reserve(skb, headsize); ++ skb_put_data(skb, buf, size); ++ skb_push(skb, headsize); ++ memset(skb->data, 0, headsize); ++ rtw_tx_fill_tx_desc(pkt_info, skb); ++ rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data); ++ ++ ret = rtw_usb_write_port(rtwdev, qsel, skb, ++ rtw_usb_write_port_complete, skb); ++ if (unlikely(ret)) ++ rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret); ++ ++ return ret; ++} ++ ++static int rtw_usb_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, ++ u32 size) ++{ ++ struct rtw_chip_info *chip = rtwdev->chip; ++ struct rtw_usb *rtwusb; ++ struct rtw_tx_pkt_info pkt_info = {0}; ++ u32 len, desclen; ++ ++ rtwusb = rtw_get_usb_priv(rtwdev); ++ if (unlikely(!rtwusb)) ++ return -EINVAL; ++ ++ pkt_info.tx_pkt_size = size; ++ pkt_info.qsel = TX_DESC_QSEL_BEACON; ++ ++ desclen = chip->tx_pkt_desc_sz; ++ len = desclen + size; ++ if (len % rtwusb->bulkout_size == 0) { ++ len += RTW_USB_PACKET_OFFSET_SZ; ++ pkt_info.offset = desclen + RTW_USB_PACKET_OFFSET_SZ; ++ pkt_info.pkt_offset = 1; ++ } else { ++ pkt_info.offset = desclen; ++ } ++ ++ return rtw_usb_write_data(rtwdev, &pkt_info, buf); ++} ++ ++static int rtw_usb_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size) ++{ ++ struct rtw_tx_pkt_info pkt_info = {0}; ++ ++ pkt_info.tx_pkt_size = size; ++ pkt_info.qsel = TX_DESC_QSEL_H2C; ++ ++ return rtw_usb_write_data(rtwdev, &pkt_info, buf); ++} ++ ++static u8 rtw_usb_tx_queue_mapping_to_qsel(struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; ++ __le16 fc = hdr->frame_control; ++ u8 qsel; ++ ++ if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))) ++ qsel = TX_DESC_QSEL_MGMT; ++ else if (skb_get_queue_mapping(skb) <= IEEE80211_AC_BK) ++ qsel = skb->priority; ++ else ++ qsel = TX_DESC_QSEL_BEACON; ++ ++ return qsel; ++} ++ ++static int rtw_usb_tx_write(struct rtw_dev *rtwdev, ++ struct rtw_tx_pkt_info *pkt_info, ++ struct sk_buff *skb) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ struct rtw_chip_info *chip = rtwdev->chip; ++ struct rtw_usb_tx_data *tx_data; ++ u8 *pkt_desc; ++ int ep; ++ ++ pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz); ++ memset(pkt_desc, 0, chip->tx_pkt_desc_sz); ++ pkt_info->qsel = rtw_usb_tx_queue_mapping_to_qsel(skb); ++ ep = rtwusb->qsel_to_ep[pkt_info->qsel]; ++ rtw_tx_fill_tx_desc(pkt_info, skb); ++ rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data); ++ tx_data = rtw_usb_get_tx_data(skb); ++ tx_data->sn = pkt_info->sn; ++ ++ skb_queue_tail(&rtwusb->tx_queue[ep], skb); ++ ++ return 0; ++} ++ ++static void rtw_usb_tx_kick_off(struct rtw_dev *rtwdev) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ ++ queue_work(rtwusb->txwq, &rtwusb->tx_work); ++} ++ ++static void rtw_usb_rx_handler(struct work_struct *work) ++{ ++ struct rtw_usb *rtwusb = container_of(work, struct rtw_usb, rx_work); ++ struct rtw_dev *rtwdev = rtwusb->rtwdev; ++ struct rtw_chip_info *chip = rtwdev->chip; ++ struct rtw_rx_pkt_stat pkt_stat; ++ struct ieee80211_rx_status rx_status; ++ struct sk_buff *skb; ++ u32 pkt_desc_sz = chip->rx_pkt_desc_sz; ++ u32 pkt_offset; ++ u8 *rx_desc; ++ int limit; ++ ++ for (limit = 0; limit < 200; limit++) { ++ skb = skb_dequeue(&rtwusb->rx_queue); ++ if (!skb) ++ break; ++ ++ rx_desc = skb->data; ++ chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat, ++ &rx_status); ++ pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + ++ pkt_stat.shift; ++ ++ if (pkt_stat.is_c2h) { ++ skb_put(skb, pkt_stat.pkt_len + pkt_offset); ++ rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, ++ skb); ++ continue; ++ } ++ ++ if (skb_queue_len(&rtwusb->rx_queue) >= RTW_USB_MAX_RXQ_LEN) { ++ rtw_err(rtwdev, "failed to get rx_queue, overflow\n"); ++ dev_kfree_skb_any(skb); ++ continue; ++ } ++ ++ skb_put(skb, pkt_stat.pkt_len); ++ skb_reserve(skb, pkt_offset); ++ memcpy(skb->cb, &rx_status, sizeof(rx_status)); ++ ieee80211_rx_irqsafe(rtwdev->hw, skb); ++ } ++} ++ ++static void rtw_usb_rx_data_put(struct rtw_usb *rtwusb, ++ struct rx_usb_ctrl_block *rxcb) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&rtwusb->rx_data_list_lock, flags); ++ list_move(&rxcb->list, &rtwusb->rx_data_free); ++ spin_unlock_irqrestore(&rtwusb->rx_data_list_lock, flags); ++} ++ ++static void rtw_usb_read_port_complete(struct urb *urb) ++{ ++ struct rx_usb_ctrl_block *rxcb = urb->context; ++ struct rtw_dev *rtwdev = rxcb->rtwdev; ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ struct sk_buff *skb = rxcb->rx_skb; ++ ++ if (urb->status == 0) { ++ if (urb->actual_length >= RTW_USB_MAX_RECVBUF_SZ || ++ urb->actual_length < 24) { ++ rtw_err(rtwdev, "failed to get urb length:%d\n", ++ urb->actual_length); ++ if (skb) ++ dev_kfree_skb_any(skb); ++ } else { ++ skb_queue_tail(&rtwusb->rx_queue, skb); ++ queue_work(rtwusb->rxwq, &rtwusb->rx_work); ++ } ++ ++ rtw_usb_rx_data_put(rtwusb, rxcb); ++ if (rtwusb->running) ++ queue_work(rtwusb->rxwq, &rtwusb->rx_refill_work); ++ } else { ++ switch (urb->status) { ++ case -EINVAL: ++ case -EPIPE: ++ case -ENODEV: ++ case -ESHUTDOWN: ++ case -ENOENT: ++ case -EPROTO: ++ case -EILSEQ: ++ case -ETIME: ++ case -ECOMM: ++ case -EOVERFLOW: ++ case -EINPROGRESS: ++ break; ++ default: ++ rtw_err(rtwdev, "status unknown=%d\n", urb->status); ++ break; ++ } ++ if (skb) ++ dev_kfree_skb_any(skb); ++ } ++} ++ ++static void rtw_usb_rx_refill_work(struct work_struct *work) ++{ ++ struct rtw_usb *rtwusb = container_of(work, struct rtw_usb, rx_refill_work); ++ struct rtw_dev *rtwdev = rtwusb->rtwdev; ++ struct rx_usb_ctrl_block *rxcb; ++ unsigned long flags; ++ int error, limit; ++ ++ for (limit = 0; limit < 200; limit++) { ++ spin_lock_irqsave(&rtwusb->rx_data_list_lock, flags); ++ ++ rxcb = list_first_entry_or_null(&rtwusb->rx_data_free, ++ struct rx_usb_ctrl_block, list); ++ ++ spin_unlock_irqrestore(&rtwusb->rx_data_list_lock, flags); ++ if (!rxcb) ++ return; ++ ++ rxcb->rx_skb = alloc_skb(RTW_USB_MAX_RECVBUF_SZ, GFP_KERNEL); ++ if (!rxcb->rx_skb) ++ return; ++ ++ usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev, ++ usb_rcvbulkpipe(rtwusb->udev, rtwusb->pipe_in), ++ rxcb->rx_skb->data, RTW_USB_MAX_RECVBUF_SZ, ++ rtw_usb_read_port_complete, rxcb); ++ ++ spin_lock_irqsave(&rtwusb->rx_data_list_lock, flags); ++ list_move(&rxcb->list, &rtwusb->rx_data_used); ++ spin_unlock_irqrestore(&rtwusb->rx_data_list_lock, flags); ++ ++ error = usb_submit_urb(rxcb->rx_urb, GFP_KERNEL); ++ if (error) { ++ kfree_skb(rxcb->rx_skb); ++ if (error != -ENODEV) ++ rtw_err(rtwdev, "Err sending rx data urb %d\n", ++ error); ++ rtw_usb_rx_data_put(rtwusb, rxcb); ++ ++ return; ++ } ++ } ++} ++ ++static void rtw_usb_cancel_rx_bufs(struct rtw_usb *rtwusb) ++{ ++ struct rx_usb_ctrl_block *rxcb; ++ ++ rtwusb->running = 0; ++ cancel_work_sync(&rtwusb->rx_refill_work); ++ ++ while (true) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&rtwusb->rx_data_list_lock, flags); ++ ++ rxcb = list_first_entry_or_null(&rtwusb->rx_data_used, ++ struct rx_usb_ctrl_block, list); ++ if (rxcb) ++ list_move(&rxcb->list, &rtwusb->rx_data_free); ++ ++ spin_unlock_irqrestore(&rtwusb->rx_data_list_lock, flags); ++ ++ if (!rxcb) ++ break; ++ ++ usb_kill_urb(rxcb->rx_urb); ++ } ++} ++ ++static void rtw_usb_free_rx_bufs(struct rtw_usb *rtwusb) ++{ ++ struct rx_usb_ctrl_block *rxcb; ++ ++ list_for_each_entry(rxcb, &rtwusb->rx_data_free, list) ++ usb_free_urb(rxcb->rx_urb); ++} ++ ++static int rtw_usb_alloc_rx_bufs(struct rtw_usb *rtwusb) ++{ ++ int i; ++ ++ for (i = 0; i < RTW_USB_RXCB_NUM; i++) { ++ struct rx_usb_ctrl_block *rxcb = &rtwusb->rx_cb[i]; ++ ++ rxcb->rtwdev = rtwusb->rtwdev; ++ rxcb->rx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!rxcb->rx_urb) ++ goto err; ++ list_add_tail(&rxcb->list, &rtwusb->rx_data_free); ++ } ++ ++ return 0; ++err: ++ rtw_usb_free_rx_bufs(rtwusb); ++ return -ENOMEM; ++} ++ ++static int rtw_usb_setup(struct rtw_dev *rtwdev) ++{ ++ /* empty function for rtw_hci_ops */ ++ return 0; ++} ++ ++static int rtw_usb_start(struct rtw_dev *rtwdev) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ ++ rtwusb->running = 1; ++ queue_work(rtwusb->rxwq, &rtwusb->rx_refill_work); ++ ++ return 0; ++} ++ ++static void rtw_usb_stop(struct rtw_dev *rtwdev) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ ++ rtw_usb_cancel_rx_bufs(rtwusb); ++} ++ ++static void rtw_usb_deep_ps(struct rtw_dev *rtwdev, bool enter) ++{ ++ /* empty function for rtw_hci_ops */ ++} ++ ++static void rtw_usb_link_ps(struct rtw_dev *rtwdev, bool enter) ++{ ++ /* empty function for rtw_hci_ops */ ++} ++ ++static void rtw_usb_interface_cfg(struct rtw_dev *rtwdev) ++{ ++ /* empty function for rtw_hci_ops */ ++} ++ ++static struct rtw_hci_ops rtw_usb_ops = { ++ .tx_write = rtw_usb_tx_write, ++ .tx_kick_off = rtw_usb_tx_kick_off, ++ .setup = rtw_usb_setup, ++ .start = rtw_usb_start, ++ .stop = rtw_usb_stop, ++ .deep_ps = rtw_usb_deep_ps, ++ .link_ps = rtw_usb_link_ps, ++ .interface_cfg = rtw_usb_interface_cfg, ++ ++ .write8 = rtw_usb_write8_async, ++ .write16 = rtw_usb_write16_async, ++ .write32 = rtw_usb_write32_async, ++ .read8 = rtw_usb_read8_sync, ++ .read16 = rtw_usb_read16_sync, ++ .read32 = rtw_usb_read32_sync, ++ ++ .write_data_rsvd_page = rtw_usb_write_data_rsvd_page, ++ .write_data_h2c = rtw_usb_write_data_h2c, ++}; ++ ++static int rtw_usb_init_rx(struct rtw_dev *rtwdev) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ ++ rtwusb->rxwq = create_singlethread_workqueue("rtw88_usb: rx wq"); ++ if (!rtwusb->rxwq) { ++ rtw_err(rtwdev, "failed to create RX work queue\n"); ++ return -ENOMEM; ++ } ++ ++ skb_queue_head_init(&rtwusb->rx_queue); ++ ++ INIT_WORK(&rtwusb->rx_work, rtw_usb_rx_handler); ++ ++ return 0; ++} ++ ++static void rtw_usb_deinit_rx(struct rtw_dev *rtwdev) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ ++ skb_queue_purge(&rtwusb->rx_queue); ++ ++ flush_workqueue(rtwusb->rxwq); ++ destroy_workqueue(rtwusb->rxwq); ++} ++ ++static int rtw_usb_init_tx(struct rtw_dev *rtwdev) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ int i; ++ ++ rtwusb->txwq = create_singlethread_workqueue("rtw88_usb: tx wq"); ++ if (!rtwusb->txwq) { ++ rtw_err(rtwdev, "failed to create TX work queue\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) ++ skb_queue_head_init(&rtwusb->tx_queue[i]); ++ ++ INIT_WORK(&rtwusb->tx_work, rtw_usb_tx_handler); ++ ++ return 0; ++} ++ ++static void rtw_usb_deinit_tx(struct rtw_dev *rtwdev) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ ++ rtw_usb_tx_queue_purge(rtwusb); ++ flush_workqueue(rtwusb->txwq); ++ destroy_workqueue(rtwusb->txwq); ++} ++ ++static int rtw_usb_intf_init(struct rtw_dev *rtwdev, ++ struct usb_interface *intf) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf)); ++ int ret; ++ ++ rtwusb->udev = udev; ++ ret = rtw_usb_parse(rtwdev, intf); ++ if (ret) ++ return ret; ++ ++ rtwusb->usb_data = kcalloc(RTW_USB_MAX_RX_COUNT, sizeof(u32), ++ GFP_KERNEL); ++ if (!rtwusb->usb_data) ++ return -ENOMEM; ++ ++ usb_set_intfdata(intf, rtwdev->hw); ++ ++ SET_IEEE80211_DEV(rtwdev->hw, &intf->dev); ++ spin_lock_init(&rtwusb->usb_lock); ++ ++ return 0; ++} ++ ++static void rtw_usb_intf_deinit(struct rtw_dev *rtwdev, ++ struct usb_interface *intf) ++{ ++ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); ++ ++ usb_put_dev(rtwusb->udev); ++ usb_set_intfdata(intf, NULL); ++} ++ ++int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ++{ ++ struct rtw_dev *rtwdev; ++ struct ieee80211_hw *hw; ++ struct rtw_usb *rtwusb; ++ int drv_data_size; ++ int ret; ++ ++ drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_usb); ++ hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops); ++ if (!hw) ++ return -ENOMEM; ++ ++ rtwdev = hw->priv; ++ rtwdev->hw = hw; ++ rtwdev->dev = &intf->dev; ++ rtwdev->chip = (struct rtw_chip_info *)id->driver_info; ++ rtwdev->hci.ops = &rtw_usb_ops; ++ rtwdev->hci.type = RTW_HCI_TYPE_USB; ++ ++ rtwusb = rtw_get_usb_priv(rtwdev); ++ rtwusb->rtwdev = rtwdev; ++ ++ INIT_WORK(&rtwusb->rx_refill_work, rtw_usb_rx_refill_work); ++ INIT_LIST_HEAD(&rtwusb->rx_data_free); ++ INIT_LIST_HEAD(&rtwusb->rx_data_used); ++ spin_lock_init(&rtwusb->rx_data_list_lock); ++ ++ ret = rtw_usb_alloc_rx_bufs(rtwusb); ++ if (ret) ++ return ret; ++ ++ ret = rtw_core_init(rtwdev); ++ if (ret) ++ goto err_release_hw; ++ ++ ret = rtw_usb_intf_init(rtwdev, intf); ++ if (ret) { ++ rtw_err(rtwdev, "failed to init USB interface\n"); ++ goto err_deinit_core; ++ } ++ ++ ret = rtw_usb_init_tx(rtwdev); ++ if (ret) { ++ rtw_err(rtwdev, "failed to init USB TX\n"); ++ goto err_destroy_usb; ++ } ++ ++ ret = rtw_usb_init_rx(rtwdev); ++ if (ret) { ++ rtw_err(rtwdev, "failed to init USB RX\n"); ++ goto err_destroy_txwq; ++ } ++ ++ ret = rtw_chip_info_setup(rtwdev); ++ if (ret) { ++ rtw_err(rtwdev, "failed to setup chip information\n"); ++ goto err_destroy_rxwq; ++ } ++ ++ ret = rtw_register_hw(rtwdev, rtwdev->hw); ++ if (ret) { ++ rtw_err(rtwdev, "failed to register hw\n"); ++ goto err_destroy_rxwq; ++ } ++ ++ return 0; ++ ++err_destroy_rxwq: ++ rtw_usb_deinit_rx(rtwdev); ++ ++err_destroy_txwq: ++ rtw_usb_deinit_tx(rtwdev); ++ ++err_destroy_usb: ++ rtw_usb_intf_deinit(rtwdev, intf); ++ ++err_deinit_core: ++ rtw_core_deinit(rtwdev); ++ ++err_release_hw: ++ ieee80211_free_hw(hw); ++ ++ return ret; ++} ++EXPORT_SYMBOL(rtw_usb_probe); ++ ++void rtw_usb_disconnect(struct usb_interface *intf) ++{ ++ struct ieee80211_hw *hw = usb_get_intfdata(intf); ++ struct rtw_dev *rtwdev; ++ struct rtw_usb *rtwusb; ++ ++ if (!hw) ++ return; ++ ++ rtwdev = hw->priv; ++ rtwusb = rtw_get_usb_priv(rtwdev); ++ ++ rtw_usb_cancel_rx_bufs(rtwusb); ++ ++ rtw_unregister_hw(rtwdev, hw); ++ rtw_usb_deinit_tx(rtwdev); ++ rtw_usb_deinit_rx(rtwdev); ++ ++ if (rtwusb->udev->state != USB_STATE_NOTATTACHED) ++ usb_reset_device(rtwusb->udev); ++ ++ rtw_usb_free_rx_bufs(rtwusb); ++ ++ rtw_usb_intf_deinit(rtwdev, intf); ++ rtw_core_deinit(rtwdev); ++ ieee80211_free_hw(hw); ++} ++EXPORT_SYMBOL(rtw_usb_disconnect); ++ ++MODULE_AUTHOR("Realtek Corporation"); ++MODULE_DESCRIPTION("Realtek 802.11ac wireless USB driver"); ++MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/drivers/net/wireless/realtek/rtw88/usb.h b/drivers/net/wireless/realtek/rtw88/usb.h +new file mode 100644 +index 0000000000000..0e219150ee87a +--- /dev/null ++++ b/drivers/net/wireless/realtek/rtw88/usb.h +@@ -0,0 +1,114 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* Copyright(c) 2018-2019 Realtek Corporation ++ */ ++ ++#ifndef __RTW_USB_H_ ++#define __RTW_USB_H_ ++ ++#define FW_8192C_START_ADDRESS 0x1000 ++#define FW_8192C_END_ADDRESS 0x5fff ++ ++#define RTW_USB_MAX_RX_COUNT 100 ++#define RTW_USB_VENQT_MAX_BUF_SIZE 254 ++#define MAX_USBCTRL_VENDORREQ_TIMES 10 ++ ++#define RTW_USB_CMD_READ 0xc0 ++#define RTW_USB_CMD_WRITE 0x40 ++#define RTW_USB_CMD_REQ 0x05 ++ ++#define RTW_USB_VENQT_CMD_IDX 0x00 ++ ++#define RTW_USB_SUPER_SPEED_BULK_SIZE 1024 ++#define RTW_USB_HIGH_SPEED_BULK_SIZE 512 ++#define RTW_USB_FULL_SPEED_BULK_SIZE 64 ++ ++#define RTW_USB_TX_SEL_HQ BIT(0) ++#define RTW_USB_TX_SEL_LQ BIT(1) ++#define RTW_USB_TX_SEL_NQ BIT(2) ++#define RTW_USB_TX_SEL_EQ BIT(3) ++ ++#define RTW_USB_BULK_IN_ADDR 0x80 ++#define RTW_USB_INT_IN_ADDR 0x81 ++ ++#define RTW_USB_HW_QUEUE_ENTRY 8 ++ ++#define RTW_USB_PACKET_OFFSET_SZ 8 ++#define RTW_USB_MAX_XMITBUF_SZ (1592 * 3) ++#define RTW_USB_MAX_RECVBUF_SZ 32768 ++ ++#define RTW_USB_RECVBUFF_ALIGN_SZ 8 ++ ++#define RTW_USB_RXAGG_SIZE 6 ++#define RTW_USB_RXAGG_TIMEOUT 10 ++ ++#define RTW_USB_RXCB_NUM 4 ++ ++#define RTW_USB_EP_MAX 4 ++ ++#define TX_DESC_QSEL_MAX 20 ++ ++#define RTW_USB_VENDOR_ID_REALTEK 0x0bda ++ ++static inline struct rtw_usb *rtw_get_usb_priv(struct rtw_dev *rtwdev) ++{ ++ return (struct rtw_usb *)rtwdev->priv; ++} ++ ++struct rx_usb_ctrl_block { ++ struct rtw_dev *rtwdev; ++ struct urb *rx_urb; ++ struct sk_buff *rx_skb; ++ struct list_head list; ++}; ++ ++struct rtw_usb_tx_data { ++ u8 sn; ++}; ++ ++struct rtw_usb { ++ struct rtw_dev *rtwdev; ++ struct usb_device *udev; ++ ++ /* protects rx_data_free and rx_data_used lists */ ++ spinlock_t rx_data_list_lock; ++ bool running; ++ struct work_struct rx_refill_work; ++ struct list_head rx_data_free; ++ struct list_head rx_data_used; ++ ++ /* protects usb_data_index */ ++ spinlock_t usb_lock; ++ __le32 *usb_data; ++ int usb_data_index; ++ ++ u32 bulkout_size; ++ u8 pipe_interrupt; ++ u8 pipe_in; ++ u8 out_ep[RTW_USB_EP_MAX]; ++ u8 qsel_to_ep[TX_DESC_QSEL_MAX]; ++ u8 usb_txagg_num; ++ ++ struct workqueue_struct *txwq, *rxwq; ++ ++ struct sk_buff_head tx_queue[RTW_USB_EP_MAX]; ++ struct work_struct tx_work; ++ ++ struct rx_usb_ctrl_block rx_cb[RTW_USB_RXCB_NUM]; ++ struct sk_buff_head rx_queue; ++ struct work_struct rx_work; ++}; ++ ++static inline struct rtw_usb_tx_data *rtw_usb_get_tx_data(struct sk_buff *skb) ++{ ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ++ ++ BUILD_BUG_ON(sizeof(struct rtw_usb_tx_data) > ++ sizeof(info->status.status_driver_data)); ++ ++ return (struct rtw_usb_tx_data *)info->status.status_driver_data; ++} ++ ++int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); ++void rtw_usb_disconnect(struct usb_interface *intf); ++ ++#endif +diff --git a/drivers/net/wireless/realtek/rtw88/util.c b/drivers/net/wireless/realtek/rtw88/util.c +index ed61c962fa27f..ce983ab8f947b 100644 +--- a/drivers/net/wireless/realtek/rtw88/util.c ++++ b/drivers/net/wireless/realtek/rtw88/util.c +@@ -167,7 +167,7 @@ struct rtw_iter_vifs_data { + struct list_head list; + }; + +-void rtw_collect_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ++static void rtw_collect_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) + { + struct rtw_iter_vifs_data *iter_stas = data; + struct rtw_vifs_entry *vifs_entry; diff --git a/package/kernel/mac80211/patches/rtl/007-rtw88-Add-rtw8821cu-chipset-support.patch b/package/kernel/mac80211/patches/rtl/007-rtw88-Add-rtw8821cu-chipset-support.patch new file mode 100644 index 000000000..c39fa01da --- /dev/null +++ b/package/kernel/mac80211/patches/rtl/007-rtw88-Add-rtw8821cu-chipset-support.patch @@ -0,0 +1,212 @@ +diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig +index 1624c5db69bac..2b500dbefbc2d 100644 +--- a/drivers/net/wireless/realtek/rtw88/Kconfig ++++ b/drivers/net/wireless/realtek/rtw88/Kconfig +@@ -75,6 +75,18 @@ config RTW88_8821CE + + 802.11ac PCIe wireless network adapter + ++config RTW88_8821CU ++ tristate "Realtek 8821CU USB wireless network adapter" ++ depends on m ++ depends on USB ++ select RTW88_CORE ++ select RTW88_USB ++ select RTW88_8821C ++ help ++ Select this option will enable support for 8821CU chipset ++ ++ 802.11ac USB wireless network adapter ++ + config RTW88_DEBUG + bool "Realtek rtw88 debug support" + depends on RTW88_CORE +diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile +index 9e095f8181483..3c1f08f12e44e 100644 +--- a/drivers/net/wireless/realtek/rtw88/Makefile ++++ b/drivers/net/wireless/realtek/rtw88/Makefile +@@ -44,6 +44,9 @@ rtw88_8821c-objs := rtw8821c.o rtw8821c_table.o + obj-$(CPTCFG_RTW88_8821CE) += rtw88_8821ce.o + rtw88_8821ce-objs := rtw8821ce.o + ++obj-$(CPTCFG_RTW88_8821CU) += rtw88_8821cu.o ++rtw88_8821cu-objs := rtw8821cu.o ++ + obj-$(CPTCFG_RTW88_PCI) += rtw88_pci.o + rtw88_pci-objs := pci.o + +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c +index 99eee128ae945..82e78559d8264 100644 +--- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c ++++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c +@@ -26,6 +26,12 @@ static void rtw8821ce_efuse_parsing(struct rtw_efuse *efuse, + ether_addr_copy(efuse->addr, map->e.mac_addr); + } + ++static void rtw8821cu_efuse_parsing(struct rtw_efuse *efuse, ++ struct rtw8821c_efuse *map) ++{ ++ ether_addr_copy(efuse->addr, map->u.mac_addr); ++} ++ + enum rtw8821ce_rf_set { + SWITCH_TO_BTG, + SWITCH_TO_WLG, +@@ -68,6 +74,9 @@ static int rtw8821c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) + case RTW_HCI_TYPE_PCIE: + rtw8821ce_efuse_parsing(efuse, map); + break; ++ case RTW_HCI_TYPE_USB: ++ rtw8821cu_efuse_parsing(efuse, map); ++ break; + default: + /* unsupported now */ + return -ENOTSUPP; +@@ -1142,6 +1151,18 @@ static void rtw8821c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) + dm_info->cck_pd_default + new_lvl * 2); + } + ++static void rtw8821c_fill_txdesc_checksum(struct rtw_dev *rtwdev, ++ struct rtw_tx_pkt_info *pkt_info, ++ u8 *txdesc) ++{ ++ struct rtw_chip_info *chip = rtwdev->chip; ++ size_t words; ++ ++ words = (pkt_info->pkt_offset * 8 + chip->tx_pkt_desc_sz) / 2; ++ ++ fill_txdesc_checksum_common(txdesc, words); ++} ++ + static struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8821c[] = { + {0x0086, + RTW_PWR_CUT_ALL_MSK, +@@ -1499,6 +1499,9 @@ + static const struct rtw_rfe_def rtw8821c_rfe_defs[] = { + [0] = RTW_DEF_RFE(8821c, 0, 0), + [2] = RTW_DEF_RFE_EXT(8821c, 0, 0, 2), ++ [4] = RTW_DEF_RFE_EXT(8821c, 0, 0, 2), ++ [6] = RTW_DEF_RFE(8821c, 0, 0), ++ [34] = RTW_DEF_RFE(8821c, 0, 0), + }; + + static struct rtw_hw_reg rtw8821c_dig[] = { +@@ -1589,6 +1611,7 @@ static struct rtw_chip_ops rtw8821c_ops = { + .config_bfee = rtw8821c_bf_config_bfee, + .set_gid_table = rtw_bf_set_gid_table, + .cfg_csi_rate = rtw_bf_cfg_csi_rate, ++ .fill_txdesc_checksum = rtw8821c_fill_txdesc_checksum, + + .coex_set_init = rtw8821c_coex_cfg_init, + .coex_set_ant_switch = rtw8821c_coex_cfg_ant_switch, +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.h b/drivers/net/wireless/realtek/rtw88/rtw8821c.h +index d9fbddd7b0f35..e2e17a822e48d 100644 +--- a/drivers/net/wireless/realtek/rtw88/rtw8821c.h ++++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.h +@@ -9,6 +9,26 @@ + + #define RCR_VHT_ACK BIT(26) + ++struct rtw8821cu_efuse { ++ u8 res4[4]; /* 0xd0 */ ++ u8 usb_optional_function; ++ u8 res5[0x1e]; ++ u8 res6[2]; ++ u8 serial[0x0b]; /* 0xf5 */ ++ u8 vid; /* 0x100 */ ++ u8 res7; ++ u8 pid; ++ u8 res8[4]; ++ u8 mac_addr[ETH_ALEN]; /* 0x107 */ ++ u8 res9[2]; ++ u8 vendor_name[0x07]; ++ u8 res10[2]; ++ u8 device_name[0x14]; ++ u8 res11[0xcf]; ++ u8 package_type; /* 0x1fb */ ++ u8 res12[0x4]; ++}; ++ + struct rtw8821ce_efuse { + u8 mac_addr[ETH_ALEN]; /* 0xd0 */ + u8 vender_id[2]; +@@ -73,6 +93,7 @@ struct rtw8821c_efuse { + u8 res[3]; + union { + struct rtw8821ce_efuse e; ++ struct rtw8821cu_efuse u; + }; + }; + +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cu.c b/drivers/net/wireless/realtek/rtw88/rtw8821cu.c +new file mode 100644 +index 0000000000000..f43e200515f5b +--- /dev/null ++++ b/drivers/net/wireless/realtek/rtw88/rtw8821cu.c +@@ -0,0 +1,50 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* Copyright(c) 2018-2019 Realtek Corporation ++ */ ++ ++#include ++#include ++#include "main.h" ++#include "rtw8821cu.h" ++#include "usb.h" ++ ++static const struct usb_device_id rtw_8821cu_id_table[] = { ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb82b, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb820, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc821, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc820, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82a, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82b, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc811, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8811CU */ ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x8811, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8811CU */ ++ { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x2006, 0xff, 0xff, 0xff), ++ .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* TOTOLINK A650UA v3 */ ++ {}, ++}; ++MODULE_DEVICE_TABLE(usb, rtw_8821cu_id_table); ++ ++static int rtw_8821cu_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ return rtw_usb_probe(intf, id); ++} ++ ++static struct usb_driver rtw_8821cu_driver = { ++ .name = "rtw_8821cu", ++ .id_table = rtw_8821cu_id_table, ++ .probe = rtw_8821cu_probe, ++ .disconnect = rtw_usb_disconnect, ++}; ++module_usb_driver(rtw_8821cu_driver); ++ ++MODULE_AUTHOR("Hans Ulli Kroll "); ++MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821cu driver"); ++MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cu.h b/drivers/net/wireless/realtek/rtw88/rtw8821cu.h +new file mode 100644 +index 0000000000000..c896792240011 +--- /dev/null ++++ b/drivers/net/wireless/realtek/rtw88/rtw8821cu.h +@@ -0,0 +1,10 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* Copyright(c) 2018-2019 Realtek Corporation ++ */ ++ ++#ifndef __RTW_8821CU_H_ ++#define __RTW_8821CU_H_ ++ ++extern struct rtw_chip_info rtw8821c_hw_spec; ++ ++#endif diff --git a/package/kernel/mac80211/patches/rtl/010-rtw88-disable-powersave-modes-for-USB-devices.patch b/package/kernel/mac80211/patches/rtl/010-rtw88-disable-powersave-modes-for-USB-devices.patch new file mode 100644 index 000000000..9fc0d6de8 --- /dev/null +++ b/package/kernel/mac80211/patches/rtl/010-rtw88-disable-powersave-modes-for-USB-devices.patch @@ -0,0 +1,14 @@ +diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c +index 3c07485d6ba47..fb5faf3bf1eed 100644 +--- a/drivers/net/wireless/realtek/rtw88/mac80211.c ++++ b/drivers/net/wireless/realtek/rtw88/mac80211.c +@@ -89,7 +89,8 @@ static int rtw_ops_config(struct ieee80211_hw *hw, u32 changed) + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { +- if (hw->conf.flags & IEEE80211_CONF_PS) { ++ if (hw->conf.flags & IEEE80211_CONF_PS && ++ rtw_hci_type(rtwdev) != RTW_HCI_TYPE_USB) { + rtwdev->ps_enabled = true; + } else { + rtwdev->ps_enabled = false; diff --git a/package/kernel/mac80211/realtek.mk b/package/kernel/mac80211/realtek.mk index 44c6c25b0..1db4c8ac2 100644 --- a/package/kernel/mac80211/realtek.mk +++ b/package/kernel/mac80211/realtek.mk @@ -25,8 +25,8 @@ config-y += RTL8XXXU_UNTESTED config-$(call config_package,rtl8723bs) += RTL8723BS config-y += STAGING -config-$(call config_package,rtw88) += RTW88 RTW88_CORE RTW88_PCI -config-y += RTW88_8822BE RTW88_8822CE RTW88_8723DE +config-$(call config_package,rtw88) += RTW88 RTW88_CORE RTW88_PCI RTW88_USB +config-y += RTW88_8821CU RTW88_8822BE RTW88_8822CE RTW88_8723DE config-$(CONFIG_PACKAGE_RTW88_DEBUG) += RTW88_DEBUG config-$(CONFIG_PACKAGE_RTW88_DEBUGFS) += RTW88_DEBUGFS @@ -195,9 +195,11 @@ endef define KernelPackage/rtw88 $(call KernelPackage/mac80211/Default) - TITLE:=Realtek RTL8822BE/RTL8822CE/RTL8723DE - DEPENDS+= @(PCI_SUPPORT) +kmod-mac80211 +@DRIVER_11AC_SUPPORT +@DRIVER_11N_SUPPORT + TITLE:=Realtek RTL8821CU/RTL8822BE/RTL8822CE/RTL8723DE + DEPENDS+= @(PCI_SUPPORT||USB_SUPPORT) +kmod-mac80211 +@DRIVER_11AC_SUPPORT +@DRIVER_11N_SUPPORT FILES:=\ + $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8821c.ko \ + $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8821cu.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8822be.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8822b.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8822ce.ko \ @@ -205,8 +207,9 @@ define KernelPackage/rtw88 $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8723de.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8723d.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_core.ko \ - $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_pci.ko - AUTOLOAD:=$(call AutoProbe,rtw88_8822be rtw88_8822ce rtw88_8723de) + $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_pci.ko \ + $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_usb.ko + AUTOLOAD:=$(call AutoProbe,rtw88_8821cu rtw88_8822be rtw88_8822ce rtw88_8723de) endef define KernelPackage/rtl8723bs