RTL8821CE 无线网卡/蓝牙驱动的编译 Linux Kernel 5.0.0-20

时间:2024-03-06 09:06:06

  RTL8821CE 在Linux Kernel 5.0 上并没有相关驱动,蓝牙设备虽可以识别但无法使用,而WiFi设备都不能识别出来。

  在搜索引擎中搜索一番,找到了一个可行的回答,但是这个回答是2017年给出的,有些过时了。但是基本思路还是不变的,只要稍作修改就可成功编译驱动程序。

  从 chili555 的回答中给出的下载链接下载驱动源代码压缩包(所在项目还在继续开发,本文使用的是2019年8月初下载的源代码)。

  按照 chili555 给出的步骤操作,在执行make命令前,要修改一下Makefile.mkrtl8821ce.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://minhaskamal.github.io/DownGit/#/home?url=https://github.com/endlessm/linux/tree/master/drivers/net/wireless/rtl8821ce

https://github.com/endlessm/linux/tree/master/drivers/net/wireless/rtl8821ce

https://askubuntu.com/questions/990378/wi-fi-not-working-on-lenovo-thinkpad-e570-realtek-rtl8821ce/990571#990571