2013年2月21日 星期四

成功在 Fedora 18 安裝「EDIMAX EW-7811Un」驅動

自上次在 Fedora 更新核心到 3.7 版後,造成無線網路不能使用,導致只能用變通辦法(請參考 “在 Fedora 18 用「EDIMAX EW-7811Un」的變通辦法”)後,對於這件事筆者一直想儘快找出解答。最近,解決了 NVIDIA FX5200 在 Fedora 18 安裝驅動程式的問題後,讓我又對 EDIMAX EW-7811Un 的問題解決,燃起信心。
    當然,Google 的結果也是找到一些不確定是直接或間接的解答,只好彙整、研究、分析,再加上許多嘗試,終於,還是找到成功安裝的步驟。

一、下載「驅動程式」、解壓縮

和前文一樣,從 Realtek 的官網下載驅動程式才安全,因為要修改「核心模組原始碼」的一個標頭檔及三個 C 程式檔,所以原有驅動程式的安裝不可以讓它自動作業,要分段執行。
    第一步先解壓縮,
[SiB@M2400 ~]$ unzip RTL8192xC_USB_linux_v3.4.4_4749.20121105.zip
解開後,產生一個名為 RTL8188C_8192C_USB_linux_v3.4.4_4749.20121105 的目錄,之後的文章中將這個目錄稱為『原始碼目錄』。
    接著把工作目錄切換到『原始碼目錄』中一個『driver』目錄,還要再做解壓縮,
[SiB@M2400 ~]$ cd RTL8188C_8192C_USB_linux_v3.4.4_4749.20121105/driver/
[SiB@M2400 driver]$ tar zxvf rtl8188C_8192C_usb_linux_v3.4.4_4749.20121105.tar.gz
解開後,產生一個名為 rtl8188C_8192C_usb_linux_v3.4.4_4749.20121105 的目錄,之後的文章中將這個目錄稱為『驅動原始碼目錄』,主要修改的檔案就在這個目錄之下。

二、下載「修補檔」參考、修改編譯腳本及程式

這裏下載「修補檔」。筆者測試結果,可以正確修改驅動程式檔,所以直接用『修補』命令的方式偷懶。先將工作目錄切換到『驅動原始碼目錄』,再用 patch 命令修補檔案,
[SiB@M2400 driver]$ cd rtl8188C_8192C_usb_linux_v3.4.4_4749.20121105
[SiB@M2400 rtl8188C_8192C_usb_linux_v3.4.4_4749.20121105]$ patch -p1 -i ~/use_kthread_run.patch
上兩個命令是接續前面的命令,第二個命令是假設所下載的『修補檔(patch file)』在用戶 SiB 的『家目錄』。所修改的檔案整理如下:
目錄檔案名修改處的列號
驅動原始碼目錄/core/rtw_mp.c1143
驅動原始碼目錄/include/osdep_service.h102, 137, 572, 829
驅動原始碼目錄/os_dep/osdep_service.c1555
驅動原始碼目錄/os_dep/linux/os_intfs.c799
上表中修改處的數字為列數。為留存完整記錄,將修改處整理在後,避免將來網路下載不到修補檔。因為檔案前面的修改都會加入文字,影響之後的列數,故建議由程式後端開始修改。

 檔案『驅動原始碼目錄/core/rtw_mp.c』的修改

第 1143 列,原文
//3 6. start thread
pmp_priv->tx.PktTxThread = kernel_thread(mp_xmit_packet_thread, pmp_priv, CLONE_FS|CLONE_FILES);
if(pmp_priv->tx.PktTxThread < 0)
 DBG_871X("Create PktTx Thread Fail !!!!!\n");
修改為
//3 6. start thread
if(!start_kthread(&pmp_priv->tx.PktTxThread, mp_xmit_packet_thread, pmp_priv, "8192cu-mp-xmit"))
 DBG_871X("Create PktTx Thread Fail !!!!!\n");
完成以上修改後,存檔備用。

 檔案『驅動原始碼目錄/include/osdep_service.h』的修改(由後而前)

第 829 或 830 列,原文為空白(檔案最後),加入三列文字成為
#ifdef PLATFORM_LINUX
extern int start_kthread(_thread_hdl_ *t_hdl, int (*threadfn)(void *data), void *data, const char *name);
#endif
第 572 列,原文(2013-3-4 增補)
#ifdef PLATFORM_LINUX
 //struct net_device *pnetdev = (struct net_device *)context;
 //daemonize("%s", pnetdev->name);
 daemonize("%s", "RTKTHREAD");
 allow_signal(SIGTERM);
#endif
修改為
#ifdef PLATFORM_LINUX
 //struct net_device *pnetdev = (struct net_device *)context;
 //daemonize("%s", pnetdev->name);
 //daemonize("%s", "RTKTHREAD");
 allow_signal(SIGTERM);
#endif
第 137 列,原文
 typedef pid_t _thread_hdl_;
修改為
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
 typedef pid_t _thread_hdl_;
#else
 typedef struct task_struct * _thread_hdl_;
#endif
第 102 或 103 列,原文為空白,加入三列文字成為
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0))
 #include <linux/kthread.h>
#endif
完成以上修改後,存檔備用。

 檔案『驅動原始碼目錄/os_dep/osdep_service.c』的修改

第 1555 列,原文為空白(檔案最後),加入 14 列文字
#ifdef PLATFORM_LINUX
int start_kthread(_thread_hdl_ *t_hdl, int (*threadfn)(void *data), void *data, const char *name)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
 *t_hdl = kernel_thread(threadfn, data, CLONE_FS|CLONE_FILES);
 if(*t_hdl < 0)
#else
 *t_hdl = kthread_run(threadfn, data, name);
 if(IS_ERR(*t_hdl))
#endif
  return 0;
 return -1;
}
#endif
完成以上修改後,存檔備用。

 檔案『驅動原始碼目錄/os_dep/linux/os_intfs.c』的修改

第 799 列,原文
#ifdef CONFIG_SDIO_HCI
 padapter->xmitThread = kernel_thread(rtw_xmit_thread, padapter, CLONE_FS|CLONE_FILES);
 if(padapter->xmitThread < 0)
  _status = _FAIL;
#endif

#ifdef CONFIG_RECV_THREAD_MODE
 padapter->recvThread = kernel_thread(recv_thread, padapter, CLONE_FS|CLONE_FILES);
 if(padapter->recvThread < 0)
  _status = _FAIL; 
#endif

 padapter->cmdThread = kernel_thread(rtw_cmd_thread, padapter, CLONE_FS|CLONE_FILES);
 if(padapter->cmdThread < 0)
  _status = _FAIL;
 else
  _rtw_down_sema(&padapter->cmdpriv.terminate_cmdthread_sema); //wait for cmd_thread to run

#ifdef CONFIG_EVENT_THREAD_MODE
 padapter->evtThread = kernel_thread(event_thread, padapter, CLONE_FS|CLONE_FILES);
 if(padapter->evtThread < 0)
  _status = _FAIL;  
#endif
修改為
#ifdef CONFIG_SDIO_HCI
 if(!start_kthread(&padapter->xmitThread, rtw_xmit_thread, padapter, "8192cu-xmit"))
  _status = _FAIL;
#endif

#ifdef CONFIG_RECV_THREAD_MODE
 if(!start_kthread(&padapter->recvThread, recv_thread, padapter, "8192cu-recv"))
  _status = _FAIL; 
#endif

 if(!start_kthread(&padapter->cmdThread, rtw_cmd_thread, padapter, "8192cu-cmd"))
  _status = _FAIL;
 else
  _rtw_down_sema(&padapter->cmdpriv.terminate_cmdthread_sema); //wait for cmd_thread to run

#ifdef CONFIG_EVENT_THREAD_MODE
 if(!start_kthread(&padapter->evtThread, event_thread, padapter, "8192cu-evt"))
  _status = _FAIL;  
#endif
完成以上修改後,存檔備用。

三、編譯「核心模組」

接下來準備下編譯命令了,先切換成 root 身份,並將工作目錄切換到『驅動原始碼目錄』,再下命令
[root@M2400 rtl8188C_8192C_usb_linux_v3.4.4_4749.20121105]# make

四、安裝「核心模組」

最後執行安裝操作,這些命令是從『原始碼目錄』下的 install.sh 這個腳本檔取出。這是 Realtek RTL8192cu 驅動程式原來的安裝步驟(解壓縮、編譯、安裝)的最後一個步驟,
[root@M2400 rtl8188C_8192C_usb_linux_v3.4.4_4749.20121105]# rmmod rtl8192cu
[root@M2400 rtl8188C_8192C_usb_linux_v3.4.4_4749.20121105]# insmod 8192cu.ko
[root@M2400 rtl8188C_8192C_usb_linux_v3.4.4_4749.20121105]# make install
這時候,讀者應該可以開始測試無線網路了。
    幾個相關的發文列在下面,有需要的讀者請自行參考:

五、抑制 Fedora 原裝驅動程式

安裝完成後,當然希望下次開機可以作業系統可以載入正確的核心模組,在重開機前還有個步驟要做,待會重開機才會正確。
    就是抑制 Fedora 原裝驅動程式,筆者發現的幾個做法,把它們都列出來,讓讀者自己選用:
  • 在檔案『/etc/modprobe.d/blacklist.conf』末,加上『blacklist rtl8192cu』這行敘述。
  • 或是在檔案『/boot/grub2/grub.conf』中,linux 開始那列命令最後,加上『modprobe.blacklist=rtl8192cu』。
注意:『  』只是為標示文字範圍,不要加進去。

後記:

  • 2013-2-22:發現 Fedora 的核心又更新到 3.7.9-201,按照本文再試一下。修改了一個忙中有錯的地方。同時簡化本文步驟,移除 version.h 的鏈結設定;這個設定在編譯 EDIMAX EW-7811Un 的核心模組時是不需要的,但編譯其它核心模組時,可能需要。因此,若是已加入 version.h 鏈結,不必移除也不會出錯。
  • 2013-3-4:Fedora 的核心又更新到 3.8.1-201。在檔案『驅動原始碼目錄/include/osdep_service.h』的第 575 列要再修改一下,編譯才不會出錯。
  • 2013-3-25:若是安裝 Fedora 17 系統,在最近的更新之後 kernel 為 3.8.3-103,同樣會出現這個問題。本文解決辦法也可適用

已測試版本:

  • Fedora 18: kernel 3.7.6-201, 3.7.7-201, 3.7.8-202, 3.7.9-201

參考資料:

沒有留言:

張貼留言

感謝你耐心看完本文,歡迎留下任何指正、建議,筆者會儘快回應。(English is also welcome.)