1. 程式人生 > >x1000e rtl wifi 移植

x1000e rtl wifi 移植

前情提要

這次是要換一個 wifi 晶片,就把這個換的過程記錄下來,因為自己也是新手,很多東西都是自己一點點摸出來的,就希望一些東西能對跟我一樣,新入門的人有些幫助,能快速入門。

基本設施

  • 基於 君正 x1000e, halley2
  • wifi 由原君正的換到 rtl8189es
  • 切換前是用的 原廠 demo 板,切換後用的是新畫的板子,上面是新的wifi 晶片

一步步的經歷

首先可以確定的,在原來的 demo 板上,一切OK。wifi 都是正常的。開始換新的晶片,並且移植程式碼

程式碼移植

替換的新 rtl 晶片是供應商提供原始碼的,所以第一步是將原始碼放到核心中去編譯。當然,編譯一般會挺順利,修改了原始碼中的配置,設定對應的編譯器之類的。(原始碼是供應商的,所以只能說明思路,程式碼不能展示)

首次執行

當然,很明顯,執行起來沒有效果,wlan0 裝置沒有生成。接著就開始想是哪裡的問題。這個 wifi 晶片是 sdio 介面的,看原理圖,sdio 部分基本不管,那麼可操作的就只有 PWR_EN 和 WAKEUP,看起來,WAKEUP 像是休眠之類的功能,問的供應商可以不管,那麼就只有 PWR_EN 了,但是這個不管怎麼設定都還是沒有效果。

下面肯定就沒有什麼辦法了,也不知道怎麼弄了,那麼就只有原始碼對比,我開始對比原來的實現,和現在的實現有什麼區別,這麼大的程式碼量,對比起來還是很難下手的。但是大家的介面都是一樣的 sdio,所以其實想一想,肯定就是一些配置的問題,於是就從配置部分去考慮,其它的不用去處理。

程式碼分析

首先開始瞭解一下程式碼。之前一直看驅動,但是沒有實踐,所以有些東西,理解不了它在程式碼上是個什麼展示方式,所以有時候別人給你提示你也不知道怎麼弄,想問別人也不知道怎麼問。

這裡首先有個所謂的 platform 模型,這是說核心已經做好的 驅動和裝置的對應框架結構,使用者只需要設計好驅動,配置一下裝置就可以用了。而一般驅動都是由晶片廠商提供好。那麼終端使用者就只要去配置裝置部分就可以了。

                platform
         device  <---->   driver
         name    -----     name
      arch/xxx/xx     driver/net/wireless/xxx

你上面這樣,驅動部分一般都放在 driver 的相關目錄下,實現 probe 函式,在載入時核心會去實現相互的關聯,不管哪個先,哪個後,最終都會去匹配,比如載入 driver ,會去找尋裝置,而裝置存在了就會去找尋驅動 。在設定 device 和 driver 的時候都要求寫個 name ,核心會根據name 部分進行匹配

在更新的核心中,device 部分不在是自己去寫程式碼了,而是基於 dts 來實現了

而裝置部分一般放在 arch 目錄下對應的平臺中,因為這部分是平臺相關的,也就是自己設計硬體之後可能需要改動的地方。

arch
  | - mips
     | - xburst  - 晶片架構 
       | - soc-x1000 - 晶片soc
            | - common
            | - chip-x1000 - 具體型號
                 | - halley2 - 具體型別
                     | - common
                     | - halley2_v10 - 更細的分類,一般自己修改硬體,需要改動引腳配置就在這裡

依據上圖,以君正 x1000e,halley2 來看看目錄結構圖,差不多就是這樣。一般需要修改的在 halley2_v10halley2/common 目錄下,halley2_v10 一般是引腳配置,halley2/common 一般是相應驅動裝置的配置。

比如剛剛 wifi 中提到的可能需要改動的兩個引腳

#define GPIO_WIFI_RST_N     GPIO_PC(17)
#define GPIO_WIFI_WAKE      GPIO_PC(16)

會有這個類似於這樣的定義,指明是哪個引腳,這個具體名字,可以根據原理圖上的去看,一般會盡可能讓名字相近,原理圖上會說明對應的引腳是哪個。

那麼怎麼去操作呢,一般我們會根據這個引腳名在相關平臺下去搜索哪裡用到了就會找到。

先說一下,君正原生的 wifi 相關的檔案有哪些

halley2/common/mmc.c - wifi sdio 介面的配置
halley2/common/43438_wlan_device.c - 裝置的配置
halley2/common/43438_wlan_power_control.c - 引腳的配置,主要是定義一些功能函式,會有裝置或驅動 呼叫
drivers/net/wireless/bcmdhd_1_141_66/dhd_linux.c - 驅動入口函式

mmc 暫時可以不關注,是 sdio 介面的使能操作,沒有修改都是正常的

43438_wlan_device.c 裡面有一段程式碼

static struct platform_device wlan_device = { 
    .name   = "bcmdhd_wlan",
    .id     = 1,
    .dev    = { 
        .platform_data = NULL,
    },  
    .resource   = wlan_resources,
    .num_resources  = ARRAY_SIZE(wlan_resources),
};

static int __init wlan_device_init(void)
{
    int ret;

    ret = platform_device_register(&wlan_device);

    return ret;
}

再對應驅動部分的

#define WIFI_PLAT_NAME      "bcmdhd_wlan"
static struct platform_driver wifi_platform_dev_driver = {                                                                                     
    .probe          = wifi_plat_dev_drv_probe,
    .remove         = wifi_plat_dev_drv_remove,
    .suspend        = wifi_plat_dev_drv_suspend,
    .resume         = wifi_plat_dev_drv_resume,
    .driver         = {
    .name   = WIFI_PLAT_NAME,
    }
};
       err = platform_driver_register(&wifi_platform_dev_driver);

驅動部分在檔案中的各個地方,我截取出重點,可以看到這裡驅動部分的name 對應的與 device 中的 name 一樣,這樣來形成對應。所以驅動部分通過 platform_driver_register 來註冊驅動 而,platform_device_register 來註冊裝置,核心就記錄他們並進行匹配。其中那個 xxx_power_control.c 中定義的引腳操作就會被這裡的驅動層所使用,具體可以看原始碼。

驅動部分這裡就不多說了,主要是裝置部分

static struct resource wlan_resources[] = { 
    [0] = { 
        .start = GPIO_WIFI_WAKE,
        .end = GPIO_WIFI_WAKE,
        .name = "bcmdhd_wlan_irq",
        .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
    },  
};

這裡是對 WAKEUP 部分的定義,就是 device 部分的 resource , 驅動部分會對應這引腳申請 irq,並定義中斷函式,這裡應該就是去實現休眠相關的東西吧。

電源部分就不取程式碼了,主要功能就是通過gpio 方式操作引腳實現開和關,當然裡也有很多細節的東西,但這裡就不說了。

裝置與驅動的程式碼架構和匹配方式就如這裡說的,其它的硬體基本也是如此,就可以按這樣的方式來看其它的硬體裝置。

繼續除錯

上面說的這些,都是我自己看程式碼總結出來的東西,下面還是要繼續除錯裝置

在程式碼移植好之後,核心配置,將原來的wifi 關掉,開啟新的,然後執行,當然,沒有成功。思考之後,覺得是device 部分沒有配置,因為 rtl 的原始碼中沒有找到,而且他們肯定不知道我們的硬體是怎麼配置的。所以手動去初始化那個裝置。甚至把這些配置加到 rtl 的原始碼驅動層中,也都沒有用。

使用工具測量 wifi 引腳也是有電壓的。列印也是有輸出的。可見流程都走了,但就是沒有生成裝置。那麼必然還是哪個地方的執行流程不對。於是再跟原來的做對比。

在程式碼中找了許久,一般 platform 架構要有 platform_driver_register, platform_device_register來進行配對,在以前的版本中都能找到,但是在新的程式碼中沒有找到 platform_driver_register 可見這裡應該有什麼問題。於是想到把以前的 wifi 也開著,現在也開關,那麼以前的操作 device 結束後,再由新的wifi 來啟動其它操作應該可以。這樣一試,果然可以。那麼問題肯定就出在 device 操作相關的問題上。

看以前的程式碼,呼叫過程應該是

platform_driver_register -> device_init -> sdio_register_driver

當然 , device_init 部分還是有很多具體的操作過程的。而在新的程式碼中只有 sdio_driver_register 部分,那麼想必在新的程式碼中加入 platform_driver_register 部分的操作應該就是可以的。但是時間不足,暫時沒有作具體驗證。

其中 sdio_register_driver 是 sdio 介面部分的功能,sdio 與 platform 的匹配部分是不一樣的

static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) },
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) },
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) },
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) },
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) },
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) },
    { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) },
    { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE)        },
    { /* end: all zeroes */             },
};

MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);

static struct sdio_driver bcmsdh_sdmmc_driver = {
    .probe      = bcmsdh_sdmmc_probe,
    .remove     = bcmsdh_sdmmc_remove,
    .name       = "bcmsdh_sdmmc",
    .id_table   = bcmsdh_sdmmc_ids,
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
    .drv = {
    .pm = &bcmsdh_sdmmc_pm_ops,
    },
#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
    };

這是老wifi 中的程式碼部分,可以看到,這裡有個 id_table ,sdio 部分的匹配過程,在網上找到是通過搜尋找到 是通過找 id_table 來確定對應的裝置的。

總結

到這裡,基本上調試出來了,後面有時間再驗證最後一個。自己也是從不懂一步步看程式碼,測試過來的。裡面也會有遺漏,出錯的地方,歡迎指正。