Linux裝置驅動程式學習(14) -Linux裝置模型(各環節的整合)

來源:互聯網
上載者:User
通過一個裝置在核心中生命週期的各個階段,可以更好地理解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開發板實驗 實驗源碼:http://blogimg.chinaunix.net/blog/upfile2/080109150139.rar 實驗過程:
[Tekkaman2440@SBC2440V4]#insmod /lib/modules/lddbus.koMount lddbus ok !Bus device is ldd0 !You can see me in sys/module/ , sys/devices/ and sys/bus/ ![Tekkaman2440@SBC2440V4]#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└── version5 directories, 9 files[Tekkaman2440@SBC2440V4]#cat /sys/bus/ldd/versionRevision: 1.9-tekkamanninja[Tekkaman2440@SBC2440V4]#rmmod lddbusThe LDD bus device ldd_bus_release : lddbus [Tekkaman2440@SBC2440V4]#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 usbnetatkbd mac80211 redboot snd_seq_oss v4l1_compatcdrom mousedev rfd_ftl snd_soc_core vtdm9000 nfs rtc_ds1307 snd_timer yaffshid ohci_hcd s3c2410_wdt spidev zc0301ide_cd printk snd sunrpckeyboard psmouse snd_pcm tcp_cubiclockd rcupdate snd_pcm_oss usbcore[Tekkaman2440@SBC2440V4]#insmod /lib/modules/sculld.kosculld: Unknown symbol register_ldd_devicesculld: Unknown symbol register_ldd_driversculld: Unknown symbol unregister_ldd_driversculld: Unknown symbol unregister_ldd_deviceinsmod: cannot insert '/lib/modules/sculld.ko': Unknown symbol in module (-1): No such file or directory[Tekkaman2440@SBC2440V4]#insmod /lib/modules/lddbus.koMount lddbus ok !Bus device is ldd0 !You can see me in sys/module/ , sys/devices/ and sys/bus/ ![Tekkaman2440@SBC2440V4]#insmod /lib/modules/sculld.ko[Tekkaman2440@SBC2440V4]#tree -AC /sys/module/lddbus/ /sys/devices/ldd0/ /sys/bus/ldd/ /sys/module/sculld//sys/module/lddbus/├── holders│ └── sculld -> http://www.cnblogs.com/../module/sculld├── initstate├── refcnt└── sections    ├── __ksymtab    └── __ksymtab_strings/sys/devices/ldd0/├── power│ └── wakeup├── sculld0│ ├── bus -> http://www.cnblogs.com/../bus/ldd│ ├── dev│ ├── driver -> http://www.cnblogs.com/../bus/ldd/drivers/sculld│ ├── power│ │ └── wakeup│ ├── subsystem -> http://www.cnblogs.com/../bus/ldd│ └── uevent├── sculld1│ ├── bus -> http://www.cnblogs.com/../bus/ldd│ ├── dev│ ├── driver -> http://www.cnblogs.com/../bus/ldd/drivers/sculld│ ├── power│ │ └── wakeup│ ├── subsystem -> http://www.cnblogs.com/../bus/ldd│ └── uevent├── sculld2│ ├── bus -> http://www.cnblogs.com/../bus/ldd│ ├── dev│ ├── driver -> http://www.cnblogs.com/../bus/ldd/drivers/sculld│ ├── power│ │ └── wakeup│ ├── subsystem -> http://www.cnblogs.com/../bus/ldd│ └── uevent├── sculld3│ ├── bus -> http://www.cnblogs.com/../bus/ldd│ ├── dev│ ├── driver -> http://www.cnblogs.com/../bus/ldd/drivers/sculld│ ├── power│ │ └── wakeup│ ├── subsystem -> http://www.cnblogs.com/../bus/ldd│ └── uevent└── uevent/sys/bus/ldd/├── devices│ ├── sculld0 -> http://www.cnblogs.com/../devices/ldd0/sculld0│ ├── sculld1 -> http://www.cnblogs.com/../devices/ldd0/sculld1│ ├── sculld2 -> http://www.cnblogs.com/../devices/ldd0/sculld2│ └── sculld3 -> http://www.cnblogs.com/../devices/ldd0/sculld3├── drivers│ └── sculld│ ├── bind│ ├── sculld0 -> http://www.cnblogs.com/http://www.cnblogs.com/devices/ldd0/sculld0│ ├── sculld1 -> http://www.cnblogs.com/http://www.cnblogs.com/devices/ldd0/sculld1│ ├── sculld2 -> http://www.cnblogs.com/http://www.cnblogs.com/devices/ldd0/sculld2│ ├── sculld3 -> http://www.cnblogs.com/http://www.cnblogs.com/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    └── __param38 directories, 33 files[Tekkaman2440@SBC2440V4]#cat /sys/bus/ldd/version /sys/devices/ldd0/sculld*/dev /sys/bus/ldd/drivers/sculld/versionRevision: 1.9-tekkamanninja252:0252:1252:2252:3$Revision: 1.21-tekkamanninja $[Tekkaman2440@SBC2440V4]#rmmod sculld[Tekkaman2440@SBC2440V4]#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 -> http://www.cnblogs.com/../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 -> http://www.cnblogs.com/../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 -> http://www.cnblogs.com/../bus/usb/drivers/hub│ │ └── usb:usbfs -> http://www.cnblogs.com/../bus/usb/drivers/usbfs│ └── parameters├── usbnet├── v4l1_compat│ └── parameters├── vt│ └── parameters├── yaffs│ └── parameters└── zc0301    ├── drivers    │ └── usb:zc0301 -> http://www.cnblogs.com/../bus/usb/drivers/zc0301    └── parameters79 directories[Tekkaman2440@SBC2440V4]#insmod /lib/modules/sculld.ko[Tekkaman2440@SBC2440V4]#rmmod lddbusrmmod: lddbus: Resource temporarily unavailable[Tekkaman2440@SBC2440V4]#rmmod sculld[Tekkaman2440@SBC2440V4]#rmmod lddbusThe LDD bus device ldd_bus_release : lddbus [Tekkaman2440@SBC2440V4]#
在系統中添加“tree”命令實驗中用到了Linux的常用命令“ tree”,若使用busybox可能沒有這個命令。如出現這種情況,可以下載此命令的源碼,交叉編譯一下,再放到根檔案系統中的/bin目錄中就好。在 CalmArrow 的部落格中可以下載:http://blog.chinaunix.net/u/21948/showart_297101.html(因為源碼的 ftp://mama.indstate.edu/linux/tree/我一直進不去, 在這裡謝謝CalmArrow
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.