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