1. 程式人生 > >【迅為iTop4412學習筆記】7.以模組方式註冊裝置,以及驅動獲取裝置資訊

【迅為iTop4412學習筆記】7.以模組方式註冊裝置,以及驅動獲取裝置資訊

宣告

以下都是我剛開始看驅動視訊的個人強行解讀,如果有誤請指出,共同進步。

本節目標

以module的方式註冊裝置


正文

我們之前提到的流程:註冊裝置->註冊驅動,匹配成功則呼叫probe函式。
(至於probe函式是用來幹啥的,這個我暫時也不瞭解,以後學深了再去探究,我先入門吧…)

先回想一下注冊裝置的流程:

  1. 我們先是在Kconfig裡仿照LEDS_CTL編寫了一個MRYANG_CTL(圖形介面才會有選項)(筆記2的內容)。
  2. 因為定義了MRYANG_CTL,所以 .config檔案才會出現巨集定義CONFIG_MRYANG_CTL(筆記2的內容)。
  3. CONFIG_MRYANG_CTL定義了,在註冊裝置章節才會定義platform_device結構體,我們定義的名字叫s3c_device_mryang_ctl,再把他傳參給linux。
  4. 這樣註冊的裝置就被直接編譯進linux了,最後編譯出來的zImage檔案,就直接包含了我們的裝置。

再回想一下注冊驅動的流程:

  1. 我們選擇以模組module的方式進行註冊、解除安裝驅動(筆記1的內容)
  2. 模組載入的時候我們選擇註冊驅動。
  3. 模組解除安裝的時候我們選擇解除安裝驅動。

對於驅動,我們並沒有像註冊裝置一樣直接編譯進核心,而是選擇以模組module的方式編寫驅動,編寫完了之後我們就insmod進行註冊驅動、rmmod進行解除安裝驅動。

其實也好想,我們舉個實際的例子:
假如我要出去玩,我可以不帶鑰匙,不帶錢,但是!我一定要帶手機!所以手機我一定要,那麼就把他貼身帶著,成為我身體的一部分(設備註冊)。
走在路上,我把手機殼套上了(驅動註冊),用了一下感覺不想用了,就取下來了(驅動解除安裝)。

但是如果我出門我感覺我什麼都是必需品,我手機要帶,我鑰匙要帶,包要帶,水要帶,吃的要帶,那我本來輕輕鬆鬆的一個人揹著一身的累贅,所以我甚至可以果斷一點,我啥也不帶了。我走在外面要手機的時候就把手機拿來(設備註冊),不用了就丟開(裝置解除安裝),一身清爽豈不是美滋滋?(不要考慮為什麼要手機的時候就能拿來手機,只是這麼一說…)

所以,也就衍生出了本章的內容

以模組module的方式註冊、解除安裝裝置

先上模組的模板程式碼

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MrYang");

static int mryang_init(void)
{
	printk(KERN_EMERG "HELLO MrYang\n");
	//platform_driver_register(&mryang_driver);
	return 0;
}

static void mryang_exit(void)
{
	printk(KERN_EMERG "Bye MrYang\n");
	//platform_driver_unregister(&mryang_driver);
}

module_init(mryang_init);
module_exit(mryang_exit);

大家應該注意到,這是一個標準的載入模組輸出HELLO MrYang,解除安裝模組輸出Bye MrYang的很簡單的一個模組程式碼模板。
我們注意到我還在載入和解除安裝模組的地方分別註釋了驅動註冊、驅動解除安裝的函式,是為了方便後面對比。

上面的程式碼直接編譯 insmod和rmmod就可以輸出載入解除安裝的資訊了

linux的程式碼名字很直觀,我們註釋的兩行函式是之前註冊驅動的時候寫的(函式傳入的結構體的定義我沒附上程式碼)

所以我們註冊驅動,名字稍微一改就行了,註冊驅動是platform_driver_register(),那麼註冊裝置就是platform_device_register(),同理解除安裝也是,他們的函式都在 include/linux/platform_device.h 裡,所以想檢視結構體和函式的可以看一下,我們使用記得包含標頭檔案!

// 標頭檔案
#include <linux/platform_device.h>
// 放入載入模組的函式裡
platform_device_register(&mryang_device);
// 放入解除安裝模組的函式裡
platform_device_unregister(&mryang_device);

函式先寫了,但是裡面有個引數是mryang_device,這是啥?我們在註冊驅動的時候就有類似的情況,我們以結構體傳參,所以無論是註冊裝置還是解除安裝裝置,我們都要定義這個結構體,而結構體內是什麼內容呢?想想我們在把裝置編譯進核心的時候做了什麼,附上當時的程式碼

#ifdef CONFIG_MRYANG_CTL
struct platform_device s3c_device_mryang_ctl = {
        .name   = "mryang_ctl",
        .id             = -1,
};

我們也是定義了一個platform_device結構體,引數給了一個name,並給了一個id號,那麼同樣的,我們就把他copy過來。因為我們之前編譯燒寫的linux核心裡已經有了mryang_ctl這個名字,所以這次我們換一個名字叫mryang_module_ctl。

struct platform_device s3c_device_mryang_module_ctl = {
        .name   = "mryang_module_ctl",
        .id             = -1,
};

是不是以為這就完了?我們直接編譯會報錯,是因為platform_device_unregister()函式在解除安裝時會呼叫release函式(可以檢視原始碼,點選 講解 可以看一下別人分析的原始碼)

所以我們要在結構體內新增release函式,如果看了結構體可能會問,沒看到有release的函式指標呀,其實是在結構體的dev結構體內,其他函式指標是

void (*release)(struct device *dev);

所以我們完整的定義結構體

struct platform_device s3c_device_mryang_module_ctl = {
        .name   = "mryang_module_ctl",
        .id             = -1,
        .dev = {
        	.release = mryang_module_release,
        }
};

release的指標指向我們自定義的 mryang_module_release() 函式,所以我們再去編寫他

static void mryang_module_release(struct device *pdv)
{
	printk(KERN_EMERG "MrYang module release!\n");
}

提示一下,這個函式名字雖然是自定義的,但是他的型別和引數可不是我們自定義的,而是我們需要檢視函式指標怎麼寫,我們就怎麼寫,一會兒還會提這些

完整的程式碼

#include <linux/init.h>
#include <linux/module.h>

#include <linux/platform_device.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MrYang");

static void mryang_module_release(struct device *dev)
{
	printk(KERN_EMERG "MrYang module release!\n");
}

struct platform_device mryang_device = {
        .name   = "mryang_module_ctl",
        .id             = -1,
        .dev = {
        	.release = mryang_module_release,
        }
};

static int mryang_init(void)
{
	printk(KERN_EMERG "HELLO MrYang\n");
	// platform_driver_register(&mryang_driver);
	platform_device_register(&mryang_device);
	return 0;
}

static void mryang_exit(void)
{
	printk(KERN_EMERG "Bye MrYang\n");
	// platform_driver_register(&mryang_driver);
	platform_device_unregister(&mryang_device);
}

module_init(mryang_init);
module_exit(mryang_exit);

make編譯出來的檔案我們在板子上試試

[[email protected]]# insmod mryang_module_device.ko                                
[  108.046209] HELLO MrYang
[[email protected]]# ls /sys/devices/platform/ | grep mr                           
mryang_ctl
mryang_module_ctl
[[email protected]]# rmmod mryang_module_device                                    
[  112.745444] Bye MrYang
[  112.746882] MrYang module release!

可以看到 輸出確實符合我們的預期,解除安裝裝置的時候呼叫release函式,通過使用命令

ls /sys/devices/platform/

也確實看到了我們的模組,mryang_ctl是當初編譯進核心的,mryang_module_ctl是我們剛剛insmod的。

以上就完成了我們這次的以模組的形式來註冊裝置(我們之前是直接把裝置編譯進核心)

到此並沒有結束。

我們無論是註冊驅動(呼叫probe()函式)還是註冊裝置(呼叫release()函式),其實引數都有一個結構體,但是我們在函式裡從來都沒有用過。

他實際上使用來裝置與驅動的“交流”的。

如果說驅動和裝置的匹配是他們看對眼了一見鍾情,那麼他們也要互相知道對方的姓名或其他資訊。同理驅動和裝置也是一樣,他們通過結構體來傳遞引數,引數就是我們配置結構體的時候的name,id等等。

所以接下來我們就來試試,在呼叫驅動的時候,是否能通過引數來讀取我們註冊的裝置的資訊。

為了方便,我們可以直接使用上節註冊驅動的檔案內容,只是在probe()裡多加幾行來列印引數。

因為我現在有兩個裝置,一個是編譯進核心的mryang_ctl,一個是以模組載入的mryang_module_ctl。兩個都一樣,我就以模組載入的註冊裝置的為例。

我們的預期是列印 裝置的 name,id,以及呼叫裝置的release() 函式,前兩個沒啥問題,後者的話我們要注意到release()的函式原型

void (*release)(struct device *dev);

傳入的引數是 結構體 device,是我們傳入的結構體的子結構體,所以我們呼叫的話,也一定是傳入結構體的結構體。

下面直接上全部程式碼,主要看probe()函式(注意驅動的名字哈,要對應哪個裝置就改成哪個裝置的名字)

#include <linux/init.h>
#include <linux/module.h>

#include <linux/platform_device.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MrYang");

int mryang_probe(struct platform_device *pdv)
{
	printk(KERN_EMERG "probe!\n");
	// 列印 name
	printk(KERN_EMERG "pdv->name = %s\n", pdv->name);
	// 列印 id
	printk(KERN_EMERG "pdv->id = %d\n", pdv->id);
	// 呼叫裝置的release函式,傳入的引數是一個指標,以及引數是platform結構體的子結構體device
	pdv->dev.release(&pdv->dev);
	return 0;
}

int mryang_remove(struct platform_device *pdv)
{
	printk(KERN_EMERG "remove!\n");
	return 0;
}

struct platform_driver mryang_driver = {
	.probe = mryang_probe,
	.remove = mryang_remove,
	.driver = {
		.name = "mryang_module_ctl",
		.owner = THIS_MODULE,
	}
};

static int mryang_init(void)
{
	printk(KERN_EMERG "HELLO MrYang\n");
	platform_driver_register(&mryang_driver);
	return 0;
}

static void mryang_exit(void)
{
	printk(KERN_EMERG "Bye MrYang\n");
	platform_driver_unregister(&mryang_driver);
}

module_init(mryang_init);
module_exit(mryang_exit);

輸出如下,注意我們剛剛裝置的模組已經insmod了,所以我這裡直接insmod驅動即可

[[email protected]]# insmod probe_linux_module.ko                                  
[   37.872810] HELLO MrYang
[   37.874240] probe!
[   37.876258] pdv->name = mryang_module_ctl
[   37.880008] pdv->id = -1
[   37.890154] MrYang module release!

輸出符合預期

  1. 首先載入模組輸出HELLO MrYang
  2. 呼叫probe列印 probe!
  3. 接著輸出裝置的name和id
  4. 最後呼叫裝置的release()函式(我們之前寫的)

以上就是全部內容

本節我們做了兩個事

一個以模組的方式來註冊裝置(而非直接編譯進核心)
一個是驅動通過結構體引數來讀取裝置的資訊