RTL8821CE 在Linux Kernel 5.0 上並沒有相關驅動,藍牙設備雖可以識別但無法使用,而WiFi設備都不能識別出來。
在搜索引擎中搜索一番,找到了一個可行的回答,但是這個回答是2017年給出的,有些過時了。但是基本思路還是不變的,只要稍作修改就可成功編譯驅動程序。
從 chili555 的回答中給出的下載鏈接下載驅動源代碼壓縮包(所在項目還在繼續開發,本文使用的是2019年8月初下載的源代碼)。
按照 chili555 給出的步驟操作,在執行make
命令前,要修改一下Makefile.mk
和rtl8821ce.mk
兩個文件內的路徑配置,只要把文件內的所有$(srctree)/
刪去就行了。
示例(行首的-
表示修改前的代碼,+
表示修改后的代碼。):
-EXTRA_CFLAGS += -I$(srctree)/$(src)/platform
+EXTRA_CFLAGS += -I$(src)/platform
_PLATFORM_FILES := platform/platform_ops.o
-EXTRA_CFLAGS += -I$(srctree)/$(src)/hal/btc
+EXTRA_CFLAGS += -I$(src)/hal/btc
編譯時出現了以下錯誤,錯誤原因是函數簽名不符。
注意:這里的os_intfs.c
文件是我修改過的,出錯的1470行對應於原始文件中的1467行。
CC [M] /home/name/nv/rtl8821ce/os_dep/linux/os_intfs.o
/home/name/nv/rtl8821ce/os_dep/linux/os_intfs.c:1470:22: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.ndo_select_queue = rtw_select_queue,
^~~~~~~~~~~~~~~~
/home/name/nv/rtl8821ce/os_dep/linux/os_intfs.c:1470:22: note: (near initialization for ‘rtw_netdev_ops.ndo_select_queue’)
cc1: some warnings being treated as errors
在源代碼中找到出錯代碼所在的結構體名稱net_device_ops
。
//rtl8821ce/os_dep/linux/os_intfs.c:1459
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
static const struct net_device_ops rtw_netdev_ops = {
//...
.ndo_start_xmit = rtw_xmit_entry,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
.ndo_select_queue = rtw_select_queue, //error: initialization from incompatible pointer type
#endif
.ndo_set_mac_address = rtw_net_set_mac_address,
//...
};
#endif
在搜索引擎搜索一番后找到了net_device_ops
的聲明,在頭文件netdevice.h
中。
發現兩函數的函數簽名不一致,os_intfs.c
中的函數實現少了第四個形參select_queue_fallback_t fallback
。
/*
/usr/src/linux-headers-5.0.0-20/include/linux/netdevice.h:1247
*/
struct net_device_ops {
//......
u16 (*ndo_select_queue)(struct net_device *dev,
struct sk_buff *skb,
struct net_device *sb_dev,
select_queue_fallback_t fallback);
//......
}
/*
rtl8821ce/os_dep/linux/os_intfs.c:1325
*/
static u16 rtw_select_queue(struct net_device *dev, struct sk_buff *skb
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
, struct net_device *sb_dev
#endif
)
{
_adapter *padapter = rtw_netdev_priv(dev);
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
skb->priority = rtw_classify8021d(skb);
if (pmlmepriv->acm_mask != 0)
skb->priority = qos_acm(pmlmepriv->acm_mask, skb->priority);
return rtw_1d_to_queue[skb->priority];
}
現在考慮如何修補這個函數實現。幸好在函數體中並沒有用到第四個形參,因而直接把缺少的形參添上就可以了(可能會引入一些BUG)。
下面是rtl8821ce/os_dep/linux/os_intfs.c
的修改內容變化(由diff -u
生成)。
@@ -1323,9 +1323,8 @@ unsigned int rtw_classify8021d(struct sk_buff *skb)
static u16 rtw_select_queue(struct net_device *dev, struct sk_buff *skb
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
,struct net_device *sb_dev
-#endif
+ ,select_queue_fallback_t fallback
)
{
_adapter *padapter = rtw_netdev_priv(dev);
之后,再進行編譯,得到下面的輸出,表示編譯通過了。之后就可以安裝和加載模塊了。注意只有關閉了安全啟動,才能加載未簽名的內核模塊。可以參考我的這篇隨筆Linux內核模塊在安全啟動模式下的簽名和安裝,自簽名模塊並加載。
LD [M] /home/name/nv/rtl8821ce/8821ce.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/name/nv/rtl8821ce/8821ce.mod.o
LD [M] /home/name/nv/rtl8821ce/8821ce.ko
相關鏈接:
https://github.com/endlessm/linux/tree/master/drivers/net/wireless/rtl8821ce