udev交叉編譯
❶ 如何通過設備節點查看i2c設備
linux下生成驅動設備節點文件的方法有3個:1、手動mknod;2、利用devfs;3、利用udev
在剛開始寫Linux設備驅動程序的時候,很多時候都是利用mknod命令手動創建設備節點,實際上Linux內核為我們提供了一組函數,可以用來在模塊載入的時候自動在/dev目錄下創建相應設備節點,並在卸載模塊時刪除該節點。
在2.6.17以前,在/dev目錄下生成設備文件很容易,
devfs_mk_bdev
devfs_mk_cdev
devfs_mk_symlink
devfs_mk_dir
devfs_remove
這幾個是純devfs的api,2.6.17以前可用,但後來devfs被sysfs+udev的形式取代,同時期sysfs文件系統可以用的api:
class_device_create_file,在2.6.26以後也不行了,現在,使用的是device_create ,從2.6.18開始可用
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, const char *fmt, …)
從2.6.26起又多了一個參數drvdata: the data to be added to the device for callbacks
不會用可以給此參數賦NULL
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, …)
下面著重講解第三種方法udev
在驅動用加入對udev的支持主要做的就是:在驅動初始化的代碼里調用class_create(…)為該設備創建一個class,再為每個設備調用device_create(…)( 在2.6較早的內核中用class_device_create)創建對應的設備。
內核中定義的struct class結構體,顧名思義,一個struct class結構體類型變數對應一個類,內核同時提供了class_create(…)函數,可以用它來創建一個類,這個類存放於sysfs下面,一旦創建好了這個類,再調用 device_create(…)函數來在/dev目錄下創建相應的設備節點。這樣,載入模塊的時候,用戶空間中的udev會自動響應 device_create(…)函數,去/sysfs下尋找對應的類從而創建設備節點。
struct class和class_create(…) 以及device_create(…)都包含在在/include/linux/device.h中,使用的時候一定要包含這個頭文件,否則編譯器會報錯。
struct class定義在頭文件include/linux/device.h中
class_create(…)在/drivers/base/class.c中實現
device_create(…)函數在/drivers/base/core.c中實現
class_destroy(…),device_destroy(…)也在/drivers/base/core.c中實現調用過程類似如下:
static struct class *spidev_class;
/*-------------------------------------------------------------------------*/
static int __devinit spidev_probe(struct spi_device *spi)
{
…
dev =device_create(spidev_class, &spi->dev, spidev->devt,
spidev, 「spidev%d.%d」,
spi->master->bus_num, spi->chip_select);
…
}
static int __devexit spidev_remove(struct spi_device *spi)
{
……
device_destroy(spidev_class, spidev->devt);
……
return 0;
}
static struct spi_driver spidev_spi = {
.driver = {
.name = 「spidev」,
.owner = THIS_MODULE,
},
.probe = spidev_probe,
.remove = __devexit_p(spidev_remove),
};
/*-------------------------------------------------------------------------*/
static int __init spidev_init(void)
{
…
spidev_class =class_create(THIS_MODULE, 「spidev」);
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
return PTR_ERR(spidev_class);
}
…
}
mole_init(spidev_init);
static void __exit spidev_exit(void)
{
……
class_destroy(spidev_class);
……
}
mole_exit(spidev_exit);
MODULE_DESCRIPTION(「User mode SPI device interface」);
MODULE_LICENSE(「GPL」);
下面以一個簡單字元設備驅動來展示如何使用這幾個函數
#include <linux/mole.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
int HELLO_MAJOR = 0;
int HELLO_MINOR = 0;
int NUMBER_OF_DEVICES = 2;
struct class *my_class;
//struct cdev cdev;
//dev_t devno;
struct hello_dev {
struct device *dev;
dev_t chrdev;
struct cdev cdev;
};
static struct hello_dev *my_hello_dev = NULL;
struct file_operations hello_fops = {
.owner = THIS_MODULE
};
static int __init hello_init (void)
{
int err = 0;
struct device *dev;
my_hello_dev = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);
if (NULL == my_hello_dev) {
printk(「%s kzalloc failed!\n」,__func__);
return -ENOMEM;
}
devno = MKDEV(HELLO_MAJOR, HELLO_MINOR);
if (HELLO_MAJOR)
err= register_chrdev_region(my_hello_dev->chrdev, 2, 「memdev」);
else
{
err = alloc_chrdev_region(&my_hello_dev->chrdev, 0, 2, 「memdev」);
HELLO_MAJOR = MAJOR(devno);
}
if (err) {
printk(「%s alloc_chrdev_region failed!\n」,__func__);
goto alloc_chrdev_err;
}
printk(「MAJOR IS %d\n」,HELLO_MAJOR);
cdev_init(&(my_hello_dev->cdev), &hello_fops);
my_hello_dev->cdev.owner = THIS_MODULE;
err = cdev_add(&(my_hello_dev->cdev), my_hello_dev->chrdev, 1);
if (err) {
printk(「%s cdev_add failed!\n」,__func__);
goto cdev_add_err;
}
printk (KERN_INFO 「Character driver Registered\n」);
my_class =class_create(THIS_MODULE,「hello_char_class」); //類名為hello_char_class
if(IS_ERR(my_class))
{
err = PTR_ERR(my_class);
printk(「%s class_create failed!\n」,__func__);
goto class_err;
}
dev = device_create(my_class,NULL,my_hello_dev->chrdev,NULL,「memdev%d」,0); //設備名為memdev
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
gyro_err(「%s device_create failed!\n」,__func__);
goto device_err;
}
printk(「hello mole initialization\n」);
return 0;
device_err:
device_destroy(my_class, my_hello_dev->chrdev);
class_err:
cdev_del(my_hello_dev->chrdev);
cdev_add_err:
unregister_chrdev_region(my_hello_dev->chrdev, 1);
alloc_chrdev_err:
kfree(my_hello_dev);
return err;
}
static void __exit hello_exit (void)
{
cdev_del (&(my_hello_dev->cdev));
unregister_chrdev_region (my_hello_dev->chrdev,1);
device_destroy(my_class, devno); //delete device node under /dev//必須先刪除設備,再刪除class類
class_destroy(my_class); //delete class created by us
printk (KERN_INFO 「char driver cleaned up\n」);
}
mole_init (hello_init);
mole_exit (hello_exit);
MODULE_LICENSE (「GPL」);
這樣,模塊載入後,就能在/dev目錄下找到memdev這個設備節點了。
例2:內核中的drivers/i2c/i2c-dev.c
在i2cdev_attach_adapter中調用device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
「i2c-%d」, adap->nr);
這樣在dev目錄就產生i2c-0 或i2c-1節點
接下來就是udev應用,udev是應用層的東西,udev需要內核sysfs和tmpfs的支持,sysfs為udev提供設備入口和uevent通道,tmpfs為udev設備文件提供存放空間
udev的源碼可以在去相關網站下載,然後就是對其在運行環境下的移植,指定交叉編譯環境,修改Makefile下的CROSS_COMPILE,如為mipsel-linux-,DESTDIR=xxx,或直接make CROSS_COMPILE=mipsel-linux-,DESTDIR=xxx 並install
把主要生成的udevd、udevstart拷貝rootfs下的/sbin/目錄內,udev的配置文件udev.conf和rules.d下的rules文件拷貝到rootfs下的/etc/目錄內
並在rootfs/etc/init.d/rcS中添加以下幾行:
echo 「Starting udevd…」
/sbin/udevd --daemon
/sbin/udevstart
(原rcS內容如下:
# mount filesystems
/bin/mount -t proc /proc /proc
/bin/mount -t sysfs sysfs /sys
/bin/mount -t tmpfs tmpfs /dev
# create necessary devices
/bin/mknod /dev/null c 1 3
/bin/mkdir /dev/pts
/bin/mount -t devpts devpts /dev/pts
/bin/mknod /dev/audio c 14 4
/bin/mknod /dev/ts c 10 16
)
這樣當系統啟動後,udevd和udevstart就會解析配置文件,並自動在/dev下創建設備節點文件
❷ 有沒有嵌入式開發的學習路線,越詳細越好
一、學習路徑
萬丈高樓平地起,不管多優秀的工程師都是從小白開始的。一條清晰合理的學習路線能幫助小白們高效率的完成基礎知識的儲備工作,注意這里是知識的儲備過程,而經驗是從實踐中得到的。學習路徑是多種多樣的,不同能力和不同基礎的人有不太相同的路徑,這里分享我自己的學習路徑,供大家參考。
1.了解計算機原理,操作系統基礎知識。了解硬碟,內存和CPU的關系,程序是如何載入到內存運行的,了解操作系統進程切換和時間片的概念。
2.學習C語言,掌握編譯器基本知識,能編寫簡單的程序。學習硬體相關知識。
3.購買洞洞板或者麵包板,配合stm32等單片機核心板及相應教材,實踐IO操作,中斷,定時器,ADC,UART通信,IIC通信,SPI通信,CAN通信等基本功能。在此過程中不斷鞏固提升C語言編程水平。
4.掌握了某一種單片機的基本編程和控制後,可以進軍嵌入式操作系統的學習。在此期間可以繼續使用STM32核心板,加購LCD串口液晶顯示屏,不需要買帶字型檔的顯示屏,簡單實用的串口顯示屏就可以。然後可以從Free RTOS開始學習,這個操作系統代碼少,概念清晰,易於學習操作系統的原理,也易於移植,基本上可以參考官網以及網路上的資料順利的將操作系統移植到STM32核心板。通過FreeRTOS,可以學習嵌入式操作系統的基本原理,並可以編寫LCD驅動程序來感受硬體驅動程序的概念。
5.學習嵌入式Linux操作系統,購買ARM9或以上版本的主控的開發板,要求開發板上至少有串口和網口。學習板級支持包的開發,交叉編譯,GDB調試,UBOOT移植,內核移植,根文件系統製作,設備樹,驅動程序編寫,網路編程相關知識。
6.學習物聯網相關模塊的使用,可以購買ESP32核心板進行wifi,藍牙的模塊控制學習,購買其他模塊實現其他小項目的練習。
通過上面的一番閉關修煉,你已經學習了嵌入式開發的主要知識架構,接下來就要多做小項目,多練習排錯,才能不斷積累經驗。
二、學習方法
1.先整再零:
對於一個實例項目,先從整體出發,保證調通,能正常運行,出現預期結果。遇到模糊的問題先跳過,整體有了一定認識後再對個別細節進行深入了解,但不能跳過深入了解細節的環節。
2.邊學邊練:
開發是一類實踐性很強的技能,嵌入式開發要與硬體打交道,就需要更多的動手操作和觀察。
學習某一方向的嵌入式開發知識時,需要給自己的學習進行必要的「投資」,購買麵包板、洞洞板、萬用表、調溫烙鐵套裝、各種器件、晶元,以至開發板。以上材料不需要一次性都買齊,可以按照當前學習的內容分階段購買,經濟條件有限的同學也不用擔心,以上材料的開銷除開發板之外都不貴,可網路購買。對於開發板,可以買學長學姐的二手板卡,能過測試就證明板卡是OK的。
有了學習材料,就要學以致用,例如今天學習了三極體做開關,就可以自己動手畫畫電路圖,然後在洞洞板上實踐一下,通過實際操作,加深印象的同時,也能驗證自己的設計方案。
3.勤於思考和提問,網路如此發達,提問的能力和技巧我就不再贅述了。
三、技能提升建議
如果你進入的是一家規模較小的公司,那麼你可能有機會接觸各類技術。這是絕佳的鍛煉機會,要注意不要特別深入某一方向而不關注其他技術,要知道大牛需要的是多方位的技能。
大公司的話,往往分工比較細致而明確,那就需要在完成自己工作的同時多關注項目組中其他同事遇到的問題,能協助解決最好,不能解決的要關註解決的情況和方法,多蹭經驗。幫助別人的同時就是在幫助自己提高,多花時間處理實際問題是難得的經驗。
不管在哪種場合工作,一定注意經驗的積累,好記不如帶墨,要用文字將經驗記錄下來,將遇到的問題詳細描述清楚,沒事的時候翻看一下,工作時間長了,你會發現這是一筆難得的財富。
限於篇幅,這里就不再多講技術的細節了,希望各位讀者在技術成長的過程當中都能有自己清晰的學習路徑,安排好自己的學習計劃,穩扎穩打!
❸ 嵌入式Linux程序設計案例與實驗教程的目錄
前言
教學建議
第1章LiFlUX開發基礎
1.1Linux系統概述
1.1.1Linux簡介
1.1.2Linux系統的特點
1.1.3Linux系統的組成
1.2Linux系統的使用
實驗1.1熟悉Linux基本命令與文件目錄系統
1.3全屏幕編輯器與vi
1.3.1vi簡介
1.3.2基本命令
1.3.3常用操作
實驗1.2全屏幕編輯器vi的使用
1.4LinuxS11ell編程
1.4.1Shell程序的編寫和執行
1.4.2Shell的變數
1.4.3Shell的測試命令
1.4.4條件語句
1.4.5循環語句
1.4.6函數
實驗1.3Shell腳本編程實驗
第2章嵌入式Linux系統基礎
2.1構建嵌入式Linux系統環境
2.1.1交叉編譯
2.1.2交叉編譯器
2.1.3NFS
實驗2.1嵌入式Linux開發環境的建立
2.2Linuxc程序設計
2.2.1C程序設計概述
2.2.2Makefile介紹
2.2.3Makefile中的變數
2.2.4Makefile隱含規則
實驗2.2Makefite與helloworld
2.3Linux多線程庫編程
2.3.1多線程
2.3.2Linux下的多線程
2.3.3生產者-消費者模型簡述
2.3.4緩沖區操作概述
2.3.5幾個線程API
實驗2.3Linux多線程使用實例生產者-消費者協議
2.4進程創建以及進程間通信
2.4.1進程概述
2.4.2進程的相關函數
2.4.3信號概述
2.4.4信號的相關函數
2.4.5管道概述
2.4.6管道的相關函數
實驗2.4進程相關的應用程序設計
綜合實驗一嵌入式平台的進程管理模擬實驗
第3章嵌入式Linux內核、引導系統和文件系統
3.1Linux內核定製、裁剪和添加
3.1.1概述
3.1.2內核目錄簡介
3.1.3配置文件和配置工具
3.1.4內核的編譯命令
實驗3.1Linux內核裁剪與編譯
3.2嵌入式引導系統技術
3.2.1概述
3.2.2Linux的引導系統vivi與ubOOt
3.3文件系統的構建
3.3.1概述
3.3.2BusyBox
綜合實驗二軟盤Linux操作系統的實現
第4章嵌入式Liflux介面設計與驅動程序
4.1驅動程序設計基礎
4.1.1Linux驅動程序簡介
4.1.2開發驅動程序的方法
4.1.3設備驅動程序的分類
4.1.4主設備號和次設備號
4.1.5設備文件系統(devfs)與Udevfs
實驗4.1虛擬驅動模塊實驗
4.2AD介面驅動程序
4.2.1AD轉換器
4.2.2AD轉換有關參數
4.2.3ARM自帶的AD轉換裝置
實驗4.2AD介面驅動程序
4.3直流電機驅動
4.3.1直流電機介紹
4.3.2直流電機的PWM原理
4.3.3PWMTIMER結構
4.3.4基於ARM的PWM相關寄存器
4.3.5關於程序實現
實驗4.3直流電機PWM驅動實驗
4.4觸摸屏介面設計與驅動
4.4.1觸摸屏的工作原理
4.4.2觸摸屏驅動晶元ADS7843
4.4.3S3C2410晶元的觸摸屏相關配置寄存器
實驗4.4tslib移植和使用
4.5顯示介面與LinuxFrameBuffer
4.5.1FrameBuffer機制介紹
4.5.2LCD簡介
實驗4.5FrameBuffer實驗
4.6V4L程序設計
4.6.1V4L概述
4.6.2V4L設備的體系結構
實驗4.6Linux視頻V4L驅動實驗
4.7OSS程序設計
4.7.1oSS概述
4.7.2OSS設備的體系結構
4.7.30SS驅動分析
4.7.4OSS用戶空間編程
實驗4.7Linux音頻OSS驅動實驗
綜合實驗三五子棋游戲的實現
第5章嵌入式Linux開源軟體移植與應用
5.1嵌入式WebServerGoAhead的移植與應用
5.1.1嵌入式web伺服器
5.1.2GoAhead介紹
5.1.3GoAhead在ARM平台上的移植
5.1.4頁面操作
實驗5.1嵌入式WebServerGoAhead實驗
5.2嵌入式WebServicegSOAP的移植與應用
5.2.1gSOAP介紹
5.2.2gSOAP裁剪
5.2.3gSOAP應用
實驗5.2WebServicegSOAP實驗
5.3嵌入式資料庫SQLite的移植與使用
5.3.1嵌入式資料庫
5.3.2SQLite介紹
5.3.3SQLite在ARM平台上的移植
5.3.4SQLite的使用
實驗5.3SQLite移植實驗
5.4播放器Mplayer的移植
5.4.1Mplayer介紹
5.4.2Mplayer在ARM平台上的移植
實驗5.4Mplayer到ARM平台上的移植
5.5ffmpeg應用:
5.5.1ffmpeg簡介
5.5.2ffmpeg在ARM上的移植
5.5.3ffmpeg命令應用實例
5.5.4ffmpeg中幾個重要的數據結構
5.5.5ffmpeg應用開發
實驗5.5ffmpeg移植與應用
5.6開源軟體移植的一般過程
5.6.1軟體移植的概念
5.6.2軟體移植過程
5.7JIME—phoneME移植
5.7.1phoneME簡介
5.7.2軟體移植過程與效果
5.8嵌入式瀏覽器konqueror移植
5.8.1konqueror簡介
5.8.2軟體移植過程與效果
綜合實驗四基於WebServiee的嵌入式計算器
第6章嵌入式Linux圖形用戶界面
6.1嵌入式GUI簡介
6.1.1嵌入式GUI的特點
6.1.2嵌入式GUI的種類
6.2嵌入式GUI—Qt
6.2.1Qt與Qt/Embedded簡介
6.2.2Qt的特點
6.2.3Qt的執行過程
6.2.4Qt的插槽機制
6.2.5一個完整的Qt程序
6.2.6QtDesigner介紹
實驗6.1Qt圖形界面相關實驗
6.3基於Qt技術的Qtopia
6.3.1Qtopia簡介
6.3.2Qtopia的功能
6.3.3Qtopia編程
實驗6.2Qtopia的移植以及編程
綜合實驗五電子點菜系統
第7章嵌入式Unux下的通信應用
7.1嵌入式Linux下的串口通信
7.1.1串口簡介
7.1.2串口編程
7.1.3串口編程應用實例
實驗7.1串口通信實驗
7.2嵌入式Linux網路編程
7.2.1網路通信
7.2.2Socket簡介
7.2.3網路編程
實驗7.2Socket相關程序設計
7.3嵌入式藍牙技術
7.3.1藍牙技術
7.3.2藍牙體系結構
7.3.3藍牙通信網路
7.3.4LinuxBluetooth軟體層
7.3.5USB適配器
實驗7.3藍牙相關實驗
7.4CAN匯流排
7.4.1CAN匯流排簡介
7.4.2CAN匯流排硬體特徵
7.4.3CAN控制器驅動
實驗7.4CAN匯流排實驗
第8章嵌入式系統硬體設計基礎與標准
8.1嵌入式系統的硬體組成
8.1.1嵌入式微處理器
8.1.2存儲器
8.1.3輸入/輸出設備
8.1.4通信與擴展介面
8.2硬體設計基礎知識
8.2.1計算機體系結構
8.2.2電子技術
8.2.3抗干擾技術
8.2.4印製電路板
8.3硬體設計中應注意的一些問題
8.3.1IC元件的選擇
8.3.2元件封裝設計
8.3.3PCB設計精度
8.3.4分離元件的正確使用
8.3.5高速PCB設計方法
8.3.6PCB設計的一般原則
實驗8.1常用模擬電路和數字電路原理
實驗8.22410—S電路原理圖閱讀
實驗8.32410—S所用晶元數據手冊閱讀
實驗8.4OMAP5910核心板電路原理
綜合實驗六基於OMAP的加密終端硬體設計
第9章OMAP5910與LinuxGateway
9.1OMAP5910體系結構
9.1.1MPU子系統
9.1.2DSP子系統
9.2LinuxDSPGateway
9.2.1DSPGateway的由來
9.2.2DSPGateway的Mailbox機制
9.2.3通信緩沖
9.2.4Mailbox命令協議
9.2.5DSPGateway的設備介面
實驗9.1OMAP910雙核間基本通信
9.3OMAP5910圖像處理
9.3.1圖片格式
9.3.2數字圖像演算法
實驗9.2OMAP圖像處理實驗
綜合實驗七基於OMAP的加密終端的實現(軟體部分)
第10章嵌入式Linux綜合項目實例
10.1基於嵌入式平台的電梯監控系統
10.1.1系統概述
10.1.2系統設計
10.1.3系統實現
10.1.4項目小結
10.2基於藍牙技術的嵌入式點菜系統
10.2.1系統概述
10.2.2系統設計
10.2.3系統實現
10.2.4項目小結
10.3基於WebSenrice的數字油田監控系統
10.3.1系統概述
10.3.2系統設計
10.3.3系統實現
10.3.4項目小結
10.4基於嵌入式與WebService的智能家居系統
10.4.1系統概述
10.4.2系統設計
10.4.3系統實現
10.4.4項目小結
10.5基於OMAP的音頻與視頻處理
10.5.1概述
10.5.2MPEG壓縮
10.5.3音視頻數據在雙處理器間的傳輸模塊設計
10.5.4音頻處理方案設計
10.5.5視頻處理方案設計
10.5.6項目小結
參考文獻
……
❹ 交叉編譯時,如何鏈接指定路徑下的庫
在整個工程的configure.in文件中加入如下代碼:
#configure.in
if test x$CC = xgcc; then
#AC_PATH_PROG(BLKID, blkid, [], [$PATH:/sbin])
#AC_PATH_PROG(VOLID, vol_id, [], [$PATH:/lib/udev])
AC_MSG_WARN($BLKID --------------------------------------) #code only for test
AM_CONDITIONAL(MY_CROSS_COMPILE,false)
else
#AC_PATH_PROG(BLKID, blkid, [], [/home/user-name/ltib/rootfs/sbin])
#AC_PATH_PROG(VOLID, vol_id, [], [/home/user-name/ltib/rootfs/lib])
AC_MSG_WARN($BLKID ++++++++++++++++++++++++++++++++++++++) #code only for test
AM_CONDITIONAL(MY_CROSS_COMPILE,true)
fi
在需要blkid庫的Makefile.am文件中
if MY_CROSS_COMPILE
AM_CPPFLAGS = -include $(top_builddir)/config.h -I ../include \
-DLOCALEDIR=\"$(localedir)\" -I /home/user-name/ltib/rootfs/usr/include
AM_CFLAGS = -fsigned-char -I /home/user-name/ltib/rootfs/usr/include
else
AM_CPPFLAGS = -include $(top_builddir)/config.h -I ../include \
-DLOCALEDIR=\"$(localedir)\"
AM_CFLAGS = -fsigned-char
endif
lib_mount_la_LIBADD = $(LDADD_common)
LDADD_common =
if MY_CROSS_COMPILE
LDADD_common += -L$(LTIB_PATH)/rootfs/usr/lib -lblkid -luuid
else
LDADD_common += -lblkid -luuid
endif
❺ 如何手動創建一個設備節點,寫出主要命令及參數
Linux下生成驅動設備節點文件的方法有3個:1、手動mknod;2、利用devfs;3、利用udev
在剛開始寫Linux設備驅動程序的時候,很多時候都是利用mknod命令手動創建設備節點,實際上Linux內核為我們提供了一組函數,可以用來在模塊載入的時候自動在/dev目錄下創建相應設備節點,並在卸載模塊時刪除該節點。
在2.6.17以前,在/dev目錄下生成設備文件很容易,
devfs_mk_bdev
devfs_mk_cdev
devfs_mk_symlink
devfs_mk_dir
devfs_remove
這幾個是純devfs的api,2.6.17以前可用,但後來devfs被sysfs+udev的形式取代,同時期sysfs文件系統可以用的api:
class_device_create_file,在2.6.26以後也不行了,現在,使用的是device_create ,從2.6.18開始可用
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, const char *fmt, ...)
從2.6.26起又多了一個參數drvdata: the data to be added to the device for callbacks
不會用可以給此參數賦NULL
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
下面著重講解第三種方法udev
在驅動用加入對udev的支持主要做的就是:在驅動初始化的代碼里調用class_create(...)為該設備創建一個class,再為每個設備調用device_create(...)( 在2.6較早的內核中用class_device_create)創建對應的設備。
內核中定義的struct class結構體,顧名思義,一個struct class結構體類型變數對應一個類,內核同時提供了class_create(…)函數,可以用它來創建一個類,這個類存放於sysfs下面,一旦創建好了這個類,再調用 device_create(…)函數來在/dev目錄下創建相應的設備節點。這樣,載入模塊的時候,用戶空間中的udev會自動響應 device_create(…)函數,去/sysfs下尋找對應的類從而創建設備節點。
struct class和class_create(…) 以及device_create(…)都包含在在/include/linux/device.h中,使用的時候一定要包含這個頭文件,否則編譯器會報錯。
struct class定義在頭文件include/linux/device.h中
class_create(…)在/drivers/base/class.c中實現
device_create(…)函數在/drivers/base/core.c中實現
class_destroy(...),device_destroy(...)也在/drivers/base/core.c中實現調用過程類似如下:
static struct class *spidev_class;
/*-------------------------------------------------------------------------*/
static int __devinit spidev_probe(struct spi_device *spi)
{
....
dev =device_create(spidev_class, &spi->dev, spidev->devt,
spidev, "spidev%d.%d",
spi->master->bus_num, spi->chip_select);
...
}
static int __devexit spidev_remove(struct spi_device *spi)
{
......
device_destroy(spidev_class, spidev->devt);
.....
return 0;
}
static struct spi_driver spidev_spi = {
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
},
.probe = spidev_probe,
.remove = __devexit_p(spidev_remove),
};
/*-------------------------------------------------------------------------*/
static int __init spidev_init(void)
{
....
spidev_class =class_create(THIS_MODULE, "spidev");
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
return PTR_ERR(spidev_class);
}
....
}
mole_init(spidev_init);
static void __exit spidev_exit(void)
{
......
class_destroy(spidev_class);
......
}
mole_exit(spidev_exit);
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
下面以一個簡單字元設備驅動來展示如何使用這幾個函數
#include <linux/mole.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
int HELLO_MAJOR = 0;
int HELLO_MINOR = 0;
int NUMBER_OF_DEVICES = 2;
struct class *my_class;
//struct cdev cdev;
//dev_t devno;
struct hello_dev {
struct device *dev;
dev_t chrdev;
struct cdev cdev;
};
static struct hello_dev *my_hello_dev = NULL;
struct file_operations hello_fops = {
.owner = THIS_MODULE
};
static int __init hello_init (void)
{
int err = 0;
struct device *dev;
my_hello_dev = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);
if (NULL == my_hello_dev) {
printk("%s kzalloc failed!\n",__func__);
return -ENOMEM;
}
devno = MKDEV(HELLO_MAJOR, HELLO_MINOR);
if (HELLO_MAJOR)
err= register_chrdev_region(my_hello_dev->chrdev, 2, "memdev");
else
{
err = alloc_chrdev_region(&my_hello_dev->chrdev, 0, 2, "memdev");
HELLO_MAJOR = MAJOR(devno);
}
if (err) {
printk("%s alloc_chrdev_region failed!\n",__func__);
goto alloc_chrdev_err;
}
printk("MAJOR IS %d\n",HELLO_MAJOR);
cdev_init(&(my_hello_dev->cdev), &hello_fops);
my_hello_dev->cdev.owner = THIS_MODULE;
err = cdev_add(&(my_hello_dev->cdev), my_hello_dev->chrdev, 1);
if (err) {
printk("%s cdev_add failed!\n",__func__);
goto cdev_add_err;
}
printk (KERN_INFO "Character driver Registered\n");
my_class =class_create(THIS_MODULE,"hello_char_class"); //類名為hello_char_class
if(IS_ERR(my_class))
{
err = PTR_ERR(my_class);
printk("%s class_create failed!\n",__func__);
goto class_err;
}
dev = device_create(my_class,NULL,my_hello_dev->chrdev,NULL,"memdev%d",0); //設備名為memdev
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
gyro_err("%s device_create failed!\n",__func__);
goto device_err;
}
printk("hello mole initialization\n");
return 0;
device_err:
device_destroy(my_class, my_hello_dev->chrdev);
class_err:
cdev_del(my_hello_dev->chrdev);
cdev_add_err:
unregister_chrdev_region(my_hello_dev->chrdev, 1);
alloc_chrdev_err:
kfree(my_hello_dev);
return err;
}
static void __exit hello_exit (void)
{
cdev_del (&(my_hello_dev->cdev));
unregister_chrdev_region (my_hello_dev->chrdev,1);
device_destroy(my_class, devno); //delete device node under /dev//必須先刪除設備,再刪除class類
class_destroy(my_class); //delete class created by us
printk (KERN_INFO "char driver cleaned up\n");
}
mole_init (hello_init);
mole_exit (hello_exit);
MODULE_LICENSE ("GPL");
這樣,模塊載入後,就能在/dev目錄下找到memdev這個設備節點了。
例2:內核中的drivers/i2c/i2c-dev.c
在i2cdev_attach_adapter中調用device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
這樣在dev目錄就產生i2c-0 或i2c-1節點
接下來就是udev應用,udev是應用層的東西,udev需要內核sysfs和tmpfs的支持,sysfs為udev提供設備入口和uevent通道,tmpfs為udev設備文件提供存放空間
udev的源碼可以在去相關網站下載,然後就是對其在運行環境下的移植,指定交叉編譯環境,修改Makefile下的CROSS_COMPILE,如為mipsel-linux-,DESTDIR=xxx,或直接make CROSS_COMPILE=mipsel-linux-,DESTDIR=xxx 並install
把主要生成的udevd、udevstart拷貝rootfs下的/sbin/目錄內,udev的配置文件udev.conf和rules.d下的rules文件拷貝到rootfs下的/etc/目錄內
並在rootfs/etc/init.d/rcS中添加以下幾行:
echo 「Starting udevd...」
/sbin/udevd --daemon
/sbin/udevstart
(原rcS內容如下:
# mount filesystems
/bin/mount -t proc /proc /proc
/bin/mount -t sysfs sysfs /sys
/bin/mount -t tmpfs tmpfs /dev
# create necessary devices
/bin/mknod /dev/null c 1 3
/bin/mkdir /dev/pts
/bin/mount -t devpts devpts /dev/pts
/bin/mknod /dev/audio c 14 4
/bin/mknod /dev/ts c 10 16
)
這樣當系統啟動後,udevd和udevstart就會解析配置文件,並自動在/dev下創建設備節點文件