字元裝置驅動程式是嵌入式linux最基本,也是最常用的驅動程式,幾乎可以描述不涉及掛載檔案系統的所有裝置。
驅動程式的介面流程圖:
#include//核心編譯時的配置檔案,檔案裡面指向另乙個由make menuconfig自動生成的檔案autoconf.h
#include#include//關於模組驅動程式的標頭檔案
#include#include//中斷相關的標頭檔案
#include//系統定時器使用的標頭檔案,
#include#include#include#include//這個標頭檔案裡面定義了linux核心的版本,用於版本排查
#include#include#include
在系統內部,i/o裝置的訪問操作通過特定的入口點來進行,而這組特定的入口點恰恰是由裝置驅動程式提供的。通常這組裝置驅動程式介面是由結構file operations結構體向系統說明的,它定義在include/linux/fs.h 中。
linx 的裝置檔案是同硬體一一對應的,因而對裝置的操作可以通過對裝置檔案的操作來實現。而這些操作方式其實就是一些標準的系統呼叫,如 open())、read()、write0)、close()等。實際上,file operations就是把系統呼叫和驅動程式關聯起來的關鍵資料結構。這個結構的每乙個成員都對應著乙個系統呼叫。
當使用者程序利用系統呼叫對裝置檔案進行讀寫操作時,這些系統呼叫通過裝置的主裝置號和次裝置號來確定相應的裝置驅動程式,然後讀取 fle operations中相應的函式指標,接著把控制權交給函式,從而完成了linux裝置驅動程式的工作。
open入口
open 函式負責開啟裝置、準備i/o。任何時候對字元裝置檔案進行開啟操作,都會呼叫裝置的open入口點。所以,open函式必須對將要進行的i/o操作做好必要的準備工作,如清除緩衝區等。如果裝置是獨佔的,則open函式必須將裝置標記成忙狀態。
close入口
close函式負責關閉裝置的操作。當最後一次使用裝置完成後,呼叫close函式,關閉裝置檔案。獨佔裝置必須標記為可再次使用。
read入口
read函式負責從裝置上讀資料和命令,有緩衝區的i/o裝置操作一般是從緩衝區裡讀資料。
write入口
write函式負責往裝置上寫資料。同理,對於有緩衝區的i/o裝置操作,一般是把資料寫入緩衝區裡。對字元裝置檔案進行寫操作將呼叫 write函式。
ioctl入口
ioctl函式執行讀、寫之外的操作,主要實現對裝置的控制。
select入口
select 函式檢查裝置,看資料是否可讀或裝置是否可用於寫資料。系統呼叫在檢查與裝置檔案相關的檔案描述符時,使用select入口點。如果驅動程式沒有提供selet入口,select 操作將認為裝置已經準備好進行任何ivo操作。由於此入口點不常用,這裡就不做詳細介紹了,有興趣的讀者可參考相關文件。
如果裝置驅動程式沒有提供上述入口點中的某乙個,系統會用預設的子程式來代替,對於不同的系統,也還有一些其他的入口點。
模組載入函式通過 register_chrdev_region( ) 或 alloc_chrdev_region( )來靜態或者動態獲取裝置號;
通過 cdev_init( ) 建立cdev與 file_operations之間的連線,通過 cdev_add( ) 向系統新增乙個cdev以完成註冊;
模組解除安裝函式通過cdev_del( )來登出cdev,通過 unregister_chrdev_region( )來釋放裝置號;
#include #include #include #include #include #include #include #include #define success (0)
#define error (-1)
struct cdev *gdev;//使用cdev結構體來描述字元裝置
dev_t devnum;//通過其成員dev_t來定義裝置號(分為主、次裝置號)以確定字元裝置的唯一性
unsigned int subdevnum = 1;//請求的連續裝置個數
int reg_major = 500;//主裝置號
int reg_minor = 0; //次裝置號
char *buffer;
//定義核心相關操作函式open()、read()、write()
int hello_open(struct inode *p, struct file *f)
ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l)
ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l)
static struct file_operations dev_fops = ;
/*初始化函式*/
int hello_init(void)
else
printk(kern_info " hello driver init \n");
gdev = kzalloc(sizeof(struct cdev), gfp_kernel);
cdev_init(gdev, &dev_fops);
cdev_add(gdev, devnum, 1);
return 0;
}void __exit hello_exit(void)
/*模組操作相關*/
module_init(hello_init);
module_exit(hello_exit);
module_license("gpl");
makefile
ifneq ($(kernelrelease),)
obj-m := hello.o
else
kdir := /home/pi/raspberry/linux-rpi-5.4.y
all:
make -c $(kdir) m=$(pwd) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers modul*
endif
#include #include #include #include #include int main(int argc, char **ar**)
/*your code*/
close(fd);
return 0;
}
載入驅動
insmod hello.ko
建立裝置節點
mknod /dev/hello c 500 0
這裡的主次裝置號要驅動檔案中的定義相對應。可以通過ls -l /dev可以檢視主次裝置號
除了可以使用mknod建立裝置節點,linux核心也提供了一組函式,可以用來在模組載入的時候自動在/dev目錄下建立相應裝置節點,並在解除安裝模組時刪除該節點,當然前提條件是使用者空間移植了udev。
核心中定義了struct class結構體,顧名思義,乙個struct class結構體型別變數對應乙個類,核心同時提供了class_create(…)函式,可以用它來建立乙個類,這個類存放於sysfs下面,一旦建立好了這個類,再呼叫device_create(…)函式來在/dev目錄下建立相應的裝置節點。這樣,載入模組的時候,使用者空間中的udev會自動響應device_create(…)函式,去/sysfs下尋找對應的類從而建立裝置節點。
struct class *cls;
struct device *dev;
新增裝置
cls = class_create(this_module, "hello_cls");
if(is_err(cls))
dev = device_create(cls, null, mkdev(reg_major, 0), null, "hello");
if(is_err(dev))
釋放裝置
class_destroy(cls);
device_destroy(cls, mkdev(reg_major, 0));
驅動 linux裝置驅動 字元裝置驅動開發
preface 前面對linux裝置驅動的相應知識點進行了總結,現在進入實踐階段!linux 裝置驅動入門篇 linux 裝置驅動掃盲篇 fedora下的字元裝置驅動開發 開發乙個基本的字元裝置驅動 在linux核心驅動中,字元裝置是最基本的裝置驅動。字元裝置包括了裝置最基本的操作,如開啟裝置 關閉...
Linux裝置驅動之《字元裝置驅動》
linux裝置中最大的特點就是裝置操作猶如檔案操作一般,在應用層看來,硬體裝置只是乙個裝置檔案。應用程式可以像操作檔案一樣對硬體裝置進行操作,如open close read write 等。下面是乙個字元裝置驅動程式的簡單實現test.c 模組分析 1.初始化裝置驅動的結構體 struct fil...
Linux裝置驅動之字元裝置驅動
一 linux裝置的分類 linux系統將裝置分成三種基本型別,每個模組通常實現為其中某一類 字元模組 塊模組或網路模組。這三種型別有 字元裝置 字元裝置是個能夠像位元組流 類似檔案 一樣被訪問的裝置,由字元裝置驅動程式來實現這種特性。字元裝置可以通過檔案系統節點來訪問,比如 dev tty1等。這...