1. 程式人生 > >Linux裝置驅動程式學習(14)

Linux裝置驅動程式學習(14)

通過一個裝置在核心中生命週期的各個階段,可以更好地理解Linux裝置模型。我將通過分析lddbus和sculld的原始碼來了解Linux裝置模型中各環節的整合。《LDD3》中的(PCI匯流排)各環節的整合這部分內容作為參考資料,因為嵌入式Linux比較少用到PCI匯流排。看這部分內容一定要先熟悉一下 lddbus 和 sculld 的原始碼。 一、lddbus模組:新增匯流排、匯出匯流排裝置和裝置驅動的註冊函式。 lddbus子系統聲明瞭一個bus_type結構,稱為ldd_bus_type 。原始碼是在編譯時初始化了這個結構體,原始碼:

/*
 * And the bus type.
 */

struct bus_type ldd_bus_type = {
    .name = "ldd",
    .match = ldd_match,
    .uevent  = ldd_uevent,
};

在將lddbus子系統裝載到核心和從核心解除安裝的原始碼如下:

static int __init ldd_bus_init(void)
{
    int ret;


    ret = bus_register(&ldd_bus_type); /*註冊匯流排,在呼叫這個函式之後ldd_bus_type 結構體將向核心註冊,在/sys/bus中出現ldd資料夾,其中包含兩個目錄:devices 和 drivers */
    if (ret)
        return ret;
    if (bus_create_file(&ldd_bus_type,

&bus_attr_version)) /*新增匯流排屬性,將在/sys/bus/ldd目錄中出現version屬性檔案*/
        printk(KERN_NOTICE "Unable to create version attribute ! /n");
    ret = device_register(&ldd_bus);/*將匯流排作為設備註冊。因為匯流排也可以是一個裝置,比如在S3C2440中SPI匯流排控制器相對於ARM920T核心來說,其實就是一個外設。呼叫此函式後,就會在/sys/devices中出現ldd0目錄*/
    if (ret)
        printk(KERN_NOTICE "Unable to register ldd0 ! /n"
);
    
    printk(KERN_NOTICE "Mount lddbus ok !/nBus device is ldd0 !/nYou can see me in sys/module/ , sys/devices/ and sys/bus/ ! /n");
    
    return ret;
}

static void ldd_bus_exit(void)
{
    device_unregister(&ldd_bus);
    bus_unregister(&ldd_bus_type);
}

module_init(ldd_bus_init);
module_exit(ldd_bus_exit);

   lddbus模組的主要部分就是這些,很簡單。因為這只不過是一個虛擬的匯流排,沒有實際的驅動。模組還匯出了載入匯流排裝置和匯流排驅動時需要用到的註冊和登出函式。對於實際的匯流排,應該還要匯出匯流排的讀寫例程。

   將匯流排裝置和驅動註冊函式放在lddbus模組,並匯出給其他的匯流排驅動程式使用,是因為註冊匯流排裝置和驅動需要匯流排結構體的資訊,而且這些註冊函式對於所有匯流排裝置和驅動都一樣。只要這個匯流排驅動一載入,其他的匯流排驅動程式就可以通過呼叫這些函式註冊匯流排裝置和驅動,方便了匯流排裝置驅動的作者,減少了程式碼的冗餘。

  這些註冊函式內部呼叫driver_register、device_register 和 driver_unregister、device_unregister 這些函式。

二、sculld模組:在scull的基礎上新增裝置和驅動註冊和登出函式。    sculld模組基本和scull模組實現的功能一致,我參考《LDD3》提供的sculld,將以前實驗過的功能較全的scull進行修改。主要的修改如下(其他還有些小改動):

//*******在原始碼的宣告階段新增如下程式碼,以增加裝置和驅動的結構體*****
struct sculld_dev *sculld_devices; /* allocated in scull_init_module */


/* Device model stuff */
static struct ldd_driver sculld_driver = {
    .version = "$Revision: 1.21-tekkamanninja $",
    .module = THIS_MODULE,
    .driver = {
        .name = "sculld",
    },
};
//**************************************************************

//******增加設備註冊函式和裝置號屬性********************************
static ssize_t sculld_show_dev(struct device *ddev, struct device_attribute *attr , char *buf)
{
    struct sculld_dev *dev = ddev->driver_data;
    return print_dev_t(buf, dev->cdev.dev);
}

static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);

static void sculld_register_dev(struct sculld_dev *dev, int index)
{
    sprintf(dev->devname, "sculld%d", index);
    dev->ldev.name = dev->devname;
    dev->ldev.driver = &sculld_driver;
    dev->ldev.dev.driver_data = dev;
    register_ldd_device(&dev->ldev);
    if (device_create_file(&dev->ldev.dev, &dev_attr_dev))
    printk( "Unable to create dev attribute ! /n");
}
//*****************************************************************

/*還要在模組的初始化函式和模組清除函式中新增裝置和驅動的註冊和登出函式*/
    sculld_register_dev(sculld_devices + i, i);
    register_ldd_driver(&sculld_driver);
    unregister_ldd_device(&sculld_devices[i].ldev);
    unregister_ldd_driver(&sculld_driver);

修改後好模組就可以實現向sysfs檔案系統匯出資訊。

三、分析裝置和驅動註冊和登出核心函式,瞭解一般的註冊、登出過程。

以下也參考了《LDD3》中的PCI驅動分析

(1)裝置的註冊

在驅動程式中對裝置進行註冊的核心函式是:

int device_register(struct device *dev)
{
    device_initialize(dev);
    return device_add(dev);
}

   在 device_register 函式中, 驅動核心初始化 device 結構體中的許多成員, 向 kobject 核心註冊裝置的 kobject ( 導致熱插拔事件產生), 接著新增裝置到其 parent 節點所擁有的裝置連結串列中。此後所有的裝置都可通過正確的順序被訪問, 並知道其位於裝置層次中的哪一點。

    裝置接著被新增到匯流排相關的裝置連結串列(包含了所有向匯流排註冊的裝置)中。接著驅動核心遍歷這個連結串列, 為每個驅動程式呼叫該匯流排的match函式。

   match函式主要是將驅動核心傳遞給它的 struct device 和 struct device_driver轉換為特定的裝置、驅動結構體 ,檢查裝置的特定資訊, 以確定驅動程式是否支援該裝置:

若不支援, 函式返回 0 給驅動核心,這樣驅動核心移向連結串列中的下一個驅動;

若支援, 函式返回 1 給驅動核心,使驅動核心設定struct device 中的 driver 指標指向這個驅動, 並呼叫在 struct device_driver 中指定的 probe 函式.

   probe 函式(又一次) 將驅動核心傳遞給它的 struct device 和 struct device_driver轉換為特定的裝置、驅動結構體 ,並再次驗證這個驅動是否支援這個裝置, 遞增裝置的引用計數, 接著呼叫匯流排驅動的 probe 函式:

若匯流排 probe 函式認為它不能處理這個裝置,則返回一個負的錯誤值給驅動核心,這樣驅動核心移向連結串列中的下一個裝置;

若這個 probe 函式能夠處理這個裝置, 則初始化這個裝置, 並返回 0 給驅動核心。這會使驅動核心新增裝置到與這個特定驅動所繫結的裝置連結串列中, 並在 /sys/bus的匯流排目錄中的 drivers 目錄中建立一個到這個裝置符號連結(指向/sys/devices中的裝置),使使用者準確知道哪個驅動被繫結到了哪個裝置。

(2)裝置的登出    在驅動程式中對裝置進行登出的核心函式是:

void device_unregister(struct device * dev)

在 device_unregister 函式中, 驅動核心將刪除這個裝置的驅動程式(如果有)指向這個裝置的符號連結, 並從它的內部裝置連結串列中刪除該裝置, 再以 device 結構中的 struct kobject 指標為引數,呼叫 kobject_del。kobject_del 函式引起使用者空間的 hotplug 呼叫,表明 kobject 現在從系統中刪除, 接著刪除所有該 kobject 以前建立的、與之相關聯的 sysfs 檔案和目錄。kobject_del 函式也去除裝置自身的 kobject 引用。此後, 所有的和這個裝置關聯的 sysfs 入口被去除, 並且和這個裝置關聯的記憶體被釋放。

(3)驅動程式的註冊

  在驅動程式中對驅動程式進行註冊的核心函式是:

int driver_register(struct device_driver * drv)

driver_register 函式初始化 struct device_driver 結構體(包括 一個裝置連結串列及其增刪物件函式 和 一個自旋鎖), 然後呼叫 bus_add_driver 函式。

bus_add_driver進行如下操作:

(1)查詢驅動關聯的匯流排:若未找到, 立刻返回負的錯誤值;
(2)根據驅動的名字和關聯的匯流排,建立驅動的 sysfs 目錄;
(3)獲取匯流排的內部鎖, 遍歷所有的已經註冊到匯流排的裝置,為這些裝置呼叫match函式, 若成功,進行剩下的繫結過程。(類似註冊裝置,不再贅述)

(4)驅動程式的登出

   刪除驅動程式是一個簡單的過程,在驅動程式中對驅動程式進行登出的核心函式是:

void driver_unregister(struct device_driver * drv)

deiver_unregister 函式通過清理在 sysfs 樹中連線到這個驅動入口的 sysfs 屬性,來完成一些基本的管理工作。然後遍歷所有屬於該驅動的裝置,為其呼叫 release 函式(類似裝置從系統中刪除時呼叫 release 函式)。

在所有的裝置與驅動程式脫離後,通常在驅動程式中會使用下面兩個函式:

down(&drv->unload_sem);
up(&drv->unload_sem);

它們在函式返回給呼叫者之前完成。這樣做是因為在安全返回前,程式碼需要等待所有的對這個驅動的引用計數為 0。
模組解除安裝時,通常都要呼叫 driver_unregister 函式作為退出的方法。 只要驅動程式被裝置引用並且等待這個鎖時,模組就需要保留在記憶體中。這使得核心知道何時可以安全從記憶體刪除驅動。

四、ARM9開發板實驗

實驗過程:

[[email protected]]#insmod /lib/modules/lddbus.ko
Mount lddbus ok !
Bus device is ldd0 !
You can see me in sys/module/ , sys/devices/ and sys/bus/ !
[[email protected]]#tree -AC /sys/module/lddbus/ /sys/devices/ldd0/ /sys/bus/ldd/
/sys/module/lddbus/
├── holders
├── initstate
├── refcnt
└── sections
    ├── __ksymtab
    └── __ksymtab_strings
/sys/devices/ldd0/
├── power
│ └── wakeup
└── uevent
/sys/bus/ldd/
├── devices
├── drivers
├── drivers_autoprobe
├── drivers_probe
└── version

5 directories, 9 files
[[email protected]]#cat /sys/bus/ldd/version
Revision: 1.9-tekkamanninja
[[email protected]]#rmmod lddbus
The LDD bus device
 ldd_bus_release : lddbus
[[email protected]]#ls /sys/module /sys/devices /sys/bus
/sys/bus:
i2c ide mmc platform serio spi usb

/sys/devices:
platform system

/sys/module:
8250 loop rd snd_seq usbnet
atkbd mac80211 redboot snd_seq_oss v4l1_compat
cdrom mousedev rfd_ftl snd_soc_core vt
dm9000 nfs rtc_ds1307 snd_timer yaffs
hid ohci_hcd s3c2410_wdt spidev zc0301
ide_cd printk snd sunrpc
keyboard psmouse snd_pcm tcp_cubic
lockd rcupdate snd_pcm_oss usbcore
[[email protected]]#insmod /lib/modules/sculld.ko
sculld: Unknown symbol register_ldd_device
sculld: Unknown symbol register_ldd_driver
sculld: Unknown symbol unregister_ldd_driver
sculld: Unknown symbol unregister_ldd_device
insmod: cannot insert '/lib/modules/sculld.ko': Unknown symbol in module (-1): No such file or directory
[[email protected]]#insmod /lib/modules/lddbus.ko
Mount lddbus ok !
Bus device is ldd0 !
You can see me in sys/module/ , sys/devices/ and sys/bus/ !
[[email protected]]#insmod /lib/modules/sculld.ko
[[email protected]]#tree -AC /sys/module/lddbus/ /sys/devices/ldd0/ /sys/bus/ldd/ /sys/module/sculld/
/sys/module/lddbus/
├── holders
│ └── sculld -> ../../../module/sculld
├── initstate
├── refcnt
└── sections
    ├── __ksymtab
    └── __ksymtab_strings
/sys/devices/ldd0/
├── power
│ └── wakeup
├── sculld0
│ ├── bus -> ../../../bus/ldd
│ ├── dev
│ ├── driver -> ../../../bus/ldd/drivers/sculld
│ ├── power
│ │ └── wakeup
│ ├── subsystem -> ../../../bus/ldd
│ └── uevent
├── sculld1
│ ├── bus -> ../../../bus/ldd
│ ├── dev
│ ├── driver -> ../../../bus/ldd/drivers/sculld
│ ├── power
│ │ └── wakeup
│ ├── subsystem -> ../../../bus/ldd
│ └── uevent
├── sculld2
│ ├── bus -> ../../../bus/ldd
│ ├── dev
│ ├── driver -> ../../../bus/ldd/drivers/sculld
│ ├── power
│ │ └── wakeup
│ ├── subsystem -> ../../../bus/ldd
│ └── uevent
├── sculld3
│ ├── bus -> ../../../bus/ldd
│ ├── dev
│ ├── driver -> ../../../bus/ldd/drivers/sculld
│ ├── power
│ │ └── wakeup
│ ├── subsystem -> ../../../bus/ldd
│ └── uevent
└── uevent
/sys/bus/ldd/
├── devices
│ ├── sculld0 -> ../../../devices/ldd0/sculld0
│ ├── sculld1 -> ../../../devices/ldd0/sculld1
│ ├── sculld2 -> ../../../devices/ldd0/sculld2
│ └── sculld3 -> ../../../devices/ldd0/sculld3
├── drivers
│ └── sculld
│ ├── bind
│ ├── sculld0 -> ../../../../devices/ldd0/sculld0
│ ├── sculld1 -> ../../../../devices/ldd0/sculld1
│ ├── sculld2 -> ../../../../devices/ldd0/sculld2
│ ├── sculld3 -> ../../../../devices/ldd0/sculld3
│ ├── unbind
│ └── version
├── drivers_autoprobe
├── drivers_probe
└── version
/sys/module/sculld/
├── holders
├── initstate
├── parameters
│ ├── scull_major
│ ├── scull_minor
│ ├── scull_nr_devs
│ ├── scull_qset
│ └── scull_quantum
├── refcnt
└── sections
    ├── __ex_table
    └── __param

38 directories, 33 files
[[email protected]]#cat /sys/bus/ldd/version /sys/devices/ldd0/sculld*/dev /sys/bus/ldd/drivers/sculld/version
Revision: 1.9-tekkamanninja
252:0
252:1
252:2
252:3
$Revision: 1.21-tekkamanninja $
[[email protected]]#rmmod sculld
[[email protected]]#tree -ACd /sys/module/lddbus/ /sys/devices/ldd0/ /sys/bus/ldd/ /sys/module/
/sys/module/lddbus/
├── holders
└── sections
/sys/devices/ldd0/
└── power
/sys/bus/ldd/
├── devices
└── drivers
/sys/module/
├── 8250
│ └── parameters
├── atkbd
│ └── drivers
│ └── serio:atkbd -> ../../../bus/serio/drivers/atkbd
├── cdrom
├── dm9000
│ └── parameters
├── hid
│ └── parameters
├── ide_cd
│ └── parameters
├── keyboard
│ └── parameters
├── lddbus
│ ├── holders
│ └── sections
├── lockd
│ └── parameters
├── loop
├── mac80211
│ └── parameters
├── mousedev
│ └── parameters
├── nfs
│ └── parameters
├── ohci_hcd
├── printk
│ └── parameters
├── psmouse
│ ├── drivers
│ │ └── serio:psmouse -> ../../../bus/serio/drivers/psmouse
│ └── parameters
├── rcupdate
├── rd
├── redboot
├── rfd_ftl
├── rtc_ds1307
├── s3c2410_wdt
├── snd
│ └── parameters
├── snd_pcm
│ └── parameters
├── snd_pcm_oss
│ └── parameters
├── snd_seq
│ └── parameters
├── snd_seq_oss
│ └── parameters
├── snd_soc_core
├── snd_timer
│ └── parameters
├── spidev
│ └── parameters
├── sunrpc
│ └── parameters
├── tcp_cubic
│ └── parameters
├── usbcore
│ ├── drivers
│ │ ├── usb:hub -> ../../../bus/usb/drivers/hub
│ │ └── usb:usbfs -> ../../../bus/usb/drivers/usbfs
│ └── parameters
├── usbnet
├── v4l1_compat
│ └── parameters
├── vt
│ └── parameters
├── yaffs
│ └── parameters
└── zc0301
    ├── drivers
    │ └── usb:zc0301 -> ../../../bus/usb/drivers/zc0301
    └── parameters

79 directories
[[email protected]]#insmod /lib/modules/sculld.ko
[[email protected]]#rmmod lddbus
rmmod: lddbus: Resource temporarily unavailable
[[email protected]]#rmmod sculld
[[email protected]]#rmmod lddbus
The LDD bus device
 ldd_bus_release : lddbus
[[email protected]]#

在系統中新增“tree”命令 實驗中用到了Linux的常用命令“tree”,若使用busybox可能沒有這個命令。如出現這種情況,可以下載此命令的原始碼,交叉編譯一下,再放到根檔案系統中的/bin目錄中就好。在 CalmArrow 的部落格中可以下載:http://blog.chinaunix.net/u/21948/showart_297101.html

相關推薦

Linux裝置驅動程式學習14

通過一個裝置在核心中生命週期的各個階段,可以更好地理解Linux裝置模型。我將通過分析lddbus和sculld的原始碼來了解Linux裝置模型中各環節的整合。《LDD3》中的(PCI匯流排)各環節的整合這部分內容作為參考資料,因為嵌入式Linux比較少用到PCI匯流排。看這

Linux裝置驅動程式學習13

匯流排 匯流排是處理器和一個或多個裝置之間的通道,在裝置模型中, 所有的裝置都通過匯流排相連, 甚至是內部的虛擬"platform"匯流排。匯流排可以相互插入。裝置模型展示了匯流排和它們所控制的裝置之間的實際連線。在 Linux 裝置模型中, 匯流排由 bus_type 結構

Linux裝置驅動程式學習7-核心的資料型別

由於前面的學習中有用到 第十一章 核心資料結構型別 的知識,所以我先看了。要點如下: 將linux 移植到新的體系結構時,開發者遇到的若干問題都與不正確的資料型別有關。堅持使用嚴格的資料型別和使用 -Wall -Wstrict-prototypes 進行編譯可能避免大部分

Linux裝置驅動程式學習12 -Linux裝置模型底層原理簡介

以《LDD3》的說法:Linux裝置模型這部分內容可以認為是高階教材,對於多數程式作者來說是不必要的。但是我個人認為:對於一個嵌入式Linux的底層程式設計師來說,這部分內容是很重要的。 以我學習的ARM9為例,有很多匯流排(如SPI、IIC、IIS等等)在Linux下已經被

Linux裝置驅動程式學習(基於2440的GPIO字元裝置驅動)

GPIO驅動程式如下:  #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <li

嵌入式Linux裝置驅動開發筆記

一、Linux裝置的分類 字元裝置、塊裝置、網路裝置,三種裝置之間的區別是資料的互動模式,分別為: 位元組流、資料塊、資料包。 二、VFS核心結構體 VFS核心結構體定義在”linux/fs.h”標頭檔案中。 1、struct inode結構體 記

linux裝置驅動歸納總結:2.操作硬體——IO記憶體

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSi

linux裝置驅動歸納總結:3.中斷下半部之tasklet

linux裝置驅動歸納總結(六):3.中斷的上半部和下半部——tasklet xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 一、什麼是下半部 中斷是一個很霸道的東西,處理

linux裝置驅動歸納總結:1.platform匯流排的裝置驅動

linux裝置驅動歸納總結(九):1.platform匯流排的裝置和驅動 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 這一節可

linux裝置驅動歸納總結:1.platform裝置驅動

http://blog.chinaunix.net/uid-25014876-id-111745.html 這一節可以理解是第八章的延伸,從這節開始介紹platform裝置驅動。 一、什麼是paltform匯流排 一個現實的linux裝置和驅

Linux裝置驅動程式學習筆記7--時間、延遲及延緩操作

#include <linux/timer.h>struct timer_list {    struct list_head entry;    unsigned long expires;/*期望定時器執行的絕對 jiffies 值,不是一個 jiffies_64 值,因為定時器不被期望在將來

Linux裝置驅動程式學習----1.裝置驅動程式簡介

裝置驅動程式簡介 更多內容請參考Linux裝置驅動程式學習----目錄 1. 簡介   Linux系統的優點是,系統內部實現細節對所有人都是公開的。Linux核心由大量複雜的程式碼組成,裝置驅動程式可以作為進入Linux核心世界大門的切入口。   裝置驅動程式在Linux核心中,是一個個獨立的黑盒子,在呼叫內

Linux裝置驅動程式架構分析之I2C架構基於3.10.1核心

作者:劉昊昱  核心版本:3.10.1 I2C體系架構的硬體實體包括兩部分: 硬體I2C Adapter:硬體I2C Adapter表示一個硬體I2C介面卡,也就是I2C控制器。一般是SOC中的一個介面,也可以用GPIO模擬。硬體I2C Adapter主要用來在I2

Linux裝置驅動程式架構分析之MMC/SD

作者:劉昊昱  核心版本:3.10.1 一、s3cmci_ops分析 在上一篇文章中我們分析了Mini2440 MMC/SD驅動的probe函式s3cmci_probe。在該函式中初始化了struct mmc_host指標變數mmc,其中,設定mmc->ops為s

Linux裝置驅動程式架構分析之platform基於3.10.1核心

作者:劉昊昱  核心版本:3.10.1 一、platform bus的註冊 platform bus註冊是通過platform_bus_init函式完成的,該函式定義在drivers/base/platform.c檔案中,其內容如下: 904int __init pl

從零開始之驅動發開、linux驅動二十、linux裝置驅動中的併發控制

本文參考自宋寶華老師的《linux驅動開發詳解》 併發(Concurrency) 指的是多個執行單元同時、 並行被執行, 而併發的執行單元對共享資源(硬體資源和軟體上的全域性變數、 靜態變數等) 的訪問則很容易導致競態(Race Conditions)   只要併發的

linux裝置驅動程式示例適用於高版本核心3.16.0

1. 字元裝置與塊裝置的 I/O 操作主要有如下不同點:    (1)塊裝置只能以塊為單位接受輸入和返回輸出,而字元裝置則以位元組為單位。大多數裝置是字元裝置,因為它們不需要緩衝而且不以固定塊大小進行操作。    (2)塊裝置對於 I/O 請求有對應的緩衝區,因此它們可以選擇

Linux系統spi驅動程式分析---

說明:本文將分析Linux-2.6.17原始碼中的spi驅動程式,其內容為本人閱讀原始碼後的一些理解。由於本人水平有限,所以內容可能比較雜亂零散,權當個人筆記記錄之用。而以下內容均以powerpc架構為例說明。 在Linux系統中,spi驅動的設計採用了分層設計模式的思想

Linux裝置驅動模型簡述原始碼剖析

 1. Linux裝置驅動模型和sysfs檔案系統 Linux核心在2.6版本中引入裝置驅動模型,簡化了驅動程式的編寫。Linux裝置驅動模型包含裝置(device)、匯流排(bus)、類(class)和驅動(driver),它們之間相互關聯。其中裝置(device)和驅動(driver)通過匯流排

Linux文件系統學習之相關概念???

正是 range 不同的 struct pan 根據 inode 存在 opera “一切皆是文件”是 Unix/Linux 的基本哲學之一。不僅普通的文件,目錄、字符設備、塊設備、套接字等在 Unix/Linux 中都是以文件被對待;它們雖然類型不同,但是對其提供的卻是同